From a85bdb6ddab7753f4762bc75f6083c09b9325cc2 Mon Sep 17 00:00:00 2001 From: Mo Zhou Date: Tue, 31 Dec 2019 08:30:40 +0000 Subject: [PATCH] Import julia_1.3.1+dfsg.orig.tar.xz [dgit import orig julia_1.3.1+dfsg.orig.tar.xz] --- .appveyor.yml | 65 + .gitattributes | 2 + .github/CODE_OF_CONDUCT.md | 23 + .github/FUNDING.yml | 1 + .github/ISSUE_TEMPLATE.md | 9 + .github/SECURITY.md | 15 + .github/SUPPORT.md | 12 + .gitignore | 31 + .mailmap | 259 + .travis.yml | 153 + CITATION.bib | 75 + CONTRIBUTING.md | 326 + HISTORY.md | 5162 +++++++++++ LICENSE.md | 85 + Make.inc | 1410 +++ Makefile | 618 ++ NEWS.md | 175 + README.md | 149 + VERSION | 1 + base/.gitignore | 9 + base/Base.jl | 408 + base/Enums.jl | 206 + base/Makefile | 243 + base/abstractarray.jl | 2228 +++++ base/abstractarraymath.jl | 457 + base/abstractdict.jl | 735 ++ base/abstractset.jl | 377 + base/accumulate.jl | 391 + base/array.jl | 2451 ++++++ base/arraymath.jl | 303 + base/arrayshow.jl | 488 + base/asyncevent.jl | 292 + base/asyncmap.jl | 421 + base/atomics.jl | 450 + base/baseext.jl | 35 + base/bitarray.jl | 1816 ++++ base/bitset.jl | 419 + base/bool.jl | 118 + base/boot.jl | 746 ++ base/broadcast.jl | 1241 +++ base/c.jl | 505 ++ base/cartesian.jl | 404 + base/channels.jl | 470 + base/char.jl | 315 + base/checked.jl | 352 + base/client.jl | 468 + base/cmd.jl | 388 + base/combinatorics.jl | 299 + base/compiler/abstractinterpretation.jl | 1288 +++ base/compiler/bootstrap.jl | 33 + base/compiler/compiler.jl | 115 + base/compiler/inferenceresult.jl | 169 + base/compiler/inferencestate.jl | 286 + base/compiler/optimize.jl | 444 + base/compiler/params.jl | 72 + base/compiler/ssair/domtree.jl | 327 + base/compiler/ssair/driver.jl | 133 + base/compiler/ssair/inlining.jl | 1314 +++ base/compiler/ssair/ir.jl | 1316 +++ base/compiler/ssair/legacy.jl | 100 + base/compiler/ssair/passes.jl | 1148 +++ base/compiler/ssair/queries.jl | 88 + base/compiler/ssair/show.jl | 753 ++ base/compiler/ssair/slot2ssa.jl | 870 ++ base/compiler/ssair/verify.jl | 213 + base/compiler/tfuncs.jl | 1482 ++++ base/compiler/typeinfer.jl | 649 ++ base/compiler/typelattice.jl | 326 + base/compiler/typelimits.jl | 490 ++ base/compiler/typeutils.jl | 174 + base/compiler/utilities.jl | 235 + base/compiler/validation.jl | 236 + base/complex.jl | 1026 +++ base/condition.jl | 163 + base/coreio.jl | 32 + base/ctypes.jl | 115 + base/deepcopy.jl | 116 + base/deprecated.jl | 199 + base/dict.jl | 794 ++ base/docs/Docs.jl | 606 ++ base/docs/basedocs.jl | 2246 +++++ base/docs/bindings.jl | 46 + base/docs/core.jl | 33 + base/docs/utils.jl | 93 + base/download.jl | 97 + base/env.jl | 168 + base/error.jl | 268 + base/errorshow.jl | 652 ++ base/essentials.jl | 837 ++ base/experimental.jl | 52 + base/exports.jl | 985 +++ base/expr.jl | 429 + base/fastmath.jl | 380 + base/file.jl | 910 ++ base/filesystem.jl | 248 + base/float.jl | 905 ++ base/floatfuncs.jl | 333 + base/gcutils.jl | 97 + base/generator.jl | 128 + base/gmp.jl | 726 ++ base/grisu/bignum.jl | 256 + base/grisu/bignums.jl | 495 ++ base/grisu/fastfixed.jl | 252 + base/grisu/fastprecision.jl | 99 + base/grisu/fastshortest.jl | 118 + base/grisu/float.jl | 258 + base/grisu/grisu.jl | 241 + base/hashing.jl | 77 + base/hashing2.jl | 181 + base/indices.jl | 465 + base/initdefs.jl | 342 + base/int.jl | 876 ++ base/intfuncs.jl | 908 ++ base/io.jl | 1109 +++ base/iobuffer.jl | 521 ++ base/iostream.jl | 494 ++ base/irrationals.jl | 189 + base/iterators.jl | 1098 +++ base/libc.jl | 396 + base/libuv.jl | 148 + base/linked_list.jl | 151 + base/loading.jl | 1515 ++++ base/lock.jl | 314 + base/locks-mt.jl | 66 + base/logging.jl | 550 ++ base/math.jl | 1142 +++ base/mathconstants.jl | 98 + base/meta.jl | 353 + base/methodshow.jl | 429 + base/missing.jl | 405 + base/mpfr.jl | 1053 +++ base/multidimensional.jl | 1748 ++++ base/multimedia.jl | 412 + base/multinverses.jl | 159 + base/namedtuple.jl | 314 + base/ntuple.jl | 69 + base/number.jl | 344 + base/operators.jl | 1131 +++ base/options.jl | 82 + base/ordering.jl | 78 + base/osutils.jl | 38 + base/pair.jl | 75 + base/parse.jl | 382 + base/path.jl | 467 + base/pcre.jl | 222 + base/permuteddimsarray.jl | 260 + base/pointer.jl | 161 + base/printf.jl | 1254 +++ base/process.jl | 644 ++ base/promotion.jl | 416 + base/range.jl | 1065 +++ base/rational.jl | 451 + base/reduce.jl | 775 ++ base/reducedim.jl | 882 ++ base/reflection.jl | 1353 +++ base/refpointer.jl | 134 + base/refvalue.jl | 33 + base/regex.jl | 712 ++ base/reinterpretarray.jl | 326 + base/reshapedarray.jl | 281 + base/rounding.jl | 249 + base/secretbuffer.jl | 188 + base/set.jl | 641 ++ base/shell.jl | 255 + base/show.jl | 2012 +++++ base/simdloop.jl | 139 + base/some.jl | 95 + base/sort.jl | 1137 +++ base/special/cbrt.jl | 149 + base/special/exp.jl | 138 + base/special/exp10.jl | 139 + base/special/hyperbolic.jl | 300 + base/special/ldexp_exp.jl | 105 + base/special/log.jl | 397 + base/special/rem_pio2.jl | 326 + base/special/trig.jl | 1125 +++ base/stacktraces.jl | 308 + base/stat.jl | 346 + base/stream.jl | 1304 +++ base/strings/basic.jl | 713 ++ base/strings/io.jl | 651 ++ base/strings/search.jl | 533 ++ base/strings/string.jl | 363 + base/strings/strings.jl | 10 + base/strings/substring.jl | 215 + base/strings/unicode.jl | 684 ++ base/strings/util.jl | 636 ++ base/subarray.jl | 422 + base/summarysize.jl | 181 + base/sysimg.jl | 97 + base/sysinfo.jl | 518 ++ base/task.jl | 678 ++ base/threadcall.jl | 100 + base/threadingconstructs.jl | 124 + base/threads.jl | 35 + base/traits.jl | 59 + base/tuple.jl | 411 + base/twiceprecision.jl | 710 ++ base/util.jl | 821 ++ base/uuid.jl | 72 + base/version.jl | 296 + base/version_git.sh | 94 + base/views.jl | 215 + base/weakkeydict.jl | 123 + contrib/README.md | 35 + contrib/add_license_to_files.jl | 198 + contrib/check-whitespace.sh | 37 + contrib/commit-name.sh | 43 + contrib/debug_bootstrap.gdb | 3 + contrib/delete-all-rpaths.sh | 33 + contrib/download_cmake.sh | 40 + contrib/filterArgs.sh | 10 + contrib/fixup-libgfortran.sh | 150 + contrib/fixup-libstdc++.sh | 34 + contrib/fixup-rpath.sh | 34 + contrib/generate_precompile.jl | 180 + contrib/install.sh | 35 + contrib/julia-config.jl | 136 + contrib/julia.appdata.xml | 35 + contrib/julia.desktop | 8 + contrib/julia.svg | 1 + contrib/mac/app/.gitignore | 3 + contrib/mac/app/Makefile | 68 + contrib/mac/app/README.md | 16 + contrib/mac/app/julia.icns | Bin 0 -> 164450 bytes contrib/mac/app/startup.applescript | 5 + contrib/mac/framework/Julia.h | 6 + contrib/mac/framework/Makefile | 189 + contrib/mac/framework/julia.entitlements | 8 + contrib/mac/framework/module.modulemap | 5 + .../ExecSandbox/ExecSandbox.entitlements | 12 + .../frameworkapp/ExecSandbox/ExecSandbox.h | 7 + .../frameworkapp/ExecSandbox/ExecSandbox.m | 149 + .../ExecSandbox/ExecSandboxProtocol.h | 31 + .../ExecSandbox/ExecSandboxProtocol.m | 14 + .../mac/frameworkapp/ExecSandbox/Info.plist | 31 + contrib/mac/frameworkapp/ExecSandbox/main.m | 28 + contrib/mac/frameworkapp/Julia.dist | 26 + .../JuliaLauncher.xcodeproj/project.pbxproj | 487 + .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcschemes/JuliaLauncher.xcscheme | 91 + .../frameworkapp/JuliaLauncher/AppDelegate.h | 6 + .../frameworkapp/JuliaLauncher/AppDelegate.m | 536 ++ .../AppIcon.appiconset/128.png | Bin 0 -> 7291 bytes .../AppIcon.appiconset/128@2x.png | Bin 0 -> 16904 bytes .../Assets.xcassets/AppIcon.appiconset/16.png | Bin 0 -> 703 bytes .../AppIcon.appiconset/16@2x.png | Bin 0 -> 1469 bytes .../AppIcon.appiconset/256.png | Bin 0 -> 16262 bytes .../AppIcon.appiconset/256@2x.png | Bin 0 -> 40480 bytes .../Assets.xcassets/AppIcon.appiconset/32.png | Bin 0 -> 1475 bytes .../AppIcon.appiconset/32@2x.png | Bin 0 -> 3368 bytes .../AppIcon.appiconset/512.png | Bin 0 -> 38856 bytes .../AppIcon.appiconset/512@2x.png | Bin 0 -> 95367 bytes .../AppIcon.appiconset/Contents.json | 68 + .../Assets.xcassets/Contents.json | 6 + .../JuliaLauncher/Base.lproj/MainMenu.xib | 679 ++ .../mac/frameworkapp/JuliaLauncher/Info.plist | 63 + .../JuliaLauncher/JuliaLauncher.entitlements | 10 + .../frameworkapp/JuliaLauncher/julia.idraw | Bin 0 -> 72288 bytes contrib/mac/frameworkapp/JuliaLauncher/main.m | 7 + contrib/mac/frameworkapp/Makefile | 124 + contrib/mac/frameworkapp/README.md | 26 + .../frameworkapp/framework-component.plist | 18 + .../installresources/conclusion.rtf | 80 + .../installresources/logo_hires.png | Bin 0 -> 26833 bytes .../frameworkapp/installresources/readme.rtf | 31 + contrib/mac/juliarc.jl | 9 + contrib/mac/mac-gtk.sh | 35 + contrib/normalize_triplet.py | 138 + contrib/prepare_release.sh | 106 + contrib/refresh_bb_tarballs.sh | 48 + contrib/relative_path.sh | 31 + contrib/stringreplace.c | 34 + contrib/travis_fastfail.sh | 28 + contrib/valgrind-julia.supp | 10 + deps/.gitignore | 3 + deps/Makefile | 204 + deps/NATIVE.cmake | 4 + deps/SuiteSparse_wrapper.c | 41 + deps/Versions.make | 43 + deps/blas.mk | 220 + deps/checksums/7z1900-x64.exe/md5 | 1 + deps/checksums/7z1900-x64.exe/sha512 | 1 + deps/checksums/7z1900.exe/md5 | 1 + deps/checksums/7z1900.exe/sha512 | 1 + .../GMP.v6.1.2-3.aarch64-linux-gnu.tar.gz/md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../GMP.v6.1.2-3.i686-linux-gnu.tar.gz/md5 | 1 + .../GMP.v6.1.2-3.i686-linux-gnu.tar.gz/sha512 | 1 + .../GMP.v6.1.2-3.i686-linux-musl.tar.gz/md5 | 1 + .../sha512 | 1 + .../GMP.v6.1.2-3.i686-w64-mingw32.tar.gz/md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../GMP.v6.1.2-3.x86_64-linux-gnu.tar.gz/md5 | 1 + .../sha512 | 1 + .../GMP.v6.1.2-3.x86_64-linux-musl.tar.gz/md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../MPFR.v4.0.2-0.i686-linux-gnu.tar.gz/md5 | 1 + .../sha512 | 1 + .../MPFR.v4.0.2-0.i686-linux-musl.tar.gz/md5 | 1 + .../sha512 | 1 + .../MPFR.v4.0.2-0.i686-w64-mingw32.tar.gz/md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../MPFR.v4.0.2-0.x86_64-linux-gnu.tar.gz/md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + deps/checksums/SuiteSparse-5.4.0.tar.gz/md5 | 1 + .../checksums/SuiteSparse-5.4.0.tar.gz/sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + deps/checksums/UnicodeData.txt/md5 | 1 + deps/checksums/UnicodeData.txt/sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../Zlib.v1.2.11-6.i686-linux-gnu.tar.gz/md5 | 1 + .../sha512 | 1 + .../Zlib.v1.2.11-6.i686-linux-musl.tar.gz/md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + deps/checksums/arpack-ng-3.3.0-testA.mtx/md5 | 1 + .../arpack-ng-3.3.0-testA.mtx/sha512 | 1 + deps/checksums/arpack-ng-3.3.0.tar.gz/md5 | 1 + deps/checksums/arpack-ng-3.3.0.tar.gz/sha512 | 1 + deps/checksums/cacert-2019-10-16.pem/md5 | 1 + deps/checksums/cacert-2019-10-16.pem/sha512 | 1 + deps/checksums/cfe-6.0.0.src.tar.xz/md5 | 1 + deps/checksums/cfe-6.0.0.src.tar.xz/sha512 | 1 + .../compiler-rt-6.0.0.src.tar.xz/md5 | 1 + .../compiler-rt-6.0.0.src.tar.xz/sha512 | 1 + deps/checksums/curl-7.61.0.tar.bz2/md5 | 1 + deps/checksums/curl-7.61.0.tar.bz2/sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../dSFMT.v2.2.3-0.i686-linux-gnu.tar.gz/md5 | 1 + .../sha512 | 1 + .../dSFMT.v2.2.3-0.i686-linux-musl.tar.gz/md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + deps/checksums/dsfmt-2.2.3.tar.gz/md5 | 1 + deps/checksums/dsfmt-2.2.3.tar.gz/sha512 | 1 + deps/checksums/gmp-6.1.2.tar.bz2/md5 | 1 + deps/checksums/gmp-6.1.2.tar.bz2/sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + deps/checksums/lapack-3.5.0.tgz/md5 | 1 + deps/checksums/lapack-3.5.0.tgz/sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + deps/checksums/libosxunwind-0.0.5.tar.gz/md5 | 1 + .../libosxunwind-0.0.5.tar.gz/sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + deps/checksums/libunwind-1.3.1.tar.gz/md5 | 1 + deps/checksums/libunwind-1.3.1.tar.gz/sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + deps/checksums/llvm-6.0.1.src.tar.xz/md5 | 1 + deps/checksums/llvm-6.0.1.src.tar.xz/sha512 | 1 + deps/checksums/llvm-7.0.1.src.tar.xz/md5 | 1 + deps/checksums/llvm-7.0.1.src.tar.xz/sha512 | 1 + deps/checksums/llvm-8.0.0.src.tar.xz/md5 | 1 + deps/checksums/llvm-8.0.0.src.tar.xz/sha512 | 1 + deps/checksums/mbedtls-2.16.0-apache.tgz/md5 | 1 + .../mbedtls-2.16.0-apache.tgz/sha512 | 1 + deps/checksums/mbedtls-2.16.0-gpl.tgz/md5 | 1 + deps/checksums/mbedtls-2.16.0-gpl.tgz/sha512 | 1 + deps/checksums/mpfr-4.0.2.tar.bz2/md5 | 1 + deps/checksums/mpfr-4.0.2.tar.bz2/sha512 | 1 + deps/checksums/nsis-3.04-setup.exe/md5 | 1 + deps/checksums/nsis-3.04-setup.exe/sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + deps/checksums/p7zip-16.2.0.tar.bz2/md5 | 1 + deps/checksums/p7zip-16.2.0.tar.bz2/sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../p7zip.v16.2.0-1.i686-linux-gnu.tar.gz/md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + deps/checksums/patchelf-0.9.tar.gz/md5 | 1 + deps/checksums/patchelf-0.9.tar.gz/sha512 | 1 + deps/checksums/pcre2-10.31.tar.bz2/md5 | 1 + deps/checksums/pcre2-10.31.tar.bz2/sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + deps/curl.mk | 77 + deps/dsfmt.mk | 72 + deps/gfortblas.alias | 50 + deps/gfortblas.c | 122 + deps/gmp.mk | 85 + deps/libdSFMT.def | 30 + deps/libgit2.mk | 120 + deps/libgit2.version | 2 + deps/libssh2.mk | 71 + deps/libssh2.version | 2 + deps/libuv.mk | 71 + deps/libuv.version | 2 + deps/libwhich.mk | 36 + deps/libwhich.version | 2 + deps/llvm-options.mk | 25 + deps/llvm-ver.make | 12 + deps/llvm.mk | 573 ++ deps/mbedtls.mk | 98 + deps/mpfr.mk | 82 + deps/objconv.mk | 43 + deps/openblas.version | 2 + deps/openlibm.mk | 38 + deps/openlibm.version | 2 + deps/p7zip.mk | 65 + deps/patchelf.mk | 48 + deps/patches/SuiteSparse-shlib.patch | 43 + deps/patches/SuiteSparse-winclang.patch | 14 + deps/patches/clang-D28477.patch | 13 + deps/patches/config.sub | 1791 ++++ deps/patches/dSFMT.c.patch | 100 + deps/patches/dSFMT.h.patch | 362 + deps/patches/gmp-config-ldflags.patch | 381 + deps/patches/gmp-exception.patch | 35 + deps/patches/gmp_alloc_overflow_func.patch | 193 + deps/patches/libgit2-agent-nonfatal.patch | 25 + deps/patches/libunwind-prefer-extbl.patch | 144 + deps/patches/libunwind-static-arm.patch | 13 + deps/patches/llvm-6.0-D44650.patch | 13 + .../llvm-6.0-D63688-wasm-isLocal.patch | 36 + .../patches/llvm-6.0-D64032-cmake-cross.patch | 69 + .../llvm-6.0-D64225-cmake-cross2.patch | 37 + .../patches/llvm-6.0-DISABLE_ABI_CHECKS.patch | 39 + deps/patches/llvm-6.0-NVPTX-addrspaces.patch | 32 + deps/patches/llvm-6.0-r327540.patch | 79 + deps/patches/llvm-6.0.0-ifconv-D45819.patch | 158 + deps/patches/llvm-6.0.0_D27296-libssp.patch | 35 + deps/patches/llvm-7.0-D44650.patch | 13 + deps/patches/llvm-7.0-D50167-scev-umin.patch | 1861 ++++ deps/patches/llvm-8.0-D50167-scev-umin.patch | 1870 ++++ .../llvm-D27629-AArch64-large_model_4.0.patch | 77 + ...lvm-D27629-AArch64-large_model_6.0.1.patch | 53 + deps/patches/llvm-D34078-vectorize-fdiv.patch | 56 + deps/patches/llvm-D42260.patch | 140 + .../llvm-D42262-jumpthreading-not-i1.patch | 82 + .../llvm-D44892-Perf-integration.patch | 677 ++ deps/patches/llvm-D45008.patch | 70 + deps/patches/llvm-D45070.patch | 28 + deps/patches/llvm-D46460.patch | 26 + deps/patches/llvm-D49832-SCEVPred.patch | 187 + deps/patches/llvm-D50010-VNCoercion-ni.patch | 89 + deps/patches/llvm-D50167-scev-umin.patch | 1153 +++ deps/patches/llvm-D51842-win64-byval-cc.patch | 164 + deps/patches/llvm-D57118-powerpc.patch | 30 + deps/patches/llvm-OProfile-line-num.patch | 48 + deps/patches/llvm-PPC-addrspaces.patch | 29 + deps/patches/llvm-r355582-avxminmax.patch | 48 + deps/patches/llvm-rL323946-LSRTy.patch | 45 + .../llvm-rL326843-missing-header.patch | 31 + deps/patches/llvm-rL326967-aligned-load.patch | 301 + deps/patches/llvm-rL327898.patch | 6131 +++++++++++++ deps/patches/llvm-rL332302.patch | 84 + deps/patches/llvm-rL332680.patch | 68 + deps/patches/llvm-rL332682.patch | 137 + deps/patches/llvm-rL332694.patch | 38 + deps/patches/llvm-rL349068-llvm-config.patch | 86 + deps/patches/llvm-symver-jlprefix.patch | 18 + deps/patches/llvm-windows-race.patch | 25 + deps/patches/llvm6-WASM-addrspaces.patch | 45 + deps/patches/llvm7-D50010-VNCoercion-ni.patch | 67 + .../patches/llvm7-D51842-win64-byval-cc.patch | 90 + deps/patches/llvm7-WASM-addrspaces.patch | 45 + deps/patches/llvm7-symver-jlprefix.patch | 18 + deps/patches/llvm7-windows-race.patch | 15 + .../patches/llvm8-D34078-vectorize-fdiv.patch | 42 + deps/patches/llvm8-WASM-addrspaces.patch | 45 + deps/patches/openblas-skylakexdgemm.patch | 22 + deps/patches/p7zip-12-CVE-2016-9296.patch | 23 + deps/patches/p7zip-13-CVE-2017-17969.patch | 35 + ...7zip-15-Enhanced-encryption-strength.patch | 298 + deps/patches/p7zip-Windows_ErrorMsg.patch | 33 + deps/pcre.mk | 63 + deps/suitesparse.mk | 158 + deps/tools/bb-install.mk | 64 + deps/tools/common.mk | 228 + deps/tools/find_python2 | 16 + deps/tools/git-external.mk | 73 + deps/tools/jlchecksum | 108 + deps/tools/jldownload | 47 + deps/tools/stdlib-external.mk | 27 + deps/unwind.mk | 117 + deps/utf8proc.mk | 41 + deps/utf8proc.version | 2 + deps/valgrind/valgrind.h | 6587 ++++++++++++++ deps/zlib.mk | 37 + deps/zlib.version | 2 + doc/.gitignore | 6 + doc/Makefile | 54 + doc/Manifest.toml | 95 + doc/NEWS-update.jl | 19 + doc/Project.toml | 3 + doc/README.md | 30 + doc/build/README.md | 25 + doc/build/arm.md | 87 + doc/build/build.md | 259 + doc/build/distributing.md | 589 ++ doc/build/freebsd.md | 19 + doc/build/linux.md | 22 + doc/build/macos.md | 7 + doc/build/windows.md | 316 + doc/images/github_metadata_develbranch.png | Bin 0 -> 65572 bytes doc/images/github_metadata_fork.png | Bin 0 -> 28113 bytes doc/images/github_metadata_pullrequest.png | Bin 0 -> 37536 bytes doc/images/jltypes.ai | 3455 ++++++++ doc/images/jltypes.svg | 687 ++ doc/images/travis-icon.png | Bin 0 -> 685 bytes doc/make.jl | 217 + doc/man/julia.1 | 213 + doc/src/assets/julia-manual.css | 11 + doc/src/assets/logo.png | Bin 0 -> 12311 bytes doc/src/base/arrays.md | 177 + doc/src/base/base.md | 417 + doc/src/base/c.md | 50 + doc/src/base/collections.md | 290 + doc/src/base/constants.md | 28 + doc/src/base/file.md | 66 + doc/src/base/io-network.md | 138 + doc/src/base/iterators.md | 18 + doc/src/base/libc.md | 18 + doc/src/base/math.md | 178 + doc/src/base/multi-threading.md | 40 + doc/src/base/numbers.md | 129 + doc/src/base/parallel.md | 55 + doc/src/base/punctuation.md | 47 + doc/src/base/simd-types.md | 35 + doc/src/base/sort.md | 189 + doc/src/base/stacktraces.md | 15 + doc/src/base/strings.md | 83 + doc/src/devdocs/ast.md | 711 ++ doc/src/devdocs/backtraces.md | 114 + doc/src/devdocs/boundscheck.md | 91 + doc/src/devdocs/callconv.md | 43 + doc/src/devdocs/cartesian.md | 140 + doc/src/devdocs/compiler.md | 115 + doc/src/devdocs/debuggingtips.md | 242 + doc/src/devdocs/eval.md | 182 + doc/src/devdocs/functions.md | 258 + doc/src/devdocs/gc-sa.md | 303 + doc/src/devdocs/inference.md | 122 + doc/src/devdocs/init.md | 230 + doc/src/devdocs/isbitsunionarrays.md | 13 + doc/src/devdocs/llvm.md | 331 + doc/src/devdocs/locks.md | 146 + doc/src/devdocs/meta.md | 48 + doc/src/devdocs/object.md | 202 + doc/src/devdocs/offset-arrays.md | 209 + doc/src/devdocs/reflection.md | 153 + doc/src/devdocs/require.md | 32 + doc/src/devdocs/sanitizers.md | 33 + doc/src/devdocs/ssair.md | 188 + doc/src/devdocs/stdio.md | 104 + doc/src/devdocs/subarrays.md | 243 + doc/src/devdocs/sysimg.md | 113 + doc/src/devdocs/types.md | 507 ++ doc/src/devdocs/valgrind.md | 63 + doc/src/index.md | 109 + doc/src/manual/arrays.md | 991 +++ doc/src/manual/calling-c-and-fortran-code.md | 1096 +++ doc/src/manual/code-loading.md | 354 + .../manual/complex-and-rational-numbers.md | 323 + doc/src/manual/constructors.md | 559 ++ doc/src/manual/control-flow.md | 987 +++ doc/src/manual/conversion-and-promotion.md | 350 + doc/src/manual/documentation.md | 568 ++ doc/src/manual/embedding.md | 553 ++ doc/src/manual/environment-variables.md | 337 + doc/src/manual/faq.md | 906 ++ doc/src/manual/functions.md | 855 ++ doc/src/manual/getting-started.md | 135 + .../handling-operating-system-variation.md | 41 + .../integers-and-floating-point-numbers.md | 719 ++ doc/src/manual/interfaces.md | 735 ++ doc/src/manual/mathematical-operations.md | 557 ++ doc/src/manual/metaprogramming.md | 1444 +++ doc/src/manual/methods.md | 1091 +++ doc/src/manual/missing.md | 393 + doc/src/manual/modules.md | 365 + doc/src/manual/networking-and-streams.md | 314 + doc/src/manual/noteworthy-differences.md | 332 + doc/src/manual/parallel-computing.md | 1891 ++++ doc/src/manual/performance-tips.md | 1523 ++++ doc/src/manual/profile.md | 344 + doc/src/manual/running-external-programs.md | 367 + doc/src/manual/stacktraces.md | 315 + doc/src/manual/strings.md | 1169 +++ doc/src/manual/style-guide.md | 415 + doc/src/manual/types.md | 1413 +++ doc/src/manual/unicode-input.md | 83 + doc/src/manual/variables-and-scoping.md | 574 ++ doc/src/manual/variables.md | 142 + doc/src/manual/workflow-tips.md | 134 + etc/startup.jl | 2 + etc/write_base_cache.jl | 12 + src/.gitignore | 22 + src/APInt-C.cpp | 533 ++ src/APInt-C.h | 89 + src/Makefile | 369 + src/abi_aarch64.cpp | 379 + src/abi_arm.cpp | 283 + src/abi_llvm.cpp | 58 + src/abi_ppc64le.cpp | 158 + src/abi_win32.cpp | 71 + src/abi_win64.cpp | 78 + src/abi_x86.cpp | 89 + src/abi_x86_64.cpp | 267 + src/anticodegen.c | 69 + src/array.c | 1275 +++ src/ast.c | 1211 +++ src/ast.scm | 499 ++ src/atomics.h | 289 + src/bin2hex.scm | 14 + src/builtin_proto.h | 44 + src/builtins.c | 1380 +++ src/ccall.cpp | 1982 +++++ src/ccalltest.c | 983 +++ src/cgmemmgr.cpp | 916 ++ src/cgutils.cpp | 2663 ++++++ src/clangsa/GCChecker.cpp | 1544 ++++ src/codegen.cpp | 7834 +++++++++++++++++ src/codegen_shared.h | 52 + src/common_symbols1.inc | 101 + src/common_symbols2.inc | 254 + src/crc32c-tables.c | 30 + src/crc32c.c | 595 ++ src/datatype.c | 1042 +++ src/debuginfo.cpp | 1678 ++++ src/debuginfo.h | 23 + src/disasm.cpp | 1026 +++ src/dlload.c | 279 + src/dump.c | 3393 +++++++ src/features_aarch32.h | 28 + src/features_aarch64.h | 25 + src/features_x86.h | 94 + src/file_constants.h | 28 + src/flisp/.gitignore | 12 + src/flisp/Makefile | 131 + src/flisp/aliases.scm | 77 + src/flisp/bootstrap.sh | 14 + src/flisp/builtins.c | 424 + src/flisp/color.lsp | 89 + src/flisp/compiler.lsp | 894 ++ src/flisp/cvalues.c | 1416 +++ src/flisp/equal.c | 384 + src/flisp/equalhash.c | 26 + src/flisp/equalhash.h | 16 + src/flisp/flisp.boot | 403 + src/flisp/flisp.c | 2491 ++++++ src/flisp/flisp.h | 513 ++ src/flisp/flmain.c | 79 + src/flisp/iostream.c | 464 + src/flisp/julia_charmap.h | 7 + src/flisp/julia_extensions.c | 361 + src/flisp/julia_opsuffs.h | 126 + src/flisp/mkboot0.lsp | 23 + src/flisp/mkboot1.lsp | 5 + src/flisp/opcodes.h | 102 + src/flisp/print.c | 818 ++ src/flisp/profile.scm | 72 + src/flisp/read.c | 726 ++ src/flisp/string.c | 300 + src/flisp/system.lsp | 1003 +++ src/flisp/table.c | 217 + src/flisp/types.c | 95 + src/flisp/unittest.lsp | 267 + src/gc-debug.c | 1379 +++ src/gc-pages.c | 314 + src/gc-stacks.c | 238 + src/gc.c | 3466 ++++++++ src/gc.h | 691 ++ src/gen_sysimg_symtab.jl | 73 + src/getopt.c | 147 + src/getopt.h | 56 + src/gf.c | 2748 ++++++ src/init.c | 904 ++ src/interpreter-stacktrace.c | 430 + src/interpreter.c | 936 ++ src/intrinsics.cpp | 1297 +++ src/intrinsics.h | 111 + src/jitlayers.cpp | 1216 +++ src/jitlayers.h | 209 + src/jl_uv.c | 1041 +++ src/jlapi.c | 460 + src/jlfrontend.scm | 227 + src/jloptions.c | 652 ++ src/jltypes.c | 2348 +++++ src/jsvm-emscripten/asyncify_setup.js | 144 + src/jsvm-emscripten/task.js | 15 + src/julia-parser.scm | 2465 ++++++ src/julia-syntax.scm | 4252 +++++++++ src/julia.expmap | 44 + src/julia.h | 2040 +++++ src/julia_assert.h | 27 + src/julia_gcext.h | 134 + src/julia_internal.h | 1076 +++ src/julia_threads.h | 312 + src/llvm-alloc-opt.cpp | 1505 ++++ src/llvm-api.cpp | 289 + src/llvm-final-gc-lowering.cpp | 357 + src/llvm-gc-invariant-verifier.cpp | 195 + src/llvm-late-gc-lowering.cpp | 2071 +++++ src/llvm-lower-handlers.cpp | 250 + src/llvm-muladd.cpp | 106 + src/llvm-multiversioning.cpp | 1088 +++ src/llvm-pass-helpers.cpp | 275 + src/llvm-pass-helpers.h | 157 + src/llvm-propagate-addrspaces.cpp | 302 + src/llvm-ptls.cpp | 290 + src/llvm-simdloop.cpp | 267 + src/llvm-version.h | 14 + src/llvmcalltest.cpp | 56 + src/locks.h | 184 + src/macroexpand.scm | 581 ++ src/match.scm | 247 + src/method.c | 788 ++ src/mk_julia_flisp_boot.scm | 5 + src/module.c | 773 ++ src/options.h | 182 + src/partr.c | 497 ++ src/precompile.c | 402 + src/processor.cpp | 850 ++ src/processor.h | 212 + src/processor_arm.cpp | 1440 +++ src/processor_fallback.cpp | 162 + src/processor_x86.cpp | 968 ++ src/rtutils.c | 1250 +++ src/runtime_ccall.cpp | 326 + src/runtime_intrinsics.c | 922 ++ src/safepoint.c | 225 + src/signal-handling.c | 289 + src/signals-mach.c | 519 ++ src/signals-unix.c | 822 ++ src/signals-win.c | 422 + src/simplevector.c | 91 + src/stackwalk.c | 541 ++ src/staticdata.c | 1645 ++++ src/subtype.c | 3741 ++++++++ src/support/.gitignore | 7 + src/support/END.h | 57 + src/support/ENTRY.amd64.h | 77 + src/support/ENTRY.i387.h | 76 + src/support/Makefile | 95 + src/support/MurmurHash3.c | 318 + src/support/MurmurHash3.h | 22 + src/support/_setjmp.win32.S | 159 + src/support/_setjmp.win64.S | 148 + src/support/analyzer_annotations.h | 49 + src/support/arraylist.c | 83 + src/support/arraylist.h | 32 + src/support/asprintf.c | 117 + src/support/bitvector.c | 61 + src/support/bitvector.h | 22 + src/support/dirname.c | 249 + src/support/dirpath.h | 20 + src/support/dtypes.h | 348 + src/support/hashing.c | 99 + src/support/hashing.h | 45 + src/support/htable.c | 67 + src/support/htable.h | 50 + src/support/htable.inc | 195 + src/support/int2str.c | 69 + src/support/ios.c | 1233 +++ src/support/ios.h | 224 + src/support/libsupport.h | 31 + src/support/libsupportinit.c | 29 + src/support/operators.c | 238 + src/support/platform.h | 119 + src/support/ptrhash.c | 30 + src/support/ptrhash.h | 18 + src/support/strptime.c | 822 ++ src/support/strtod.c | 288 + src/support/strtod.h | 17 + src/support/timefuncs.c | 73 + src/support/timefuncs.h | 23 + src/support/tzfile.h | 170 + src/support/utf8.c | 599 ++ src/support/utf8.h | 106 + src/support/utils.h | 63 + src/support/win32_ucontext.c | 89 + src/support/win32_ucontext.h | 27 + src/symbol.c | 148 + src/sys.c | 663 ++ src/table.c | 189 + src/task.c | 1127 +++ src/threading.c | 542 ++ src/threading.h | 35 + src/timing.c | 75 + src/timing.h | 165 + src/tls.h | 41 + src/toplevel.c | 907 ++ src/typemap.c | 1100 +++ src/utils.scm | 95 + src/uv_constants.h | 27 + stdlib/.gitignore | 3 + stdlib/Base64/Project.toml | 9 + stdlib/Base64/docs/src/index.md | 10 + stdlib/Base64/src/Base64.jl | 49 + stdlib/Base64/src/buffer.jl | 39 + stdlib/Base64/src/decode.jl | 218 + stdlib/Base64/src/encode.jl | 213 + stdlib/Base64/test/runtests.jl | 115 + stdlib/CRC32c/Project.toml | 9 + stdlib/CRC32c/docs/src/index.md | 6 + stdlib/CRC32c/src/CRC32c.jl | 52 + stdlib/CRC32c/test/runtests.jl | 64 + stdlib/Dates/Project.toml | 12 + stdlib/Dates/docs/src/index.md | 825 ++ stdlib/Dates/src/Dates.jl | 84 + stdlib/Dates/src/accessors.jl | 156 + stdlib/Dates/src/adjusters.jl | 392 + stdlib/Dates/src/arithmetic.jl | 88 + stdlib/Dates/src/conversions.jl | 121 + stdlib/Dates/src/deprecated.jl | 67 + stdlib/Dates/src/io.jl | 610 ++ stdlib/Dates/src/parse.jl | 325 + stdlib/Dates/src/periods.jl | 472 + stdlib/Dates/src/query.jl | 664 ++ stdlib/Dates/src/ranges.jl | 64 + stdlib/Dates/src/rounding.jl | 285 + stdlib/Dates/src/types.jl | 420 + stdlib/Dates/test/accessors.jl | 222 + stdlib/Dates/test/adjusters.jl | 502 ++ stdlib/Dates/test/arithmetic.jl | 482 + stdlib/Dates/test/conversions.jl | 126 + stdlib/Dates/test/io.jl | 539 ++ stdlib/Dates/test/periods.jl | 440 + stdlib/Dates/test/query.jl | 215 + stdlib/Dates/test/ranges.jl | 585 ++ stdlib/Dates/test/rounding.jl | 228 + stdlib/Dates/test/runtests.jl | 9 + stdlib/Dates/test/testgroups | 10 + stdlib/Dates/test/types.jl | 266 + stdlib/DelimitedFiles/Project.toml | 12 + stdlib/DelimitedFiles/docs/src/index.md | 11 + stdlib/DelimitedFiles/src/DelimitedFiles.jl | 831 ++ stdlib/DelimitedFiles/test/runtests.jl | 308 + stdlib/Distributed/Project.toml | 14 + stdlib/Distributed/docs/src/index.md | 68 + stdlib/Distributed/src/Distributed.jl | 100 + stdlib/Distributed/src/cluster.jl | 1324 +++ stdlib/Distributed/src/clusterserialize.jl | 254 + stdlib/Distributed/src/macros.jl | 357 + stdlib/Distributed/src/managers.jl | 537 ++ stdlib/Distributed/src/messages.jl | 219 + stdlib/Distributed/src/pmap.jl | 298 + stdlib/Distributed/src/precompile.jl | 212 + stdlib/Distributed/src/process_messages.jl | 397 + stdlib/Distributed/src/remotecall.jl | 641 ++ stdlib/Distributed/src/workerpool.jl | 356 + stdlib/Distributed/test/distributed_exec.jl | 1671 ++++ stdlib/Distributed/test/runtests.jl | 12 + stdlib/Distributed/test/topology.jl | 143 + stdlib/FileWatching/Project.toml | 8 + stdlib/FileWatching/docs/src/index.md | 9 + stdlib/FileWatching/src/FileWatching.jl | 801 ++ stdlib/FileWatching/test/runtests.jl | 444 + stdlib/Future/Project.toml | 12 + stdlib/Future/docs/src/index.md | 9 + stdlib/Future/src/Future.jl | 42 + stdlib/Future/test/runtests.jl | 4 + stdlib/InteractiveUtils/Project.toml | 12 + stdlib/InteractiveUtils/docs/src/index.md | 26 + .../InteractiveUtils/src/InteractiveUtils.jl | 332 + stdlib/InteractiveUtils/src/clipboard.jl | 132 + stdlib/InteractiveUtils/src/codeview.jl | 152 + stdlib/InteractiveUtils/src/editless.jl | 139 + stdlib/InteractiveUtils/src/macros.jl | 249 + stdlib/InteractiveUtils/test/runtests.jl | 391 + stdlib/LibGit2/Project.toml | 11 + stdlib/LibGit2/docs/src/index.md | 161 + stdlib/LibGit2/src/LibGit2.jl | 1010 +++ stdlib/LibGit2/src/blame.jl | 58 + stdlib/LibGit2/src/blob.jl | 87 + stdlib/LibGit2/src/callbacks.jl | 365 + stdlib/LibGit2/src/commit.jl | 147 + stdlib/LibGit2/src/config.jl | 239 + stdlib/LibGit2/src/consts.jl | 428 + stdlib/LibGit2/src/diff.jl | 142 + stdlib/LibGit2/src/error.jl | 105 + stdlib/LibGit2/src/gitcredential.jl | 311 + stdlib/LibGit2/src/index.jl | 218 + stdlib/LibGit2/src/merge.jl | 272 + stdlib/LibGit2/src/oid.jl | 220 + stdlib/LibGit2/src/rebase.jl | 119 + stdlib/LibGit2/src/reference.jl | 359 + stdlib/LibGit2/src/remote.jl | 416 + stdlib/LibGit2/src/repository.jl | 523 ++ stdlib/LibGit2/src/signature.jl | 72 + stdlib/LibGit2/src/status.jl | 57 + stdlib/LibGit2/src/strarray.jl | 15 + stdlib/LibGit2/src/tag.jl | 88 + stdlib/LibGit2/src/tree.jl | 193 + stdlib/LibGit2/src/types.jl | 1444 +++ stdlib/LibGit2/src/utils.jl | 177 + stdlib/LibGit2/src/walker.jl | 173 + stdlib/LibGit2/test/keys/invalid | 27 + stdlib/LibGit2/test/keys/invalid.pub | 1 + stdlib/LibGit2/test/keys/valid | 27 + stdlib/LibGit2/test/keys/valid-passphrase | 30 + stdlib/LibGit2/test/keys/valid-passphrase.pub | 1 + stdlib/LibGit2/test/keys/valid.pub | 1 + stdlib/LibGit2/test/libgit2-helpers.jl | 78 + stdlib/LibGit2/test/libgit2.jl | 3066 +++++++ stdlib/LibGit2/test/online.jl | 93 + stdlib/LibGit2/test/runtests.jl | 4 + stdlib/LibGit2/test/testgroups | 2 + stdlib/Libdl/Project.toml | 8 + stdlib/Libdl/docs/src/index.md | 15 + stdlib/Libdl/src/Libdl.jl | 309 + stdlib/Libdl/test/runtests.jl | 225 + stdlib/LinearAlgebra/Project.toml | 13 + stdlib/LinearAlgebra/docs/src/index.md | 670 ++ stdlib/LinearAlgebra/src/LinearAlgebra.jl | 451 + stdlib/LinearAlgebra/src/adjtrans.jl | 283 + stdlib/LinearAlgebra/src/bidiag.jl | 803 ++ stdlib/LinearAlgebra/src/bitarray.jl | 258 + stdlib/LinearAlgebra/src/blas.jl | 1630 ++++ stdlib/LinearAlgebra/src/bunchkaufman.jl | 434 + stdlib/LinearAlgebra/src/cholesky.jl | 727 ++ stdlib/LinearAlgebra/src/dense.jl | 1521 ++++ stdlib/LinearAlgebra/src/deprecated.jl | 7 + stdlib/LinearAlgebra/src/diagonal.jl | 666 ++ stdlib/LinearAlgebra/src/eigen.jl | 623 ++ stdlib/LinearAlgebra/src/exceptions.jl | 45 + stdlib/LinearAlgebra/src/factorization.jl | 159 + stdlib/LinearAlgebra/src/generic.jl | 1599 ++++ stdlib/LinearAlgebra/src/givens.jl | 400 + stdlib/LinearAlgebra/src/hessenberg.jl | 567 ++ stdlib/LinearAlgebra/src/lapack.jl | 6447 ++++++++++++++ stdlib/LinearAlgebra/src/ldlt.jl | 176 + stdlib/LinearAlgebra/src/lq.jl | 335 + stdlib/LinearAlgebra/src/lu.jl | 739 ++ stdlib/LinearAlgebra/src/matmul.jl | 974 ++ stdlib/LinearAlgebra/src/qr.jl | 908 ++ stdlib/LinearAlgebra/src/schur.jl | 349 + stdlib/LinearAlgebra/src/special.jl | 340 + .../LinearAlgebra/src/structuredbroadcast.jl | 191 + stdlib/LinearAlgebra/src/svd.jl | 618 ++ stdlib/LinearAlgebra/src/symmetric.jl | 929 ++ stdlib/LinearAlgebra/src/transpose.jl | 203 + stdlib/LinearAlgebra/src/triangular.jl | 2583 ++++++ stdlib/LinearAlgebra/src/tridiag.jl | 659 ++ stdlib/LinearAlgebra/src/uniformscaling.jl | 402 + stdlib/LinearAlgebra/test/addmul.jl | 221 + stdlib/LinearAlgebra/test/adjtrans.jl | 507 ++ stdlib/LinearAlgebra/test/ambiguous_exec.jl | 4 + stdlib/LinearAlgebra/test/bidiag.jl | 458 + stdlib/LinearAlgebra/test/blas.jl | 442 + stdlib/LinearAlgebra/test/bunchkaufman.jl | 158 + stdlib/LinearAlgebra/test/cholesky.jl | 395 + stdlib/LinearAlgebra/test/dense.jl | 906 ++ stdlib/LinearAlgebra/test/diagonal.jl | 598 ++ stdlib/LinearAlgebra/test/eigen.jl | 144 + stdlib/LinearAlgebra/test/generic.jl | 412 + stdlib/LinearAlgebra/test/givens.jl | 73 + stdlib/LinearAlgebra/test/hessenberg.jl | 99 + stdlib/LinearAlgebra/test/lapack.jl | 705 ++ stdlib/LinearAlgebra/test/lq.jl | 194 + stdlib/LinearAlgebra/test/lu.jl | 335 + stdlib/LinearAlgebra/test/matmul.jl | 600 ++ stdlib/LinearAlgebra/test/pinv.jl | 187 + stdlib/LinearAlgebra/test/qr.jl | 249 + stdlib/LinearAlgebra/test/runtests.jl | 5 + stdlib/LinearAlgebra/test/schur.jl | 129 + stdlib/LinearAlgebra/test/special.jl | 407 + .../LinearAlgebra/test/structuredbroadcast.jl | 126 + stdlib/LinearAlgebra/test/svd.jl | 178 + stdlib/LinearAlgebra/test/symmetric.jl | 608 ++ stdlib/LinearAlgebra/test/testgroups | 26 + stdlib/LinearAlgebra/test/testutils.jl | 27 + stdlib/LinearAlgebra/test/triangular.jl | 603 ++ stdlib/LinearAlgebra/test/trickyarithmetic.jl | 62 + stdlib/LinearAlgebra/test/tridiag.jl | 464 + stdlib/LinearAlgebra/test/uniformscaling.jl | 333 + stdlib/Logging/Project.toml | 8 + stdlib/Logging/docs/src/index.md | 288 + stdlib/Logging/src/ConsoleLogger.jl | 164 + stdlib/Logging/src/Logging.jl | 62 + stdlib/Logging/test/runtests.jl | 259 + stdlib/Makefile | 35 + stdlib/Markdown/Project.toml | 11 + stdlib/Markdown/docs/src/index.md | 385 + stdlib/Markdown/src/Common/Common.jl | 11 + stdlib/Markdown/src/Common/block.jl | 357 + stdlib/Markdown/src/Common/inline.jl | 187 + stdlib/Markdown/src/GitHub/GitHub.jl | 66 + stdlib/Markdown/src/GitHub/table.jl | 171 + stdlib/Markdown/src/IPython/IPython.jl | 35 + stdlib/Markdown/src/Julia/Julia.jl | 15 + stdlib/Markdown/src/Julia/interp.jl | 48 + stdlib/Markdown/src/Markdown.jl | 63 + stdlib/Markdown/src/parse/config.jl | 82 + stdlib/Markdown/src/parse/parse.jl | 98 + stdlib/Markdown/src/parse/util.jl | 206 + stdlib/Markdown/src/render/html.jl | 191 + stdlib/Markdown/src/render/latex.jl | 174 + stdlib/Markdown/src/render/plain.jl | 140 + stdlib/Markdown/src/render/rich.jl | 30 + stdlib/Markdown/src/render/rst.jl | 145 + .../src/render/terminal/formatting.jl | 45 + stdlib/Markdown/src/render/terminal/render.jl | 168 + stdlib/Markdown/test/runtests.jl | 1131 +++ stdlib/Mmap/Project.toml | 9 + stdlib/Mmap/docs/src/index.md | 7 + stdlib/Mmap/src/Mmap.jl | 346 + stdlib/Mmap/test/runtests.jl | 321 + stdlib/Pkg.version | 2 + stdlib/Printf/Project.toml | 11 + stdlib/Printf/docs/src/index.md | 6 + stdlib/Printf/src/Printf.jl | 69 + stdlib/Printf/test/runtests.jl | 298 + stdlib/Profile/Project.toml | 12 + stdlib/Profile/docs/src/index.md | 17 + stdlib/Profile/src/Profile.jl | 672 ++ stdlib/Profile/test/runtests.jl | 86 + stdlib/REPL/Project.toml | 14 + stdlib/REPL/docs/src/index.md | 532 ++ stdlib/REPL/src/LineEdit.jl | 2426 +++++ stdlib/REPL/src/REPL.jl | 1159 +++ stdlib/REPL/src/REPLCompletions.jl | 743 ++ stdlib/REPL/src/TerminalMenus/AbstractMenu.jl | 264 + stdlib/REPL/src/TerminalMenus/LICENSE.md | 22 + .../REPL/src/TerminalMenus/MultiSelectMenu.jl | 114 + stdlib/REPL/src/TerminalMenus/RadioMenu.jl | 79 + .../REPL/src/TerminalMenus/TerminalMenus.jl | 26 + stdlib/REPL/src/TerminalMenus/config.jl | 65 + stdlib/REPL/src/TerminalMenus/util.jl | 92 + stdlib/REPL/src/Terminals.jl | 180 + stdlib/REPL/src/docview.jl | 628 ++ stdlib/REPL/src/emoji_symbols.jl | 859 ++ stdlib/REPL/src/latex_symbols.jl | 2610 ++++++ stdlib/REPL/test/FakeTerminals.jl | 21 + .../test/TerminalMenus/multiselect_menu.jl | 37 + stdlib/REPL/test/TerminalMenus/radio_menu.jl | 40 + stdlib/REPL/test/TerminalMenus/runtests.jl | 44 + stdlib/REPL/test/lineedit.jl | 882 ++ stdlib/REPL/test/repl.jl | 1041 +++ stdlib/REPL/test/replcompletions.jl | 1015 +++ stdlib/REPL/test/runtests.jl | 14 + stdlib/Random/Project.toml | 14 + stdlib/Random/docs/src/index.md | 325 + stdlib/Random/src/DSFMT.jl | 267 + stdlib/Random/src/RNGs.jl | 614 ++ stdlib/Random/src/Random.jl | 417 + stdlib/Random/src/generation.jl | 483 + stdlib/Random/src/misc.jl | 382 + stdlib/Random/src/normal.jl | 683 ++ stdlib/Random/test/runtests.jl | 776 ++ stdlib/SHA/LICENSE.md | 58 + stdlib/SHA/Project.toml | 8 + stdlib/SHA/docs/src/index.md | 47 + stdlib/SHA/src/SHA.jl | 90 + stdlib/SHA/src/base_functions.jl | 42 + stdlib/SHA/src/common.jl | 81 + stdlib/SHA/src/constants.jl | 131 + stdlib/SHA/src/hmac.jl | 35 + stdlib/SHA/src/sha1.jl | 95 + stdlib/SHA/src/sha2.jl | 136 + stdlib/SHA/src/sha3.jl | 83 + stdlib/SHA/src/types.jl | 155 + stdlib/SHA/test/perf.jl | 47 + stdlib/SHA/test/runtests.jl | 302 + stdlib/Serialization/Project.toml | 10 + stdlib/Serialization/docs/src/index.md | 7 + stdlib/Serialization/src/Serialization.jl | 1355 +++ stdlib/Serialization/src/precompile.jl | 40 + stdlib/Serialization/test/runtests.jl | 564 ++ stdlib/SharedArrays/Project.toml | 14 + stdlib/SharedArrays/docs/src/index.md | 11 + stdlib/SharedArrays/src/SharedArrays.jl | 699 ++ stdlib/SharedArrays/test/runtests.jl | 316 + stdlib/Sockets/Project.toml | 9 + stdlib/Sockets/docs/src/index.md | 31 + stdlib/Sockets/src/IPAddr.jl | 312 + stdlib/Sockets/src/PipeServer.jl | 97 + stdlib/Sockets/src/Sockets.jl | 804 ++ stdlib/Sockets/src/addrinfo.jl | 314 + stdlib/Sockets/test/nettest.jl | 183 + stdlib/Sockets/test/runtests.jl | 533 ++ stdlib/SparseArrays/Project.toml | 14 + stdlib/SparseArrays/docs/src/index.md | 232 + stdlib/SparseArrays/src/SparseArrays.jl | 61 + stdlib/SparseArrays/src/abstractsparse.jl | 115 + stdlib/SparseArrays/src/deprecated.jl | 5 + stdlib/SparseArrays/src/higherorderfns.jl | 1157 +++ stdlib/SparseArrays/src/linalg.jl | 1503 ++++ stdlib/SparseArrays/src/sparseconvert.jl | 285 + stdlib/SparseArrays/src/sparsematrix.jl | 3650 ++++++++ stdlib/SparseArrays/src/sparsevector.jl | 2088 +++++ stdlib/SparseArrays/test/ambiguous_exec.jl | 4 + stdlib/SparseArrays/test/higherorderfns.jl | 694 ++ stdlib/SparseArrays/test/runtests.jl | 5 + stdlib/SparseArrays/test/sparse.jl | 2719 ++++++ stdlib/SparseArrays/test/sparsevector.jl | 1370 +++ stdlib/SparseArrays/test/testgroups | 3 + stdlib/Statistics/Project.toml | 13 + stdlib/Statistics/docs/src/index.md | 19 + stdlib/Statistics/src/Statistics.jl | 1058 +++ stdlib/Statistics/test/runtests.jl | 728 ++ stdlib/SuiteSparse/Project.toml | 17 + stdlib/SuiteSparse/src/SuiteSparse.jl | 31 + stdlib/SuiteSparse/src/cholmod.jl | 1839 ++++ stdlib/SuiteSparse/src/cholmod_h.jl | 79 + stdlib/SuiteSparse/src/deprecated.jl | 1 + stdlib/SuiteSparse/src/spqr.jl | 429 + stdlib/SuiteSparse/src/umfpack.jl | 572 ++ stdlib/SuiteSparse/src/umfpack_h.jl | 43 + stdlib/SuiteSparse/test/cholmod.jl | 854 ++ stdlib/SuiteSparse/test/runtests.jl | 10 + stdlib/SuiteSparse/test/spqr.jl | 110 + stdlib/SuiteSparse/test/umfpack.jl | 192 + stdlib/Test/Project.toml | 8 + stdlib/Test/docs/src/index.md | 269 + stdlib/Test/src/Test.jl | 1721 ++++ stdlib/Test/src/logging.jl | 258 + stdlib/Test/test/runtests.jl | 920 ++ stdlib/UUIDs/Project.toml | 12 + stdlib/UUIDs/docs/src/index.md | 8 + stdlib/UUIDs/src/UUIDs.jl | 132 + stdlib/UUIDs/test/runtests.jl | 56 + stdlib/Unicode/Project.toml | 9 + stdlib/Unicode/docs/src/index.md | 7 + stdlib/Unicode/src/Unicode.jl | 84 + stdlib/Unicode/test/runtests.jl | 406 + sysimage.mk | 86 + test/.gitignore | 4 + test/Makefile | 38 + test/TestPkg/Manifest.toml | 2 + test/TestPkg/Project.toml | 6 + test/TestPkg/src/TestPkg.jl | 7 + test/abstractarray.jl | 997 +++ test/ambiguous.jl | 308 + test/arrayops.jl | 2670 ++++++ test/asyncmap.jl | 63 + test/backtrace.jl | 222 + test/bigint.jl | 472 + test/bitarray.jl | 1635 ++++ test/bitset.jl | 350 + test/boundscheck.jl | 18 + test/boundscheck_exec.jl | 255 + test/broadcast.jl | 856 ++ test/cartesian.jl | 66 + test/ccall.jl | 1548 ++++ test/channels.jl | 458 + test/char.jl | 292 + test/checked.jl | 336 + test/choosetests.jl | 187 + test/clangsa/GCPushPop.cpp | 36 + test/clangsa/MissingRoots.c | 350 + test/client.jl | 37 + test/cmdlineargs.jl | 637 ++ test/combinatorics.jl | 102 + test/compiler/codegen.jl | 401 + test/compiler/contextual.jl | 137 + test/compiler/inference.jl | 2447 +++++ test/compiler/inline.jl | 267 + test/compiler/interpreter_exec.jl | 107 + test/compiler/irpasses.jl | 257 + test/compiler/ssair.jl | 165 + test/compiler/validation.jl | 132 + test/complex.jl | 1150 +++ test/copy.jl | 195 + test/core.jl | 7082 +++++++++++++++ test/depot/packages/Baz/81oLe/src/Baz.jl | 6 + test/depot/packages/Foo/I05Qq/src/Foo.jl | 7 + test/deprecation_exec.jl | 122 + test/dict.jl | 1074 +++ test/docs.jl | 1161 +++ test/download.jl | 56 + test/embedding/.gitignore | 2 + test/embedding/LocalModule.jl | 15 + test/embedding/Makefile | 60 + test/embedding/embedding-test.jl | 32 + test/embedding/embedding.c | 181 + test/embedding/include_and_eval.jl | 5 + test/enums.jl | 169 + test/env.jl | 104 + test/error.jl | 83 + test/errorshow.jl | 580 ++ test/euler.jl | 655 ++ test/exceptions.jl | 302 + test/fastmath.jl | 237 + test/file.jl | 1386 +++ test/float16.jl | 182 + test/floatapprox.jl | 72 + test/floatfuncs.jl | 102 + test/functional.jl | 221 + test/gcext/.gitignore | 2 + test/gcext/LocalTest.jl | 102 + test/gcext/Makefile | 60 + test/gcext/gcext-test.jl | 42 + test/gcext/gcext.c | 672 ++ test/generic_map_tests.jl | 81 + test/goto.jl | 170 + test/grisu.jl | 1764 ++++ test/hashing.jl | 245 + test/int.jl | 307 + test/interpreter.jl | 32 + test/intfuncs.jl | 302 + test/intrinsics.jl | 108 + test/iobuffer.jl | 322 + test/iostream.jl | 111 + test/iterators.jl | 649 ++ test/keywordargs.jl | 355 + test/llvmcall.jl | 251 + test/llvmcall2.jl | 38 + test/llvmpasses/.gitignore | 1 + test/llvmpasses/Makefile | 8 + test/llvmpasses/aliasscopes.jl | 61 + test/llvmpasses/alloc-opt.jl | 264 + test/llvmpasses/alloc-opt2.jl | 92 + test/llvmpasses/final-lower-gc.ll | 73 + test/llvmpasses/gcroots.ll | 524 ++ test/llvmpasses/late-lower-gc.ll | 94 + test/llvmpasses/lit.cfg | 18 + test/llvmpasses/loopinfo.jl | 138 + test/llvmpasses/lower-handlers.ll | 25 + test/llvmpasses/muladd.ll | 28 + test/llvmpasses/noinline.jl | 21 + test/llvmpasses/propagate-addrspace.ll | 60 + test/llvmpasses/refinements.ll | 220 + test/llvmpasses/returnstwicegc.ll | 30 + test/llvmpasses/safepoint_stress.jl | 27 + test/llvmpasses/simdloop.ll | 95 + test/loading.jl | 655 ++ test/logging.jl | 355 + test/math.jl | 1026 +++ test/meta.jl | 212 + test/misc.jl | 758 ++ test/missing.jl | 529 ++ test/mod2pi.jl | 268 + test/mpfr.jl | 983 +++ test/namedtuple.jl | 269 + test/netload/memtest.jl | 62 + test/numbers.jl | 2636 ++++++ test/offsetarray.jl | 565 ++ test/operators.jl | 215 + test/osutils.jl | 57 + test/parse.jl | 354 + test/path.jl | 270 + test/precompile.jl | 784 ++ test/project/Manifest.toml | 28 + test/project/Project.toml | 6 + test/project/deps/Bar/src/Bar.jl | 6 + test/project/deps/Foo1/src/Foo.jl | 7 + test/project/deps/Foo2.jl/src/Foo.jl | 6 + test/project/deps/Qux.jl | 5 + test/ranges.jl | 1548 ++++ test/rational.jl | 379 + test/read.jl | 576 ++ test/reduce.jl | 535 ++ test/reducedim.jl | 400 + test/reflection.jl | 892 ++ test/regex.jl | 142 + test/reinterpretarray.jl | 202 + test/rounding.jl | 328 + test/runtests.jl | 307 + test/secretbuffer.jl | 75 + test/sets.jl | 668 ++ test/show.jl | 1555 ++++ test/simdloop.jl | 176 + test/some.jl | 100 + test/sorting.jl | 401 + test/spawn.jl | 677 ++ test/specificity.jl | 308 + test/stack_overflow.jl | 19 + test/stacktraces.jl | 170 + test/staged.jl | 294 + test/stress.jl | 106 + test/strings/basic.jl | 985 +++ test/strings/io.jl | 271 + test/strings/search.jl | 384 + test/strings/types.jl | 333 + test/strings/util.jl | 385 + test/subarray.jl | 624 ++ test/subtype.jl | 1687 ++++ test/syntax.jl | 1949 ++++ test/sysinfo.jl | 8 + test/test_exec.jl | 5 + test/test_sourcepath.jl | 23 + test/testdefs.jl | 47 + test/testenv.jl | 35 + test/testhelpers/FakePTYs.jl | 65 + test/testhelpers/Furlongs.jl | 81 + test/testhelpers/MacroCalls.jl | 17 + test/testhelpers/OffsetArrays.jl | 145 + test/testhelpers/PhysQuantities.jl | 26 + test/testhelpers/Quaternions.jl | 39 + test/testhelpers/coverage_file.info | 10 + test/testhelpers/coverage_file.jl | 17 + test/testhelpers/llvmpasses.jl | 27 + test/threads.jl | 15 + test/threads_exec.jl | 703 ++ test/triplequote.jl | 68 + test/tuple.jl | 453 + test/unicode/utf8.jl | 38 + test/util/segfault.jl | 3 + test/util/throw_error_exception.jl | 3 + test/vecelement.jl | 121 + test/version.jl | 229 + test/worlds.jl | 201 + ui/.gitignore | 5 + ui/Makefile | 94 + ui/repl.c | 224 + 1934 files changed, 464487 insertions(+) create mode 100644 .appveyor.yml create mode 100644 .gitattributes create mode 100644 .github/CODE_OF_CONDUCT.md create mode 100644 .github/FUNDING.yml create mode 100644 .github/ISSUE_TEMPLATE.md create mode 100644 .github/SECURITY.md create mode 100644 .github/SUPPORT.md create mode 100644 .gitignore create mode 100644 .mailmap create mode 100644 .travis.yml create mode 100644 CITATION.bib create mode 100644 CONTRIBUTING.md create mode 100644 HISTORY.md create mode 100644 LICENSE.md create mode 100644 Make.inc create mode 100644 Makefile create mode 100644 NEWS.md create mode 100644 README.md create mode 100644 VERSION create mode 100644 base/.gitignore create mode 100644 base/Base.jl create mode 100644 base/Enums.jl create mode 100644 base/Makefile create mode 100644 base/abstractarray.jl create mode 100644 base/abstractarraymath.jl create mode 100644 base/abstractdict.jl create mode 100644 base/abstractset.jl create mode 100644 base/accumulate.jl create mode 100644 base/array.jl create mode 100644 base/arraymath.jl create mode 100644 base/arrayshow.jl create mode 100644 base/asyncevent.jl create mode 100644 base/asyncmap.jl create mode 100644 base/atomics.jl create mode 100644 base/baseext.jl create mode 100644 base/bitarray.jl create mode 100644 base/bitset.jl create mode 100644 base/bool.jl create mode 100644 base/boot.jl create mode 100644 base/broadcast.jl create mode 100644 base/c.jl create mode 100644 base/cartesian.jl create mode 100644 base/channels.jl create mode 100644 base/char.jl create mode 100644 base/checked.jl create mode 100644 base/client.jl create mode 100644 base/cmd.jl create mode 100644 base/combinatorics.jl create mode 100644 base/compiler/abstractinterpretation.jl create mode 100644 base/compiler/bootstrap.jl create mode 100644 base/compiler/compiler.jl create mode 100644 base/compiler/inferenceresult.jl create mode 100644 base/compiler/inferencestate.jl create mode 100644 base/compiler/optimize.jl create mode 100644 base/compiler/params.jl create mode 100644 base/compiler/ssair/domtree.jl create mode 100644 base/compiler/ssair/driver.jl create mode 100644 base/compiler/ssair/inlining.jl create mode 100644 base/compiler/ssair/ir.jl create mode 100644 base/compiler/ssair/legacy.jl create mode 100644 base/compiler/ssair/passes.jl create mode 100644 base/compiler/ssair/queries.jl create mode 100644 base/compiler/ssair/show.jl create mode 100644 base/compiler/ssair/slot2ssa.jl create mode 100644 base/compiler/ssair/verify.jl create mode 100644 base/compiler/tfuncs.jl create mode 100644 base/compiler/typeinfer.jl create mode 100644 base/compiler/typelattice.jl create mode 100644 base/compiler/typelimits.jl create mode 100644 base/compiler/typeutils.jl create mode 100644 base/compiler/utilities.jl create mode 100644 base/compiler/validation.jl create mode 100644 base/complex.jl create mode 100644 base/condition.jl create mode 100644 base/coreio.jl create mode 100644 base/ctypes.jl create mode 100644 base/deepcopy.jl create mode 100644 base/deprecated.jl create mode 100644 base/dict.jl create mode 100644 base/docs/Docs.jl create mode 100644 base/docs/basedocs.jl create mode 100644 base/docs/bindings.jl create mode 100644 base/docs/core.jl create mode 100644 base/docs/utils.jl create mode 100644 base/download.jl create mode 100644 base/env.jl create mode 100644 base/error.jl create mode 100644 base/errorshow.jl create mode 100644 base/essentials.jl create mode 100644 base/experimental.jl create mode 100644 base/exports.jl create mode 100644 base/expr.jl create mode 100644 base/fastmath.jl create mode 100644 base/file.jl create mode 100644 base/filesystem.jl create mode 100644 base/float.jl create mode 100644 base/floatfuncs.jl create mode 100644 base/gcutils.jl create mode 100644 base/generator.jl create mode 100644 base/gmp.jl create mode 100644 base/grisu/bignum.jl create mode 100644 base/grisu/bignums.jl create mode 100644 base/grisu/fastfixed.jl create mode 100644 base/grisu/fastprecision.jl create mode 100644 base/grisu/fastshortest.jl create mode 100644 base/grisu/float.jl create mode 100644 base/grisu/grisu.jl create mode 100644 base/hashing.jl create mode 100644 base/hashing2.jl create mode 100644 base/indices.jl create mode 100644 base/initdefs.jl create mode 100644 base/int.jl create mode 100644 base/intfuncs.jl create mode 100644 base/io.jl create mode 100644 base/iobuffer.jl create mode 100644 base/iostream.jl create mode 100644 base/irrationals.jl create mode 100644 base/iterators.jl create mode 100644 base/libc.jl create mode 100644 base/libuv.jl create mode 100644 base/linked_list.jl create mode 100644 base/loading.jl create mode 100644 base/lock.jl create mode 100644 base/locks-mt.jl create mode 100644 base/logging.jl create mode 100644 base/math.jl create mode 100644 base/mathconstants.jl create mode 100644 base/meta.jl create mode 100644 base/methodshow.jl create mode 100644 base/missing.jl create mode 100644 base/mpfr.jl create mode 100644 base/multidimensional.jl create mode 100644 base/multimedia.jl create mode 100644 base/multinverses.jl create mode 100644 base/namedtuple.jl create mode 100644 base/ntuple.jl create mode 100644 base/number.jl create mode 100644 base/operators.jl create mode 100644 base/options.jl create mode 100644 base/ordering.jl create mode 100644 base/osutils.jl create mode 100644 base/pair.jl create mode 100644 base/parse.jl create mode 100644 base/path.jl create mode 100644 base/pcre.jl create mode 100644 base/permuteddimsarray.jl create mode 100644 base/pointer.jl create mode 100644 base/printf.jl create mode 100644 base/process.jl create mode 100644 base/promotion.jl create mode 100644 base/range.jl create mode 100644 base/rational.jl create mode 100644 base/reduce.jl create mode 100644 base/reducedim.jl create mode 100644 base/reflection.jl create mode 100644 base/refpointer.jl create mode 100644 base/refvalue.jl create mode 100644 base/regex.jl create mode 100644 base/reinterpretarray.jl create mode 100644 base/reshapedarray.jl create mode 100644 base/rounding.jl create mode 100644 base/secretbuffer.jl create mode 100644 base/set.jl create mode 100644 base/shell.jl create mode 100644 base/show.jl create mode 100644 base/simdloop.jl create mode 100644 base/some.jl create mode 100644 base/sort.jl create mode 100644 base/special/cbrt.jl create mode 100644 base/special/exp.jl create mode 100644 base/special/exp10.jl create mode 100644 base/special/hyperbolic.jl create mode 100644 base/special/ldexp_exp.jl create mode 100644 base/special/log.jl create mode 100644 base/special/rem_pio2.jl create mode 100644 base/special/trig.jl create mode 100644 base/stacktraces.jl create mode 100644 base/stat.jl create mode 100644 base/stream.jl create mode 100644 base/strings/basic.jl create mode 100644 base/strings/io.jl create mode 100644 base/strings/search.jl create mode 100644 base/strings/string.jl create mode 100644 base/strings/strings.jl create mode 100644 base/strings/substring.jl create mode 100644 base/strings/unicode.jl create mode 100644 base/strings/util.jl create mode 100644 base/subarray.jl create mode 100644 base/summarysize.jl create mode 100644 base/sysimg.jl create mode 100644 base/sysinfo.jl create mode 100644 base/task.jl create mode 100644 base/threadcall.jl create mode 100644 base/threadingconstructs.jl create mode 100644 base/threads.jl create mode 100644 base/traits.jl create mode 100644 base/tuple.jl create mode 100644 base/twiceprecision.jl create mode 100644 base/util.jl create mode 100644 base/uuid.jl create mode 100644 base/version.jl create mode 100644 base/version_git.sh create mode 100644 base/views.jl create mode 100644 base/weakkeydict.jl create mode 100644 contrib/README.md create mode 100644 contrib/add_license_to_files.jl create mode 100755 contrib/check-whitespace.sh create mode 100755 contrib/commit-name.sh create mode 100644 contrib/debug_bootstrap.gdb create mode 100755 contrib/delete-all-rpaths.sh create mode 100755 contrib/download_cmake.sh create mode 100755 contrib/filterArgs.sh create mode 100755 contrib/fixup-libgfortran.sh create mode 100755 contrib/fixup-libstdc++.sh create mode 100755 contrib/fixup-rpath.sh create mode 100644 contrib/generate_precompile.jl create mode 100755 contrib/install.sh create mode 100755 contrib/julia-config.jl create mode 100644 contrib/julia.appdata.xml create mode 100644 contrib/julia.desktop create mode 100644 contrib/julia.svg create mode 100644 contrib/mac/app/.gitignore create mode 100644 contrib/mac/app/Makefile create mode 100644 contrib/mac/app/README.md create mode 100644 contrib/mac/app/julia.icns create mode 100644 contrib/mac/app/startup.applescript create mode 100644 contrib/mac/framework/Julia.h create mode 100644 contrib/mac/framework/Makefile create mode 100644 contrib/mac/framework/julia.entitlements create mode 100644 contrib/mac/framework/module.modulemap create mode 100644 contrib/mac/frameworkapp/ExecSandbox/ExecSandbox.entitlements create mode 100644 contrib/mac/frameworkapp/ExecSandbox/ExecSandbox.h create mode 100644 contrib/mac/frameworkapp/ExecSandbox/ExecSandbox.m create mode 100644 contrib/mac/frameworkapp/ExecSandbox/ExecSandboxProtocol.h create mode 100644 contrib/mac/frameworkapp/ExecSandbox/ExecSandboxProtocol.m create mode 100644 contrib/mac/frameworkapp/ExecSandbox/Info.plist create mode 100644 contrib/mac/frameworkapp/ExecSandbox/main.m create mode 100644 contrib/mac/frameworkapp/Julia.dist create mode 100644 contrib/mac/frameworkapp/JuliaLauncher.xcodeproj/project.pbxproj create mode 100644 contrib/mac/frameworkapp/JuliaLauncher.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 contrib/mac/frameworkapp/JuliaLauncher.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 contrib/mac/frameworkapp/JuliaLauncher.xcodeproj/xcshareddata/xcschemes/JuliaLauncher.xcscheme create mode 100644 contrib/mac/frameworkapp/JuliaLauncher/AppDelegate.h create mode 100644 contrib/mac/frameworkapp/JuliaLauncher/AppDelegate.m create mode 100644 contrib/mac/frameworkapp/JuliaLauncher/Assets.xcassets/AppIcon.appiconset/128.png create mode 100644 contrib/mac/frameworkapp/JuliaLauncher/Assets.xcassets/AppIcon.appiconset/128@2x.png create mode 100644 contrib/mac/frameworkapp/JuliaLauncher/Assets.xcassets/AppIcon.appiconset/16.png create mode 100644 contrib/mac/frameworkapp/JuliaLauncher/Assets.xcassets/AppIcon.appiconset/16@2x.png create mode 100644 contrib/mac/frameworkapp/JuliaLauncher/Assets.xcassets/AppIcon.appiconset/256.png create mode 100644 contrib/mac/frameworkapp/JuliaLauncher/Assets.xcassets/AppIcon.appiconset/256@2x.png create mode 100644 contrib/mac/frameworkapp/JuliaLauncher/Assets.xcassets/AppIcon.appiconset/32.png create mode 100644 contrib/mac/frameworkapp/JuliaLauncher/Assets.xcassets/AppIcon.appiconset/32@2x.png create mode 100644 contrib/mac/frameworkapp/JuliaLauncher/Assets.xcassets/AppIcon.appiconset/512.png create mode 100644 contrib/mac/frameworkapp/JuliaLauncher/Assets.xcassets/AppIcon.appiconset/512@2x.png create mode 100644 contrib/mac/frameworkapp/JuliaLauncher/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 contrib/mac/frameworkapp/JuliaLauncher/Assets.xcassets/Contents.json create mode 100644 contrib/mac/frameworkapp/JuliaLauncher/Base.lproj/MainMenu.xib create mode 100644 contrib/mac/frameworkapp/JuliaLauncher/Info.plist create mode 100644 contrib/mac/frameworkapp/JuliaLauncher/JuliaLauncher.entitlements create mode 100644 contrib/mac/frameworkapp/JuliaLauncher/julia.idraw create mode 100644 contrib/mac/frameworkapp/JuliaLauncher/main.m create mode 100644 contrib/mac/frameworkapp/Makefile create mode 100644 contrib/mac/frameworkapp/README.md create mode 100644 contrib/mac/frameworkapp/framework-component.plist create mode 100644 contrib/mac/frameworkapp/installresources/conclusion.rtf create mode 100644 contrib/mac/frameworkapp/installresources/logo_hires.png create mode 100644 contrib/mac/frameworkapp/installresources/readme.rtf create mode 100644 contrib/mac/juliarc.jl create mode 100644 contrib/mac/mac-gtk.sh create mode 100755 contrib/normalize_triplet.py create mode 100755 contrib/prepare_release.sh create mode 100755 contrib/refresh_bb_tarballs.sh create mode 100755 contrib/relative_path.sh create mode 100644 contrib/stringreplace.c create mode 100755 contrib/travis_fastfail.sh create mode 100644 contrib/valgrind-julia.supp create mode 100644 deps/.gitignore create mode 100644 deps/Makefile create mode 100644 deps/NATIVE.cmake create mode 100644 deps/SuiteSparse_wrapper.c create mode 100644 deps/Versions.make create mode 100644 deps/blas.mk create mode 100644 deps/checksums/7z1900-x64.exe/md5 create mode 100644 deps/checksums/7z1900-x64.exe/sha512 create mode 100755 deps/checksums/7z1900.exe/md5 create mode 100755 deps/checksums/7z1900.exe/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-3.aarch64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-3.aarch64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-3.aarch64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-3.aarch64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-3.arm-linux-gnueabihf.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-3.arm-linux-gnueabihf.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-3.arm-linux-musleabihf.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-3.arm-linux-musleabihf.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-3.i686-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-3.i686-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-3.i686-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-3.i686-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-3.i686-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-3.i686-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-3.powerpc64le-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-3.powerpc64le-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-3.x86_64-apple-darwin14.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-3.x86_64-apple-darwin14.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-3.x86_64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-3.x86_64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-3.x86_64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-3.x86_64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-3.x86_64-unknown-freebsd11.1.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-3.x86_64-unknown-freebsd11.1.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-3.x86_64-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-3.x86_64-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc4.tar.gz/md5 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc7.tar.gz/md5 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc8.tar.gz/md5 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc4.tar.gz/md5 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc7.tar.gz/md5 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc8.tar.gz/md5 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc4.tar.gz/md5 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc7.tar.gz/md5 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc8.tar.gz/md5 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc4.tar.gz/md5 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc7.tar.gz/md5 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc8.tar.gz/md5 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc4.tar.gz/md5 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc7.tar.gz/md5 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc8.tar.gz/md5 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc4.tar.gz/md5 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc7.tar.gz/md5 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc8.tar.gz/md5 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc4.tar.gz/md5 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc7.tar.gz/md5 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc8.tar.gz/md5 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc4.tar.gz/md5 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc7.tar.gz/md5 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc8.tar.gz/md5 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc4.tar.gz/md5 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc7.tar.gz/md5 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc8.tar.gz/md5 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc4.tar.gz/md5 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc7.tar.gz/md5 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc8.tar.gz/md5 create mode 100644 deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/LibCURL.v7.61.0-1.aarch64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibCURL.v7.61.0-1.aarch64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibCURL.v7.61.0-1.aarch64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/LibCURL.v7.61.0-1.aarch64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/LibCURL.v7.61.0-1.arm-linux-gnueabihf.tar.gz/md5 create mode 100644 deps/checksums/LibCURL.v7.61.0-1.arm-linux-gnueabihf.tar.gz/sha512 create mode 100644 deps/checksums/LibCURL.v7.61.0-1.arm-linux-musleabihf.tar.gz/md5 create mode 100644 deps/checksums/LibCURL.v7.61.0-1.arm-linux-musleabihf.tar.gz/sha512 create mode 100644 deps/checksums/LibCURL.v7.61.0-1.i686-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibCURL.v7.61.0-1.i686-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibCURL.v7.61.0-1.i686-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/LibCURL.v7.61.0-1.i686-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/LibCURL.v7.61.0-1.i686-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/LibCURL.v7.61.0-1.i686-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/LibCURL.v7.61.0-1.powerpc64le-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibCURL.v7.61.0-1.powerpc64le-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibCURL.v7.61.0-1.x86_64-apple-darwin14.tar.gz/md5 create mode 100644 deps/checksums/LibCURL.v7.61.0-1.x86_64-apple-darwin14.tar.gz/sha512 create mode 100644 deps/checksums/LibCURL.v7.61.0-1.x86_64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibCURL.v7.61.0-1.x86_64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibCURL.v7.61.0-1.x86_64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/LibCURL.v7.61.0-1.x86_64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/LibCURL.v7.61.0-1.x86_64-unknown-freebsd11.1.tar.gz/md5 create mode 100644 deps/checksums/LibCURL.v7.61.0-1.x86_64-unknown-freebsd11.1.tar.gz/sha512 create mode 100644 deps/checksums/LibCURL.v7.61.0-1.x86_64-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/LibCURL.v7.61.0-1.x86_64-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.2-0.aarch64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.2-0.aarch64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.2-0.aarch64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.2-0.aarch64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.2-0.arm-linux-gnueabihf.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.2-0.arm-linux-gnueabihf.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.2-0.arm-linux-musleabihf.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.2-0.arm-linux-musleabihf.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.2-0.i686-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.2-0.i686-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.2-0.i686-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.2-0.i686-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.2-0.i686-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.2-0.i686-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.2-0.powerpc64le-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.2-0.powerpc64le-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.2-0.x86_64-apple-darwin14.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.2-0.x86_64-apple-darwin14.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.2-0.x86_64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.2-0.x86_64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.2-0.x86_64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.2-0.x86_64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.2-0.x86_64-unknown-freebsd11.1.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.2-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.2-0.x86_64-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.2-0.x86_64-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/LibOSXUnwind.v0.0.5-0.x86_64-apple-darwin14.tar.gz/md5 create mode 100644 deps/checksums/LibOSXUnwind.v0.0.5-0.x86_64-apple-darwin14.tar.gz/sha512 create mode 100644 deps/checksums/LibOSXUnwind.v0.0.5.x86_64-apple-darwin14.tar.gz/md5 create mode 100644 deps/checksums/LibOSXUnwind.v0.0.5.x86_64-apple-darwin14.tar.gz/sha512 create mode 100644 deps/checksums/LibSSH2.v1.9.0-0.aarch64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibSSH2.v1.9.0-0.aarch64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibSSH2.v1.9.0-0.aarch64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/LibSSH2.v1.9.0-0.aarch64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/LibSSH2.v1.9.0-0.arm-linux-gnueabihf.tar.gz/md5 create mode 100644 deps/checksums/LibSSH2.v1.9.0-0.arm-linux-gnueabihf.tar.gz/sha512 create mode 100644 deps/checksums/LibSSH2.v1.9.0-0.arm-linux-musleabihf.tar.gz/md5 create mode 100644 deps/checksums/LibSSH2.v1.9.0-0.arm-linux-musleabihf.tar.gz/sha512 create mode 100644 deps/checksums/LibSSH2.v1.9.0-0.i686-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibSSH2.v1.9.0-0.i686-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibSSH2.v1.9.0-0.i686-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/LibSSH2.v1.9.0-0.i686-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/LibSSH2.v1.9.0-0.i686-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/LibSSH2.v1.9.0-0.i686-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/LibSSH2.v1.9.0-0.powerpc64le-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibSSH2.v1.9.0-0.powerpc64le-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibSSH2.v1.9.0-0.x86_64-apple-darwin14.tar.gz/md5 create mode 100644 deps/checksums/LibSSH2.v1.9.0-0.x86_64-apple-darwin14.tar.gz/sha512 create mode 100644 deps/checksums/LibSSH2.v1.9.0-0.x86_64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibSSH2.v1.9.0-0.x86_64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibSSH2.v1.9.0-0.x86_64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/LibSSH2.v1.9.0-0.x86_64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/LibSSH2.v1.9.0-0.x86_64-unknown-freebsd11.1.tar.gz/md5 create mode 100644 deps/checksums/LibSSH2.v1.9.0-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 create mode 100644 deps/checksums/LibSSH2.v1.9.0-0.x86_64-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/LibSSH2.v1.9.0-0.x86_64-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.aarch64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.aarch64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.aarch64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.aarch64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.arm-linux-gnueabihf.tar.gz/md5 create mode 100644 deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.arm-linux-gnueabihf.tar.gz/sha512 create mode 100644 deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.arm-linux-musleabihf.tar.gz/md5 create mode 100644 deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.arm-linux-musleabihf.tar.gz/sha512 create mode 100644 deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.i686-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.i686-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.i686-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.i686-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.i686-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.i686-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.powerpc64le-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.powerpc64le-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-apple-darwin14.tar.gz/md5 create mode 100644 deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-apple-darwin14.tar.gz/sha512 create mode 100644 deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-unknown-freebsd11.1.tar.gz/md5 create mode 100644 deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 create mode 100644 deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.aarch64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.aarch64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.aarch64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.aarch64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.arm-linux-gnueabihf.tar.gz/md5 create mode 100644 deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.arm-linux-gnueabihf.tar.gz/sha512 create mode 100644 deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.arm-linux-musleabihf.tar.gz/md5 create mode 100644 deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.arm-linux-musleabihf.tar.gz/sha512 create mode 100644 deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.i686-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.i686-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.i686-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.i686-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.i686-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.i686-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.powerpc64le-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.powerpc64le-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-apple-darwin14.tar.gz/md5 create mode 100644 deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-apple-darwin14.tar.gz/sha512 create mode 100644 deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-unknown-freebsd11.1.tar.gz/md5 create mode 100644 deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 create mode 100644 deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/LibUnwind.v1.3.1-4.aarch64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibUnwind.v1.3.1-4.aarch64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibUnwind.v1.3.1-4.aarch64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/LibUnwind.v1.3.1-4.aarch64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/LibUnwind.v1.3.1-4.arm-linux-gnueabihf.tar.gz/md5 create mode 100644 deps/checksums/LibUnwind.v1.3.1-4.arm-linux-gnueabihf.tar.gz/sha512 create mode 100644 deps/checksums/LibUnwind.v1.3.1-4.arm-linux-musleabihf.tar.gz/md5 create mode 100644 deps/checksums/LibUnwind.v1.3.1-4.arm-linux-musleabihf.tar.gz/sha512 create mode 100644 deps/checksums/LibUnwind.v1.3.1-4.i686-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibUnwind.v1.3.1-4.i686-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibUnwind.v1.3.1-4.i686-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/LibUnwind.v1.3.1-4.i686-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/LibUnwind.v1.3.1-4.powerpc64le-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibUnwind.v1.3.1-4.powerpc64le-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibUnwind.v1.3.1-4.x86_64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibUnwind.v1.3.1-4.x86_64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibUnwind.v1.3.1-4.x86_64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/LibUnwind.v1.3.1-4.x86_64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/LibUnwind.v1.3.1-4.x86_64-unknown-freebsd11.1.tar.gz/md5 create mode 100644 deps/checksums/LibUnwind.v1.3.1-4.x86_64-unknown-freebsd11.1.tar.gz/sha512 create mode 100644 deps/checksums/MPFR.v4.0.2-0.aarch64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/MPFR.v4.0.2-0.aarch64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/MPFR.v4.0.2-0.aarch64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/MPFR.v4.0.2-0.aarch64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/MPFR.v4.0.2-0.arm-linux-gnueabihf.tar.gz/md5 create mode 100644 deps/checksums/MPFR.v4.0.2-0.arm-linux-gnueabihf.tar.gz/sha512 create mode 100644 deps/checksums/MPFR.v4.0.2-0.arm-linux-musleabihf.tar.gz/md5 create mode 100644 deps/checksums/MPFR.v4.0.2-0.arm-linux-musleabihf.tar.gz/sha512 create mode 100644 deps/checksums/MPFR.v4.0.2-0.i686-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/MPFR.v4.0.2-0.i686-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/MPFR.v4.0.2-0.i686-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/MPFR.v4.0.2-0.i686-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/MPFR.v4.0.2-0.i686-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/MPFR.v4.0.2-0.i686-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/MPFR.v4.0.2-0.powerpc64le-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/MPFR.v4.0.2-0.powerpc64le-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/MPFR.v4.0.2-0.x86_64-apple-darwin14.tar.gz/md5 create mode 100644 deps/checksums/MPFR.v4.0.2-0.x86_64-apple-darwin14.tar.gz/sha512 create mode 100644 deps/checksums/MPFR.v4.0.2-0.x86_64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/MPFR.v4.0.2-0.x86_64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/MPFR.v4.0.2-0.x86_64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/MPFR.v4.0.2-0.x86_64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/MPFR.v4.0.2-0.x86_64-unknown-freebsd11.1.tar.gz/md5 create mode 100644 deps/checksums/MPFR.v4.0.2-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 create mode 100644 deps/checksums/MPFR.v4.0.2-0.x86_64-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/MPFR.v4.0.2-0.x86_64-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/MbedTLS.v2.16.0-v0.17.0.aarch64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/MbedTLS.v2.16.0-v0.17.0.aarch64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/MbedTLS.v2.16.0-v0.17.0.aarch64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/MbedTLS.v2.16.0-v0.17.0.aarch64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/MbedTLS.v2.16.0-v0.17.0.arm-linux-gnueabihf.tar.gz/md5 create mode 100644 deps/checksums/MbedTLS.v2.16.0-v0.17.0.arm-linux-gnueabihf.tar.gz/sha512 create mode 100644 deps/checksums/MbedTLS.v2.16.0-v0.17.0.arm-linux-musleabihf.tar.gz/md5 create mode 100644 deps/checksums/MbedTLS.v2.16.0-v0.17.0.arm-linux-musleabihf.tar.gz/sha512 create mode 100644 deps/checksums/MbedTLS.v2.16.0-v0.17.0.i686-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/MbedTLS.v2.16.0-v0.17.0.i686-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/MbedTLS.v2.16.0-v0.17.0.i686-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/MbedTLS.v2.16.0-v0.17.0.i686-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/MbedTLS.v2.16.0-v0.17.0.i686-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/MbedTLS.v2.16.0-v0.17.0.i686-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/MbedTLS.v2.16.0-v0.17.0.powerpc64le-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/MbedTLS.v2.16.0-v0.17.0.powerpc64le-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-apple-darwin14.tar.gz/md5 create mode 100644 deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-apple-darwin14.tar.gz/sha512 create mode 100644 deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-unknown-freebsd11.1.tar.gz/md5 create mode 100644 deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-unknown-freebsd11.1.tar.gz/sha512 create mode 100644 deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/Objconv.v2.49.0-0.aarch64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/Objconv.v2.49.0-0.aarch64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/Objconv.v2.49.0-0.aarch64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/Objconv.v2.49.0-0.aarch64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/Objconv.v2.49.0-0.arm-linux-gnueabihf.tar.gz/md5 create mode 100644 deps/checksums/Objconv.v2.49.0-0.arm-linux-gnueabihf.tar.gz/sha512 create mode 100644 deps/checksums/Objconv.v2.49.0-0.arm-linux-musleabihf.tar.gz/md5 create mode 100644 deps/checksums/Objconv.v2.49.0-0.arm-linux-musleabihf.tar.gz/sha512 create mode 100644 deps/checksums/Objconv.v2.49.0-0.i686-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/Objconv.v2.49.0-0.i686-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/Objconv.v2.49.0-0.i686-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/Objconv.v2.49.0-0.i686-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/Objconv.v2.49.0-0.i686-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/Objconv.v2.49.0-0.i686-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/Objconv.v2.49.0-0.powerpc64le-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/Objconv.v2.49.0-0.powerpc64le-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/Objconv.v2.49.0-0.x86_64-apple-darwin14.tar.gz/md5 create mode 100644 deps/checksums/Objconv.v2.49.0-0.x86_64-apple-darwin14.tar.gz/sha512 create mode 100644 deps/checksums/Objconv.v2.49.0-0.x86_64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/Objconv.v2.49.0-0.x86_64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/Objconv.v2.49.0-0.x86_64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/Objconv.v2.49.0-0.x86_64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/Objconv.v2.49.0-0.x86_64-unknown-freebsd11.1.tar.gz/md5 create mode 100644 deps/checksums/Objconv.v2.49.0-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 create mode 100644 deps/checksums/Objconv.v2.49.0-0.x86_64-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/Objconv.v2.49.0-0.x86_64-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-gnu-gcc4.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-gnu-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-gnu-gcc7.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-gnu-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-gnu-gcc8.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-gnu-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-musl-gcc4.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-musl-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-musl-gcc7.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-musl-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-musl-gcc8.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-musl-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-gnueabihf-gcc4.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-gnueabihf-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-gnueabihf-gcc7.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-gnueabihf-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-gnueabihf-gcc8.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-gnueabihf-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-musleabihf-gcc4.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-musleabihf-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-musleabihf-gcc7.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-musleabihf-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-musleabihf-gcc8.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-musleabihf-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-gnu-gcc4.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-gnu-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-gnu-gcc7.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-gnu-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-gnu-gcc8.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-gnu-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-musl-gcc4.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-musl-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-musl-gcc7.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-musl-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-musl-gcc8.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-musl-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.i686-w64-mingw32-gcc4.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.i686-w64-mingw32-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.i686-w64-mingw32-gcc7.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.i686-w64-mingw32-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.i686-w64-mingw32-gcc8.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.i686-w64-mingw32-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.powerpc64le-linux-gnu-gcc4.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.powerpc64le-linux-gnu-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.powerpc64le-linux-gnu-gcc7.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.powerpc64le-linux-gnu-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.powerpc64le-linux-gnu-gcc8.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.powerpc64le-linux-gnu-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.x86_64-apple-darwin14-gcc4.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.x86_64-apple-darwin14-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.x86_64-apple-darwin14-gcc7.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.x86_64-apple-darwin14-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.x86_64-apple-darwin14-gcc8.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.x86_64-apple-darwin14-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-gnu-gcc4.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-gnu-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-gnu-gcc7.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-gnu-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-gnu-gcc8.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-gnu-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-musl-gcc4.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-musl-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-musl-gcc7.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-musl-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-musl-gcc8.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-musl-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.x86_64-unknown-freebsd11.1-gcc4.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.x86_64-unknown-freebsd11.1-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.x86_64-unknown-freebsd11.1-gcc7.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.x86_64-unknown-freebsd11.1-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.x86_64-unknown-freebsd11.1-gcc8.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.x86_64-unknown-freebsd11.1-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.x86_64-w64-mingw32-gcc4.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.x86_64-w64-mingw32-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.x86_64-w64-mingw32-gcc7.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.x86_64-w64-mingw32-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.x86_64-w64-mingw32-gcc8.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.5-2.x86_64-w64-mingw32-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-gnu-gcc4.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-gnu-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-gnu-gcc7.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-gnu-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-gnu-gcc8.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-gnu-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-musl-gcc4.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-musl-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-musl-gcc7.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-musl-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-musl-gcc8.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-musl-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.arm-linux-gnueabihf-gcc4.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.arm-linux-gnueabihf-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.arm-linux-gnueabihf-gcc7.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.arm-linux-gnueabihf-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.arm-linux-gnueabihf-gcc8.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.arm-linux-gnueabihf-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.arm-linux-musleabihf-gcc4.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.arm-linux-musleabihf-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.arm-linux-musleabihf-gcc7.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.arm-linux-musleabihf-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.arm-linux-musleabihf-gcc8.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.arm-linux-musleabihf-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.i686-linux-gnu-gcc4.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.i686-linux-gnu-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.i686-linux-gnu-gcc7.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.i686-linux-gnu-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.i686-linux-gnu-gcc8.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.i686-linux-gnu-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.i686-linux-musl-gcc4.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.i686-linux-musl-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.i686-linux-musl-gcc7.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.i686-linux-musl-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.i686-linux-musl-gcc8.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.i686-linux-musl-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.i686-w64-mingw32-gcc4.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.i686-w64-mingw32-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.i686-w64-mingw32-gcc7.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.i686-w64-mingw32-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.i686-w64-mingw32-gcc8.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.i686-w64-mingw32-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.powerpc64le-linux-gnu-gcc4.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.powerpc64le-linux-gnu-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.powerpc64le-linux-gnu-gcc7.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.powerpc64le-linux-gnu-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.powerpc64le-linux-gnu-gcc8.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.powerpc64le-linux-gnu-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.x86_64-apple-darwin14-gcc4.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.x86_64-apple-darwin14-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.x86_64-apple-darwin14-gcc7.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.x86_64-apple-darwin14-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.x86_64-apple-darwin14-gcc8.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.x86_64-apple-darwin14-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-gnu-gcc4.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-gnu-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-gnu-gcc7.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-gnu-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-gnu-gcc8.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-gnu-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-musl-gcc4.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-musl-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-musl-gcc7.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-musl-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-musl-gcc8.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-musl-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.x86_64-unknown-freebsd11.1-gcc4.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.x86_64-unknown-freebsd11.1-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.x86_64-unknown-freebsd11.1-gcc7.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.x86_64-unknown-freebsd11.1-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.x86_64-unknown-freebsd11.1-gcc8.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.x86_64-unknown-freebsd11.1-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.x86_64-w64-mingw32-gcc4.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.x86_64-w64-mingw32-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.x86_64-w64-mingw32-gcc7.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.x86_64-w64-mingw32-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.x86_64-w64-mingw32-gcc8.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.6.0-0.x86_64-w64-mingw32-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/PCRE2.v10.31.0-0.aarch64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/PCRE2.v10.31.0-0.aarch64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/PCRE2.v10.31.0-0.aarch64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/PCRE2.v10.31.0-0.aarch64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/PCRE2.v10.31.0-0.arm-linux-gnueabihf.tar.gz/md5 create mode 100644 deps/checksums/PCRE2.v10.31.0-0.arm-linux-gnueabihf.tar.gz/sha512 create mode 100644 deps/checksums/PCRE2.v10.31.0-0.arm-linux-musleabihf.tar.gz/md5 create mode 100644 deps/checksums/PCRE2.v10.31.0-0.arm-linux-musleabihf.tar.gz/sha512 create mode 100644 deps/checksums/PCRE2.v10.31.0-0.i686-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/PCRE2.v10.31.0-0.i686-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/PCRE2.v10.31.0-0.i686-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/PCRE2.v10.31.0-0.i686-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/PCRE2.v10.31.0-0.i686-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/PCRE2.v10.31.0-0.i686-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/PCRE2.v10.31.0-0.powerpc64le-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/PCRE2.v10.31.0-0.powerpc64le-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/PCRE2.v10.31.0-0.x86_64-apple-darwin14.tar.gz/md5 create mode 100644 deps/checksums/PCRE2.v10.31.0-0.x86_64-apple-darwin14.tar.gz/sha512 create mode 100644 deps/checksums/PCRE2.v10.31.0-0.x86_64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/PCRE2.v10.31.0-0.x86_64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/PCRE2.v10.31.0-0.x86_64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/PCRE2.v10.31.0-0.x86_64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/PCRE2.v10.31.0-0.x86_64-unknown-freebsd11.1.tar.gz/md5 create mode 100644 deps/checksums/PCRE2.v10.31.0-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 create mode 100644 deps/checksums/PCRE2.v10.31.0-0.x86_64-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/PCRE2.v10.31.0-0.x86_64-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/Pkg-f71e2c5a119b9c850f9b357fc8c56068f5b51cc0.tar.gz/md5 create mode 100644 deps/checksums/Pkg-f71e2c5a119b9c850f9b357fc8c56068f5b51cc0.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse-5.4.0.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse-5.4.0.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-gnu-gcc4.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-gnu-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-gnu-gcc7.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-gnu-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-gnu-gcc8.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-gnu-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-musl-gcc4.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-musl-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-musl-gcc7.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-musl-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-musl-gcc8.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-musl-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-gnueabihf-gcc4.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-gnueabihf-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-gnueabihf-gcc7.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-gnueabihf-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-gnueabihf-gcc8.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-gnueabihf-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-musleabihf-gcc4.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-musleabihf-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-musleabihf-gcc7.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-musleabihf-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-musleabihf-gcc8.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-musleabihf-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-gnu-gcc4.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-gnu-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-gnu-gcc7.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-gnu-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-gnu-gcc8.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-gnu-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-musl-gcc4.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-musl-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-musl-gcc7.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-musl-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-musl-gcc8.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-musl-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.i686-w64-mingw32-gcc4.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.i686-w64-mingw32-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.i686-w64-mingw32-gcc7.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.i686-w64-mingw32-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.i686-w64-mingw32-gcc8.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.i686-w64-mingw32-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.powerpc64le-linux-gnu-gcc4.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.powerpc64le-linux-gnu-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.powerpc64le-linux-gnu-gcc7.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.powerpc64le-linux-gnu-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.powerpc64le-linux-gnu-gcc8.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.powerpc64le-linux-gnu-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.x86_64-apple-darwin14-gcc4.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.x86_64-apple-darwin14-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.x86_64-apple-darwin14-gcc7.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.x86_64-apple-darwin14-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.x86_64-apple-darwin14-gcc8.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.x86_64-apple-darwin14-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-gnu-gcc4.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-gnu-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-gnu-gcc7.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-gnu-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-gnu-gcc8.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-gnu-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-musl-gcc4.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-musl-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-musl-gcc7.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-musl-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-musl-gcc8.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-musl-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.x86_64-unknown-freebsd11.1-gcc4.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.x86_64-unknown-freebsd11.1-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.x86_64-unknown-freebsd11.1-gcc7.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.x86_64-unknown-freebsd11.1-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.x86_64-unknown-freebsd11.1-gcc8.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.x86_64-unknown-freebsd11.1-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.x86_64-w64-mingw32-gcc4.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.x86_64-w64-mingw32-gcc4.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.x86_64-w64-mingw32-gcc7.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.x86_64-w64-mingw32-gcc7.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.x86_64-w64-mingw32-gcc8.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-2.x86_64-w64-mingw32-gcc8.tar.gz/sha512 create mode 100644 deps/checksums/UnicodeData.txt/md5 create mode 100644 deps/checksums/UnicodeData.txt/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-6.aarch64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-6.aarch64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-6.aarch64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-6.aarch64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-6.arm-linux-gnueabihf.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-6.arm-linux-gnueabihf.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-6.arm-linux-musleabihf.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-6.arm-linux-musleabihf.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-6.i686-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-6.i686-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-6.i686-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-6.i686-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-6.i686-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-6.i686-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-6.powerpc64le-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-6.powerpc64le-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-6.x86_64-apple-darwin14.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-6.x86_64-apple-darwin14.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-6.x86_64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-6.x86_64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-6.x86_64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-6.x86_64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-6.x86_64-unknown-freebsd11.1.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-6.x86_64-unknown-freebsd11.1.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-6.x86_64-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-6.x86_64-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/arpack-ng-3.3.0-testA.mtx/md5 create mode 100644 deps/checksums/arpack-ng-3.3.0-testA.mtx/sha512 create mode 100644 deps/checksums/arpack-ng-3.3.0.tar.gz/md5 create mode 100644 deps/checksums/arpack-ng-3.3.0.tar.gz/sha512 create mode 100644 deps/checksums/cacert-2019-10-16.pem/md5 create mode 100644 deps/checksums/cacert-2019-10-16.pem/sha512 create mode 100644 deps/checksums/cfe-6.0.0.src.tar.xz/md5 create mode 100644 deps/checksums/cfe-6.0.0.src.tar.xz/sha512 create mode 100644 deps/checksums/compiler-rt-6.0.0.src.tar.xz/md5 create mode 100644 deps/checksums/compiler-rt-6.0.0.src.tar.xz/sha512 create mode 100644 deps/checksums/curl-7.61.0.tar.bz2/md5 create mode 100644 deps/checksums/curl-7.61.0.tar.bz2/sha512 create mode 100644 deps/checksums/dSFMT.v2.2.3-0.aarch64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/dSFMT.v2.2.3-0.aarch64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/dSFMT.v2.2.3-0.aarch64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/dSFMT.v2.2.3-0.aarch64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/dSFMT.v2.2.3-0.arm-linux-gnueabihf.tar.gz/md5 create mode 100644 deps/checksums/dSFMT.v2.2.3-0.arm-linux-gnueabihf.tar.gz/sha512 create mode 100644 deps/checksums/dSFMT.v2.2.3-0.arm-linux-musleabihf.tar.gz/md5 create mode 100644 deps/checksums/dSFMT.v2.2.3-0.arm-linux-musleabihf.tar.gz/sha512 create mode 100644 deps/checksums/dSFMT.v2.2.3-0.i686-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/dSFMT.v2.2.3-0.i686-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/dSFMT.v2.2.3-0.i686-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/dSFMT.v2.2.3-0.i686-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/dSFMT.v2.2.3-0.i686-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/dSFMT.v2.2.3-0.i686-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/dSFMT.v2.2.3-0.powerpc64le-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/dSFMT.v2.2.3-0.powerpc64le-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/dSFMT.v2.2.3-0.x86_64-apple-darwin14.tar.gz/md5 create mode 100644 deps/checksums/dSFMT.v2.2.3-0.x86_64-apple-darwin14.tar.gz/sha512 create mode 100644 deps/checksums/dSFMT.v2.2.3-0.x86_64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/dSFMT.v2.2.3-0.x86_64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/dSFMT.v2.2.3-0.x86_64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/dSFMT.v2.2.3-0.x86_64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/dSFMT.v2.2.3-0.x86_64-unknown-freebsd11.1.tar.gz/md5 create mode 100644 deps/checksums/dSFMT.v2.2.3-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 create mode 100644 deps/checksums/dSFMT.v2.2.3-0.x86_64-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/dSFMT.v2.2.3-0.x86_64-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/dsfmt-2.2.3.tar.gz/md5 create mode 100644 deps/checksums/dsfmt-2.2.3.tar.gz/sha512 create mode 100644 deps/checksums/gmp-6.1.2.tar.bz2/md5 create mode 100644 deps/checksums/gmp-6.1.2.tar.bz2/sha512 create mode 100644 deps/checksums/i686-4.9.2-release-win32-sjlj-rt_v4-rev3.7z/md5 create mode 100644 deps/checksums/i686-4.9.2-release-win32-sjlj-rt_v4-rev3.7z/sha512 create mode 100644 deps/checksums/lapack-3.5.0.tgz/md5 create mode 100644 deps/checksums/lapack-3.5.0.tgz/sha512 create mode 100644 deps/checksums/libgit2-b3e1a56ebb2b9291e82dc027ba9cbcfc3ead54d3.tar.gz/md5 create mode 100644 deps/checksums/libgit2-b3e1a56ebb2b9291e82dc027ba9cbcfc3ead54d3.tar.gz/sha512 create mode 100644 deps/checksums/libosxunwind-0.0.5.tar.gz/md5 create mode 100644 deps/checksums/libosxunwind-0.0.5.tar.gz/sha512 create mode 100644 deps/checksums/libssh2-42d37aa63129a1b2644bf6495198923534322d64.tar.gz/md5 create mode 100644 deps/checksums/libssh2-42d37aa63129a1b2644bf6495198923534322d64.tar.gz/sha512 create mode 100644 deps/checksums/libunwind-1.3.1.tar.gz/md5 create mode 100644 deps/checksums/libunwind-1.3.1.tar.gz/sha512 create mode 100644 deps/checksums/libuv-35b1504507a7a4168caae3d78db54d1121b121e1.tar.gz/md5 create mode 100644 deps/checksums/libuv-35b1504507a7a4168caae3d78db54d1121b121e1.tar.gz/sha512 create mode 100644 deps/checksums/libwhich-81e9723c0273d78493dc8c8ed570f68d9ce7e89e.tar.gz/md5 create mode 100644 deps/checksums/libwhich-81e9723c0273d78493dc8c8ed570f68d9ce7e89e.tar.gz/sha512 create mode 100644 deps/checksums/llvm-6.0.1.src.tar.xz/md5 create mode 100644 deps/checksums/llvm-6.0.1.src.tar.xz/sha512 create mode 100644 deps/checksums/llvm-7.0.1.src.tar.xz/md5 create mode 100644 deps/checksums/llvm-7.0.1.src.tar.xz/sha512 create mode 100644 deps/checksums/llvm-8.0.0.src.tar.xz/md5 create mode 100644 deps/checksums/llvm-8.0.0.src.tar.xz/sha512 create mode 100644 deps/checksums/mbedtls-2.16.0-apache.tgz/md5 create mode 100644 deps/checksums/mbedtls-2.16.0-apache.tgz/sha512 create mode 100644 deps/checksums/mbedtls-2.16.0-gpl.tgz/md5 create mode 100644 deps/checksums/mbedtls-2.16.0-gpl.tgz/sha512 create mode 100644 deps/checksums/mpfr-4.0.2.tar.bz2/md5 create mode 100644 deps/checksums/mpfr-4.0.2.tar.bz2/sha512 create mode 100644 deps/checksums/nsis-3.04-setup.exe/md5 create mode 100644 deps/checksums/nsis-3.04-setup.exe/sha512 create mode 100644 deps/checksums/openblas-eebc18928715775c9ed254684edee16e4efe0342.tar.gz/md5 create mode 100644 deps/checksums/openblas-eebc18928715775c9ed254684edee16e4efe0342.tar.gz/sha512 create mode 100644 deps/checksums/openlibm-ce69bf1f32d3e2e9791da36c9e33ba38670d5576.tar.gz/md5 create mode 100644 deps/checksums/openlibm-ce69bf1f32d3e2e9791da36c9e33ba38670d5576.tar.gz/sha512 create mode 100644 deps/checksums/openspecfun-39699a1c1824bf88410cabb8a7438af91ea98f4c.tar.gz/md5 create mode 100644 deps/checksums/openspecfun-39699a1c1824bf88410cabb8a7438af91ea98f4c.tar.gz/sha512 create mode 100644 deps/checksums/p7zip-16.2.0.tar.bz2/md5 create mode 100644 deps/checksums/p7zip-16.2.0.tar.bz2/sha512 create mode 100644 deps/checksums/p7zip.v16.2.0-1.aarch64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/p7zip.v16.2.0-1.aarch64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/p7zip.v16.2.0-1.aarch64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/p7zip.v16.2.0-1.aarch64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/p7zip.v16.2.0-1.arm-linux-gnueabihf.tar.gz/md5 create mode 100644 deps/checksums/p7zip.v16.2.0-1.arm-linux-gnueabihf.tar.gz/sha512 create mode 100644 deps/checksums/p7zip.v16.2.0-1.arm-linux-musleabihf.tar.gz/md5 create mode 100644 deps/checksums/p7zip.v16.2.0-1.arm-linux-musleabihf.tar.gz/sha512 create mode 100644 deps/checksums/p7zip.v16.2.0-1.i686-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/p7zip.v16.2.0-1.i686-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/p7zip.v16.2.0-1.i686-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/p7zip.v16.2.0-1.i686-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/p7zip.v16.2.0-1.i686-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/p7zip.v16.2.0-1.i686-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/p7zip.v16.2.0-1.powerpc64le-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/p7zip.v16.2.0-1.powerpc64le-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/p7zip.v16.2.0-1.x86_64-apple-darwin14.tar.gz/md5 create mode 100644 deps/checksums/p7zip.v16.2.0-1.x86_64-apple-darwin14.tar.gz/sha512 create mode 100644 deps/checksums/p7zip.v16.2.0-1.x86_64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/p7zip.v16.2.0-1.x86_64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/p7zip.v16.2.0-1.x86_64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/p7zip.v16.2.0-1.x86_64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/p7zip.v16.2.0-1.x86_64-unknown-freebsd11.1.tar.gz/md5 create mode 100644 deps/checksums/p7zip.v16.2.0-1.x86_64-unknown-freebsd11.1.tar.gz/sha512 create mode 100644 deps/checksums/p7zip.v16.2.0-1.x86_64-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/p7zip.v16.2.0-1.x86_64-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/patchelf-0.9.tar.gz/md5 create mode 100644 deps/checksums/patchelf-0.9.tar.gz/sha512 create mode 100644 deps/checksums/pcre2-10.31.tar.bz2/md5 create mode 100644 deps/checksums/pcre2-10.31.tar.bz2/sha512 create mode 100644 deps/checksums/utf8proc-5c632c57426f2e4246e3b64dd2fd088d3920f9e5.tar.gz/md5 create mode 100644 deps/checksums/utf8proc-5c632c57426f2e4246e3b64dd2fd088d3920f9e5.tar.gz/sha512 create mode 100644 deps/checksums/x86_64-4.9.2-release-win32-seh-rt_v4-rev3.7z/md5 create mode 100644 deps/checksums/x86_64-4.9.2-release-win32-seh-rt_v4-rev3.7z/sha512 create mode 100644 deps/checksums/zlib-cacf7f1d4e3d44d871b605da3b647f07d718623f.tar.gz/md5 create mode 100644 deps/checksums/zlib-cacf7f1d4e3d44d871b605da3b647f07d718623f.tar.gz/sha512 create mode 100644 deps/curl.mk create mode 100644 deps/dsfmt.mk create mode 100644 deps/gfortblas.alias create mode 100644 deps/gfortblas.c create mode 100644 deps/gmp.mk create mode 100644 deps/libdSFMT.def create mode 100644 deps/libgit2.mk create mode 100644 deps/libgit2.version create mode 100644 deps/libssh2.mk create mode 100644 deps/libssh2.version create mode 100644 deps/libuv.mk create mode 100644 deps/libuv.version create mode 100644 deps/libwhich.mk create mode 100644 deps/libwhich.version create mode 100644 deps/llvm-options.mk create mode 100644 deps/llvm-ver.make create mode 100644 deps/llvm.mk create mode 100644 deps/mbedtls.mk create mode 100644 deps/mpfr.mk create mode 100644 deps/objconv.mk create mode 100644 deps/openblas.version create mode 100644 deps/openlibm.mk create mode 100644 deps/openlibm.version create mode 100644 deps/p7zip.mk create mode 100644 deps/patchelf.mk create mode 100644 deps/patches/SuiteSparse-shlib.patch create mode 100644 deps/patches/SuiteSparse-winclang.patch create mode 100644 deps/patches/clang-D28477.patch create mode 100755 deps/patches/config.sub create mode 100644 deps/patches/dSFMT.c.patch create mode 100644 deps/patches/dSFMT.h.patch create mode 100644 deps/patches/gmp-config-ldflags.patch create mode 100644 deps/patches/gmp-exception.patch create mode 100644 deps/patches/gmp_alloc_overflow_func.patch create mode 100644 deps/patches/libgit2-agent-nonfatal.patch create mode 100644 deps/patches/libunwind-prefer-extbl.patch create mode 100644 deps/patches/libunwind-static-arm.patch create mode 100644 deps/patches/llvm-6.0-D44650.patch create mode 100644 deps/patches/llvm-6.0-D63688-wasm-isLocal.patch create mode 100644 deps/patches/llvm-6.0-D64032-cmake-cross.patch create mode 100644 deps/patches/llvm-6.0-D64225-cmake-cross2.patch create mode 100644 deps/patches/llvm-6.0-DISABLE_ABI_CHECKS.patch create mode 100644 deps/patches/llvm-6.0-NVPTX-addrspaces.patch create mode 100644 deps/patches/llvm-6.0-r327540.patch create mode 100644 deps/patches/llvm-6.0.0-ifconv-D45819.patch create mode 100644 deps/patches/llvm-6.0.0_D27296-libssp.patch create mode 100644 deps/patches/llvm-7.0-D44650.patch create mode 100644 deps/patches/llvm-7.0-D50167-scev-umin.patch create mode 100644 deps/patches/llvm-8.0-D50167-scev-umin.patch create mode 100644 deps/patches/llvm-D27629-AArch64-large_model_4.0.patch create mode 100644 deps/patches/llvm-D27629-AArch64-large_model_6.0.1.patch create mode 100644 deps/patches/llvm-D34078-vectorize-fdiv.patch create mode 100644 deps/patches/llvm-D42260.patch create mode 100644 deps/patches/llvm-D42262-jumpthreading-not-i1.patch create mode 100644 deps/patches/llvm-D44892-Perf-integration.patch create mode 100644 deps/patches/llvm-D45008.patch create mode 100644 deps/patches/llvm-D45070.patch create mode 100644 deps/patches/llvm-D46460.patch create mode 100644 deps/patches/llvm-D49832-SCEVPred.patch create mode 100644 deps/patches/llvm-D50010-VNCoercion-ni.patch create mode 100644 deps/patches/llvm-D50167-scev-umin.patch create mode 100644 deps/patches/llvm-D51842-win64-byval-cc.patch create mode 100644 deps/patches/llvm-D57118-powerpc.patch create mode 100644 deps/patches/llvm-OProfile-line-num.patch create mode 100644 deps/patches/llvm-PPC-addrspaces.patch create mode 100644 deps/patches/llvm-r355582-avxminmax.patch create mode 100644 deps/patches/llvm-rL323946-LSRTy.patch create mode 100644 deps/patches/llvm-rL326843-missing-header.patch create mode 100644 deps/patches/llvm-rL326967-aligned-load.patch create mode 100644 deps/patches/llvm-rL327898.patch create mode 100644 deps/patches/llvm-rL332302.patch create mode 100644 deps/patches/llvm-rL332680.patch create mode 100644 deps/patches/llvm-rL332682.patch create mode 100644 deps/patches/llvm-rL332694.patch create mode 100644 deps/patches/llvm-rL349068-llvm-config.patch create mode 100644 deps/patches/llvm-symver-jlprefix.patch create mode 100644 deps/patches/llvm-windows-race.patch create mode 100644 deps/patches/llvm6-WASM-addrspaces.patch create mode 100644 deps/patches/llvm7-D50010-VNCoercion-ni.patch create mode 100644 deps/patches/llvm7-D51842-win64-byval-cc.patch create mode 100644 deps/patches/llvm7-WASM-addrspaces.patch create mode 100644 deps/patches/llvm7-symver-jlprefix.patch create mode 100644 deps/patches/llvm7-windows-race.patch create mode 100644 deps/patches/llvm8-D34078-vectorize-fdiv.patch create mode 100644 deps/patches/llvm8-WASM-addrspaces.patch create mode 100644 deps/patches/openblas-skylakexdgemm.patch create mode 100644 deps/patches/p7zip-12-CVE-2016-9296.patch create mode 100644 deps/patches/p7zip-13-CVE-2017-17969.patch create mode 100644 deps/patches/p7zip-15-Enhanced-encryption-strength.patch create mode 100644 deps/patches/p7zip-Windows_ErrorMsg.patch create mode 100644 deps/pcre.mk create mode 100644 deps/suitesparse.mk create mode 100644 deps/tools/bb-install.mk create mode 100644 deps/tools/common.mk create mode 100755 deps/tools/find_python2 create mode 100644 deps/tools/git-external.mk create mode 100755 deps/tools/jlchecksum create mode 100755 deps/tools/jldownload create mode 100644 deps/tools/stdlib-external.mk create mode 100644 deps/unwind.mk create mode 100644 deps/utf8proc.mk create mode 100644 deps/utf8proc.version create mode 100644 deps/valgrind/valgrind.h create mode 100644 deps/zlib.mk create mode 100644 deps/zlib.version create mode 100644 doc/.gitignore create mode 100644 doc/Makefile create mode 100644 doc/Manifest.toml create mode 100644 doc/NEWS-update.jl create mode 100644 doc/Project.toml create mode 100644 doc/README.md create mode 100644 doc/build/README.md create mode 100644 doc/build/arm.md create mode 100644 doc/build/build.md create mode 100644 doc/build/distributing.md create mode 100644 doc/build/freebsd.md create mode 100644 doc/build/linux.md create mode 100644 doc/build/macos.md create mode 100644 doc/build/windows.md create mode 100644 doc/images/github_metadata_develbranch.png create mode 100644 doc/images/github_metadata_fork.png create mode 100644 doc/images/github_metadata_pullrequest.png create mode 100644 doc/images/jltypes.ai create mode 100644 doc/images/jltypes.svg create mode 100644 doc/images/travis-icon.png create mode 100644 doc/make.jl create mode 100644 doc/man/julia.1 create mode 100644 doc/src/assets/julia-manual.css create mode 100644 doc/src/assets/logo.png create mode 100644 doc/src/base/arrays.md create mode 100644 doc/src/base/base.md create mode 100644 doc/src/base/c.md create mode 100644 doc/src/base/collections.md create mode 100644 doc/src/base/constants.md create mode 100644 doc/src/base/file.md create mode 100644 doc/src/base/io-network.md create mode 100644 doc/src/base/iterators.md create mode 100644 doc/src/base/libc.md create mode 100644 doc/src/base/math.md create mode 100644 doc/src/base/multi-threading.md create mode 100644 doc/src/base/numbers.md create mode 100644 doc/src/base/parallel.md create mode 100644 doc/src/base/punctuation.md create mode 100644 doc/src/base/simd-types.md create mode 100644 doc/src/base/sort.md create mode 100644 doc/src/base/stacktraces.md create mode 100644 doc/src/base/strings.md create mode 100644 doc/src/devdocs/ast.md create mode 100644 doc/src/devdocs/backtraces.md create mode 100644 doc/src/devdocs/boundscheck.md create mode 100644 doc/src/devdocs/callconv.md create mode 100644 doc/src/devdocs/cartesian.md create mode 100644 doc/src/devdocs/compiler.md create mode 100644 doc/src/devdocs/debuggingtips.md create mode 100644 doc/src/devdocs/eval.md create mode 100644 doc/src/devdocs/functions.md create mode 100644 doc/src/devdocs/gc-sa.md create mode 100644 doc/src/devdocs/inference.md create mode 100644 doc/src/devdocs/init.md create mode 100644 doc/src/devdocs/isbitsunionarrays.md create mode 100644 doc/src/devdocs/llvm.md create mode 100644 doc/src/devdocs/locks.md create mode 100644 doc/src/devdocs/meta.md create mode 100644 doc/src/devdocs/object.md create mode 100644 doc/src/devdocs/offset-arrays.md create mode 100644 doc/src/devdocs/reflection.md create mode 100644 doc/src/devdocs/require.md create mode 100644 doc/src/devdocs/sanitizers.md create mode 100644 doc/src/devdocs/ssair.md create mode 100644 doc/src/devdocs/stdio.md create mode 100644 doc/src/devdocs/subarrays.md create mode 100644 doc/src/devdocs/sysimg.md create mode 100644 doc/src/devdocs/types.md create mode 100644 doc/src/devdocs/valgrind.md create mode 100644 doc/src/index.md create mode 100644 doc/src/manual/arrays.md create mode 100644 doc/src/manual/calling-c-and-fortran-code.md create mode 100644 doc/src/manual/code-loading.md create mode 100644 doc/src/manual/complex-and-rational-numbers.md create mode 100644 doc/src/manual/constructors.md create mode 100644 doc/src/manual/control-flow.md create mode 100644 doc/src/manual/conversion-and-promotion.md create mode 100644 doc/src/manual/documentation.md create mode 100644 doc/src/manual/embedding.md create mode 100644 doc/src/manual/environment-variables.md create mode 100644 doc/src/manual/faq.md create mode 100644 doc/src/manual/functions.md create mode 100644 doc/src/manual/getting-started.md create mode 100644 doc/src/manual/handling-operating-system-variation.md create mode 100644 doc/src/manual/integers-and-floating-point-numbers.md create mode 100644 doc/src/manual/interfaces.md create mode 100644 doc/src/manual/mathematical-operations.md create mode 100644 doc/src/manual/metaprogramming.md create mode 100644 doc/src/manual/methods.md create mode 100644 doc/src/manual/missing.md create mode 100644 doc/src/manual/modules.md create mode 100644 doc/src/manual/networking-and-streams.md create mode 100644 doc/src/manual/noteworthy-differences.md create mode 100644 doc/src/manual/parallel-computing.md create mode 100644 doc/src/manual/performance-tips.md create mode 100644 doc/src/manual/profile.md create mode 100644 doc/src/manual/running-external-programs.md create mode 100644 doc/src/manual/stacktraces.md create mode 100644 doc/src/manual/strings.md create mode 100644 doc/src/manual/style-guide.md create mode 100644 doc/src/manual/types.md create mode 100644 doc/src/manual/unicode-input.md create mode 100644 doc/src/manual/variables-and-scoping.md create mode 100644 doc/src/manual/variables.md create mode 100644 doc/src/manual/workflow-tips.md create mode 100644 etc/startup.jl create mode 100644 etc/write_base_cache.jl create mode 100644 src/.gitignore create mode 100644 src/APInt-C.cpp create mode 100644 src/APInt-C.h create mode 100644 src/Makefile create mode 100644 src/abi_aarch64.cpp create mode 100644 src/abi_arm.cpp create mode 100644 src/abi_llvm.cpp create mode 100644 src/abi_ppc64le.cpp create mode 100644 src/abi_win32.cpp create mode 100644 src/abi_win64.cpp create mode 100644 src/abi_x86.cpp create mode 100644 src/abi_x86_64.cpp create mode 100644 src/anticodegen.c create mode 100644 src/array.c create mode 100644 src/ast.c create mode 100644 src/ast.scm create mode 100644 src/atomics.h create mode 100644 src/bin2hex.scm create mode 100644 src/builtin_proto.h create mode 100644 src/builtins.c create mode 100644 src/ccall.cpp create mode 100644 src/ccalltest.c create mode 100644 src/cgmemmgr.cpp create mode 100644 src/cgutils.cpp create mode 100644 src/clangsa/GCChecker.cpp create mode 100644 src/codegen.cpp create mode 100644 src/codegen_shared.h create mode 100644 src/common_symbols1.inc create mode 100644 src/common_symbols2.inc create mode 100644 src/crc32c-tables.c create mode 100644 src/crc32c.c create mode 100644 src/datatype.c create mode 100644 src/debuginfo.cpp create mode 100644 src/debuginfo.h create mode 100644 src/disasm.cpp create mode 100644 src/dlload.c create mode 100644 src/dump.c create mode 100644 src/features_aarch32.h create mode 100644 src/features_aarch64.h create mode 100644 src/features_x86.h create mode 100644 src/file_constants.h create mode 100644 src/flisp/.gitignore create mode 100644 src/flisp/Makefile create mode 100644 src/flisp/aliases.scm create mode 100755 src/flisp/bootstrap.sh create mode 100644 src/flisp/builtins.c create mode 100644 src/flisp/color.lsp create mode 100644 src/flisp/compiler.lsp create mode 100644 src/flisp/cvalues.c create mode 100644 src/flisp/equal.c create mode 100644 src/flisp/equalhash.c create mode 100644 src/flisp/equalhash.h create mode 100644 src/flisp/flisp.boot create mode 100644 src/flisp/flisp.c create mode 100644 src/flisp/flisp.h create mode 100644 src/flisp/flmain.c create mode 100644 src/flisp/iostream.c create mode 100644 src/flisp/julia_charmap.h create mode 100644 src/flisp/julia_extensions.c create mode 100644 src/flisp/julia_opsuffs.h create mode 100644 src/flisp/mkboot0.lsp create mode 100644 src/flisp/mkboot1.lsp create mode 100644 src/flisp/opcodes.h create mode 100644 src/flisp/print.c create mode 100644 src/flisp/profile.scm create mode 100644 src/flisp/read.c create mode 100644 src/flisp/string.c create mode 100644 src/flisp/system.lsp create mode 100644 src/flisp/table.c create mode 100644 src/flisp/types.c create mode 100644 src/flisp/unittest.lsp create mode 100644 src/gc-debug.c create mode 100644 src/gc-pages.c create mode 100644 src/gc-stacks.c create mode 100644 src/gc.c create mode 100644 src/gc.h create mode 100644 src/gen_sysimg_symtab.jl create mode 100644 src/getopt.c create mode 100644 src/getopt.h create mode 100644 src/gf.c create mode 100644 src/init.c create mode 100644 src/interpreter-stacktrace.c create mode 100644 src/interpreter.c create mode 100644 src/intrinsics.cpp create mode 100644 src/intrinsics.h create mode 100644 src/jitlayers.cpp create mode 100644 src/jitlayers.h create mode 100644 src/jl_uv.c create mode 100644 src/jlapi.c create mode 100644 src/jlfrontend.scm create mode 100644 src/jloptions.c create mode 100644 src/jltypes.c create mode 100644 src/jsvm-emscripten/asyncify_setup.js create mode 100644 src/jsvm-emscripten/task.js create mode 100644 src/julia-parser.scm create mode 100644 src/julia-syntax.scm create mode 100644 src/julia.expmap create mode 100644 src/julia.h create mode 100644 src/julia_assert.h create mode 100644 src/julia_gcext.h create mode 100644 src/julia_internal.h create mode 100644 src/julia_threads.h create mode 100644 src/llvm-alloc-opt.cpp create mode 100644 src/llvm-api.cpp create mode 100644 src/llvm-final-gc-lowering.cpp create mode 100644 src/llvm-gc-invariant-verifier.cpp create mode 100644 src/llvm-late-gc-lowering.cpp create mode 100644 src/llvm-lower-handlers.cpp create mode 100644 src/llvm-muladd.cpp create mode 100644 src/llvm-multiversioning.cpp create mode 100644 src/llvm-pass-helpers.cpp create mode 100644 src/llvm-pass-helpers.h create mode 100644 src/llvm-propagate-addrspaces.cpp create mode 100644 src/llvm-ptls.cpp create mode 100644 src/llvm-simdloop.cpp create mode 100644 src/llvm-version.h create mode 100644 src/llvmcalltest.cpp create mode 100644 src/locks.h create mode 100644 src/macroexpand.scm create mode 100644 src/match.scm create mode 100644 src/method.c create mode 100644 src/mk_julia_flisp_boot.scm create mode 100644 src/module.c create mode 100644 src/options.h create mode 100644 src/partr.c create mode 100644 src/precompile.c create mode 100644 src/processor.cpp create mode 100644 src/processor.h create mode 100644 src/processor_arm.cpp create mode 100644 src/processor_fallback.cpp create mode 100644 src/processor_x86.cpp create mode 100644 src/rtutils.c create mode 100644 src/runtime_ccall.cpp create mode 100644 src/runtime_intrinsics.c create mode 100644 src/safepoint.c create mode 100644 src/signal-handling.c create mode 100644 src/signals-mach.c create mode 100644 src/signals-unix.c create mode 100644 src/signals-win.c create mode 100644 src/simplevector.c create mode 100644 src/stackwalk.c create mode 100644 src/staticdata.c create mode 100644 src/subtype.c create mode 100644 src/support/.gitignore create mode 100644 src/support/END.h create mode 100644 src/support/ENTRY.amd64.h create mode 100644 src/support/ENTRY.i387.h create mode 100644 src/support/Makefile create mode 100644 src/support/MurmurHash3.c create mode 100644 src/support/MurmurHash3.h create mode 100644 src/support/_setjmp.win32.S create mode 100644 src/support/_setjmp.win64.S create mode 100644 src/support/analyzer_annotations.h create mode 100644 src/support/arraylist.c create mode 100644 src/support/arraylist.h create mode 100644 src/support/asprintf.c create mode 100644 src/support/bitvector.c create mode 100644 src/support/bitvector.h create mode 100644 src/support/dirname.c create mode 100644 src/support/dirpath.h create mode 100644 src/support/dtypes.h create mode 100644 src/support/hashing.c create mode 100644 src/support/hashing.h create mode 100644 src/support/htable.c create mode 100644 src/support/htable.h create mode 100644 src/support/htable.inc create mode 100644 src/support/int2str.c create mode 100644 src/support/ios.c create mode 100644 src/support/ios.h create mode 100644 src/support/libsupport.h create mode 100644 src/support/libsupportinit.c create mode 100644 src/support/operators.c create mode 100644 src/support/platform.h create mode 100644 src/support/ptrhash.c create mode 100644 src/support/ptrhash.h create mode 100644 src/support/strptime.c create mode 100644 src/support/strtod.c create mode 100644 src/support/strtod.h create mode 100644 src/support/timefuncs.c create mode 100644 src/support/timefuncs.h create mode 100644 src/support/tzfile.h create mode 100644 src/support/utf8.c create mode 100644 src/support/utf8.h create mode 100644 src/support/utils.h create mode 100644 src/support/win32_ucontext.c create mode 100644 src/support/win32_ucontext.h create mode 100644 src/symbol.c create mode 100644 src/sys.c create mode 100644 src/table.c create mode 100644 src/task.c create mode 100644 src/threading.c create mode 100644 src/threading.h create mode 100644 src/timing.c create mode 100644 src/timing.h create mode 100644 src/tls.h create mode 100644 src/toplevel.c create mode 100644 src/typemap.c create mode 100644 src/utils.scm create mode 100644 src/uv_constants.h create mode 100644 stdlib/.gitignore create mode 100644 stdlib/Base64/Project.toml create mode 100644 stdlib/Base64/docs/src/index.md create mode 100644 stdlib/Base64/src/Base64.jl create mode 100644 stdlib/Base64/src/buffer.jl create mode 100644 stdlib/Base64/src/decode.jl create mode 100644 stdlib/Base64/src/encode.jl create mode 100644 stdlib/Base64/test/runtests.jl create mode 100644 stdlib/CRC32c/Project.toml create mode 100644 stdlib/CRC32c/docs/src/index.md create mode 100644 stdlib/CRC32c/src/CRC32c.jl create mode 100644 stdlib/CRC32c/test/runtests.jl create mode 100644 stdlib/Dates/Project.toml create mode 100644 stdlib/Dates/docs/src/index.md create mode 100644 stdlib/Dates/src/Dates.jl create mode 100644 stdlib/Dates/src/accessors.jl create mode 100644 stdlib/Dates/src/adjusters.jl create mode 100644 stdlib/Dates/src/arithmetic.jl create mode 100644 stdlib/Dates/src/conversions.jl create mode 100644 stdlib/Dates/src/deprecated.jl create mode 100644 stdlib/Dates/src/io.jl create mode 100644 stdlib/Dates/src/parse.jl create mode 100644 stdlib/Dates/src/periods.jl create mode 100644 stdlib/Dates/src/query.jl create mode 100644 stdlib/Dates/src/ranges.jl create mode 100644 stdlib/Dates/src/rounding.jl create mode 100644 stdlib/Dates/src/types.jl create mode 100644 stdlib/Dates/test/accessors.jl create mode 100644 stdlib/Dates/test/adjusters.jl create mode 100644 stdlib/Dates/test/arithmetic.jl create mode 100644 stdlib/Dates/test/conversions.jl create mode 100644 stdlib/Dates/test/io.jl create mode 100644 stdlib/Dates/test/periods.jl create mode 100644 stdlib/Dates/test/query.jl create mode 100644 stdlib/Dates/test/ranges.jl create mode 100644 stdlib/Dates/test/rounding.jl create mode 100644 stdlib/Dates/test/runtests.jl create mode 100644 stdlib/Dates/test/testgroups create mode 100644 stdlib/Dates/test/types.jl create mode 100644 stdlib/DelimitedFiles/Project.toml create mode 100644 stdlib/DelimitedFiles/docs/src/index.md create mode 100644 stdlib/DelimitedFiles/src/DelimitedFiles.jl create mode 100644 stdlib/DelimitedFiles/test/runtests.jl create mode 100644 stdlib/Distributed/Project.toml create mode 100644 stdlib/Distributed/docs/src/index.md create mode 100644 stdlib/Distributed/src/Distributed.jl create mode 100644 stdlib/Distributed/src/cluster.jl create mode 100644 stdlib/Distributed/src/clusterserialize.jl create mode 100644 stdlib/Distributed/src/macros.jl create mode 100644 stdlib/Distributed/src/managers.jl create mode 100644 stdlib/Distributed/src/messages.jl create mode 100644 stdlib/Distributed/src/pmap.jl create mode 100644 stdlib/Distributed/src/precompile.jl create mode 100644 stdlib/Distributed/src/process_messages.jl create mode 100644 stdlib/Distributed/src/remotecall.jl create mode 100644 stdlib/Distributed/src/workerpool.jl create mode 100644 stdlib/Distributed/test/distributed_exec.jl create mode 100644 stdlib/Distributed/test/runtests.jl create mode 100644 stdlib/Distributed/test/topology.jl create mode 100644 stdlib/FileWatching/Project.toml create mode 100644 stdlib/FileWatching/docs/src/index.md create mode 100644 stdlib/FileWatching/src/FileWatching.jl create mode 100644 stdlib/FileWatching/test/runtests.jl create mode 100644 stdlib/Future/Project.toml create mode 100644 stdlib/Future/docs/src/index.md create mode 100644 stdlib/Future/src/Future.jl create mode 100644 stdlib/Future/test/runtests.jl create mode 100644 stdlib/InteractiveUtils/Project.toml create mode 100644 stdlib/InteractiveUtils/docs/src/index.md create mode 100644 stdlib/InteractiveUtils/src/InteractiveUtils.jl create mode 100644 stdlib/InteractiveUtils/src/clipboard.jl create mode 100644 stdlib/InteractiveUtils/src/codeview.jl create mode 100644 stdlib/InteractiveUtils/src/editless.jl create mode 100644 stdlib/InteractiveUtils/src/macros.jl create mode 100644 stdlib/InteractiveUtils/test/runtests.jl create mode 100644 stdlib/LibGit2/Project.toml create mode 100644 stdlib/LibGit2/docs/src/index.md create mode 100644 stdlib/LibGit2/src/LibGit2.jl create mode 100644 stdlib/LibGit2/src/blame.jl create mode 100644 stdlib/LibGit2/src/blob.jl create mode 100644 stdlib/LibGit2/src/callbacks.jl create mode 100644 stdlib/LibGit2/src/commit.jl create mode 100644 stdlib/LibGit2/src/config.jl create mode 100644 stdlib/LibGit2/src/consts.jl create mode 100644 stdlib/LibGit2/src/diff.jl create mode 100644 stdlib/LibGit2/src/error.jl create mode 100644 stdlib/LibGit2/src/gitcredential.jl create mode 100644 stdlib/LibGit2/src/index.jl create mode 100644 stdlib/LibGit2/src/merge.jl create mode 100644 stdlib/LibGit2/src/oid.jl create mode 100644 stdlib/LibGit2/src/rebase.jl create mode 100644 stdlib/LibGit2/src/reference.jl create mode 100644 stdlib/LibGit2/src/remote.jl create mode 100644 stdlib/LibGit2/src/repository.jl create mode 100644 stdlib/LibGit2/src/signature.jl create mode 100644 stdlib/LibGit2/src/status.jl create mode 100644 stdlib/LibGit2/src/strarray.jl create mode 100644 stdlib/LibGit2/src/tag.jl create mode 100644 stdlib/LibGit2/src/tree.jl create mode 100644 stdlib/LibGit2/src/types.jl create mode 100644 stdlib/LibGit2/src/utils.jl create mode 100644 stdlib/LibGit2/src/walker.jl create mode 100644 stdlib/LibGit2/test/keys/invalid create mode 100644 stdlib/LibGit2/test/keys/invalid.pub create mode 100644 stdlib/LibGit2/test/keys/valid create mode 100644 stdlib/LibGit2/test/keys/valid-passphrase create mode 100644 stdlib/LibGit2/test/keys/valid-passphrase.pub create mode 100644 stdlib/LibGit2/test/keys/valid.pub create mode 100644 stdlib/LibGit2/test/libgit2-helpers.jl create mode 100644 stdlib/LibGit2/test/libgit2.jl create mode 100644 stdlib/LibGit2/test/online.jl create mode 100644 stdlib/LibGit2/test/runtests.jl create mode 100644 stdlib/LibGit2/test/testgroups create mode 100644 stdlib/Libdl/Project.toml create mode 100644 stdlib/Libdl/docs/src/index.md create mode 100644 stdlib/Libdl/src/Libdl.jl create mode 100644 stdlib/Libdl/test/runtests.jl create mode 100644 stdlib/LinearAlgebra/Project.toml create mode 100644 stdlib/LinearAlgebra/docs/src/index.md create mode 100644 stdlib/LinearAlgebra/src/LinearAlgebra.jl create mode 100644 stdlib/LinearAlgebra/src/adjtrans.jl create mode 100644 stdlib/LinearAlgebra/src/bidiag.jl create mode 100644 stdlib/LinearAlgebra/src/bitarray.jl create mode 100644 stdlib/LinearAlgebra/src/blas.jl create mode 100644 stdlib/LinearAlgebra/src/bunchkaufman.jl create mode 100644 stdlib/LinearAlgebra/src/cholesky.jl create mode 100644 stdlib/LinearAlgebra/src/dense.jl create mode 100644 stdlib/LinearAlgebra/src/deprecated.jl create mode 100644 stdlib/LinearAlgebra/src/diagonal.jl create mode 100644 stdlib/LinearAlgebra/src/eigen.jl create mode 100644 stdlib/LinearAlgebra/src/exceptions.jl create mode 100644 stdlib/LinearAlgebra/src/factorization.jl create mode 100644 stdlib/LinearAlgebra/src/generic.jl create mode 100644 stdlib/LinearAlgebra/src/givens.jl create mode 100644 stdlib/LinearAlgebra/src/hessenberg.jl create mode 100644 stdlib/LinearAlgebra/src/lapack.jl create mode 100644 stdlib/LinearAlgebra/src/ldlt.jl create mode 100644 stdlib/LinearAlgebra/src/lq.jl create mode 100644 stdlib/LinearAlgebra/src/lu.jl create mode 100644 stdlib/LinearAlgebra/src/matmul.jl create mode 100644 stdlib/LinearAlgebra/src/qr.jl create mode 100644 stdlib/LinearAlgebra/src/schur.jl create mode 100644 stdlib/LinearAlgebra/src/special.jl create mode 100644 stdlib/LinearAlgebra/src/structuredbroadcast.jl create mode 100644 stdlib/LinearAlgebra/src/svd.jl create mode 100644 stdlib/LinearAlgebra/src/symmetric.jl create mode 100644 stdlib/LinearAlgebra/src/transpose.jl create mode 100644 stdlib/LinearAlgebra/src/triangular.jl create mode 100644 stdlib/LinearAlgebra/src/tridiag.jl create mode 100644 stdlib/LinearAlgebra/src/uniformscaling.jl create mode 100644 stdlib/LinearAlgebra/test/addmul.jl create mode 100644 stdlib/LinearAlgebra/test/adjtrans.jl create mode 100644 stdlib/LinearAlgebra/test/ambiguous_exec.jl create mode 100644 stdlib/LinearAlgebra/test/bidiag.jl create mode 100644 stdlib/LinearAlgebra/test/blas.jl create mode 100644 stdlib/LinearAlgebra/test/bunchkaufman.jl create mode 100644 stdlib/LinearAlgebra/test/cholesky.jl create mode 100644 stdlib/LinearAlgebra/test/dense.jl create mode 100644 stdlib/LinearAlgebra/test/diagonal.jl create mode 100644 stdlib/LinearAlgebra/test/eigen.jl create mode 100644 stdlib/LinearAlgebra/test/generic.jl create mode 100644 stdlib/LinearAlgebra/test/givens.jl create mode 100644 stdlib/LinearAlgebra/test/hessenberg.jl create mode 100644 stdlib/LinearAlgebra/test/lapack.jl create mode 100644 stdlib/LinearAlgebra/test/lq.jl create mode 100644 stdlib/LinearAlgebra/test/lu.jl create mode 100644 stdlib/LinearAlgebra/test/matmul.jl create mode 100644 stdlib/LinearAlgebra/test/pinv.jl create mode 100644 stdlib/LinearAlgebra/test/qr.jl create mode 100644 stdlib/LinearAlgebra/test/runtests.jl create mode 100644 stdlib/LinearAlgebra/test/schur.jl create mode 100644 stdlib/LinearAlgebra/test/special.jl create mode 100644 stdlib/LinearAlgebra/test/structuredbroadcast.jl create mode 100644 stdlib/LinearAlgebra/test/svd.jl create mode 100644 stdlib/LinearAlgebra/test/symmetric.jl create mode 100644 stdlib/LinearAlgebra/test/testgroups create mode 100644 stdlib/LinearAlgebra/test/testutils.jl create mode 100644 stdlib/LinearAlgebra/test/triangular.jl create mode 100644 stdlib/LinearAlgebra/test/trickyarithmetic.jl create mode 100644 stdlib/LinearAlgebra/test/tridiag.jl create mode 100644 stdlib/LinearAlgebra/test/uniformscaling.jl create mode 100644 stdlib/Logging/Project.toml create mode 100644 stdlib/Logging/docs/src/index.md create mode 100644 stdlib/Logging/src/ConsoleLogger.jl create mode 100644 stdlib/Logging/src/Logging.jl create mode 100644 stdlib/Logging/test/runtests.jl create mode 100644 stdlib/Makefile create mode 100644 stdlib/Markdown/Project.toml create mode 100644 stdlib/Markdown/docs/src/index.md create mode 100644 stdlib/Markdown/src/Common/Common.jl create mode 100644 stdlib/Markdown/src/Common/block.jl create mode 100644 stdlib/Markdown/src/Common/inline.jl create mode 100644 stdlib/Markdown/src/GitHub/GitHub.jl create mode 100644 stdlib/Markdown/src/GitHub/table.jl create mode 100644 stdlib/Markdown/src/IPython/IPython.jl create mode 100644 stdlib/Markdown/src/Julia/Julia.jl create mode 100644 stdlib/Markdown/src/Julia/interp.jl create mode 100644 stdlib/Markdown/src/Markdown.jl create mode 100644 stdlib/Markdown/src/parse/config.jl create mode 100644 stdlib/Markdown/src/parse/parse.jl create mode 100644 stdlib/Markdown/src/parse/util.jl create mode 100644 stdlib/Markdown/src/render/html.jl create mode 100644 stdlib/Markdown/src/render/latex.jl create mode 100644 stdlib/Markdown/src/render/plain.jl create mode 100644 stdlib/Markdown/src/render/rich.jl create mode 100644 stdlib/Markdown/src/render/rst.jl create mode 100644 stdlib/Markdown/src/render/terminal/formatting.jl create mode 100644 stdlib/Markdown/src/render/terminal/render.jl create mode 100644 stdlib/Markdown/test/runtests.jl create mode 100644 stdlib/Mmap/Project.toml create mode 100644 stdlib/Mmap/docs/src/index.md create mode 100644 stdlib/Mmap/src/Mmap.jl create mode 100644 stdlib/Mmap/test/runtests.jl create mode 100644 stdlib/Pkg.version create mode 100644 stdlib/Printf/Project.toml create mode 100644 stdlib/Printf/docs/src/index.md create mode 100644 stdlib/Printf/src/Printf.jl create mode 100644 stdlib/Printf/test/runtests.jl create mode 100644 stdlib/Profile/Project.toml create mode 100644 stdlib/Profile/docs/src/index.md create mode 100644 stdlib/Profile/src/Profile.jl create mode 100644 stdlib/Profile/test/runtests.jl create mode 100644 stdlib/REPL/Project.toml create mode 100644 stdlib/REPL/docs/src/index.md create mode 100644 stdlib/REPL/src/LineEdit.jl create mode 100644 stdlib/REPL/src/REPL.jl create mode 100644 stdlib/REPL/src/REPLCompletions.jl create mode 100644 stdlib/REPL/src/TerminalMenus/AbstractMenu.jl create mode 100644 stdlib/REPL/src/TerminalMenus/LICENSE.md create mode 100644 stdlib/REPL/src/TerminalMenus/MultiSelectMenu.jl create mode 100644 stdlib/REPL/src/TerminalMenus/RadioMenu.jl create mode 100644 stdlib/REPL/src/TerminalMenus/TerminalMenus.jl create mode 100644 stdlib/REPL/src/TerminalMenus/config.jl create mode 100644 stdlib/REPL/src/TerminalMenus/util.jl create mode 100644 stdlib/REPL/src/Terminals.jl create mode 100644 stdlib/REPL/src/docview.jl create mode 100644 stdlib/REPL/src/emoji_symbols.jl create mode 100644 stdlib/REPL/src/latex_symbols.jl create mode 100644 stdlib/REPL/test/FakeTerminals.jl create mode 100644 stdlib/REPL/test/TerminalMenus/multiselect_menu.jl create mode 100644 stdlib/REPL/test/TerminalMenus/radio_menu.jl create mode 100644 stdlib/REPL/test/TerminalMenus/runtests.jl create mode 100644 stdlib/REPL/test/lineedit.jl create mode 100644 stdlib/REPL/test/repl.jl create mode 100644 stdlib/REPL/test/replcompletions.jl create mode 100644 stdlib/REPL/test/runtests.jl create mode 100644 stdlib/Random/Project.toml create mode 100644 stdlib/Random/docs/src/index.md create mode 100644 stdlib/Random/src/DSFMT.jl create mode 100644 stdlib/Random/src/RNGs.jl create mode 100644 stdlib/Random/src/Random.jl create mode 100644 stdlib/Random/src/generation.jl create mode 100644 stdlib/Random/src/misc.jl create mode 100644 stdlib/Random/src/normal.jl create mode 100644 stdlib/Random/test/runtests.jl create mode 100644 stdlib/SHA/LICENSE.md create mode 100644 stdlib/SHA/Project.toml create mode 100644 stdlib/SHA/docs/src/index.md create mode 100644 stdlib/SHA/src/SHA.jl create mode 100644 stdlib/SHA/src/base_functions.jl create mode 100644 stdlib/SHA/src/common.jl create mode 100644 stdlib/SHA/src/constants.jl create mode 100644 stdlib/SHA/src/hmac.jl create mode 100644 stdlib/SHA/src/sha1.jl create mode 100644 stdlib/SHA/src/sha2.jl create mode 100644 stdlib/SHA/src/sha3.jl create mode 100644 stdlib/SHA/src/types.jl create mode 100644 stdlib/SHA/test/perf.jl create mode 100644 stdlib/SHA/test/runtests.jl create mode 100644 stdlib/Serialization/Project.toml create mode 100644 stdlib/Serialization/docs/src/index.md create mode 100644 stdlib/Serialization/src/Serialization.jl create mode 100644 stdlib/Serialization/src/precompile.jl create mode 100644 stdlib/Serialization/test/runtests.jl create mode 100644 stdlib/SharedArrays/Project.toml create mode 100644 stdlib/SharedArrays/docs/src/index.md create mode 100644 stdlib/SharedArrays/src/SharedArrays.jl create mode 100644 stdlib/SharedArrays/test/runtests.jl create mode 100644 stdlib/Sockets/Project.toml create mode 100644 stdlib/Sockets/docs/src/index.md create mode 100644 stdlib/Sockets/src/IPAddr.jl create mode 100644 stdlib/Sockets/src/PipeServer.jl create mode 100644 stdlib/Sockets/src/Sockets.jl create mode 100644 stdlib/Sockets/src/addrinfo.jl create mode 100644 stdlib/Sockets/test/nettest.jl create mode 100644 stdlib/Sockets/test/runtests.jl create mode 100644 stdlib/SparseArrays/Project.toml create mode 100644 stdlib/SparseArrays/docs/src/index.md create mode 100644 stdlib/SparseArrays/src/SparseArrays.jl create mode 100644 stdlib/SparseArrays/src/abstractsparse.jl create mode 100644 stdlib/SparseArrays/src/deprecated.jl create mode 100644 stdlib/SparseArrays/src/higherorderfns.jl create mode 100644 stdlib/SparseArrays/src/linalg.jl create mode 100644 stdlib/SparseArrays/src/sparseconvert.jl create mode 100644 stdlib/SparseArrays/src/sparsematrix.jl create mode 100644 stdlib/SparseArrays/src/sparsevector.jl create mode 100644 stdlib/SparseArrays/test/ambiguous_exec.jl create mode 100644 stdlib/SparseArrays/test/higherorderfns.jl create mode 100644 stdlib/SparseArrays/test/runtests.jl create mode 100644 stdlib/SparseArrays/test/sparse.jl create mode 100644 stdlib/SparseArrays/test/sparsevector.jl create mode 100644 stdlib/SparseArrays/test/testgroups create mode 100644 stdlib/Statistics/Project.toml create mode 100644 stdlib/Statistics/docs/src/index.md create mode 100644 stdlib/Statistics/src/Statistics.jl create mode 100644 stdlib/Statistics/test/runtests.jl create mode 100644 stdlib/SuiteSparse/Project.toml create mode 100644 stdlib/SuiteSparse/src/SuiteSparse.jl create mode 100644 stdlib/SuiteSparse/src/cholmod.jl create mode 100644 stdlib/SuiteSparse/src/cholmod_h.jl create mode 100644 stdlib/SuiteSparse/src/deprecated.jl create mode 100644 stdlib/SuiteSparse/src/spqr.jl create mode 100644 stdlib/SuiteSparse/src/umfpack.jl create mode 100644 stdlib/SuiteSparse/src/umfpack_h.jl create mode 100644 stdlib/SuiteSparse/test/cholmod.jl create mode 100644 stdlib/SuiteSparse/test/runtests.jl create mode 100644 stdlib/SuiteSparse/test/spqr.jl create mode 100644 stdlib/SuiteSparse/test/umfpack.jl create mode 100644 stdlib/Test/Project.toml create mode 100644 stdlib/Test/docs/src/index.md create mode 100644 stdlib/Test/src/Test.jl create mode 100644 stdlib/Test/src/logging.jl create mode 100644 stdlib/Test/test/runtests.jl create mode 100644 stdlib/UUIDs/Project.toml create mode 100644 stdlib/UUIDs/docs/src/index.md create mode 100644 stdlib/UUIDs/src/UUIDs.jl create mode 100644 stdlib/UUIDs/test/runtests.jl create mode 100644 stdlib/Unicode/Project.toml create mode 100644 stdlib/Unicode/docs/src/index.md create mode 100644 stdlib/Unicode/src/Unicode.jl create mode 100644 stdlib/Unicode/test/runtests.jl create mode 100644 sysimage.mk create mode 100644 test/.gitignore create mode 100644 test/Makefile create mode 100644 test/TestPkg/Manifest.toml create mode 100644 test/TestPkg/Project.toml create mode 100644 test/TestPkg/src/TestPkg.jl create mode 100644 test/abstractarray.jl create mode 100644 test/ambiguous.jl create mode 100644 test/arrayops.jl create mode 100644 test/asyncmap.jl create mode 100644 test/backtrace.jl create mode 100644 test/bigint.jl create mode 100644 test/bitarray.jl create mode 100644 test/bitset.jl create mode 100644 test/boundscheck.jl create mode 100644 test/boundscheck_exec.jl create mode 100644 test/broadcast.jl create mode 100644 test/cartesian.jl create mode 100644 test/ccall.jl create mode 100644 test/channels.jl create mode 100644 test/char.jl create mode 100644 test/checked.jl create mode 100644 test/choosetests.jl create mode 100644 test/clangsa/GCPushPop.cpp create mode 100644 test/clangsa/MissingRoots.c create mode 100644 test/client.jl create mode 100644 test/cmdlineargs.jl create mode 100644 test/combinatorics.jl create mode 100644 test/compiler/codegen.jl create mode 100644 test/compiler/contextual.jl create mode 100644 test/compiler/inference.jl create mode 100644 test/compiler/inline.jl create mode 100644 test/compiler/interpreter_exec.jl create mode 100644 test/compiler/irpasses.jl create mode 100644 test/compiler/ssair.jl create mode 100644 test/compiler/validation.jl create mode 100644 test/complex.jl create mode 100644 test/copy.jl create mode 100644 test/core.jl create mode 100644 test/depot/packages/Baz/81oLe/src/Baz.jl create mode 100644 test/depot/packages/Foo/I05Qq/src/Foo.jl create mode 100644 test/deprecation_exec.jl create mode 100644 test/dict.jl create mode 100644 test/docs.jl create mode 100644 test/download.jl create mode 100644 test/embedding/.gitignore create mode 100644 test/embedding/LocalModule.jl create mode 100644 test/embedding/Makefile create mode 100644 test/embedding/embedding-test.jl create mode 100644 test/embedding/embedding.c create mode 100644 test/embedding/include_and_eval.jl create mode 100644 test/enums.jl create mode 100644 test/env.jl create mode 100644 test/error.jl create mode 100644 test/errorshow.jl create mode 100644 test/euler.jl create mode 100644 test/exceptions.jl create mode 100644 test/fastmath.jl create mode 100644 test/file.jl create mode 100644 test/float16.jl create mode 100644 test/floatapprox.jl create mode 100644 test/floatfuncs.jl create mode 100644 test/functional.jl create mode 100644 test/gcext/.gitignore create mode 100644 test/gcext/LocalTest.jl create mode 100644 test/gcext/Makefile create mode 100644 test/gcext/gcext-test.jl create mode 100644 test/gcext/gcext.c create mode 100644 test/generic_map_tests.jl create mode 100644 test/goto.jl create mode 100644 test/grisu.jl create mode 100644 test/hashing.jl create mode 100644 test/int.jl create mode 100644 test/interpreter.jl create mode 100644 test/intfuncs.jl create mode 100644 test/intrinsics.jl create mode 100644 test/iobuffer.jl create mode 100644 test/iostream.jl create mode 100644 test/iterators.jl create mode 100644 test/keywordargs.jl create mode 100644 test/llvmcall.jl create mode 100644 test/llvmcall2.jl create mode 100644 test/llvmpasses/.gitignore create mode 100644 test/llvmpasses/Makefile create mode 100644 test/llvmpasses/aliasscopes.jl create mode 100644 test/llvmpasses/alloc-opt.jl create mode 100644 test/llvmpasses/alloc-opt2.jl create mode 100644 test/llvmpasses/final-lower-gc.ll create mode 100644 test/llvmpasses/gcroots.ll create mode 100644 test/llvmpasses/late-lower-gc.ll create mode 100644 test/llvmpasses/lit.cfg create mode 100644 test/llvmpasses/loopinfo.jl create mode 100644 test/llvmpasses/lower-handlers.ll create mode 100644 test/llvmpasses/muladd.ll create mode 100644 test/llvmpasses/noinline.jl create mode 100644 test/llvmpasses/propagate-addrspace.ll create mode 100644 test/llvmpasses/refinements.ll create mode 100644 test/llvmpasses/returnstwicegc.ll create mode 100644 test/llvmpasses/safepoint_stress.jl create mode 100644 test/llvmpasses/simdloop.ll create mode 100644 test/loading.jl create mode 100644 test/logging.jl create mode 100644 test/math.jl create mode 100644 test/meta.jl create mode 100644 test/misc.jl create mode 100644 test/missing.jl create mode 100644 test/mod2pi.jl create mode 100644 test/mpfr.jl create mode 100644 test/namedtuple.jl create mode 100644 test/netload/memtest.jl create mode 100644 test/numbers.jl create mode 100644 test/offsetarray.jl create mode 100644 test/operators.jl create mode 100644 test/osutils.jl create mode 100644 test/parse.jl create mode 100644 test/path.jl create mode 100644 test/precompile.jl create mode 100644 test/project/Manifest.toml create mode 100644 test/project/Project.toml create mode 100644 test/project/deps/Bar/src/Bar.jl create mode 100644 test/project/deps/Foo1/src/Foo.jl create mode 100644 test/project/deps/Foo2.jl/src/Foo.jl create mode 100644 test/project/deps/Qux.jl create mode 100644 test/ranges.jl create mode 100644 test/rational.jl create mode 100644 test/read.jl create mode 100644 test/reduce.jl create mode 100644 test/reducedim.jl create mode 100644 test/reflection.jl create mode 100644 test/regex.jl create mode 100644 test/reinterpretarray.jl create mode 100644 test/rounding.jl create mode 100644 test/runtests.jl create mode 100644 test/secretbuffer.jl create mode 100644 test/sets.jl create mode 100644 test/show.jl create mode 100644 test/simdloop.jl create mode 100644 test/some.jl create mode 100644 test/sorting.jl create mode 100644 test/spawn.jl create mode 100644 test/specificity.jl create mode 100644 test/stack_overflow.jl create mode 100644 test/stacktraces.jl create mode 100644 test/staged.jl create mode 100644 test/stress.jl create mode 100644 test/strings/basic.jl create mode 100644 test/strings/io.jl create mode 100644 test/strings/search.jl create mode 100644 test/strings/types.jl create mode 100644 test/strings/util.jl create mode 100644 test/subarray.jl create mode 100644 test/subtype.jl create mode 100644 test/syntax.jl create mode 100644 test/sysinfo.jl create mode 100644 test/test_exec.jl create mode 100644 test/test_sourcepath.jl create mode 100644 test/testdefs.jl create mode 100644 test/testenv.jl create mode 100644 test/testhelpers/FakePTYs.jl create mode 100644 test/testhelpers/Furlongs.jl create mode 100644 test/testhelpers/MacroCalls.jl create mode 100644 test/testhelpers/OffsetArrays.jl create mode 100644 test/testhelpers/PhysQuantities.jl create mode 100644 test/testhelpers/Quaternions.jl create mode 100644 test/testhelpers/coverage_file.info create mode 100644 test/testhelpers/coverage_file.jl create mode 100644 test/testhelpers/llvmpasses.jl create mode 100644 test/threads.jl create mode 100644 test/threads_exec.jl create mode 100644 test/triplequote.jl create mode 100644 test/tuple.jl create mode 100644 test/unicode/utf8.jl create mode 100644 test/util/segfault.jl create mode 100644 test/util/throw_error_exception.jl create mode 100644 test/vecelement.jl create mode 100644 test/version.jl create mode 100644 test/worlds.jl create mode 100644 ui/.gitignore create mode 100644 ui/Makefile create mode 100644 ui/repl.c diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 0000000..2e504f1 --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,65 @@ +environment: + global: + CCACHE_DIR: C:\ccache + CYG_MIRROR: http://cygwin.mirror.constant.com + CYG_CACHE: '%CYG_ROOT%\var\cache\setup' + CYG_BASH: '%CYG_ROOT%\bin\bash' + + matrix: + - MINGW_ARCH: "i686" + CYG_ROOT: C:\cygwin + CYG_SETUP: setup-x86.exe + JULIA_TEST_MAXRSS_MB: 500 + + - MINGW_ARCH: "x86_64" + CYG_ROOT: C:\cygwin64 + CYG_SETUP: setup-x86_64.exe + JULIA_TEST_MAXRSS_MB: 450 + +# Only build on master and PR's for now, not personal branches +# Whether or not PR's get built is determined in the webhook settings +branches: + only: + - master + - /^release-.*/ + +# Note: use `[ci skip]` or `[skip ci]` anywhere in the commit message and AppVeyor won't be +# built for that commit. You can use `[skip appveyor]` to explicitly skip AppVeyor and +# allow other CI to still run. +skip_commits: +# Add [av skip] to commit messages for docfixes, etc to reduce load on queue + message: /\[av skip\]/ +# Skip running CI for changes only to the documentation +# https://github.com/JuliaLang/julia/pull/27356#discussion_r192536676 +# files: +# - doc/ + +notifications: + - provider: Email + on_build_success: false + on_build_failure: false + on_build_status_changed: false + +cache: + - '%CYG_CACHE%' + - '%CCACHE_DIR%' + +init: + - git config --global core.autocrlf input + +install: + - '%CYG_ROOT%\%CYG_SETUP% -gnq -R "%CYG_ROOT%" -s "%CYG_MIRROR%" -l "%CYG_CACHE%" -P make,python2,libiconv,curl,time,p7zip,ccache,mingw64-%MINGW_ARCH%-gcc-g++,mingw64-%MINGW_ARCH%-gcc-fortran > NULL 2>&1' + - '%CYG_ROOT%\bin\cygcheck -dc cygwin' + +build_script: + - 'echo Building Julia' + - '%CYG_BASH% -lc "cd $APPVEYOR_BUILD_FOLDER && ./contrib/windows/appveyor_build.sh"' + +test_script: + - 'echo Testing Julia' + - usr\bin\julia -e "Base.require(Main, :InteractiveUtils).versioninfo()" + - usr\bin\julia --sysimage-native-code=no -e "true" + - cd julia-* && .\bin\julia.exe --check-bounds=yes share\julia\test\runtests.jl all && + .\bin\julia.exe --check-bounds=yes share\julia\test\runtests.jl LibGit2/online download + - cd .. + - usr\bin\julia usr\share\julia\test\embedding\embedding-test.jl test\embedding\embedding.exe diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..c84e620 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# treat patches as files that should not be modified +*.patch -text diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..a4c3b6b --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,23 @@ +Julia Community Standards +========================= + +The Julia community is committed to maintaining a welcoming, civil and constructive environment. We expect the following standards to be observed and upheld by all participants in any community forum (mailing lists, GitHub, IRC, etc.). + +**Be respectful and inclusive.** +Please do not use overtly sexual language or imagery, and do not attack anyone based on any aspect of personal identity, including gender, sexuality, religion, ethnicity, race, age or ability. Keep in mind that what you write in public forums is read by many people who don't know you personally, so please refrain from making prejudiced or sexual jokes and comments – even ones that you might consider acceptable in private. Ask yourself if a comment or statement might make someone feel unwelcomed or like an outsider. + +In particular, do not sexualize the term "Julia" or any other aspects of the project. While "Julia" is a female name in many parts of the world, the programming language is not a person and does not have a gender. + +**Give credit.** +All participants in the Julia community are expected to respect copyright laws and ethical attribution standards. This applies to both code and written materials, such as documentation or blog posts. Materials that violate the law, are plagiaristic, or ethically dubious in some way will be removed from officially-maintained lists of resources. + +If you believe one of these standards has been violated, you can either file an issue on an appropriate repository or confidentially contact the [Julia Stewards](https://julialang.org/community/stewards/) at [stewards@julialang.org](mailto:stewards@julialang.org). Keep in mind that most mistakes are due to ignorance rather than malice. + +**Be concise.** +Constructive criticism and suggestions are welcome, but high-traffic forums do not generally have the bandwidth for extensive discourse. Consider writing a blog post if you feel that you have enough to say on a particular subject. + +**Get involved.** +The Julia community is built on a foundation of reciprocity and collaboration. Be aware that most community members contribute on a voluntary basis, so ideas and bug reports are ok, but demands are not. Pull requests are always welcomed – see the [guidelines for contributing](https://github.com/JuliaLang/julia/blob/master/CONTRIBUTING.md) to read about how to get started. + +**Any concerns?** +If you have a conflict or concern that requires resolution, please contact the [Julia Community Stewards](https://julialang.org/community/stewards/). diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..5201c4d --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +custom: https://numfocus.org/donate-to-julia diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..6070714 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,9 @@ +If you have a question please search or post to our Discourse site: https://discourse.julialang.org. +We use the GitHub issue tracker for bug reports and feature requests only. + +If you're submitting a bug report, be sure to include as much relevant information as +possible, including a minimal reproducible example and the output of `versioninfo()`. +If you're experiencing a problem with a particular package, open an issue on that +package's repository instead. + +Thanks for contributing to the Julia project! diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 0000000..bdf8e8d --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,15 @@ +# Security Policy + +## Supported Versions + +The LTS and current stable releases of Julia are the ones supported for security updates. + +| Version | Supported | +| ------- | ------------------ | +| LTS | :white_check_mark: | +| Stable | :white_check_mark: | +| < 1.0 | :x: | + +## Reporting a Vulnerability + +Please report security vulnerabilities by emailing security@julialang.org. diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md new file mode 100644 index 0000000..ca8b032 --- /dev/null +++ b/.github/SUPPORT.md @@ -0,0 +1,12 @@ +# Getting support for Julia + +We use the GitHub issue tracker for bug reports and feature requests only. If +what you'd like to do is best described as a bug report or a code contribution +then you should submit a GitHub issue or pull request as usual. Please see our +[Notes for Julia +Contributors](https://github.com/JuliaLang/julia/blob/master/CONTRIBUTING.md) +for how to file a bug report, our contributor checklist and other helpful +information. But if you have come here for help, or if you are unsure whether +the behavior you're experiencing is a bug, then you should see our [Community +page](https://julialang.org/community/) for a list of other places where you can +get support first. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a4e7f0a --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +/*.tar.gz +/tmp +/dist +/dist-extras +/julia +/julia.bat +/usr +/oprofile_data +/usr-staging +/Make.user +/julia-* +/source-dist.tmp +/source-dist.tmp1 + +*.exe +*.dll +*.dwo +*.do +*.o +*.obj +*.so +*.dylib +*.dSYM +*.jl.cov +*.jl.*.cov +*.jl.mem +*.jl.*.mem +*.ji + +/perf* +.DS_Store diff --git a/.mailmap b/.mailmap new file mode 100644 index 0000000..bcb3c84 --- /dev/null +++ b/.mailmap @@ -0,0 +1,259 @@ +JuliaLang +JuliaLang + +Jeff Bezanson +Jeff Bezanson +Jeff Bezanson +Jeff Bezanson +Jeff Bezanson +Jeff Bezanson +Jeff Bezanson +Jeff Bezanson +Jeff Bezanson +Jeff Bezanson +Jeff Bezanson +Jeff Bezanson +Jeff Bezanson +Jeff Bezanson + +Stefan Karpinski +Stefan Karpinski +Stefan Karpinski + +Viral B. Shah +Viral B. Shah +Viral B. Shah +Viral B. Shah + +George Xing +George Xing + +Stephan Boyer +Stephan Boyer +Stephan Boyer +Stephan Boyer + +Jiahao Chen Jiahao Chen (陈家豪) + +Giuseppe Zingales +Giuseppe Zingales + +Jameson Nash +Jameson Nash +Jameson Nash +Jameson Nash +Jameson Nash + +Alan Edelman + +PlayMyCode +PlayMyCode + +Corey M. Hoffstein +Corey M. Hoffstein + +Stefan Kroboth + +Tim Holy +Tim Holy + +Patrick O'Leary + +Ivan Mantova + +Keno Fischer +Keno Fischer +Keno Fischer +Keno Fischer +Keno Fischer +Keno Fischer + +Harlan Harris +Harlan Harris + +Douglas Bates + +Andreas Noack Jensen +Andreas Noack Jensen +Andreas Noack Jensen +Andreas Noack Jensen + +Westley Argentum Hennigh +Westley Argentum Hennigh + +George V. Neville-Neil +George V. Neville-Neil + +Alessandro Andrioni Silva +Alessandro Andrioni Silva + +Toivo Henningsson +Toivo Henningsson + +Adam Savitzky +Adam Savitzky + +David Slate + +Francois Pepin + +Waldir Pimenta + +Steven G. Johnson +Steven G. Johnson +Steven G. Johnson +Steven G. Johnson +Steven G. Johnson + +Isaiah Norton +Isaiah Norton +Isaiah Norton +Blake Johnson +Blake Johnson + +Marcus Silva +Marcus Silva + +Amit Murthy + +Tanmay Mohapatra +Tanmay Mohapatra + +Dan Luu +Dan Luu + +Kevin Bache +Kevin Bache + +Rick +Rick + +David Smith +David Smith + +Carlos Becker + +Fabian R Lischka + +James J Porter + +Jay Weisskopf + +Joseph Perla + +Jutho +Jutho + +Tomas Lycken +Tomas Lycken + +Simon Byrne +Simon Byrne + +Jake Bolewski + +Leah Hanson + +Lei Wang +Lei Wang + +Kevin Squire + +Michael Fox <415fox@gmail.com> + +Miles Gould + +Pierre-Yves Gerardy + +Ron Rock + +Tony Kelman + +Tobias Knopp + +Ben Arthur +Ben Arthur + +Odd Andersen +Odd Andersen + +Ronan Arraes Jardim Chagas +Ronan Arraes Jardim Chagas + +Tim Besard +Tim Besard + +Tracy Wadleigh +Tracy Wadleigh + +Mike Innes + +Sean Garborg + +Scott P. Jones +Scott P. Jones + +M. Prentis +M. Prentis + +Peter +Peter + +Rafael Fourquet +Rafael Fourquet +Rafael Fourquet + +Dan Wlasiuk +Dan Wlasiuk + +Rene Donner +Rene Donner + +Waldir Pimenta +Waldir Pimenta + +Young Wu + +Daan Huybrechs + +Jey Kottalam +Jey Kottalam + +John Myles White +John Myles White + +Katharine Hyatt +Katharine Hyatt +Katharine Hyatt +Katharine Hyatt +Katharine Hyatt + +Oscar Blumberg +Oscar Blumberg + +Seth Bromberger +Seth Bromberger + +Tomas Lycken +Tomas Lycken + +Tracy Wadleigh +Tracy Wadleigh + +Sacha Verweij +Sacha Verweij +Sacha Verweij +Sacha Verweij <5799177+Sacha0@users.noreply.github.com> + +Kristoffer Carlsson +Kristoffer Carlsson + +Valentin Churavy +Valentin Churavy +Valentin Churavy + +Curtis Vogt +Curtis Vogt + +Rafael Fourquet +Rafael Fourquet diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..665ceba --- /dev/null +++ b/.travis.yml @@ -0,0 +1,153 @@ +language: cpp +sudo: required +dist: trusty +matrix: + include: + - os: linux + env: ARCH="i686" + compiler: "g++-5 -m32" + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - ccache + - libssl1.0.0 + - bar + - time + - binutils + - gcc-5 + - g++-5 + - gcc-5-multilib + - g++-5-multilib + - make:i386 + - libssl-dev:i386 + - gfortran-5 + - gfortran-5-multilib + - os: linux + env: ARCH="x86_64" + compiler: "g++-5 -m64" + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - ccache + - libssl1.0.0 + - bar + - time + - g++-5 + - gfortran-5 + - os: osx + env: ARCH="x86_64" + osx_image: xcode8.3 +cache: ccache +branches: + only: + - master + - /^release-.*/ + - /^v\d+\.\d+\.\d+$/ +notifications: + email: false +before_install: + - make check-whitespace + - if [ `uname` = "Linux" ]; then + contrib/travis_fastfail.sh || exit 1; + mkdir -p $HOME/bin; + ln -s /usr/bin/gcc-5 $HOME/bin/gcc; + ln -s /usr/bin/g++-5 $HOME/bin/g++; + ln -s /usr/bin/gfortran-5 $HOME/bin/gfortran; + ln -s /usr/bin/gcc-5 $HOME/bin/x86_64-linux-gnu-gcc; + ln -s /usr/bin/g++-5 $HOME/bin/x86_64-linux-gnu-g++; + gcc --version; + BAR="bar -i 30"; + BUILDOPTS="-j5 VERBOSE=1 FORCE_ASSERTIONS=1 LLVM_ASSERTIONS=1 USECCACHE=1 USE_BINARYBUILDER_LIBUV=0 USE_BINARYBUILDER_LIBUNWIND=0 USE_BINARYBUILDER_LLVM=0"; + echo "override ARCH=$ARCH" >> Make.user; + sudo sh -c "echo 0 > /proc/sys/net/ipv6/conf/lo/disable_ipv6"; + export JULIA_CPU_THREADS=4; + export JULIA_TEST_MAXRSS_MB=1200; + TESTSTORUN="all"; + elif [ `uname` = "Darwin" ]; then + brew update; + brew install -v jq pv ccache; + export PATH="$(brew --prefix ccache)/libexec:$PATH"; + BAR="pv -i 30"; + contrib/travis_fastfail.sh || exit 1; + brew rm --force gcc gmp mpfr pcre2; + brew install -v gcc gmp mpfr pcre2; + BUILDOPTS="-j3 USECLANG=1 USECCACHE=1 VERBOSE=1 FORCE_ASSERTIONS=1"; + for proj in LLVM LLVM_ASSERTS OPENBLAS SUITESPARSE OPENLIBM; do + BUILDOPTS="$BUILDOPTS USE_BINARYBUILDER_${proj}=1"; + done; + for lib in GMP MPFR LIBUNWIND; do + BUILDOPTS="$BUILDOPTS USE_SYSTEM_$lib=1"; + done; + spawn_DYLD_FALLBACK_LIBRARY_PATH="/usr/local/lib:/lib:/usr/lib"; + export JULIA_MACOS_SPAWN="DYLD_FALLBACK_LIBRARY_PATH=\"$spawn_DYLD_FALLBACK_LIBRARY_PATH\" \$1"; + export BUILDOPTS="$BUILDOPTS spawn=\$(JULIA_MACOS_SPAWN)"; + export JULIA_CPU_THREADS=2; + export JULIA_TEST_MAXRSS_MB=600; + TESTSTORUN="all --skip linalg/triangular subarray"; fi # TODO: re enable these if possible without timing out + - echo "override JULIA_CPU_TARGET=generic;native" >> Make.user + - wget http://http.debian.net/debian/pool/main/m/moreutils/moreutils_0.62.orig.tar.xz + - tar -xJvf moreutils_0.62.orig.tar.xz && mv moreutils-0.62 moreutils +script: + - echo BUILDOPTS=$BUILDOPTS + - export BUILDOPTS + # compile / install dependencies + - contrib/download_cmake.sh + - make -C moreutils mispipe + - make $BUILDOPTS -C base version_git.jl.phony + # capture the log, but only print it if `make deps` fails + # try to show the end of the log first, because this log might be very long (> 4MB) + # and thus be truncated by travis + - moreutils/mispipe "make \$BUILDOPTS NO_GIT=1 -C deps 2> deps-err.log" "$BAR" > deps.log || + { echo "-- deps build log stderr tail 100 --------------------------------------"; + tail -n 100 deps-err.log; + echo "-- deps build log stdout tail 100 --------------------------------------"; + tail -n 100 deps.log; + echo "-- deps build log stderr all -------------------------------------------"; + cat deps-err.log; + echo "-- deps build log stdout all -------------------------------------------"; + cat deps.log; + echo "-- end of deps build log -----------------------------------------------"; + false; } + # compile / install Julia + - make $BUILDOPTS NO_GIT=1 prefix=/tmp/julia release | moreutils/ts -s "%.s" + - make $BUILDOPTS NO_GIT=1 prefix=/tmp/julia install | moreutils/ts -s "%.s" + - make $BUILDOPTS NO_GIT=1 build-stats + - du -sk /tmp/julia/* + - ls -l /tmp/julia/lib + - ls -l /tmp/julia/lib/julia + - FILES_CHANGED=$(git diff --name-only $TRAVIS_COMMIT_RANGE -- || git ls-files) + - cd .. && mv julia julia2 + # run tests + - /tmp/julia/bin/julia --sysimage-native-code=no -e 'true' + # - /tmp/julia/bin/julia-debug --sysimage-native-code=no -e 'true' + - /tmp/julia/bin/julia -e 'Base.require(Main, :InteractiveUtils).versioninfo()' + - pushd /tmp/julia/share/julia/test + # skip tests if only files within the "doc" dir have changed + - if [ $(echo "$FILES_CHANGED" | grep -cv '^doc/') -gt 0 ]; then + /tmp/julia/bin/julia --check-bounds=yes runtests.jl $TESTSTORUN && + /tmp/julia/bin/julia --check-bounds=yes runtests.jl LibGit2/online download; fi + - popd + # test that the embedding code works on our installation + - mkdir /tmp/embedding-test && + make check -C /tmp/julia/share/julia/test/embedding + JULIA="/tmp/julia/bin/julia" + BIN=/tmp/embedding-test + "$(cd julia2 && make print-CC)" + # restore initial state and prepare for travis caching + - mv julia2 julia && + rm -f julia/deps/scratch/libgit2-*/CMakeFiles/CMakeOutput.log + # run the LLVM tests on Linux + - if [ `uname` = "Linux" ]; then + pushd julia && make -C test/llvmpasses && popd; fi + # run the doctests on Linux 64-bit + - if [ `uname` = "Linux" ] && [ $ARCH = "x86_64" ]; then + pushd julia && make -C doc doctest=true && popd; fi +# uncomment the following if failures are suspected to be due to the out-of-memory killer +# - dmesg +after_success: + - if [ `uname` = "Linux" ] && [ $ARCH = "x86_64" ]; then + cd julia && make -C doc deploy; fi diff --git a/CITATION.bib b/CITATION.bib new file mode 100644 index 0000000..c5c851d --- /dev/null +++ b/CITATION.bib @@ -0,0 +1,75 @@ +% This article is the definitive citation for Julia. +@article{Julia-2017, + title={Julia: A fresh approach to numerical computing}, + author={Bezanson, Jeff and Edelman, Alan and Karpinski, Stefan and Shah, Viral B}, + journal={SIAM {R}eview}, + volume={59}, + number={1}, + pages={65--98}, + year={2017}, + publisher={SIAM}, + doi={10.1137/141000671} +} + +% The following citations are about specific aspects of Julia. + +@article{Julia-2019-a, + author = {Bezanson, Jeff and Chen, Jiahao and Chung, Benjamin and Karpinski, Stefan and Shah, Viral B. and Vitek, Jan and Zoubritzky, Lionel}, + title = {Julia: Dynamism and Performance Reconciled by Design}, + journal = {Proc. ACM Program. Lang.}, + issue_date = {November 2018}, + volume = {2}, + number = {OOPSLA}, + month = oct, + year = {2018}, + issn = {2475-1421}, + pages = {120:1--120:23}, + articleno = {120}, + numpages = {23}, + url = {http://doi.acm.org/10.1145/3276490}, + doi = {10.1145/3276490}, + acmid = {3276490}, + publisher = {ACM}, + address = {New York, NY, USA}, + keywords = {dynamic languages, just-in-time compilation, multiple dispatch}, +} + +@article{Julia-2019-b, + author = {Zappa Nardelli, Francesco and Belyakova, Julia and Pelenitsyn, Artem and Chung, Benjamin and Bezanson, Jeff and Vitek, Jan}, + title = {Julia Subtyping: A Rational Reconstruction}, + journal = {Proc. ACM Program. Lang.}, + issue_date = {November 2018}, + volume = {2}, + number = {OOPSLA}, + month = oct, + year = {2018}, + issn = {2475-1421}, + pages = {113:1--113:27}, + articleno = {113}, + numpages = {27}, + url = {http://doi.acm.org/10.1145/3276483}, + doi = {10.1145/3276483}, + acmid = {3276483}, + publisher = {ACM}, + address = {New York, NY, USA}, + keywords = {Multiple Dispatch, Subtyping}, +} + +@inproceedings{Julia-2014, + author = {Bezanson, Jeff and Chen, Jiahao and Karpinski, Stefan and Shah, Viral and Edelman, Alan}, + title = {Array Operators Using Multiple Dispatch: A Design Methodology for Array Implementations in Dynamic Languages}, + booktitle = {Proceedings of ACM SIGPLAN International Workshop on Libraries, Languages, and Compilers for Array Programming}, + series = {ARRAY'14}, + year = {2014}, + isbn = {978-1-4503-2937-8}, + location = {Edinburgh, United Kingdom}, + pages = {56:56--56:61}, + articleno = {56}, + numpages = {6}, + url = {http://doi.acm.org/10.1145/2627373.2627383}, + doi = {10.1145/2627373.2627383}, + acmid = {2627383}, + publisher = {ACM}, + address = {New York, NY, USA}, + keywords = {Julia, array indexing, dynamic dispatch, multiple dispatch, static analysis, type inference}, +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..e97afc5 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,326 @@ +# Notes for Julia Contributors + +Hi! If you are new to the Julia community: welcome, and thanks for trying Julia. Please be sure to respect our [community standards](https://julialang.org/community/standards) in all interactions. + +If you are already familiar with Julia itself, this blog post by Katharine Hyatt on [Making your first Julia pull request](https://kshyatt.github.io/post/firstjuliapr/) is a great way to get started. + +## Learning Julia + +[The learning page](https://julialang.org/learning) has a great list of resources for new and experienced users alike. [This tutorial video](https://www.youtube.com/watch?v=vWkgEddb4-A) is one recommended starting point, as is the "[Invitation to Julia](https://www.youtube.com/watch?v=gQ1y5NUD_RI)" workshop video from JuliaCon 2015 ([slide materials here](https://github.com/dpsanders/invitation_to_julia)). The [Julia documentation](https://docs.julialang.org/en/latest) covers the language and core library features, and is searchable. + +## Before filing an issue + +- Reporting a potential bug? Please read the "[How to file a bug report](https://github.com/JuliaLang/julia/blob/master/CONTRIBUTING.md#how-to-file-a-bug-report)" section to make sure that all necessary information is included. + +- Contributing code? Be sure to review the [contributor checklist](https://github.com/JuliaLang/julia/blob/master/CONTRIBUTING.md#contributor-checklist) for helpful tips on the tools we use to build Julia. + +- Library feature requests are generally not accepted on this issue tracker. New libraries should be developed as [packages](https://julialang.github.io/Pkg.jl/v1/creating-packages/). Discuss ideas for libraries at the [Julia Discourse forum](https://discourse.julialang.org). Doing so will often lead to pointers to existing projects and bring together collaborators with common interests. + +## Contributor Checklist + +* Create a [GitHub account](https://github.com/signup/free). + +* [Fork Julia](https://github.com/JuliaLang/julia/fork). + +* Build the software and libraries (the first time takes a while, but it's fast after that). Detailed build instructions are in the [README](https://github.com/JuliaLang/julia/tree/master/README.md). Julia depends on several external packages; most are automatically downloaded and installed, but are less frequently updated than Julia itself. + +* Keep Julia current. Julia is a fast-moving target, and many details of the language are still settling out. Keep the repository up-to-date and rebase work-in-progress frequently to make merges simpler. + +* Learn to use [git](https://git-scm.com), the version control system used by GitHub and the Julia project. Try a tutorial such as the one [provided by GitHub](https://try.GitHub.io/levels/1/challenges/1). + +* Review discussions on the [Julia Discourse forum](https://discourse.julialang.org). + +* For more detailed tips, read the [submission guide](https://github.com/JuliaLang/julia/blob/master/CONTRIBUTING.md#submitting-contributions) below. + +* Relax and have fun! + +## How to file a bug report + +A useful bug report filed as a GitHub issue provides information about how to reproduce the error. + +1. Before opening a new [GitHub issue](https://github.com/JuliaLang/julia/issues): + - Try searching the existing issues or the [Julia Discourse forum](https://discourse.julialang.org) to see if someone else has already noticed the same problem. + - Try some simple debugging techniques to help isolate the problem. + - Try running the code with the debug build of Julia with `make debug`, which produces the `usr/bin/julia-debug`. + - Consider running `julia-debug` with a debugger such as `gdb` or `lldb`. Obtaining even a simple [backtrace](http://www.unknownroad.com/rtfm/gdbtut/gdbsegfault.html) is very useful. + - If Julia segfaults, try following [these debugging tips](https://docs.julialang.org/en/latest/devdocs/backtraces#Reporting-and-analyzing-crashes-(segfaults)-1) to help track down the specific origin of the bug. + +2. If the problem is caused by a Julia package rather than core Julia, file a bug report with the relevant package author rather than here. + +3. When filing a bug report, provide where possible: + - The full error message, including the backtrace. + - A minimal working example, i.e. the smallest chunk of code that triggers the error. Ideally, this should be code that can be pasted into a REPL or run from a source file. If the code is larger than (say) 50 lines, consider putting it in a [gist](https://gist.github.com). + - The version of Julia as provided by the `versioninfo()` command. Occasionally, the longer output produced by `versioninfo(verbose = true)` may be useful also, especially if the issue is related to a specific package. + +4. When pasting code blocks or output, put triple backquotes (\`\`\`) around the text so GitHub will format it nicely. Code statements should be surrounded by single backquotes (\`). Be aware that the `@` sign tags users on GitHub, so references to macros should always be in single backquotes. See [GitHub's guide on Markdown](https://guides.github.com/features/mastering-markdown) for more formatting tricks. + +## Submitting contributions + +### Contributing a Julia package + +Julia has a built-in [package manager](https://julialang.github.io/Pkg.jl/v1/) based on `git`. A number of [packages](https://pkg.julialang.org) across many domains are already available for Julia. Developers are encouraged to provide their libraries as a Julia package. The manual provides instructions on [creating Julia packages](https://julialang.github.io/Pkg.jl/v1/creating-packages/). + +For developers who need to wrap C libraries so that they can be called from Julia, the [Clang.jl](https://github.com/ihnorton/Clang.jl) package can help generate the wrappers automatically from the C header files. + +### Package Compatibility Across Releases + +Sometimes, you might find that while your package works +on the current release, it might not work on the upcoming release or nightly. +This is due to the fact that some Julia functions (after some discussion) +could be deprecated or removed altogether. This may cause your package to break or +throw a number of deprecation warnings on usage. Therefore it is highly recommended +to port your package to latest Julia release. + +However, porting a package to the latest release may cause the package to break on +earlier Julia releases. To maintain compatibility across releases, use +[`Compat.jl`](https://github.com/JuliaLang/Compat.jl). Find the fix for your package +from the README, and specify the minimum version of Compat that provides the fix +in your REQUIRE file. To find the correct minimum version, refer to +[this guide](https://github.com/JuliaLang/Compat.jl/#tagging-the-correct-minimum-version-of-compat). + +### Writing tests + +There are never enough tests. Track [code coverage at Coveralls](https://coveralls.io/r/JuliaLang/julia), and help improve it. + +1. Go visit https://coveralls.io/r/JuliaLang/julia. + +2. Browse through the source files and find some untested functionality (highlighted in red) that you think you might be able to write a test for. + +3. Write a test that exercises this functionality---you can add your test to one of the existing files, or start a new one, whichever seems most appropriate to you. If you're adding a new test file, make sure you include it in the list of tests in `test/choosetests.jl`. https://docs.julialang.org/en/latest/stdlib/Test/ may be helpful in explaining how the testing infrastructure works. + +4. Run `make test-all` to rebuild Julia and run your new test(s). If you had to fix a bug or add functionality in `base`, this will ensure that your test passes and that you have not introduced extraneous whitespace. + +5. Submit the test as a pull request (PR). + +* Code for the buildbot configuration is maintained at: https://github.com/staticfloat/julia-buildbot +* You can see the current buildbot setup at: https://build.julialang.org/builders +* [Issue 9493](https://github.com/JuliaLang/julia/issues/9493) and [issue 11885](https://github.com/JuliaLang/julia/issues/11885) have more detailed discussion on code coverage. + +Coveralls shows functionality that still needs "proof of concept" tests. These are important, as are tests for tricky edge cases, such as converting between integer types when the number to convert is near the maximum of the range of one of the integer types. Even if a function already has some coverage on Coveralls, it may still benefit from tests for edge cases. + +### Improving documentation + +*By contributing documentation to Julia, you are agreeing to release it under the [MIT License](https://github.com/JuliaLang/julia/tree/master/LICENSE.md).* + +Julia's documentation source files are stored in the `doc/` directory and all docstrings are found in `base/`. Like everything else these can be modified using `git`. Documentation is built with [Documenter.jl](https://github.com/JuliaDocs/Documenter.jl), which uses Markdown syntax. The HTML documentation can be built locally by running + +``` +make docs +``` + +from Julia's root directory. This will rebuild the Julia system image, then install or update the package dependencies required to build the documentation, and finally build the HTML documentation and place the resulting files in `doc/_build/html/`. + +> **Note** +> +> When making changes to any of Julia's documentation it is recommended that you run `make docs` to check the your changes are valid and do not produce any errors before opening a pull request. + +Below are outlined the three most common types of documentation changes and the steps required to perform them. Please note that the following instructions do not cover the full range of features provided by Documenter.jl. Refer to [Documenter's documentation](https://juliadocs.github.io/Documenter.jl/stable) if you encounter anything that is not covered by the sections below. + +#### Modifying files in `doc/src/` + +Most of the source text for the Julia Manual is located in `doc/src/`. To update or add new text to any one of the existing files the following steps should be followed: + +1. update the text in whichever `.md` files are applicable; +2. run `make docs` from the root directory; +3. check the output in `doc/_build/html/` to make sure the changes are correct; +4. commit your changes and open a pull request. + +> **Note** +> +> The contents of `doc/_build/` does **not** need to be committed when you make changes. + +To add a **new file** to `doc/src/` rather than updating a file replace step `1` above with + +1. add the file to the appropriate subdirectory in `doc/src/` and also add the file path to the `PAGES` vector in `doc/make.jl`. + +#### Modifying an existing docstring in `base/` + +All docstrings are written inline above the methods or types they are associated with and can be found by clicking on the `source` link that appears below each docstring in the HTML file. The steps needed to make a change to an existing docstring are listed below: + +1. find the docstring in `base/`; +2. update the text in the docstring; +3. run `make docs` from the root directory; +4. check the output in `doc/_build/html/` to make sure the changes are correct; +5. commit your changes and open a pull request. + +#### Adding a new docstring to `base/` + +The steps required to add a new docstring are listed below: + +1. find a suitable definition in `base/` that the docstring will be most applicable to; +2. add a docstring above the definition; +3. find a suitable `@docs` code block in one of the `doc/src/stdlib/` files where you would like the docstring to appear; +4. add the name of the definition to the `@docs` code block. For example, with a docstring added to a function `bar` + + ```julia + "..." + function bar(args...) + # ... + end + ``` + + you would add the name `bar` to a `@docs` block in `doc/src/stdlib/` + + ```@docs + foo + bar # <-- Added this one. + baz + ``` + +5. run `make docs` from the root directory; +6. check the output in `doc/_build/html` to make sure the changes are correct; +7. commit your changes and open a pull request. + +#### Doctests + +Examples written within docstrings can be used as testcases known as "doctests" by annotating code blocks with `jldoctest`. + + ```jldoctest + julia> uppercase("Docstring test") + "DOCSTRING TEST" + ``` + +A doctest needs to match an interactive REPL including the `julia>` prompt. To run doctests you need to run `make -C doc doctest=true` from the root directory. It is recommended to add the header `# Examples` above the doctests. + +#### News-worthy changes + +For new functionality and other substantial changes, add a brief summary to `NEWS.md`. The news item should cross reference the pull request (PR) parenthetically, in the form `([#pr])`. To add the PR reference number, first create the PR, then push an additional commit updating `NEWS.md` with the PR reference number. We periodically run `./julia doc/NEWS-update.jl` from the julia directory to update the cross-reference links, but this should not be done in a typical PR in order to avoid conflicting commits. + +#### Annotations for new features, deprecations and behavior changes + +API additions and deprecations, and minor behavior changes are allowed in minor version releases. +For documented features that are part of the public API, a compatibility note should be added into +the manual or the docstring. It should state the Julia minor version that changed the behavior +and have a brief message describing the change. + +At the moment, this should always be done with the following `compat` admonition +(so that it would be possible to programmatically find the annotations in the future): + + ``` + !!! compat "Julia 1.X" + This method was added in Julia 1.X. + ``` + +### Contributing to core functionality or base libraries + +*By contributing code to Julia, you are agreeing to release it under the [MIT License](https://github.com/JuliaLang/julia/tree/master/LICENSE.md).* + +The Julia community uses [GitHub issues](https://github.com/JuliaLang/julia/issues) to track and discuss problems, feature requests, and pull requests (PR). You can make pull requests for incomplete features to get code review. The convention is to prefix the pull request title with "WIP:" for Work In Progress, or "RFC:" for Request for Comments when work is completed and ready for merging. This will prevent accidental merging of work that is in progress. + +Note: These instructions are for adding to or improving functionality in the base library. Before getting started, it can be helpful to discuss the proposed changes or additions on the [Julia Discourse forum](https://discourse.julialang.org) or in a GitHub issue---it's possible your proposed change belongs in a package rather than the core language. Also, keep in mind that changing stuff in the base can potentially break a lot of things. Finally, because of the time required to build Julia, note that it's usually faster to develop your code in stand-alone files, get it working, and then migrate it into the base libraries. + +Add new code to Julia's base libraries as follows (this is the "basic" approach; see a more efficient approach in the next section): + + 1. Edit the appropriate file in the `base/` directory, or add new files if necessary. Create tests for your functionality and add them to files in the `test/` directory. If you're editing C or Scheme code, most likely it lives in `src/` or one of its subdirectories, although some aspects of Julia's REPL initialization live in `ui/`. + + 2. Add any new files to `sysimg.jl` in order to build them into the Julia system image. + + 3. Add any necessary export symbols in `exports.jl`. + + 4. Include your tests in `test/Makefile` and `test/choosetests.jl`. + +Build as usual, and do `make clean testall` to test your contribution. If your contribution includes changes to Makefiles or external dependencies, make sure you can build Julia from a clean tree using `git clean -fdx` or equivalent (be careful – this command will delete any files lying around that aren't checked into git). + +Note: You can run specific test files with `make`: + + make test-bitarray + +or with the `runtests.jl` script, e.g. to run `test/bitarray.jl` and `test/math.jl`: + + ./usr/bin/julia test/runtests.jl bitarray math + +Make sure that [Travis](https://www.travis-ci.org) greenlights the pull request with a [`Good to merge` message](https://blog.travis-ci.com/2012-09-04-pull-requests-just-got-even-more-awesome). + +#### Modifying base more efficiently with Revise.jl + +[Revise](https://github.com/timholy/Revise.jl) is a package that +tracks changes in source files and automatically updates function +definitions in your running Julia session. Using it, you can make +extensive changes to Base without needing to rebuild in order to test +your changes. + +Here is the standard procedure: + +1. If you are planning changes to any types or macros, make those + changes and build julia using `make`. (This is + necessary because `Revise` cannot handle changes to type + definitions or macros.) Unless it's + required to get Julia to build, you do not have to add any + functionality based on the new types, just the type definitions + themselves. + +2. Start a Julia REPL session. Then issue the following commands: + +```julia +using Revise # if you aren't launching it in your `.julia/config/startup.jl` +Revise.track(Base) +``` + +3. Edit files in `base/`, save your edits, and test the + functionality. + +If you need to restart your Julia session, just start at step 2 above. +`Revise.track(Base)` will note any changes from when Julia was last +built and incorporate them automatically. You only need to rebuild +Julia if you made code-changes that Revise cannot handle. + +### Code Formatting Guidelines + +#### General Formatting Guidelines for Julia code contributions + + - 4 spaces per indentation level, no tabs + - use whitespace to make the code more readable + - no whitespace at the end of a line (trailing whitespace) + - comments are good, especially when they explain the algorithm + - try to adhere to a 92 character line length limit + - use upper camel case convention for modules, type names + - use lower case with underscores for method names + - it is generally preferred to use ASCII operators and identifiers over + Unicode equivalents whenever possible + - in docstring refer to the language as "Julia" and the executable as "`julia`" + +#### General Formatting Guidelines For C code contributions + + - 4 spaces per indentation level, no tabs + - space between if and ( (if (x) ...) + - newline before opening { in function definitions + - f(void) for 0-argument function declarations + - newline between } and else instead of } else { + - if one part of an if..else chain uses { } then all should + - no whitespace at the end of a line + +### Git Recommendations For Pull Requests + + - Avoid working from the `master` branch of your fork, creating a new branch will make it easier if Julia's `master` changes and you need to update your pull request. + - Try to [squash](http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html) together small commits that make repeated changes to the same section of code so your pull request is easier to review, and Julia's history won't have any broken intermediate commits. A reasonable number of separate well-factored commits is fine, especially for larger changes. + - If any conflicts arise due to changes in Julia's `master`, prefer updating your pull request branch with `git rebase` versus `git merge` or `git pull`, since the latter will introduce merge commits that clutter the git history with noise that makes your changes more difficult to review. + - If you see any unrelated changes to submodules like `deps/libuv`, `deps/openlibm`, etc., try running `git submodule update` first. + - Descriptive commit messages are good. + - Using `git add -p` or `git add -i` can be useful to avoid accidentally committing unrelated changes. + - GitHub does not send notifications when you push a new commit to a pull request, so please add a comment to the pull request thread to let reviewers know when you've made changes. + - When linking to specific lines of code in discussion of an issue or pull request, hit the `y` key while viewing code on GitHub to reload the page with a URL that includes the specific version that you're viewing. That way any lines of code that you refer to will still make sense in the future, even if the content of the file changes. + - Whitespace can be automatically removed from existing commits with `git rebase`. + - To remove whitespace for the previous commit, run + `git rebase --whitespace=fix HEAD~1`. + - To remove whitespace relative to the `master` branch, run + `git rebase --whitespace=fix master`. + +## Resources + +* Julia + - **Homepage:** + - **Community:** + - **Source code:** + - **Documentation:** + - **Code coverage:** + +* Design of Julia + - [Julia: A Fresh Approach to Numerical Computing](https://julialang.org/research/julia-fresh-approach-BEKS.pdf) + - [Julia: Dynamism and Performance Reconciled by Design](http://janvitek.org/pubs/oopsla18b.pdf) + - [All Julia Publications](https://julialang.org/research) + +* Using GitHub + - [Using Julia with GitHub (video)](https://www.youtube.com/watch?v=wnFYV3ZKtOg) + - [Using Julia on GitHub (notes for video)](https://gist.github.com/2712118#file_Julia_git_pull_request.md) + - [General GitHub documentation](https://help.github.com) + - [GitHub pull request documentation](https://help.github.com/articles/creating-a-pull-request/) diff --git a/HISTORY.md b/HISTORY.md new file mode 100644 index 0000000..a0cda2f --- /dev/null +++ b/HISTORY.md @@ -0,0 +1,5162 @@ +Julia v1.2 Release Notes +======================== + +New language features +--------------------- + +* Argument splatting (`x...`) can now be used in calls to the `new` pseudo-function in + constructors ([#30577]). +* Support for Unicode 12.0.0 ([#31561]). +* Added `⋆` (`\star`) as unary operator ([#31604]). + +Language changes +---------------- + +* Empty entries in `JULIA_DEPOT_PATH` are now expanded to default depot entries ([#31009]). + +Multi-threading changes +----------------------- + +* The `Condition` type now has a thread-safe replacement, accessed as `Threads.Condition`. + With that addition, task scheduling primitives such as `ReentrantLock` are now thread-safe ([#30061]). +* It is possible to schedule and switch Tasks during `@threads` loops, and perform limited I/O ([#31438]). + +Build system changes +-------------------- + +* The build system now prefers downloading prebuilt binary tarballs for most dependencies on + supported systems, disable by setting `USE_BINARYBUILDER=0` at `make` time ([#31441]). + +New library functions +--------------------- + +* `getipaddrs()` function returns all the IP addresses of the local machine, with IPv4 addresses sorting before IPv6 addresses ([#30349, #30604]). +* `getipaddr(addr_type)` and `getipaddrs(addr_type)` functions returns an IP address(es) of the desired type of the local machine ([#30604]). +* Added `Base.hasproperty` and `Base.hasfield` ([#28850]). +* One argument `!=(x)`, `>(x)`, `>=(x)`, `<(x)`, `<=(x)` have been added, returning partially-applied + versions of the functions, similar to the existing `==(x)` and `isequal(x)` methods ([#30915]). + +Standard library changes +------------------------ + +* `Enum` now behaves like a scalar when used in broadcasting ([#30670]). +* If a `pipeline` is specified with `append=true` set, but no redirection, an `ArgumentError` + is thrown, rather than a `ErrorException` ([#27900]). +* Functions that invoke commands (e.g. `run(::Cmd)`) now throw a `ProcessFailedException` + rather than an `ErrorException`, if those commands exit with non-zero exit code ([#27900]). +* The `extrema` function now accepts a function argument in the same manner as `minimum` and + `maximum` ([#30323]). +* `hasmethod` can now check for matching keyword argument names ([#30712]). +* `startswith` and `endswith` now accept a `Regex` for the second argument ([#29790]). +* `retry` supports arbitrary callable objects ([#30382]). +* A no-argument constructor for `Ptr{T}` has been added which constructs a null pointer ([#30919]). +* `strip` now accepts a function argument in the same manner as `lstrip` and `rstrip` ([#31211]). +* `mktempdir` now accepts a `prefix` keyword argument to customize the file name ([#31230], [#22922]). +* `keytype` and `valtype` now work on `AbstractArray`, and return the `eltype` of `keys(...)` and + `values(...)` respectively ([#27749]). +* `nextfloat(::BigFloat)` and `prevfloat(::BigFloat)` now returns a value with the same precision + as their argument, which means that (in particular) `nextfloat(prevfloat(x)) == x` whereas + previously this could result in a completely different value with a different precision ([#31310]). +* `mapreduce` now accepts multiple iterators, similar to `map` ([#31532]). +* `filter` now supports `SkipMissing`-wrapped arrays ([#31235]). +* Objects created by calling `skipmissing` on an array can now be indexed using indices + from the parent at non-missing positions. This allows functions such as + `findall`, `findfirst`, `argmin`/`argmax` and `findmin`/`findmax` to work with these + objects, returning the index of matching non-missing elements in the parent ([#31008]). +* `inv(::Missing)` has now been added and returns `missing` ([#31451]). +* `nextfloat(::BigFloat, n::Integer)` and `prevfloat(::BigFloat, n::Integer)` methods + have been added ([#31310]). + +#### LinearAlgebra +* Added keyword arguments `rtol`, `atol` to `pinv` and `nullspace` ([#29998]). +* `UniformScaling` instances are now callable such that e.g. `I(3)` will produce a `Diagonal` matrix ([#30298]). +* Eigenvalues λ of general matrices are now sorted lexicographically by (Re λ, Im λ) ([#21598]). +* `one` for structured matrices (`Diagonal`, `Bidiagonal`, `Tridiagonal`, `Symtridiagonal`) now preserves + structure and type ([#29777]). +* `diagm(v)` is now a shorthand for `diagm(0 => v)` ([#31125]). + +#### SparseArrays +* Performance improvements for sparse matrix-matrix multiplication ([#30372]). +* Sparse vector outer products are more performant and maintain sparsity in products of the + form `kron(u, v')`, `u * v'`, and `u .* v'` where `u` and `v` are sparse vectors or column + views ([#24980]). +* The `sprand` function is now 2 to 5 times faster ([#30494]). As a consequence of this change, the random stream of matrices produced with `sprand` and `sprandn` has changed. + +#### Sockets + +* `getipaddrs` returns IP addresses in the order provided by libuv ([#32260]). +* `getipaddr` prefers to return the first `IPv4` interface address provided by libuv ([#32260]). + +#### Dates +* Fixed `repr` such that it displays `DateTime` as it would be entered in Julia ([#30200]). + +#### Statistics +* `quantile` now accepts in all cases collections whose `eltype` is not a subtype of `Number` ([#30938]). + +#### Miscellaneous +* Since environment variables on Windows are case-insensitive, `ENV` now converts its keys + to uppercase for display, iteration, and copying ([#30593]). + +External dependencies +--------------------- + +* libgit2 has been updated to v0.27.7 ([#30584]). +* OpenBLAS has been updated to v0.3.5 ([#30583]). +* MbedTLS has been updated to v2.16.0 ([#30618]). +* libunwind has been updated to v1.3.1 ([#30724]). + + +[#21598]: https://github.com/JuliaLang/julia/issues/21598 +[#22922]: https://github.com/JuliaLang/julia/issues/22922 +[#24980]: https://github.com/JuliaLang/julia/issues/24980 +[#27749]: https://github.com/JuliaLang/julia/issues/27749 +[#27900]: https://github.com/JuliaLang/julia/issues/27900 +[#28850]: https://github.com/JuliaLang/julia/issues/28850 +[#29777]: https://github.com/JuliaLang/julia/issues/29777 +[#29790]: https://github.com/JuliaLang/julia/issues/29790 +[#29998]: https://github.com/JuliaLang/julia/issues/29998 +[#30061]: https://github.com/JuliaLang/julia/issues/30061 +[#30200]: https://github.com/JuliaLang/julia/issues/30200 +[#30298]: https://github.com/JuliaLang/julia/issues/30298 +[#30323]: https://github.com/JuliaLang/julia/issues/30323 +[#30372]: https://github.com/JuliaLang/julia/issues/30372 +[#30382]: https://github.com/JuliaLang/julia/issues/30382 +[#30494]: https://github.com/JuliaLang/julia/issues/30494 +[#30577]: https://github.com/JuliaLang/julia/issues/30577 +[#30583]: https://github.com/JuliaLang/julia/issues/30583 +[#30584]: https://github.com/JuliaLang/julia/issues/30584 +[#30593]: https://github.com/JuliaLang/julia/issues/30593 +[#30604]: https://github.com/JuliaLang/julia/issues/30604 +[#30618]: https://github.com/JuliaLang/julia/issues/30618 +[#30670]: https://github.com/JuliaLang/julia/issues/30670 +[#30712]: https://github.com/JuliaLang/julia/issues/30712 +[#30724]: https://github.com/JuliaLang/julia/issues/30724 +[#30915]: https://github.com/JuliaLang/julia/issues/30915 +[#30919]: https://github.com/JuliaLang/julia/issues/30919 +[#30938]: https://github.com/JuliaLang/julia/issues/30938 +[#31008]: https://github.com/JuliaLang/julia/issues/31008 +[#31009]: https://github.com/JuliaLang/julia/issues/31009 +[#31125]: https://github.com/JuliaLang/julia/issues/31125 +[#31211]: https://github.com/JuliaLang/julia/issues/31211 +[#31230]: https://github.com/JuliaLang/julia/issues/31230 +[#31235]: https://github.com/JuliaLang/julia/issues/31235 +[#31310]: https://github.com/JuliaLang/julia/issues/31310 +[#31438]: https://github.com/JuliaLang/julia/issues/31438 +[#31441]: https://github.com/JuliaLang/julia/issues/31441 +[#31451]: https://github.com/JuliaLang/julia/issues/31451 +[#31532]: https://github.com/JuliaLang/julia/issues/31532 +[#31561]: https://github.com/JuliaLang/julia/issues/31561 +[#31604]: https://github.com/JuliaLang/julia/issues/31604 +[#32260]: https://github.com/JuliaLang/julia/issues/32260 + +Julia v1.1 Release Notes +======================== + +New language features +--------------------- + + * An *exception stack* is maintained on each task to make exception handling + more robust and enable root cause analysis. The stack may be accessed using + the experimental function `Base.catch_stack` ([#28878]). + * The experimental macro `Base.@locals` returns a dictionary of current local variable names + and values ([#29733]). + * Binary `~` can now be dotted, as in `x .~ y` ([#30341]). + +Language changes +---------------- + + * Parser inputs ending with a comma are now consistently treated as incomplete. + Previously they were sometimes parsed as tuples, depending on whitespace ([#28506]). + * Spaces were accidentally allowed in broadcast call syntax, e.g. `f. (x)`. They are now + disallowed, consistent with normal function call syntax ([#29781]). + * Big integer literals and command syntax (backticks) are now parsed with the name of + the macro (`@int128_str`, `@uint128_str`, `@big_str`, `@cmd`) qualified to refer + to the `Core` module ([#29968]). + * Using the same name for both a local variable and a static parameter is now an error instead + of a warning ([#29429]). + * `findall(in(b), a)` now returns a `CartesianIndex` when `a` is a matrix or a higher-dimensional array, + for consistency with other `findall` methods. Use `LinearIndices(a)[findall(in(b), a)]` to get + the old behavior, or `CartesianIndices(a)[findall(in(b), a)]` to get the new behavior + on previous Julia versions ([#30226]). + * `findmin(::BitArray)` and `findmax(::BitArray)` now return a `CartesianIndex` when `a` is a matrix + or a higher-dimensional array, for consistency with other array types. + Use `LinearIndices(a)[findmin(a)[2]]` to get the old behavior, or `CartesianIndices(a)[findmin(a)[2]]` + to get the new behavior on previous Julia versions ([#30102]). + * Method signatures such as + `f(::Type{T}, ::T) where {T <: X}` and + `f(::Type{X}, ::Any)` + are now considered ambiguous. Previously a bug caused the first one to be considered more specific in + some cases ([#30160]). + +Command-line option changes +--------------------------- + + * When a script run in interactive mode (`-i`) throws an error, the REPL now starts after + the error is displayed. Previously the REPL only started if the script completed without + error ([#21233]). + +New library functions +--------------------- + + * `splitpath(p::String)` function, which is the opposite of `joinpath(parts...)`: it splits a filepath + into its components ([#28156]). + * `isnothing(::Any)` predicate, to check whether the argument is `nothing`. ([#29679]). + * `getpid(::Process)` method ([#24064]). + * `eachrow`, `eachcol` and `eachslice` functions provide efficient iterators over slices of arrays ([#29749]). + * `fieldtypes(T::Type)` which returns the declared types of the field in type T ([#29600]). + * `uuid5` has been added to the `UUIDs` standard library ([#28761]). + * Predicates `Sys.isfreebsd`, `Sys.isopenbsd`, `Sys.isnetbsd`, and `Sys.isdragonfly` for + detecting BSD systems have been added ([#30249]). + * Internal `Base.disable_library_threading` that sets libraries to use one thread. + It executes function hooks that have been registered with + `Base.at_disable_library_threading` ([#30004]). + +Standard library changes +------------------------ + + * `CartesianIndices` can now be constructed from two `CartesianIndex`es `I` and `J` with `I:J` ([#29440]). + * `CartesianIndices` support broadcasting arithmetic (+ and -) with a `CartesianIndex` ([#29890]). + * `copy!` support for arrays, dicts, and sets has been moved to Base from the Future package ([#29173]). + * Channels now convert inserted values (like containers) instead of requiring types to match ([#29092]). + * `range` can accept the stop value as a positional argument, e.g. `range(1,10,step=2)` ([#28708]). + * `diff` now supports arrays of arbitrary dimensionality and can operate over any dimension ([#29827]). + * The constructor `BigFloat(::BigFloat)` now respects the global precision setting and always + returns a `BigFloat` with precision equal to `precision(BigFloat)` ([#29127]). The optional + `precision` argument to override the global setting is now a keyword instead of positional + argument ([#29157]). + * The use of scientific notation when printing `BigFloat` values is now consistent with other floating point + types ([#29211]). + * `Regex` now behaves like a scalar when used in broadcasting ([#29913]). + * `Char` now behaves like a read-only 0-dimensional array ([#29819]). + * `parse` now allows strings representing integer 0 and 1 for type `Bool` ([#29980]). + * `Base.tail` now works on named tuples ([#29595]). + * The process id is appended to malloc log files in order to track memory allocations of + multiple processes ([#29969]). + * `Base.julia_cmd` now propagates the `--inline=(yes|no)` flag ([#29858]). + * `Base.@kwdef` can now be used for parametric structs, and for structs with supertypes ([#29316]). + * `merge(::NamedTuple, ::NamedTuple...)` can now be used with more than 2 `NamedTuple`s ([#29259]). + * New `ncodeunits(c::Char)` method as a fast equivalent to `ncodeunits(string(c))` ([#29153]). + * New `sort!(::AbstractArray; dims)` method that can sort the array along the `dims` dimension ([#28902]). + * `range` now accepts `stop` as a positional argument ([#28708]). + * `get(A::AbstractArray, (), default)` now returns `A[]` instead of an empty array ([#30270]). + * `parse(Bool, str)` is now supported ([#29997]). + * `copyto!(::AbstractMatrix, ::UniformScaling)` now supports rectangular matrices ([#28790]). + * `current_project()` now searches the parent directories of a Git repository for a `Project.toml` file. + This also affects the behavior of the `--project` command line option when using the default + `--project=@.` ([#29108]). + * The `spawn` API is now more flexible and supports taking IOBuffer directly as an I/O stream, + converting to a system pipe as needed ([#30278]). + +#### Dates + * New `DateTime(::Date, ::Time)` constructor ([#29754]). + * `TimeZone` now behaves like a scalar when used in broadcasting ([#30159]). + +#### InteractiveUtils + * `edit` can now be called on a module to edit the file that defines it ([#29636]). + * All compiler-reflection tools (i.e. the `code_` class of functions and macros) now print accurate + line number and inlining information in a common style, and take an optional parameter (debuginfo=:default) + to control the verbosity of the metadata shown ([#29893]). + +#### LinearAlgebra + * `isdiag` and `isposdef` for `Diagonal` and `UniformScaling` ([#29638]). + * `mul!`, `rmul!` and `lmul!` methods for `UniformScaling` ([#29506]). + * `Symmetric` and `Hermitian` matrices now preserve the wrapper when scaled with a number ([#29469]). + * Exponentiation operator `^` now supports raising an `Irrational` to an `AbstractMatrix` power ([#29782]). + * Added keyword arguments `rtol`, `atol` to `rank` ([#29926]). + +#### Random + * `randperm` and `randcycle` now use the type of their argument to determine the element type of + the returned array ([#29670]). + * A new method `rand(::Tuple)` implements sampling from the values of a tuple ([#25278]). + * `serialize` and `deserialize` now accept a filename argument, like `write` and `read` ([#30151]). + +#### SparseArrays + * `sprandn` now supports specifying the output element type ([#30083]). + +#### Statistics + * `mean` and `var` now handle more kinds of empty inputs ([#29033]). + +External dependencies +--------------------- + + * 7zip (bundled with Julia on Windows) has been upgraded from version 16.04 to 18.05 ([#30035]). + * Busybox is no longer bundled with Julia on Windows ([#30022]). + * OpenBLAS has been upgraded from 0.3.2 to 0.3.3 ([#29845]). + * The source code for Pkg is no longer included in JuliaLang/julia. Pkg is instead + downloaded during the build process ([#29615]). + * LLVM has been upgraded to 6.0.1 and support for LLVM < 6.0 has been dropped ([#28745], [#28696]). + * Pkg has been upgraded to version 1.1 ([#30342]). + +Deprecated or removed +--------------------- + + * `one(i::CartesianIndex)` should be replaced with `oneunit(i::CartesianIndex)` ([#29442]). + * The internal array `Base.Grisu.DIGITS` is deprecated; new code should use `Base.Grisu.getbuf()` + to get an appropriate task-local buffer and pass it to `grisu()` instead ([#29907]). + * The internal function `Base._default_type(T)` has been removed. Calls to it should be + replaced with just the argument `T` ([#29739]). + * `peakflops` has been scheduled to move from `InteractiveUtils` to `LinearAlgebra` + but is already now available as `LinearAlgebra.peakflops` ([#29978]). + + +[#21233]: https://github.com/JuliaLang/julia/issues/21233 +[#24064]: https://github.com/JuliaLang/julia/issues/24064 +[#25278]: https://github.com/JuliaLang/julia/issues/25278 +[#28156]: https://github.com/JuliaLang/julia/issues/28156 +[#28506]: https://github.com/JuliaLang/julia/issues/28506 +[#28696]: https://github.com/JuliaLang/julia/issues/28696 +[#28708]: https://github.com/JuliaLang/julia/issues/28708 +[#28745]: https://github.com/JuliaLang/julia/issues/28745 +[#28761]: https://github.com/JuliaLang/julia/issues/28761 +[#28790]: https://github.com/JuliaLang/julia/issues/28790 +[#28878]: https://github.com/JuliaLang/julia/issues/28878 +[#28902]: https://github.com/JuliaLang/julia/issues/28902 +[#29033]: https://github.com/JuliaLang/julia/issues/29033 +[#29092]: https://github.com/JuliaLang/julia/issues/29092 +[#29108]: https://github.com/JuliaLang/julia/issues/29108 +[#29127]: https://github.com/JuliaLang/julia/issues/29127 +[#29153]: https://github.com/JuliaLang/julia/issues/29153 +[#29157]: https://github.com/JuliaLang/julia/issues/29157 +[#29173]: https://github.com/JuliaLang/julia/issues/29173 +[#29211]: https://github.com/JuliaLang/julia/issues/29211 +[#29259]: https://github.com/JuliaLang/julia/issues/29259 +[#29316]: https://github.com/JuliaLang/julia/issues/29316 +[#29429]: https://github.com/JuliaLang/julia/issues/29429 +[#29440]: https://github.com/JuliaLang/julia/issues/29440 +[#29442]: https://github.com/JuliaLang/julia/issues/29442 +[#29469]: https://github.com/JuliaLang/julia/issues/29469 +[#29506]: https://github.com/JuliaLang/julia/issues/29506 +[#29595]: https://github.com/JuliaLang/julia/issues/29595 +[#29600]: https://github.com/JuliaLang/julia/issues/29600 +[#29615]: https://github.com/JuliaLang/julia/issues/29615 +[#29636]: https://github.com/JuliaLang/julia/issues/29636 +[#29638]: https://github.com/JuliaLang/julia/issues/29638 +[#29670]: https://github.com/JuliaLang/julia/issues/29670 +[#29679]: https://github.com/JuliaLang/julia/issues/29679 +[#29733]: https://github.com/JuliaLang/julia/issues/29733 +[#29739]: https://github.com/JuliaLang/julia/issues/29739 +[#29749]: https://github.com/JuliaLang/julia/issues/29749 +[#29754]: https://github.com/JuliaLang/julia/issues/29754 +[#29781]: https://github.com/JuliaLang/julia/issues/29781 +[#29782]: https://github.com/JuliaLang/julia/issues/29782 +[#29819]: https://github.com/JuliaLang/julia/issues/29819 +[#29827]: https://github.com/JuliaLang/julia/issues/29827 +[#29845]: https://github.com/JuliaLang/julia/issues/29845 +[#29858]: https://github.com/JuliaLang/julia/issues/29858 +[#29890]: https://github.com/JuliaLang/julia/issues/29890 +[#29893]: https://github.com/JuliaLang/julia/issues/29893 +[#29907]: https://github.com/JuliaLang/julia/issues/29907 +[#29913]: https://github.com/JuliaLang/julia/issues/29913 +[#29926]: https://github.com/JuliaLang/julia/issues/29926 +[#29968]: https://github.com/JuliaLang/julia/issues/29968 +[#29969]: https://github.com/JuliaLang/julia/issues/29969 +[#29978]: https://github.com/JuliaLang/julia/issues/29978 +[#29980]: https://github.com/JuliaLang/julia/issues/29980 +[#29997]: https://github.com/JuliaLang/julia/issues/29997 +[#30004]: https://github.com/JuliaLang/julia/issues/30004 +[#30022]: https://github.com/JuliaLang/julia/issues/30022 +[#30035]: https://github.com/JuliaLang/julia/issues/30035 +[#30083]: https://github.com/JuliaLang/julia/issues/30083 +[#30102]: https://github.com/JuliaLang/julia/issues/30102 +[#30151]: https://github.com/JuliaLang/julia/issues/30151 +[#30159]: https://github.com/JuliaLang/julia/issues/30159 +[#30160]: https://github.com/JuliaLang/julia/issues/30160 +[#30226]: https://github.com/JuliaLang/julia/issues/30226 +[#30249]: https://github.com/JuliaLang/julia/issues/30249 +[#30270]: https://github.com/JuliaLang/julia/issues/30270 +[#30278]: https://github.com/JuliaLang/julia/issues/30278 +[#30341]: https://github.com/JuliaLang/julia/issues/30341 +[#30342]: https://github.com/JuliaLang/julia/issues/30342 + +Julia v1.0.0 Release Notes +========================== + +Julia v1.0 is identical to the v0.7 release, with the exception that +it removes all deprecations and deprecation related warnings. When +upgrading a codebase from v0.6, the process is to first get the code +to work on v0.7, and fix all the deprecation warnings. Once the code +runs on v0.7 without warnings, it should be good to run on v1.0. + +Refer to the [Release Notes for +v0.7](https://github.com/JuliaLang/julia/blob/master/HISTORY.md) for a +detailed list of changes from Julia v0.6. + +Standard Library Changes +------------------------ + +* The `Libdl` module's methods `dlopen()` and `dlsym()` have gained a + `throw_error` keyword argument, replacing the now-deprecated `dlopen_e()` + and `dlsym_e()` methods. When `throw_error` is `false`, failure to locate + a shared library or symbol will return `nothing` rather than `C_NULL`. + ([#28888]) + +Deprecated or removed +--------------------- + +* The old package manager (now called `OldPkg`) has been moved to a + separate repository at https://github.com/JuliaArchive/OldPkg.jl ([#27930]) + + +[#27930]: https://github.com/JuliaLang/julia/issues/27930 +[#28888]: https://github.com/JuliaLang/julia/issues/28888 + +Julia v0.7.0 Release Notes +========================== + +New language features +--------------------- + + * Local variables can be tested for being defined + using the new `@isdefined variable` macro ([#22281]). + + * Destructuring in function arguments: when an expression such as `(x, y)` is used as + a function argument name, the argument is unpacked into local variables `x` and `y` + as in the assignment `(x, y) = arg` ([#6614]). + + * Named tuples, with the syntax `(a=1, b=2)`. These behave very similarly to tuples, + except components can also be accessed by name using dot syntax `t.a` ([#22194]). + + * Keyword argument containers (`kw` in `f(; kw...)`) are now based on named tuples. Dictionary + functions like `haskey` and indexing can be used on them, and name-value pairs can be + iterated using `pairs(kw)`. `kw` can no longer contain multiple entries for the same + argument name ([#4916]). + + * Custom infix operators can now be defined by appending Unicode + combining marks, primes, and sub/superscripts to other operators. + For example, `+̂ₐ″` is parsed as an infix operator with the same + precedence as `+` ([#22089]). + + * The macro call syntax `@macroname[args]` is now available and is parsed + as `@macroname([args])` ([#23519]). + + * The construct `if @generated ...; else ...; end` can be used to provide both + `@generated` and normal implementations of part of a function. Surrounding code + will be common to both versions ([#23168]). + + * Added `⟂` (`\perp`) operator with comparison precedence ([#24404]). + + * The `missing` singleton object (of type `Missing`) has been added to represent + missing values ([#24653]). It propagates through standard operators and mathematical functions, + and implements three-valued logic, similar to SQLs `NULL` and R's `NA`. + + * Field access via dot-syntax can now be overloaded by adding methods to + `Base.getproperty` and `Base.setproperty!` ([#1974]), optionally along with + a corresponding `Base.propertynames` method for reflection ([#25311]). + + * Values for `Enum`s can now be specified inside of a `begin` block when using the + `@enum` macro ([#25424]). + + * Keyword arguments can be required: if a default value is omitted, then an + exception is thrown if the caller does not assign the keyword a value ([#25830]). + + * The pair operator `=>` is now broadcastable as `.=>` which was previously a parsing error ([#27447]) + +Language changes +---------------- + + * The syntax for parametric methods, `function f{T}(x::T)`, has been + changed to `function f(x::T) where {T}` ([#11310]). + + * The fallback constructor that calls `convert` is deprecated. Instead, new types should + prefer to define constructors, and add `convert` methods that call those constructors + only as necessary ([#15120]). + + * The syntax `1.+2` is deprecated, since it is ambiguous: it could mean either + `1 .+ 2` (the current meaning) or `1. + 2` ([#19089]). + + * Mutable structs with no fields are no longer singletons; it is now possible to make + multiple instances of them that can be distinguished by `===` ([#25854]). + Zero-size immutable structs are still singletons. + + * In string and character literals, backslash `\` may no longer + precede unrecognized escape characters ([#22800]). + + * Juxtaposing binary, octal, and hexadecimal literals is deprecated, since it can lead to + confusing code such as `0xapi == 0xa * pi` ([#16356]). + + * Numeric literal juxtaposition now has slighty lower precedence than unary operators, + so for example `√2x` parses as `(√2) * x` ([#27641]). + + * Declaring arguments as `x::ANY` to avoid specialization has been replaced + by `@nospecialize x`. ([#22666]). + + This can also be used in global scope, to apply to all subsequent method definitions + in the module (until `@specialize`). ([#28065]) + + * Keyword argument default values are now evaluated in successive scopes --- + the scope for each expression includes only previous keyword arguments, in + left-to-right order ([#17240]). + + * The parsing of `1<<2*3` as `1<<(2*3)` is deprecated, and will change to + `(1<<2)*3` in a future version ([#13079]). + + * The parsing of `<|` is now right associative. `|>` remains left associative ([#24153]). + + * `:` now parses like other operators, as a call to a function named `:`, instead of + calling `colon` ([#25947]). + + * `{ }` expressions now use `braces` and `bracescat` as expression heads instead + of `cell1d` and `cell2d`, and parse similarly to `vect` and `vcat` ([#8470]). + + * Nested `if` expressions that arise from the keyword `elseif` now use `elseif` + as their expression head instead of `if` ([#21774]). + + * `let` blocks now parse the same as `for` loops; the first argument is either an + assignment or `block` of assignments, and the second argument is a block of + statements ([#21774]). + + * `do` syntax now parses to an expression with head `:do`, instead of as a function + call ([#21774]). + + * Parsed and lowered forms of type definitions have been synchronized with their + new keywords ([#23157]). Expression heads are renamed as follows: + + + `type` => `struct` + + + `bitstype` => `primitive` (order of arguments is also reversed, to match syntax) + + + `composite_type` => `struct_type` + + + `bits_type` => `primitive_type` + + * The `global` keyword now only introduces a new binding if one doesn't already exist + in the module. + This means that assignment to a global (`global sin = 3`) may now throw the error: + "cannot assign variable Base.sin from module Main", rather than emitting a warning. + Additionally, the new bindings are now created before the statement is executed. + For example, `f() = (global sin = "gluttony"; nothing)` will now resolve which module + contains `sin` eagerly, rather than delaying that decision until `f` is run. ([#22984]). + + * `global const` declarations may no longer appear inside functions ([#12010]). + + * Uninitialized `BitArray` constructors of the form `BitArray[{N}](shape...)` have been + deprecated in favor of equivalents accepting `undef` (an alias for + `UndefInitializer()`) as their first argument, as in + `BitArray[{N}](undef, shape...)`. For example, `BitVector(3)` is now + `BitVector(undef, 3)`, `BitMatrix((2, 4))` is now + `BitMatrix(undef, (2, 4))`, and `BitArray{3}(11, 13, 17)` is now + `BitArray{3}(undef, 11, 14, 17)` ([#24785]). + + * Dispatch rules have been simplified: + method matching is now determined exclusively by subtyping; + the rule that method type parameters must also be captured has been removed. + Instead, attempting to access the unconstrained parameters will throw an `UndefVarError`. + Linting in package tests is recommended to confirm that the set of methods + which might throw `UndefVarError` when accessing the static parameters + (`need_to_handle_undef_sparam = Set{Any}(m.sig for m in Test.detect_unbound_args(Base, recursive=true))`) + is equal (`==`) to some known set (`expected = Set()`). ([#23117]) + + * `const` declarations on local variables were previously ignored. They now give a + warning, so that this syntax can be disallowed or given a new meaning in a + future version ([#5148]). + + * Placing an expression after `catch`, as in `catch f(x)`, is deprecated. + Use `catch; f(x)` instead ([#19987]). + + * In `for i = ...`, if a local variable `i` already existed it would be overwritten + during the loop. This behavior is deprecated, and in the future `for` loop variables + will always be new variables local to the loop ([#22314]). + The old behavior of overwriting an existing variable is available via `for outer i = ...`. + + * In `for i in x`, `x` used to be evaluated in a new scope enclosing the `for` loop. + Now it is evaluated in the scope outside the `for` loop. + + * In `for i in x, j in y`, all variables now have fresh bindings on each iteration of the + innermost loop. For example, an assignment to `i` will not be visible on the next `j` + loop iteration ([#330]). + + * Variable bindings local to `while` loop bodies are now freshly allocated on each loop iteration, + matching the behavior of `for` loops. + + * Prefix `&` for by-reference arguments to `ccall` has been deprecated in favor of + `Ref` argument types ([#6080]). + + * The constructor `Ref(x::T)` now always returns a `Ref{T}` ([#21527]). + + * All line numbers in ASTs are represented by `LineNumberNode`s; the `:line` expression + head is no longer used. `QuoteNode`s are also consistently used for quoted symbols instead + of the `:quote` expression head (though `:quote` `Expr`s are still used for quoted + expressions) ([#23885]). + + * The `+` and `-` methods for `Number` and `UniformScaling` are not ambiguous anymore since `+` + and `-` no longer do automatic broadcasting. Hence, the methods for `UniformScaling` and `Number` are + no longer deprecated ([#23923]). + + * The keyword `importall` is deprecated. Use `using` and/or individual `import` statements + instead ([#22789]). + + * `reduce(+, [...])` and `reduce(*, [...])` no longer widen the iterated over arguments to + system word size. `sum` and `prod` still preserve this behavior. ([#22825]) + + * Like `_`, variable names consisting only of underscores can be assigned, + but accessing their values is deprecated ([#24221]). + + * Raw string literal escaping rules have been changed to make it possible to write all strings. + The rule is that backslashes escape both quotes and other backslashes, but only when a sequence + of backslashes precedes a quote character. Thus, 2n backslashes followed by a quote encodes n + backslashes and the end of the literal while 2n+1 backslashes followed by a quote encodes n + backslashes followed by a quote character ([#22926]). + + * `reprmime(mime, x)` has been renamed to `repr(mime, x)`, and along with `repr(x)` + and `sprint` it now accepts an optional `context` keyword for `IOContext` attributes. + `stringmime` has been moved to the Base64 stdlib package ([#25990]). + + * The syntax `(x...)` for constructing a tuple is deprecated; use `(x...,)` instead ([#24452]). + + * Non-parenthesized interpolated variables in strings, e.g. `"$x"`, must be followed + by a character that will never be an allowed identifier character (currently + operators, space/control characters, or common punctuation characters) ([#25231]). + + * The syntax `using A.B` can now only be used when `A.B` is a module, and the syntax + `using A: B` can only be used for adding single bindings ([#8000]). + + * `=>` now has its own precedence level, giving it strictly higher precedence than + `=` and `,` ([#25391]). + + * The conditions under which unary operators followed by `(` are parsed as prefix function + calls have changed ([#26154]). + + * `begin` is disallowed inside indexing expressions, in order to enable the syntax + `a[begin]` (for selecting the first element) in the future ([#23354]). + + * Underscores for `_italics_` and `__bold__` are now supported by the Base Markdown + parser. ([#25564]) + + * `…` (`\dots`) and `⁝` (`\tricolon`) are now parsed as binary operators ([#26262]). + + * Assignment syntax (`a=b`) inside square bracket expressions (e.g. `A[...]`, `[x, y]`) + is deprecated. It will likely be reclaimed in a later version for passing keyword + arguments. Note this does not affect updating operators like `+=` ([#25631]). + + * `try` blocks without `catch` or `finally` are no longer allowed. An explicit empty + `catch` block should be written instead ([#27554]). + + * `AbstractArray` types that use unconventional (not 1-based) indexing can now support + `size`, `length`, and `@inbounds`. To optionally enforce conventional indices, + you can `@assert !has_offset_axes(A)`. + + * Module pre-compilation is now the default for code loading. Adding a + `__precompile__()` declaration is no longer necessary, although + `__precompile__(false)` can still be used to opt-out ([#26991]). + +Breaking changes +---------------- + +This section lists changes that do not have deprecation warnings. + + * The package manager `Pkg` has been replaced with a new one. See the manual entries on + "Code Loading" and "Pkg" for documentation. + + * `replace(s::AbstractString, pat=>repl)` for function `repl` arguments formerly + passed a substring to `repl` in all cases. It now passes substrings for + string patterns `pat`, but a `Char` for character patterns (when `pat` is a + `Char`, collection of `Char`, or a character predicate) ([#25815]). + + * `readuntil` now does *not* include the delimiter in its result, matching the + behavior of `readline`. Pass `keep=true` to get the old behavior ([#25633]). + + * `lu` methods now return decomposition objects such as `LU` rather than + tuples of arrays or tuples of numbers ([#26997], [#27159], [#27212]). + + * `schur` methods now return decomposition objects such as `Schur` and + `GeneralizedSchur` rather than tuples of arrays ([#26997], [#27159], [#27212]). + + * `lq` methods now return decomposition objects such as `LQ` + rather than tuples of arrays ([#26997], [#27159], [#27212]). + + * `qr` methods now return decomposition objects such as `QR`, `QRPivoted`, + and `QRCompactWY` rather than tuples of arrays ([#26997], [#27159], [#27212]). + + * `svd` methods now return decomposition objects such as `SVD` and + `GeneralizedSVD` rather than tuples of arrays or tuples of numbers ([#26997], [#27159], [#27212]). + + * `countlines` now always counts the last non-empty line even if it does not + end with EOL, matching the behavior of `eachline` and `readlines` ([#25845]). + + * `getindex(s::String, r::UnitRange{Int})` now throws `StringIndexError` if `last(r)` + is not a valid index into `s` ([#22572]). + + * `ntuple(f, n::Integer)` throws `ArgumentError` if `n` is negative. + Previously an empty tuple was returned ([#21697]). + + * `⋮`, `⋱`, `⋰`, and `⋯` are now parsed as binary operators, not ordinary + identifiers. `≔`, `≕`, and `⩴` now parse with assignment rather than comparison + precedence ([#26262]). + + * Juxtaposing string literals (e.g. `"x"y`) is now a syntax error ([#20575]). + + * `finalizer(function, object)` now returns `object` rather than `nothing` ([#24679]). + + * The constructor of `SubString` now checks if the requested view range + is defined by valid indices in the parent `AbstractString` ([#22511]). + + * Macro calls with `for` expressions are now parsed as generators inside + function argument lists ([#18650]). Examples: + + + `sum(@inbounds a[i] for i = 1:n)` used to give a syntax error, but is now + parsed as `sum(@inbounds(a[i]) for i = 1:n)`. + + + `sum(@m x for i = 1:n end)` used to parse the argument to `sum` as a 2-argument + call to macro `@m`, but now parses it as a generator plus a syntax error + for the dangling `end`. + + * `@__DIR__` returns the current working directory rather than `nothing` when not run + from a file ([#21759]). + + * `@__FILE__` and `@__DIR__` return information relative to the file that it was parsed from, + rather than from the task-local `SOURCE_PATH` global when it was expanded. + + * All macros receive an extra argument `__source__::LineNumberNode` which describes the + parser location in the source file for the `@` of the macro call. + It can be accessed as a normal argument variable in the body of the macro. + This is implemented by inserting an extra leading argument into the + `Expr(:macrocall, :@name, LineNumberNode(...), args...)` + surface syntax. ([#21746]) + + * Passing the same keyword argument multiple times is now a syntax error ([#16937]). + + * `getsockname` on a `TCPSocket` now returns the locally bound address and port + of the socket. Previously the address of the remote endpoint was being + returned ([#21825]). + + * The `~/.juliarc.jl` file has been moved to `~/.julia/config/startup.jl` and + `/etc/julia/juliarc.jl` file has been renamed to `/etc/julia/startup.jl` ([#26161]). + + * Using `ARGS` within `startup.jl` files or within a .jl file loaded with `--load` will no + longer contain the script name as the first argument. Instead, the script name will be + assigned to `PROGRAM_FILE`. ([#22092]) + + * The format for a `ClusterManager` specifying the cookie on the command line is now + `--worker=`. `--worker ` will not work as it is now an optional argument. + + * The representation of `CartesianRange` has changed to a + tuple-of-AbstractUnitRanges; the `start` and `stop` fields are no + longer present. Use `first(R)` and `last(R)` to obtain + start/stop. ([#20974]) + + * The `Diagonal`, `Bidiagonal`, `Tridiagonal` and `SymTridiagonal` type definitions have + changed from `Diagonal{T}`, `Bidiagonal{T}`, `Tridiagonal{T}` and `SymTridiagonal{T}` + to `Diagonal{T,V<:AbstractVector{T}}`, `Bidiagonal{T,V<:AbstractVector{T}}`, + `Tridiagonal{T,V<:AbstractVector{T}}` and `SymTridiagonal{T,V<:AbstractVector{T}}` + respectively ([#22718], [#22925], [#23035], [#23154]). + + * The immediate supertype of `BitArray` is now simply `AbstractArray`. `BitArray` is no longer + considered a subtype of `DenseArray` and `StridedArray` ([#25858]). + + * When called with an argument that contains `NaN` elements, `findmin` and `findmax` now return the + first `NaN` found and its corresponding index. Previously, `NaN` elements were ignored. + The new behavior matches that of `min`, `max`, `minimum`, and `maximum`. + + * `isapprox(x,y)` now tests `norm(x-y) <= max(atol, rtol*max(norm(x), norm(y)))` + rather than `norm(x-y) <= atol + ...`, and `rtol` defaults to zero + if an `atol > 0` is specified ([#22742]). + + * Spaces are no longer allowed between `@` and the name of a macro in a macro call ([#22868]). + + * Juxtaposition of a non-literal with a macro call (`x@macro`) is no longer valid syntax ([#22868]). + + * On a cluster, all files are now loaded from the local file system rather than node 1 ([#22588]). + To load the same file everywhere from node 1, one possible alternative is to broadcast a call to `include_string`: + `@everywhere include_string(Main, $(read("filename", String)), "filename")`. + Improving upon this API is left as an opportunity for packages. + + * `randperm(n)` and `randcycle(n)` now always return a `Vector{Int}` (independent of + the type of `n`). Use the corresponding mutating functions `randperm!` and `randcycle!` + to control the array type ([#22723]). + + * Hermitian now ignores any imaginary components in the diagonal instead of checking + the diagonal. ([#17367]) + + * Worker-worker connections are setup lazily for an `:all_to_all` topology. Use keyword + arg `lazy=false` to force all connections to be setup during a `addprocs` call. ([#22814]) + + * In `joinpath(a, b)` on Windows, if the drive specifications of `a` and `b` do not match, + `joinpath` now returns `b` instead of throwing an `ArgumentError`. `joinpath(path...)` is + defined to be left associative, so if any argument has a drive path which does not match + the drive of the join of the preceding paths, the prior ones are dropped. ([#20912]) + + * `^(A::AbstractMatrix{<:Integer}, p::Integer)` now throws a `DomainError` + if `p < 0`, unless `A == one(A)` or `A == -one(A)` (same as for + `^(A::Integer, p::Integer)`) ([#23366]). + + * `^(A::AbstractMatrix{<:Integer}, p::Integer)` now promotes the element type in the same + way as `^(A::Integer, p::Integer)`. This means, for instance, that `[1 1; 0 1]^big(1)` + will return a `Matrix{BigInt}` instead of a `Matrix{Int}` ([#23366]). + + * The element type of the input is now preserved in `unique`. Previously the element type + of the output was shrunk to fit the union of the type of each element in the input. + ([#22696]) + + * The `promote` function now raises an error if its arguments are of different types + and if attempting to convert them to a common type fails to change any of their types. + This avoids stack overflows in the common case of definitions like + `f(x, y) = f(promote(x, y)...)` ([#22801]). + + * `indmin` and `indmax` have been renamed to `argmin` and `argmax`, respectively ([#25654]). + + * `findmin`, `findmax`, `argmin`, and `argmax` used to always return linear indices. + They now return `CartesianIndex`es for all but 1-d arrays, and in general return + the `keys` of indexed collections (e.g. dictionaries) ([#22907]). + + * The `openspecfun` library is no longer built and shipped with Julia, as it is no longer + used internally ([#22390]). + + * All loaded packages used to have bindings in `Main` (e.g. `Main.Package`). This is no + longer the case; now bindings will only exist for packages brought into scope by + typing `using Package` or `import Package` ([#17997]). + + * The rules for mixed-signedness integer arithmetic (e.g. `Int32(1) + UInt64(1)`) have been + simplified: if the arguments have different sizes (in bits), then the type of the larger + argument is used. If the arguments have the same size, the unsigned type is used ([#9292]). + + * All command line arguments passed via `-e`, `-E`, and `-L` will be executed in the order + given on the command line ([#23665]). + + * `I` now yields `UniformScaling{Bool}(true)` rather than `UniformScaling{Int64}(1)` + to better preserve types in operations involving `I` ([#24396]). + + * The return type of `reinterpret` has changed to `ReinterpretArray`. `reinterpret` on sparse + arrays has been discontinued. + + * `Base.find_in_path` is now `Base.find_package` or `Base.find_source_file` ([#24320]). + + * `finalizer` now takes functions or pointers as its first argument, and the object being + finalized as its second (rather than the reverse). For the majority of use cases + deprecation warnings will be triggered. However, deprecation warnings will not trigger where + (1) the callable argument is not a subtype of `Function`; or (2) both arguments are + `Function`s or `Ptr{Cvoid}`s ([#24605]). + + * The `kill` function now throws errors on user error (e.g. on permission + errors), but returns successfully if the process had previously exited. + Its return value has been removed. Use the `process_running` function + to determine if a process has already exited. + + * The logging system has been redesigned - `info` and `warn` are deprecated + and replaced with the logging macros `@info`, `@warn`, `@debug` and + `@error`. The `logging` function is also deprecated and replaced with + `AbstractLogger` and the functions from the new standard `Logging` library. + ([#24490]) + + * The `RevString` type has been removed from the language; `reverse(::String)` returns + a `String` with code points (or fragments thereof) in reverse order. In general, + `reverse(s)` should return a string of the same type and encoding as `s` with code + points in reverse order; any string type overrides `reverse` to return a different + type of string must also override `reverseind` to compute reversed indices correctly. + + * `eachindex(A, B...)` now requires that all inputs have the same number of elements. + When the chosen indexing is Cartesian, they must have the same axes. + + * `AbstractRange` objects are now considered as equal to other `AbstractArray` objects + by `==` and `isequal` if all of their elements are equal ([#16401]). + This has required changing the hashing algorithm: ranges now use an O(N) fallback + instead of a O(1) specialized method unless they define the `Base.RangeStepStyle` + trait; see its documentation for details. Types which support subtraction (operator + `-`) must now implement `widen` for hashing to work inside heterogeneous arrays. + + * `findn(x::AbstractArray)` has been deprecated in favor of `findall(!iszero, x)`, which + now returns cartesian indices for multidimensional arrays (see below, [#25532]). + + * Broadcasting operations are no longer fused into a single operation by Julia's parser. + Instead, a lazy `Broadcasted` object is created to represent the fused expression and + then realized with `copy(bc::Broadcasted)` or `copyto!(dest, bc::Broadcasted)` + to evaluate the wrapper. Consequently, package authors generally need to specialize + `copy` and `copyto!` methods rather than `broadcast` and `broadcast!`. This also allows + for more customization and control of fused broadcasts. See the + [Interfaces chapter](https://docs.julialang.org/en/latest/manual/interfaces/#man-interfaces-broadcasting-1) + for more information. + + * `find` has been renamed to `findall`. `findall`, `findfirst`, `findlast`, `findnext` + now take and/or return the same type of indices as `keys`/`pairs` for `AbstractArray`, + `AbstractDict`, `AbstractString`, `Tuple` and `NamedTuple` objects ([#24774], [#25545]). + In particular, this means that they use `CartesianIndex` objects for matrices + and higher-dimensional arrays instead of linear indices as was previously the case. + Use `LinearIndices(a)[findall(f, a)]` and similar constructs to compute linear indices. + + * The `find*` functions, i.e. `findnext`, `findprev`, `findfirst`, + and `findlast`, as well as `indexin`, now return `nothing` when no match is found rather + than `0` or `0:-1` ([#25472], [#25662], [#26149]) + + * The `Base.HasShape` iterator trait has gained a type parameter `N` indicating the + number of dimensions, which must correspond to the length of the tuple returned by + `size` ([#25655]). + + * `AbstractSet` objects are now considered equal by `==` and `isequal` if all of their + elements are equal ([#25368]). This has required changing the hashing algorithm + for `BitSet`. + + * the default behavior of `titlecase` is changed in two ways ([#23393]): + + characters not starting a word are converted to lowercase; + a new keyword argument `strict` is added which + allows to get the old behavior when it's `false`. + + any non-letter character is considered as a word separator; + to get the old behavior (only "space" characters are considered as + word separators), use the keyword `wordsep=isspace`. + + * `writedlm` in the standard library module DelimitedFiles now writes numeric values + using `print` rather than `print_shortest` ([#25745]). + + * The `tempname` function used to create a file on Windows but not on other + platforms. It now never creates a file ([#9053]). + + * The `fieldnames` and `propertynames` functions now return a tuple rather than + an array ([#25725]). + + * `indexin` now returns the first rather than the last matching index ([#25998]). + + * `parse(::Type, ::Char)` now uses a default base of 10, like other number parsing + methods, instead of 36 ([#26576]). + + * `isequal` for `Ptr`s now compares element types; `==` still compares only addresses + ([#26858]). + + * `widen` on 8- and 16-bit integer types now widens to 16- and 32-bit types, respectively. ([#28045]). + + * `mv`,`cp`, `touch`, `mkdir`, `mkpath`, `chmod` and `chown` now return the path that was created/modified + rather than `nothing` ([#27071]). + + * Regular expressions now default to UCP mode. Escape sequences such as `\w` + will now match based on unicode character properties, e.g. `r"\w+"` will + match `café` (not just `caf`). Add the `a` modifier (e.g. `r"\w+"a`) to + restore the previous behavior ([#27189]). + + * `@sync` now waits only for *lexically* enclosed (i.e. visible directly in the source + text of its argument) `@async` expressions. If you need to wait for a task created by + a called function `f`, have `f` return the task and put `@async wait(f(...))` within + the `@sync` block. + This change makes `@schedule` redundant with `@async`, so `@schedule` has been + deprecated ([#27164]). + + * `norm(A::AbstractMatrix, p=2)` computes no longer the operator/matrix norm but the `norm` of `A` + as for other iterables, i.e. as if it were a vector. Especially, `norm(A::AbstractMatrix)` is the + Frobenius norm. To compute the operator/matrix norm, use the new function `opnorm` ([#27401]). + + * `dot(u, v)` now acts recursively. Instead of `sum(u[i]' * v[i] for i in ...)`, it computes + `sum(dot(u[i], v[i]) for i in ...)`, similarly to `vecdot` before ([#27401]). + + * `Sys.CPU_CORES` has been renamed to `Sys.CPU_THREADS`; it still gives the number + of "logical cores" (including hyperthreading) rather than the number of physical + cores present on the CPU. Similarly, the environment variable `JULIA_CPU_CORES` is + deprecated in favor of `JULIA_CPU_THREADS` ([#27856]). + + * `WeakKeyDict` does not convert keys on insertion anymore (#24941). + +Library improvements +-------------------- + + * The function `thisind(s::AbstractString, i::Integer)` returns the largest valid index + less or equal than `i` in the string `s` or `0` if no such index exists ([#24414]). + + * Support for Unicode 11 ([#28266]). + + * `Char` is now a subtype of `AbstractChar`, and most of the functions that + take character arguments now accept any `AbstractChar` ([#26286]). + + * `pathof(module)` returns the path a module was imported from ([#28310]). + + * `bytes2hex` now accepts an optional `io` argument to output to a hexadecimal stream + without allocating a `String` first ([#27121]). + + * `String(array)` now accepts an arbitrary `AbstractVector{UInt8}`. For `Vector` + inputs, it "steals" the memory buffer, leaving them with an empty buffer which + is guaranteed not to be shared with the `String` object. For other types of vectors + (in particular immutable vectors), a copy is made and the input is not truncated ([#26093]). + + * `Irrational` is now a subtype of `AbstractIrrational` ([#24245]). + + * Introduced the `empty` function, the functional pair to `empty!` which returns a new, + empty container ([#24390]). + + * Jump to first/last history entries in the REPL via "Alt-<" and "Alt->" ([#22829]). + + * REPL LaTeX-like tab completions have been simplified for several Unicode characters, + e.g. `𝔸` is now `\bbA` rather than `\BbbA` ([#25980]). + + * The function `chop` now accepts two arguments `head` and `tail` allowing to specify + number of characters to remove from the head and tail of the string ([#24126]). + + * `get(io, :color, false)` can now be used to query whether a stream `io` supports + [ANSI color codes](https://en.wikipedia.org/wiki/ANSI_escape_code) ([#25067]), + rather than using the undocumented `Base.have_color` global flag. + + * `print_with_color` has been deprecated in favor of + `printstyled([io], xs...; bold=false, color=:normal)` for printing styled text ([#25522]). + + * Functions `first` and `last` now accept `nchar` argument for `AbstractString`. + If this argument is used they return a string consisting of first/last `nchar` + characters from the original string ([#23960]). + + * Expressions `x^-n` where `n` is an *integer literal* now correspond to `inv(x)^n`. + For example, `x^-1` is now essentially a synonym for `inv(x)`, and works + in a type-stable way even if `typeof(x) != typeof(inv(x))` ([#24240]). + + * New `Iterators.reverse(itr)` for reverse-order iteration ([#24187]). Iterator + types `T` can implement `start` etc. for `Iterators.Reverse{T}` to support this. + + * The functions `nextind` and `prevind` now accept `nchar` argument that indicates + the number of characters to move ([#23805]). + + * The functions `strip`, `lstrip` and `rstrip` now return `SubString` ([#22496]). + + * The functions `strwidth` and `charwidth` have been merged into `textwidth`([#20816]). + + * The functions `base` and `digits` digits now accept a negative + base (like `ndigits` did) ([#21692]). + + * The function `randn` now accepts complex arguments (`Complex{T <: AbstractFloat}`) + ([#21973]). + + * `parse(Complex{T}, string)` can parse complex numbers in some common formats ([#24713]). + + * The function `rand` can now pick up random elements from strings, associatives + and sets ([#22228], [#21960], [#18155], [#22224]). + + * It's now possible to specify the characters to pick from in the `randstring` function ([#22222]). + + * Allow multidimensional arrays in `shuffle` and `shuffle!` functions ([#22226]). + + * Method lists are now printed as a numbered list. In addition, the source code of a + method can be opened in an editor by entering the corresponding number in the REPL + and pressing `^Q` ([#22007]). + + * `getpeername` on a `TCPSocket` returns the address and port of the remote + endpoint of the TCP connection ([#21825]). + + * `resize!` and `sizehint!` methods no longer over-reserve memory when the + requested array size is more than double of its current size ([#22038]). + + * The `crc32c` function for CRC-32c checksums is now exported ([#22274]). + + * `eye(::Type{Diagonal{T}}, m::Integer)` has been deprecated in favor of + `Diagonal{T}(I, m)` ([#24415]). + + * The output of `versioninfo` is now controlled with keyword arguments ([#21974]). + + * The function `LibGit2.set_remote_url` now always sets both the fetch and push URLs for a + git repo. Additionally, the argument order was changed to be consistent with the git + command line tool ([#22062]). + + * Added `unique!` which is an inplace version of `unique` ([#20549]). + + * `@test isequal(x, y)` and `@test isapprox(x, y)` now prints an evaluated expression when + the test fails ([#22296]). + + * Uses of `Val{c}` in `Base` has been replaced with `Val{c}()`, which is now easily + accessible via the efficient constructor `Val(c)`. Functions are defined as + `f(::Val{c}) = ...` and called by `f(Val(c))`. Notable affected functions include: + `ntuple`, `Base.literal_pow`, `sqrtm`, `lufact`, `lufact!`, `qrfact`, `qrfact!`, + `cholfact`, `cholfact!`, `_broadcast!`, `reshape`, `cat` and `cat_t`. + + * A new `@macroexpand1` macro for non recursive macro expansion ([#21662]). + + * `Char`s can now be concatenated with `String`s and/or other `Char`s using `*` ([#22532]). + + * `Diagonal`, `Bidiagonal`, `Tridiagonal` and `SymTridiagonal` are now parameterized on + the type of the wrapped vectors, allowing `Diagonal`, `Bidiagonal`, `Tridiagonal` and + `SymTridiagonal` matrices with arbitrary `AbstractVector`s + ([#22718], [#22925], [#23035], [#23154]). + + * Mutating versions of `randperm` and `randcycle` have been added: + `randperm!` and `randcycle!` ([#22723]). + + * `BigFloat` random numbers can now be generated ([#22720]). + + * The efficiency of random generation for MersenneTwister RNGs has been improved for + integers, `Float64` and ranges; as a result, given a seed, the produced stream of numbers + has changed ([#27560], [#25277], [#25197], [#25058], [#25047]). + + * REPL Undo via Ctrl-/ and Ctrl-_ + + * `diagm` now accepts several diagonal index/vector `Pair`s ([#24047]). + + * `isequal`, `==`, and `in` have one argument "curried" forms. For example `isequal(x)` + returns a function that compares its argument to `x` using `isequal` ([#26436]). + + * `reinterpret` now works on any AbstractArray using the new `ReinterpretArray` type. + This supersedes the old behavior of reinterpret on Arrays. As a result, reinterpreting + arrays with different alignment requirements (removed in 0.6) is once again allowed ([#23750]). + + * The `keys` of an `Associative` are now an `AbstractSet`. `Base.KeyIterator{<:Associative}` + has been changed to `KeySet{K, <:Associative{K}} <: AbstractSet{K}` ([#24580]). + + * New function `ncodeunits(s::AbstractString)` gives the number of code units in a string. + The generic definition is constant time but calls `lastindex(s)` which may be inefficient. + Therefore custom string types may want to define direct `ncodeunits` methods. + + * `reverseind(s::AbstractString, i::Integer)` now has an efficient generic fallback, so + custom string types do not need to provide their own efficient definitions. The generic + definition relies on `ncodeunits` however, so for optimal performance you may need to + define a custom method for that function. + + * The global RNG is being re-seeded with its own seed at the beginning of each `@testset`, + and have its original state restored at the end ([#24445]). This is breaking for testsets + relying implicitly on the global RNG being in a specific state. + + * `permutedims(m::AbstractMatrix)` is now short for `permutedims(m, (2,1))`, and is now a + more convenient way of making a "shallow transpose" of a 2D array. This is the + recommended approach for manipulating arrays of data, rather than the recursively + defined, linear-algebra function `transpose`. Similarly, + `permutedims(v::AbstractVector)` will create a row matrix ([#24839]). + + * A new `replace(A, old=>new)` function is introduced to replace `old` by `new` in + collection `A`. There is also another method with a different API, and + a mutating variant, `replace!` ([#22324], [#25697], [#26206], [#27944]). + + * Adding integers to `CartesianIndex` objects is now deprecated. Instead of + `i::Int + x::CartesianIndex`, use `i*one(x) + x` ([#26284]). + + * `CartesianRange` changes ([#24715]): + - Inherits from `AbstractArray`, and linear indexing can be used to provide + linear-to-cartesian conversion ([#24715]) + - It has a new constructor taking an array + + * several missing set-like operations have been added ([#23528]): + `union`, `intersect`, `symdiff`, `setdiff` are now implemented for + all collections with arbitrary many arguments, as well as the + mutating counterparts (`union!` etc.). The performance is also + much better in many cases. Note that this change is slightly + breaking: all the non-mutating functions always return a new + object even if only one argument is passed. Moreover the semantics + of `intersect` and `symdiff` is changed for vectors: + + `intersect` doesn't preserve the multiplicity anymore (use `filter` for + the old behavior) + + `symdiff` has been made consistent with the corresponding methods for + other containers, by taking the multiplicity of the arguments into account. + Use `unique` to get the old behavior. + + * The `linearindices` function has been deprecated in favor of the new + `LinearIndices` type, which additionally provides conversion from + cartesian indices to linear indices using the normal indexing operation. + ([#24715], [#26775]). + + * `IdDict{K,V}` replaces `ObjectIdDict`. It has type parameters + like other `AbstractDict` subtypes and its constructors mirror the + ones of `Dict`. ([#25210]) + + * `IOBuffer` can take the `sizehint` keyword argument to suggest a capacity of + the buffer ([#25944]). + + * `lstrip` and `rstrip` now accept a predicate function that defaults to `isspace` + ([#27309]). + + * `trunc`, `floor`, `ceil`, and `round` specify `digits`, `sigdigits` and `base` using + keyword arguments. ([#26156], [#26670]) + + * `Sys.which()` provides a cross-platform method to find executable files, similar to + the Unix `which` command. ([#26559]) + + * Added an optimized method of `vecdot` for taking the Frobenius inner product + of sparse matrices. ([#27470]) + + * Added an optimized method of `kron` for taking the tensor product of two + `Diagonal` matrices. ([27581]) + + * An official API for extending `rand` is now defined ([#23964], [#25002]). + + * The constructor `MersenneTwister()` is re-enabled, producing a randomly initialized RNG + (similar to `Random.seed!(MersenneTwister(0))`) ([#21909]). + + * `BitSet` can now store any `Int` (instead of only positive ones) ([#25029]). + + * The initial element `v0` in `reduce(op, v0, itr)` has been replaced with an `init` + optional keyword argument, as in `reduce(op, itr; init=v0)`. Similarly for `foldl`, + `foldr`, `mapreduce`, `mapfoldl`, `mapfoldr`, `accumulate` and `accumulate!`. + ([#27711], [#27859]) + +Compiler/Runtime improvements +----------------------------- + + * The inlining heuristic now models the approximate runtime cost of + a method (using some strongly-simplifying assumptions). Functions + are inlined unless their estimated runtime cost substantially + exceeds the cost of setting up and issuing a subroutine + call. ([#22210], [#22732]) + + * Inference recursion-detection heuristics are now more precise, + allowing them to be triggered less often, but being more aggressive when they + are triggered to drive the inference computation to a solution ([#23912]). + + * Inference now propagates constants inter-procedurally, and can compute + various constants expressions at compile-time ([#24362]). + + * The LLVM SLP Vectorizer optimization pass is now enabled at the default + optimization level. + +Deprecated or removed +--------------------- + + * The `JULIA_HOME` environment variable has been renamed to `JULIA_BINDIR` and + `Base.JULIA_HOME` has been moved to `Sys.BINDIR` ([#20899]). + + * The keyword `immutable` is fully deprecated to `struct`, and + `type` is fully deprecated to `mutable struct` ([#19157], [#20418]). + + * `lufact`, `schurfact`, `lqfact`, `qrfact`, `ldltfact`, `svdfact`, + `bkfact`, `hessfact`, `eigfact`, and `cholfact` have respectively been + deprecated to `lu`, `schur`, `lq`, `qr`, `ldlt`, `svd`, `bunchkaufman`, + `hessenberg`, `eigen`, and `cholesky` ([#26997], [#27159], [#27212]). + + * `lufact!`, `schurfact!`, `lqfact!`, `qrfact!`, `ldltfact!`, `svdfact!`, + `bkfact!`, `hessfact!`, and `eigfact!` have respectively been deprecated to + `lu!`, `schur!`, `lq!`, `qr!`, `ldlt!`, `svd!`, `bunchkaufman!`, + `hessenberg!`, and `eigen!` ([#26997], [#27159], [#27212]). + + * `eig(A[, args...])` has been deprecated in favor of `eigen(A[, args...])`. + Whereas the former returns a tuple of arrays, the latter returns an `Eigen` object. + So for a direct replacement, use `(eigen(A[, args...])...,)`. But going forward, + consider using the direct result of `eigen(A[, args...])` instead, either + destructured into its components (`vals, vecs = eigen(A[, args...])`) or + as an `Eigen` object (`X = eigen(A[, args...])`) ([#26997], [#27159], [#27212]). + + * `eig(A::AbstractMatrix, B::AbstractMatrix)` and `eig(A::Number, B::Number)` + have been deprecated in favor of `eigen(A, B)`. Whereas the former each return + a tuple of arrays, the latter returns a `GeneralizedEigen` object. So for a direct + replacement, use `(eigen(A, B)...,)`. But going forward, consider using the + direct result of `eigen(A, B)` instead, either destructured into its components + (`vals, vecs = eigen(A, B)`), or as a `GeneralizedEigen` object + (`X = eigen(A, B)`) ([#26997], [#27159], [#27212]). + + * `ordschur(T::StridedMatrix{Ty}, Z::StridedMatrix{Ty}, select::Union{Vector{Bool},BitVector})` + and `ordschur(S::StridedMatrix{Ty}, T::StridedMatrix{Ty}, Q::StridedMatrix{Ty}, + Z::StridedMatrix{Ty}, select::Union{Vector{Bool},BitVector})` and their respective + inplace versions have been deprecated. + Use `ordschur(schur::Schur, select::Union{Vector{Bool},BitVector})` and + `ordschur(gschur::GeneralizedSchur, select::Union{Vector{Bool},BitVector})` instead + ([#28155]). + + * Indexing into multidimensional arrays with more than one index but fewer indices than there are + dimensions is no longer permitted when those trailing dimensions have lengths greater than 1. + Instead, reshape the array or add trailing indices so the dimensionality and number of indices + match ([#14770], [#23628]). + + * The use of a positional dimension argument has largely been deprecated in favor of a + `dims` keyword argument. This includes the functions `sum`, `prod`, `maximum`, + `minimum`, `all`, `any`, `findmax`, `findmin`, `mean`, `varm`, `std`, `var`, `cov`, + `cor`, `median`, `mapreducedim`, `reducedim`, `sort`, `accumulate`, `accumulate!`, + `cumsum`, `cumsum!`, `cumprod`, `cumprod!`, `flipdim`, `dropdims`, and `cat` ([#25501], [#26660], [#27100]). + + * `indices(a)` and `indices(a,d)` have been deprecated in favor of `axes(a)` and + `axes(a, d)` ([#25057]). + + * `EnvHash` has been renamed to `EnvDict` ([#24167]). + + * Uninitialized `Array` constructors of the form + `Array[{T,N}](shape...)` have been deprecated in favor of equivalents + accepting `undef` (an alias for `UndefInitializer()`) as their first argument, + as in `Array[{T,N}](undef, shape...)`. For example, + `Vector(3)` is now `Vector(undef, 3)`, `Matrix{Int}((2, 4))` is now, + `Matrix{Int}(undef, (2, 4))`, and `Array{Float32,3}(11, 13, 17)` is now + `Array{Float32,3}(undef, 11, 13, 17)` ([#24781]). + + * Previously `setindex!(A, x, I...)` (and the syntax `A[I...] = x`) supported two + different modes of operation when supplied with a set of non-scalar indices `I` + (e.g., at least one index is an `AbstractArray`) depending upon the value of `x` + on the right hand side. If `x` is an `AbstractArray`, its _contents_ are copied + elementwise into the locations in `A` selected by `I` and it must have the same + number of elements as `I` selects locations. Otherwise, if `x` is not an + `AbstractArray`, then its _value_ is implicitly broadcast to all locations to + all locations in `A` selected by `I`. This latter behavior—implicitly broadcasting + "scalar"-like values across many locations—is now deprecated in favor of explicitly + using the broadcasted assignment syntax `A[I...] .= x` or `fill!(view(A, I...), x)` + ([#26347]). + + * `broadcast_getindex(A, I...)` and `broadcast_setindex!(A, v, I...)` are deprecated in + favor of `getindex.((A,), I...)` and `setindex!.((A,), v, I...)`, respectively ([#27075]). + + * `LinAlg.fillslots!` has been renamed `LinAlg.fillstored!` ([#25030]). + + * `fill!(A::Diagonal, x)` and `fill!(A::AbstractTriangular, x)` have been deprecated + in favor of `Base.LinAlg.fillstored!(A, x)` ([#24413]). + + * `eye` has been deprecated in favor of `I` and `Matrix` constructors. Please see the + deprecation warnings for replacement details ([#24438]). + + * `zeros(D::Diagonal[, opts...])` has been deprecated ([#24654]). + + * Using Bool values directly as indices is now deprecated and will be an error in the future. Convert + them to `Int` before indexing if you intend to access index `1` for `true` and `0` for `false`. + + * `slicedim(A, d, i)` has been deprecated in favor of `copy(selectdim(A, d, i))`. The new + `selectdim` function now always returns a view into `A`; in many cases the `copy` is + not necessary. Previously, `slicedim` on a vector `V` over dimension `d=1` and scalar + index `i` would return the just selected element (unless `V` was a `BitVector`). This + has now been made consistent: `selectdim` now always returns a view into the original + array, with a zero-dimensional view in this specific case ([#26009]). + + * `whos` has been renamed `varinfo`, and now returns a markdown table instead of printing + output ([#12131]). + + * Uninitialized `RowVector` constructors of the form `RowVector{T}(shape...)` have been + deprecated in favor of equivalents accepting `undef` (an alias for + `UndefInitializer()`) as their first argument, as in + `RowVector{T}(undef, shape...)`. For example, `RowVector{Int}(3)` is now + `RowVector{Int}(undef, 3)`, and `RowVector{Float32}((1, 4))` is now + `RowVector{Float32}(undef, (1, 4))` ([#24786]). + + * `writecsv(io, a; opts...)` has been deprecated in favor of + `writedlm(io, a, ','; opts...)` ([#23529]). + + * The method `srand(rng, filename, n=4)` has been deprecated ([#21359]). + + * `readcsv(io[, T::Type]; opts...)` has been deprecated in favor of + `readdlm(io, ','[, T]; opts...)` ([#23530]). + + * `sparse(s::UniformScaling, m::Integer)` has been deprecated in favor of the + three-argument equivalent `sparse(s::UniformScaling, m, n)` ([#24472]). + + * The `cholfact`/`cholfact!` methods that accepted an `uplo` symbol have been deprecated + in favor of using `Hermitian` (or `Symmetric`) views ([#22187], [#22188]). + + * The `thin` keyword argument for orthogonal decomposition methods has + been deprecated in favor of `full`, which has the opposite meaning: + `thin == true` if and only if `full == false` ([#24279]). + + * `isposdef(A::AbstractMatrix, UL::Symbol)` and `isposdef!(A::AbstractMatrix, UL::Symbol)` + have been deprecated in favor of `isposdef(Hermitian(A, UL))` and `isposdef!(Hermitian(A, UL))` + respectively ([#22245]). + + * The `bkfact`/`bkfact!` methods that accepted `uplo` and `issymmetric` symbols have been deprecated + in favor of using `Hermitian` (or `Symmetric`) views ([#22605]). + + * The function `current_module` is deprecated and replaced with `@__MODULE__`. + This caused the deprecation of some reflection methods (such as `macroexpand` and + `isconst`), which now require a module argument. And it caused the bugfix of other + default arguments to use the Main module (including `whos`, `which`) ([#22064]). + + * `expand(ex)` and `expand(module, ex)` have been deprecated in favor of + `Meta.lower(module, ex)` ([#22064], [#24278]). + + * `ones(A::AbstractArray[, opts...])` and `zeros(A::AbstractArray[, opts...])` methods + have been deprecated. For `zeros(A)`, consider `zero(A)`. For `ones(A)` or `zeros(A)`, + consider `ones(size(A))`, `zeros(size(A))`, `fill(v, size(A))` for `v` an appropriate + one or zero, `fill!(copy(A), {1|0})`, `fill!(similar(A), {1|0})`, or any of the preceding + with different element type and/or shape depending on `opts...`. Where strictly + necessary, consider `fill!(similar(A[, opts...]), {one(eltype(A)) | zero(eltype(A))})`. + For an algebraic multiplicative identity, consider `one(A)` ([#24656]). + + * The `similar(dims->f(..., dims...), [T], axes...)` method to add offset array support + to a function `f` that would otherwise create a non-offset array has been deprecated. + Instead, call `f(..., axes...)` directly and, if needed, the offset array implementation + should add offset axis support to the function `f` directly ([#26733]). + + * The functions `ones` and `zeros` used to accept any objects as dimensional arguments, + implicitly converting them to `Int`s. This is now deprecated; only `Integer`s or + `AbstractUnitRange`s are accepted as arguments. Instead, convert the arguments before + calling `ones` or `zeros` ([#26733]). + + * The variadic `size(A, dim1, dim2, dims...)` method to return a tuple of multiple + dimension lengths of `A` has been deprecated ([#26862]). + + * The `Operators` module is deprecated. Instead, import required operators explicitly + from `Base`, e.g. `import Base: +, -, *, /` ([#22251]). + + * Bindings to the FFTW library have been removed from Base. The DFT framework for building FFT + implementations is now in AbstractFFTs.jl, the bindings to the FFTW library are in FFTW.jl, + and the Base signal processing functions which used FFTs are now in DSP.jl ([#21956]). + + * The `corrected` positional argument to `cov` has been deprecated in favor of + a keyword argument with the same name ([#21709]). + + * Omitting spaces around the `?` and the `:` tokens in a ternary expression has been deprecated. + Ternaries must now include some amount of whitespace, e.g. `x ? a : b` rather than + `x?a:b` ([#22523] and [#22712]). + + * `?` can no longer be used as an identifier name ([#22712]) + + * The method `replace(s::AbstractString, pat, r, [count])` is deprecated + in favor of `replace(s::AbstractString, pat => r; [count])` ([#25165]). + Moreover, `count` cannot be negative anymore (use `typemax(Int)` instead ([#22325]). + + * `read(io, type, dims)` is deprecated to `read!(io, Array{type}(undef, dims))` ([#21450]). + + * `read(::IO, ::Ref)` is now a method of `read!`, since it mutates its `Ref` argument ([#21592]). + + * `nb_available` is now `bytesavailable` ([#25634]). + + * `skipchars(io::IO, predicate; linecomment=nothing)` is deprecated in favor of + `skipchars(predicate, io::IO; linecomment=nothing)` ([#25667]). + + * `Bidiagonal` constructors now use a `Symbol` (`:U` or `:L`) for the upper/lower + argument, instead of a `Bool` or a `Char` ([#22703]). + + * `Bidiagonal`, `Tridiagonal` and `SymTridiagonal` constructors that automatically + converted the input vectors to the same type are deprecated in favor of explicit + conversion ([#22925], [#23035], [#23154]. + + * Calling `nfields` on a type to find out how many fields its instances have is deprecated. + Use `fieldcount` instead. Use `nfields` only to get the number of fields in a specific object ([#22350]). + + * `fieldnames` now operates only on types. To get the names of fields in an object, use + `fieldnames(typeof(x))` ([#22350]). + + * `InexactError`, `DomainError`, and `OverflowError` now take + arguments. `InexactError(func::Symbol, type, -3)` now prints as + "ERROR: InexactError: func(type, -3)", `DomainError(val, + [msg])` prints as "ERROR: DomainError with val:\nmsg", + and `OverflowError(msg)` prints as "ERROR: OverflowError: msg". + ([#20005], [#22751], [#22761]) + + * The operating system identification functions: `is_linux`, `is_bsd`, `is_apple`, `is_unix`, + and `is_windows`, have been deprecated in favor of `Sys.islinux`, `Sys.isbsd`, `Sys.isapple`, + `Sys.isunix`, and `Sys.iswindows`, respectively ([#22182]). + + * The forms of `read`, `readstring`, and `eachline` that accepted both a `Cmd` object and an + input stream are deprecated. Use e.g. `read(pipeline(stdin, cmd))` instead ([#22762]). + + * The unexported type `AbstractIOBuffer` has been renamed to `GenericIOBuffer` ([#17360] [#22796]). + + * `IOBuffer(data::AbstractVector{UInt8}, read::Bool, write::Bool, maxsize::Integer)`, + `IOBuffer(read::Bool, write::Bool)`, and `IOBuffer(maxsize::Integer)` are + deprecated in favor of constructors taking keyword arguments ([#25872]). + + * `Display` has been renamed to `AbstractDisplay` ([#24831]). + + * Remaining vectorized methods over `SparseVector`s, particularly `floor`, `ceil`, + `trunc`, `round`, and most common transcendental functions such as `exp`, `log`, and + `sin` variants, have been deprecated in favor of dot-syntax ([#22961]). + + * The method `String(io::IOBuffer)` is deprecated to `String(take!(copy(io)))` ([#21438]). + + * The function `readstring` is deprecated in favor of `read(io, String)` ([#22793]) + + * The function `showall` is deprecated. Showing entire values is the default, unless an + `IOContext` specifying `:limit=>true` is in use ([#22847]). + + * `issubtype` has been deprecated in favor of `<:` (which used to be an alias for `issubtype`). + + * Calling `write` on non-isbits arrays is deprecated in favor of explicit loops or + `serialize` ([#6466]). + + * The default `startup.jl` file on Windows has been removed. Now must explicitly include the + full path if you need access to executables or libraries in the `Sys.BINDIR` directory, e.g. + `joinpath(Sys.BINDIR, "7z.exe")` for `7z.exe` ([#21540]). + + * `sqrtm` has been deprecated in favor of `sqrt` ([#23504]). + + * `expm` has been deprecated in favor of `exp` ([#23233]). + + * `logm` has been deprecated in favor of `log` ([#23505]). + + * `full` has been deprecated in favor of more specific, better defined alternatives. + On structured matrices `A`, consider instead `Matrix(A)`, `Array(A)`, + `SparseMatrixCSC(A)`, or `sparse(A)`. On sparse arrays `S`, consider instead + `Vector(S)`, `Matrix(S)`, or `Array(S)` as appropriate. On factorizations `F`, + consider instead `Matrix(F)`, `Array(F)`, `AbstractMatrix(F)`, or `AbstractArray(F)`. + On implicit orthogonal factors `Q`, consider instead `Matrix(Q)` or `Array(Q)`; for + implicit orthogonal factors that can be recovered in square or truncated form, + see the deprecation message for square recovery instructions. On `Symmetric`, + `Hermitian`, or `AbstractTriangular` matrices `A`, consider instead `Matrix(S)`, + `Array(S)`, `SparseMatrixCSC(S)`, or `sparse(S)`. On `Symmetric` matrices `A` + particularly, consider instead `LinAlg.copytri!(copy(parent(A)), A.uplo)`. On + `Hermitian` matrices `A` particularly, consider instead + `LinAlg.copytri!(copy(parent(A)), A.uplo, true)`. On `UpperTriangular` matrices `A` + particularly, consider instead `triu!(copy(parent(A)))`. On `LowerTriangular` matrices + `A` particularly, consider instead `tril!(copy(parent(A)))` ([#24250]). + + * `speye` has been deprecated in favor of `I`, `sparse`, and `SparseMatrixCSC` + constructor methods ([#24356]). + + * Calling `union` with no arguments is deprecated; construct an empty set with an appropriate + element type using `Set{T}()` instead ([#23144]). + + * Vectorized `DateTime`, `Date`, and `format` methods have been deprecated in favor of + dot-syntax ([#23207]). + + * `Base.cpad` has been removed; use an appropriate combination of `rpad` and `lpad` + instead ([#23187]). + + * `ctranspose` and `ctranspose!` have been deprecated in favor of `adjoint` and `adjoint!`, + respectively ([#23235]). + + * `filter` and `filter!` on dictionaries now pass a single `key=>value` pair to the + argument function, instead of two arguments ([#17886]). + + * `rol`, `rol!`, `ror`, and `ror!` have been deprecated in favor of specialized methods for + `circshift`/`circshift!` ([#23404]). + + * `Base.SparseArrays.SpDiagIterator` has been removed ([#23261]). + + * The function `cfunction`, has been deprecated in favor of a macro form `@cfunction`. + Most existing uses can be upgraded simply by adding a `@`. + The new syntax now additionally supports allocating closures at runtime, + for dealing with C APIs that don't provide a separate `void* env`-type callback + argument. ([#26486]) + + * `diagm(v::AbstractVector, k::Integer=0)` has been deprecated in favor of + `diagm(k => v)` ([#24047]). + + * `diagm(x::Number)` has been deprecated in favor of `fill(x, 1, 1)` ([#24047]). + + * `diagm(A::SparseMatrixCSC)` has been deprecated in favor of + `spdiagm(sparsevec(A))` ([#23341]). + + * `diagm(A::BitMatrix)` has been deprecated, use `diagm(0 => vec(A))` or + `BitMatrix(Diagonal(vec(A)))` instead ([#23373], [#24047]). + + * `ℯ` (written as `\mscre` or `\euler`) is now the only (by default) exported + name for Euler's number, and the type has changed from `Irrational{:e}` to + `Irrational{:ℯ}` ([#23427]). + + * The mathematical constants `π`, `pi`, `ℯ`, `e`, `γ`, `eulergamma`, `catalan`, `φ` and + `golden` have been moved from `Base` to a new module; `Base.MathConstants`. + Only `π`, `pi` and `ℯ` are now exported by default from `Base` ([#23427]). + + * `eu` (previously an alias for `ℯ`) has been deprecated in favor of `ℯ` (or `MathConstants.e`) ([#23427]). + + * `GMP.gmp_version()`, `GMP.GMP_VERSION`, `GMP.gmp_bits_per_limb()`, and `GMP.GMP_BITS_PER_LIMB` + have been renamed to `GMP.version()`, `GMP.VERSION`, `GMP.bits_per_limb()`, and `GMP.BITS_PER_LIMB`, + respectively. Similarly, `MPFR.get_version()`, has been renamed to `MPFR.version()` ([#23323]). Also, + `LinAlg.LAPACK.laver()` has been renamed to `LinAlg.LAPACK.version()` and now returns a `VersionNumber`. + + * `select`, `select!`, `selectperm` and `selectperm!` have been renamed respectively to + `partialsort`, `partialsort!`, `partialsortperm` and `partialsortperm!` ([#23051]). + + * The `Range` abstract type has been renamed to `AbstractRange` ([#23570]). + + * `map` on dictionaries previously operated on `key=>value` pairs. This behavior is deprecated, + and in the future `map` will operate only on values ([#5794]). + + * `map` on sets previously returned a `Set`, possibly changing the order or number of elements. This + behavior is deprecated and in the future `map` will preserve order and number of elements ([#26980]). + + * Previously, broadcast defaulted to treating its arguments as scalars if they were not + arrays. This behavior is deprecated, and in the future `broadcast` will default to + iterating over all its arguments. Wrap arguments you wish to be treated as scalars with + `Ref()` or a 1-tuple. Package developers can choose to allow a non-iterable type `T` to + always behave as a scalar by implementing `broadcastable(x::T) = Ref(x)` ([#26212]). + + * Automatically broadcasted `+` and `-` for `array + scalar`, `scalar - array`, and so-on have + been deprecated due to inconsistency with linear algebra. Use `.+` and `.-` for these operations + instead ([#22880], [#22932]). + + * `flipbits!(B)` is deprecated in favor of using in-place broadcast to negate each element: + `B .= .!B` ([#27067]). + + * `isleaftype` is deprecated in favor of the simpler predicates `isconcretetype` and `isdispatchtuple`. + Concrete types are those that might equal `typeof(x)` for some `x`; + `isleaftype` included some types for which this is not true. Those are now categorized more precisely + as "dispatch tuple types" and "!has_free_typevars" (not exported). ([#17086], [#25496]) + + * `contains(eq, itr, item)` is deprecated in favor of `any` with a predicate ([#23716]). + + * `spdiagm(x::AbstractVector)` has been deprecated in favor of `sparse(Diagonal(x))` + alternatively `spdiagm(0 => x)` ([#23757]). + + * `spdiagm(x::AbstractVector, d::Integer)` and `spdiagm(x::Tuple{<:AbstractVector}, d::Tuple{<:Integer})` + have been deprecated in favor of `spdiagm(d => x)` and `spdiagm(d[1] => x[1], d[2] => x[2], ...)` + respectively. The new `spdiagm` implementation now always returns a square matrix ([#23757]). + + * `spones(A::AbstractSparseArray)` has been deprecated in favor of + `LinAlg.fillstored!(copy(A), 1)` ([#25037]). + + * Constructors for `LibGit2.UserPasswordCredentials` and `LibGit2.SSHCredentials` which take a + `prompt_if_incorrect` argument are deprecated. Instead, prompting behavior is controlled using + the `allow_prompt` keyword in the `LibGit2.CredentialPayload` constructor ([#23690]). + + * `gradient` is deprecated and will be removed in the next release ([#23816]). + + * The timing functions `tic`, `toc`, and `toq` are deprecated in favor of `@time` and `@elapsed` + ([#17046]). + + * Methods of `findfirst`, `findnext`, `findlast`, and `findprev` that accept a value to + search for are deprecated in favor of passing a predicate ([#19186], [#10593]). + + * `find` functions now operate only on booleans by default. To look for non-zeros, use + `x->x!=0` or `!iszero` ([#23120]). + + * The ability of `reinterpret` to yield `Array`s of different type than the underlying storage + has been removed. The `reinterpret` function is still available, but now returns a + `ReinterpretArray`. The three argument form of `reinterpret` that implicitly reshapes + has been deprecated ([#23750]). + + * `bits` has been deprecated in favor of `bitstring` ([#24281], [#24263]). + + * `num2hex` and `hex2num` have been deprecated in favor of `reinterpret` combined with `parse`/`hex` ([#22088]). + + * `copy!` is deprecated for `AbstractSet` and `AbstractDict`, with the intention to re-enable + it with a cleaner meaning in a future version ([#24844]). + + * `copy!` (resp. `unsafe_copy!`) is deprecated for `AbstractArray` and is renamed `copyto!` + (resp. `unsafe_copyto!`); it will be re-introduced with a different meaning in a future + version ([#24808]). + + * `a:b` is deprecated for constructing a `StepRange` when `a` and `b` have physical units + (Dates and Times). Use `a:s:b`, where `s = Dates.Day(1)` or `s = Dates.Second(1)`. + + * `trues(A::AbstractArray)` and `falses(A::AbstractArray)` are deprecated in favor of + `trues(size(A))` and `falses(size(A))` respectively ([#24595]). + + * `workspace` is discontinued, check out [Revise.jl](https://github.com/timholy/Revise.jl) + for an alternative workflow ([#25046]). + + * `cumsum`, `cumprod`, `accumulate`, their mutating versions, and `diff` all now require a `dim` + argument instead of defaulting to using the first dimension unless there is only + one dimension ([#24684], [#25457]). + + * The `sum_kbn` and `cumsum_kbn` functions have been moved to the + [KahanSummation](https://github.com/JuliaMath/KahanSummation.jl) package ([#24869]). + + * `isnumber` has been renamed to `isnumeric` ([#25021]). + + * `isalpha` has been renamed to `isletter` ([#26932]). + + * `is_assigned_char` and `normalize_string` have been renamed to `isassigned` and + `normalize`, and moved to the new `Unicode` standard library module. + `graphemes` has also been moved to that module ([#25021]). + + * Sparse array functionality has moved to the `SparseArrays` standard library module ([#25249]). + + * Linear algebra functionality, and specifically the `LinAlg` module has moved to the + `LinearAlgebra` standard library module ([#25571]). + + * `@printf` and `@sprintf` have been moved to the `Printf` standard library ([#23929],[#25056]). + + * The `Libdl` module has moved to the `Libdl` standard library module ([#25459]). + + * The aliases `Complex32`, `Complex64` and `Complex128` have been deprecated in favor of `ComplexF16`, + `ComplexF32` and `ComplexF64` respectively ([#24647]). + + * `Base.parentindexes` and `SharedArrays.localindexes` have been renamed to `parentindices` + and `localindices`, respectively. Similarly, the `indexes` field in the `SubArray` type + has been renamed to `indices` without deprecation ([#25088]). + + * `Associative` has been deprecated in favor of `AbstractDict` ([#25012]). + + * `Void` has been renamed back to `Nothing` with an alias `Cvoid` for use when calling C + with a return type of `Cvoid` or a return or argument type of `Ptr{Cvoid}` ([#25162]). + + * `Nullable{T}` has been deprecated and moved to the Nullables package ([#23642]). Use + `Union{T, Nothing}` instead, or `Union{Some{T}, Nothing}` if `nothing` is a possible + value (i.e. `Nothing <: T`). `isnull(x)` can be replaced with `x === nothing` and + `unsafe_get`/`get` can be dropped or replaced with `coalesce`. + `NullException` has been removed. + + * `unshift!` and `shift!` have been renamed to `pushfirst!` and `popfirst!` ([#23902]) + + * `ipermute!` has been deprecated in favor of `invpermute!` ([#25168]). + + * `CartesianRange` has been renamed `CartesianIndices` ([#24715]). + + * `sub2ind` and `ind2sub` are deprecated in favor of using `CartesianIndices` and `LinearIndices` ([#24715]). + + * `getindex(F::Factorization, s::Symbol)` (usually seen as e.g. `F[:Q]`) is deprecated + in favor of dot overloading (`getproperty`) so factors should now be accessed as e.g. + `F.Q` instead of `F[:Q]` ([#25184]). + + * `search` and `rsearch` have been deprecated in favor of `findfirst`/`findnext` and + `findlast`/`findprev` respectively, in combination with curried `isequal` and `in` + predicates for some methods ([#24673]). + + * `search(buf::IOBuffer, delim::UInt8)` has been deprecated in favor of either `occursin(delim, buf)` + (to test containment) or `readuntil(buf, delim)` (to read data up to `delim`) ([#26600]). + + * `ismatch(regex, str)` has been deprecated in favor of `occursin(regex, str)` ([#26283]). + + * `matchall` has been deprecated in favor of `collect(m.match for m in eachmatch(r, s))` ([#26071]). + + * `similar(::Associative)` has been deprecated in favor of `empty(::Associative)`, and + `similar(::Associative, ::Pair{K, V})` has been deprecated in favour of + `empty(::Associative, K, V)` ([#24390]). + + * `findin(a, b)` has been deprecated in favor of `findall(in(b), a)` ([#24673]). + + * `module_name` has been deprecated in favor of a new, general `nameof` function. Similarly, + the unexported `Base.function_name` and `Base.datatype_name` have been deprecated in favor + of `nameof` methods ([#25622]). + + * The module `Random.dSFMT` is renamed `Random.DSFMT` ([#25567]). + + * `Random.RandomDevice(unlimited::Bool)` (on non-Windows systems) is deprecated in favor of + `Random.RandomDevice(; unlimited=unlimited)` ([#25668]). + + * The generic implementations of `strides(::AbstractArray)` and `stride(::AbstractArray, ::Int)` + have been deprecated. Subtypes of `AbstractArray` that implement the newly introduced strided + array interface should define their own `strides` method ([#25321]). + + * `module_parent`, `Base.datatype_module`, and `Base.function_module` have been deprecated + in favor of `parentmodule` ([#TODO]). + + * `rand(t::Tuple{Vararg{Int}})` is deprecated in favor of `rand(Float64, t)` or `rand(t...)`; + `rand(::Tuple)` will have another meaning in the future ([#25429], [#25278]). + + * `randjump`, which produced an array, is deprecated in favor of the + scalar version `Future.randjump` used with `accumulate` ([#27746]). + + * The `assert` function (and `@assert` macro) have been documented that they are not guaranteed to run under various optimization levels and should therefore not be used to e.g. verify passwords. + + * `ObjectIdDict` has been deprecated in favor of `IdDict{Any,Any}` ([#25210]). + + * `gc` and `gc_enable` have been deprecated in favor of `GC.gc` and `GC.enable` ([#25616]). + + * `Base.@gc_preserve` has been deprecated in favor of `GC.@preserve` ([#25616]). + + * `print_shortest` has been discontinued, but is still available in the `Base.Grisu` + submodule ([#25745]). + + * `scale!` has been deprecated in favor of `mul!`, `lmul!`, and `rmul!` ([#25701], [#25812]). + + * The `remove_destination` keyword argument to `cp`, `mv`, and the unexported `cptree` + has been renamed to `force` ([#25979]). + + * `contains` has been deprecated in favor of a more general `occursin` function, which + takes its arguments in reverse order from `contains` ([#26283]). + + * `Regex` objects are no longer callable. Use `occursin` instead ([#26283]). + + * The methods of `range` based on positional arguments have been deprecated in favor of + keyword arguments ([#25896]). + + * `linspace` has been deprecated in favor of `range` with `stop` and `length` keyword + arguments ([#25896]). + + * `LinSpace` has been renamed to `LinRange` ([#25896]). + + * `logspace` has been deprecated to its definition ([#25896]). + + * `endof(a)` has been renamed to `lastindex(a)`, and the `end` keyword in indexing expressions now + lowers to either `lastindex(a)` (in the case with only one index) or `lastindex(a, d)` (in cases + where there is more than one index and `end` appears at dimension `d`) ([#23554], [#25763]). + + * `DateTime()`, `Date()`, and `Time()` have been deprecated, instead use `DateTime(1)`, `Date(1)` + and `Time(0)` respectively ([#23724]). + + * The fallback method `^(x, p::Integer)` is deprecated. If your type relied on this definition, + add a method such as `^(x::MyType, p::Integer) = Base.power_by_squaring(x, p)` ([#23332]). + + * `DevNull`, `STDIN`, `STDOUT`, and `STDERR` have been renamed to `devnull`, `stdin`, `stdout`, + and `stderr`, respectively ([#25786]). + + * `wait` and `fetch` on `Task` now resemble the interface of `Future`. + + * `showcompact(io, x...)` has been deprecated in favor of + `show(IOContext(io, :compact => true), x...)` ([#26080]). + Use `sprint(show, x..., context=:compact => true)` instead of `sprint(showcompact, x...)`. + + * `isupper`, `islower`, `ucfirst` and `lcfirst` have been deprecated in favor of `isuppercase`, + `islowercase`, `uppercasefirst` and `lowercasefirst`, respectively ([#26442]). + + * `signif` has been deprecated in favor of the `sigdigits` keyword argument to `round`. + + * `Base.IntSet` has been deprecated in favor of `Base.BitSet` ([#24282]). + + * `setrounding` has been deprecated for `Float32` and `Float64`, as the behaviour was too unreliable ([#26935]). + + * `gamma`, `lgamma`, `beta`, `lbeta` and `lfact` have been moved to + [SpecialFunctions.jl](https://github.com/JuliaMath/SpecialFunctions.jl) ([#27459], [#27473]). + + * `atan2` is now a 2-argument method of `atan` ([#27248]). + + * The functions `eigs` and `svds` have been moved to the `Arpack.jl` package ([#27616]). + + * `vecdot` and `vecnorm` are deprecated in favor of `dot` and `norm`, respectively ([#27401]). + + * `clipboard` has been moved to the `InteractiveUtils` standard library package + (along with other utilities mostly used at the interactive prompt, such as `edit` + and `less`) ([#27635]). + + * `ndigits(n, b, [pad])` is deprecated in favor of `ndigits(n, base=b, pad=pad)` ([#27908]). + + * `squeeze` is deprecated in favor of `dropdims`. + + * `srand` is deprecated in favor of the unexported `Random.seed!` ([#27726]). + + * `realmin`/`realmax` are deprecated in favor of `floatmin`/`floatmax` ([#28302]). + + * `sortrows`/`sortcols` have been deprecated in favor of the more general `sortslices`. + + * `nextpow2`/`prevpow2` have been deprecated in favor of the more general `nextpow`/`prevpow` functions. + +Command-line option changes +--------------------------- + + * New option `--warn-overwrite={yes|no}` to control the warning for overwriting method + definitions. The default is `no` ([#23002]). + + * New option `--banner={yes,no}` allows suppressing or forcing the printing of the + startup banner, overriding the default behavior (banner in REPL, no banner otherwise). + The `--quiet` option implies `--banner=no` even in REPL mode but can be overridden by + passing `--quiet` together with `--banner=yes` ([#23342]). + + * The option `--precompiled` has been renamed to `--sysimage-native-code` ([#23054]). + + * The option `--compilecache` has been renamed to `--compiled-modules` ([#23054]). + + +[#330]: https://github.com/JuliaLang/julia/issues/330 +[#1974]: https://github.com/JuliaLang/julia/issues/1974 +[#4916]: https://github.com/JuliaLang/julia/issues/4916 +[#5148]: https://github.com/JuliaLang/julia/issues/5148 +[#5794]: https://github.com/JuliaLang/julia/issues/5794 +[#6080]: https://github.com/JuliaLang/julia/issues/6080 +[#6466]: https://github.com/JuliaLang/julia/issues/6466 +[#6614]: https://github.com/JuliaLang/julia/issues/6614 +[#8000]: https://github.com/JuliaLang/julia/issues/8000 +[#8470]: https://github.com/JuliaLang/julia/issues/8470 +[#9053]: https://github.com/JuliaLang/julia/issues/9053 +[#9292]: https://github.com/JuliaLang/julia/issues/9292 +[#10593]: https://github.com/JuliaLang/julia/issues/10593 +[#11310]: https://github.com/JuliaLang/julia/issues/11310 +[#12010]: https://github.com/JuliaLang/julia/issues/12010 +[#12131]: https://github.com/JuliaLang/julia/issues/12131 +[#13079]: https://github.com/JuliaLang/julia/issues/13079 +[#14770]: https://github.com/JuliaLang/julia/issues/14770 +[#15120]: https://github.com/JuliaLang/julia/issues/15120 +[#16356]: https://github.com/JuliaLang/julia/issues/16356 +[#16401]: https://github.com/JuliaLang/julia/issues/16401 +[#16937]: https://github.com/JuliaLang/julia/issues/16937 +[#17046]: https://github.com/JuliaLang/julia/issues/17046 +[#17086]: https://github.com/JuliaLang/julia/issues/17086 +[#17240]: https://github.com/JuliaLang/julia/issues/17240 +[#17360]: https://github.com/JuliaLang/julia/issues/17360 +[#17367]: https://github.com/JuliaLang/julia/issues/17367 +[#17886]: https://github.com/JuliaLang/julia/issues/17886 +[#17997]: https://github.com/JuliaLang/julia/issues/17997 +[#18155]: https://github.com/JuliaLang/julia/issues/18155 +[#18650]: https://github.com/JuliaLang/julia/issues/18650 +[#19089]: https://github.com/JuliaLang/julia/issues/19089 +[#19157]: https://github.com/JuliaLang/julia/issues/19157 +[#19186]: https://github.com/JuliaLang/julia/issues/19186 +[#19987]: https://github.com/JuliaLang/julia/issues/19987 +[#20005]: https://github.com/JuliaLang/julia/issues/20005 +[#20418]: https://github.com/JuliaLang/julia/issues/20418 +[#20549]: https://github.com/JuliaLang/julia/issues/20549 +[#20575]: https://github.com/JuliaLang/julia/issues/20575 +[#20816]: https://github.com/JuliaLang/julia/issues/20816 +[#20899]: https://github.com/JuliaLang/julia/issues/20899 +[#20912]: https://github.com/JuliaLang/julia/issues/20912 +[#20974]: https://github.com/JuliaLang/julia/issues/20974 +[#21359]: https://github.com/JuliaLang/julia/issues/21359 +[#21438]: https://github.com/JuliaLang/julia/issues/21438 +[#21450]: https://github.com/JuliaLang/julia/issues/21450 +[#21527]: https://github.com/JuliaLang/julia/issues/21527 +[#21540]: https://github.com/JuliaLang/julia/issues/21540 +[#21592]: https://github.com/JuliaLang/julia/issues/21592 +[#21662]: https://github.com/JuliaLang/julia/issues/21662 +[#21692]: https://github.com/JuliaLang/julia/issues/21692 +[#21697]: https://github.com/JuliaLang/julia/issues/21697 +[#21709]: https://github.com/JuliaLang/julia/issues/21709 +[#21746]: https://github.com/JuliaLang/julia/issues/21746 +[#21759]: https://github.com/JuliaLang/julia/issues/21759 +[#21774]: https://github.com/JuliaLang/julia/issues/21774 +[#21825]: https://github.com/JuliaLang/julia/issues/21825 +[#21909]: https://github.com/JuliaLang/julia/issues/21909 +[#21956]: https://github.com/JuliaLang/julia/issues/21956 +[#21960]: https://github.com/JuliaLang/julia/issues/21960 +[#21973]: https://github.com/JuliaLang/julia/issues/21973 +[#21974]: https://github.com/JuliaLang/julia/issues/21974 +[#22007]: https://github.com/JuliaLang/julia/issues/22007 +[#22038]: https://github.com/JuliaLang/julia/issues/22038 +[#22062]: https://github.com/JuliaLang/julia/issues/22062 +[#22064]: https://github.com/JuliaLang/julia/issues/22064 +[#22088]: https://github.com/JuliaLang/julia/issues/22088 +[#22089]: https://github.com/JuliaLang/julia/issues/22089 +[#22092]: https://github.com/JuliaLang/julia/issues/22092 +[#22182]: https://github.com/JuliaLang/julia/issues/22182 +[#22187]: https://github.com/JuliaLang/julia/issues/22187 +[#22188]: https://github.com/JuliaLang/julia/issues/22188 +[#22194]: https://github.com/JuliaLang/julia/issues/22194 +[#22210]: https://github.com/JuliaLang/julia/issues/22210 +[#22222]: https://github.com/JuliaLang/julia/issues/22222 +[#22224]: https://github.com/JuliaLang/julia/issues/22224 +[#22226]: https://github.com/JuliaLang/julia/issues/22226 +[#22228]: https://github.com/JuliaLang/julia/issues/22228 +[#22245]: https://github.com/JuliaLang/julia/issues/22245 +[#22251]: https://github.com/JuliaLang/julia/issues/22251 +[#22274]: https://github.com/JuliaLang/julia/issues/22274 +[#22281]: https://github.com/JuliaLang/julia/issues/22281 +[#22296]: https://github.com/JuliaLang/julia/issues/22296 +[#22314]: https://github.com/JuliaLang/julia/issues/22314 +[#22324]: https://github.com/JuliaLang/julia/issues/22324 +[#22325]: https://github.com/JuliaLang/julia/issues/22325 +[#22350]: https://github.com/JuliaLang/julia/issues/22350 +[#22390]: https://github.com/JuliaLang/julia/issues/22390 +[#22496]: https://github.com/JuliaLang/julia/issues/22496 +[#22511]: https://github.com/JuliaLang/julia/issues/22511 +[#22523]: https://github.com/JuliaLang/julia/issues/22523 +[#22532]: https://github.com/JuliaLang/julia/issues/22532 +[#22572]: https://github.com/JuliaLang/julia/issues/22572 +[#22588]: https://github.com/JuliaLang/julia/issues/22588 +[#22605]: https://github.com/JuliaLang/julia/issues/22605 +[#22666]: https://github.com/JuliaLang/julia/issues/22666 +[#22696]: https://github.com/JuliaLang/julia/issues/22696 +[#22703]: https://github.com/JuliaLang/julia/issues/22703 +[#22712]: https://github.com/JuliaLang/julia/issues/22712 +[#22718]: https://github.com/JuliaLang/julia/issues/22718 +[#22720]: https://github.com/JuliaLang/julia/issues/22720 +[#22723]: https://github.com/JuliaLang/julia/issues/22723 +[#22732]: https://github.com/JuliaLang/julia/issues/22732 +[#22742]: https://github.com/JuliaLang/julia/issues/22742 +[#22751]: https://github.com/JuliaLang/julia/issues/22751 +[#22761]: https://github.com/JuliaLang/julia/issues/22761 +[#22762]: https://github.com/JuliaLang/julia/issues/22762 +[#22789]: https://github.com/JuliaLang/julia/issues/22789 +[#22793]: https://github.com/JuliaLang/julia/issues/22793 +[#22796]: https://github.com/JuliaLang/julia/issues/22796 +[#22800]: https://github.com/JuliaLang/julia/issues/22800 +[#22801]: https://github.com/JuliaLang/julia/issues/22801 +[#22814]: https://github.com/JuliaLang/julia/issues/22814 +[#22825]: https://github.com/JuliaLang/julia/issues/22825 +[#22829]: https://github.com/JuliaLang/julia/issues/22829 +[#22847]: https://github.com/JuliaLang/julia/issues/22847 +[#22868]: https://github.com/JuliaLang/julia/issues/22868 +[#22880]: https://github.com/JuliaLang/julia/issues/22880 +[#22907]: https://github.com/JuliaLang/julia/issues/22907 +[#22925]: https://github.com/JuliaLang/julia/issues/22925 +[#22926]: https://github.com/JuliaLang/julia/issues/22926 +[#22932]: https://github.com/JuliaLang/julia/issues/22932 +[#22961]: https://github.com/JuliaLang/julia/issues/22961 +[#22984]: https://github.com/JuliaLang/julia/issues/22984 +[#23002]: https://github.com/JuliaLang/julia/issues/23002 +[#23035]: https://github.com/JuliaLang/julia/issues/23035 +[#23051]: https://github.com/JuliaLang/julia/issues/23051 +[#23054]: https://github.com/JuliaLang/julia/issues/23054 +[#23117]: https://github.com/JuliaLang/julia/issues/23117 +[#23120]: https://github.com/JuliaLang/julia/issues/23120 +[#23144]: https://github.com/JuliaLang/julia/issues/23144 +[#23154]: https://github.com/JuliaLang/julia/issues/23154 +[#23157]: https://github.com/JuliaLang/julia/issues/23157 +[#23168]: https://github.com/JuliaLang/julia/issues/23168 +[#23187]: https://github.com/JuliaLang/julia/issues/23187 +[#23207]: https://github.com/JuliaLang/julia/issues/23207 +[#23233]: https://github.com/JuliaLang/julia/issues/23233 +[#23235]: https://github.com/JuliaLang/julia/issues/23235 +[#23261]: https://github.com/JuliaLang/julia/issues/23261 +[#23323]: https://github.com/JuliaLang/julia/issues/23323 +[#23332]: https://github.com/JuliaLang/julia/issues/23332 +[#23341]: https://github.com/JuliaLang/julia/issues/23341 +[#23342]: https://github.com/JuliaLang/julia/issues/23342 +[#23354]: https://github.com/JuliaLang/julia/issues/23354 +[#23366]: https://github.com/JuliaLang/julia/issues/23366 +[#23373]: https://github.com/JuliaLang/julia/issues/23373 +[#23393]: https://github.com/JuliaLang/julia/issues/23393 +[#23404]: https://github.com/JuliaLang/julia/issues/23404 +[#23427]: https://github.com/JuliaLang/julia/issues/23427 +[#23504]: https://github.com/JuliaLang/julia/issues/23504 +[#23505]: https://github.com/JuliaLang/julia/issues/23505 +[#23519]: https://github.com/JuliaLang/julia/issues/23519 +[#23528]: https://github.com/JuliaLang/julia/issues/23528 +[#23529]: https://github.com/JuliaLang/julia/issues/23529 +[#23530]: https://github.com/JuliaLang/julia/issues/23530 +[#23554]: https://github.com/JuliaLang/julia/issues/23554 +[#23570]: https://github.com/JuliaLang/julia/issues/23570 +[#23628]: https://github.com/JuliaLang/julia/issues/23628 +[#23642]: https://github.com/JuliaLang/julia/issues/23642 +[#23665]: https://github.com/JuliaLang/julia/issues/23665 +[#23690]: https://github.com/JuliaLang/julia/issues/23690 +[#23716]: https://github.com/JuliaLang/julia/issues/23716 +[#23724]: https://github.com/JuliaLang/julia/issues/23724 +[#23750]: https://github.com/JuliaLang/julia/issues/23750 +[#23757]: https://github.com/JuliaLang/julia/issues/23757 +[#23805]: https://github.com/JuliaLang/julia/issues/23805 +[#23816]: https://github.com/JuliaLang/julia/issues/23816 +[#23885]: https://github.com/JuliaLang/julia/issues/23885 +[#23902]: https://github.com/JuliaLang/julia/issues/23902 +[#23912]: https://github.com/JuliaLang/julia/issues/23912 +[#23923]: https://github.com/JuliaLang/julia/issues/23923 +[#23929]: https://github.com/JuliaLang/julia/issues/23929 +[#23960]: https://github.com/JuliaLang/julia/issues/23960 +[#23964]: https://github.com/JuliaLang/julia/issues/23964 +[#24047]: https://github.com/JuliaLang/julia/issues/24047 +[#24126]: https://github.com/JuliaLang/julia/issues/24126 +[#24153]: https://github.com/JuliaLang/julia/issues/24153 +[#24167]: https://github.com/JuliaLang/julia/issues/24167 +[#24187]: https://github.com/JuliaLang/julia/issues/24187 +[#24221]: https://github.com/JuliaLang/julia/issues/24221 +[#24240]: https://github.com/JuliaLang/julia/issues/24240 +[#24245]: https://github.com/JuliaLang/julia/issues/24245 +[#24250]: https://github.com/JuliaLang/julia/issues/24250 +[#24263]: https://github.com/JuliaLang/julia/issues/24263 +[#24278]: https://github.com/JuliaLang/julia/issues/24278 +[#24279]: https://github.com/JuliaLang/julia/issues/24279 +[#24281]: https://github.com/JuliaLang/julia/issues/24281 +[#24282]: https://github.com/JuliaLang/julia/issues/24282 +[#24320]: https://github.com/JuliaLang/julia/issues/24320 +[#24356]: https://github.com/JuliaLang/julia/issues/24356 +[#24362]: https://github.com/JuliaLang/julia/issues/24362 +[#24390]: https://github.com/JuliaLang/julia/issues/24390 +[#24396]: https://github.com/JuliaLang/julia/issues/24396 +[#24404]: https://github.com/JuliaLang/julia/issues/24404 +[#24413]: https://github.com/JuliaLang/julia/issues/24413 +[#24414]: https://github.com/JuliaLang/julia/issues/24414 +[#24415]: https://github.com/JuliaLang/julia/issues/24415 +[#24438]: https://github.com/JuliaLang/julia/issues/24438 +[#24445]: https://github.com/JuliaLang/julia/issues/24445 +[#24452]: https://github.com/JuliaLang/julia/issues/24452 +[#24472]: https://github.com/JuliaLang/julia/issues/24472 +[#24490]: https://github.com/JuliaLang/julia/issues/24490 +[#24580]: https://github.com/JuliaLang/julia/issues/24580 +[#24595]: https://github.com/JuliaLang/julia/issues/24595 +[#24605]: https://github.com/JuliaLang/julia/issues/24605 +[#24647]: https://github.com/JuliaLang/julia/issues/24647 +[#24653]: https://github.com/JuliaLang/julia/issues/24653 +[#24654]: https://github.com/JuliaLang/julia/issues/24654 +[#24656]: https://github.com/JuliaLang/julia/issues/24656 +[#24673]: https://github.com/JuliaLang/julia/issues/24673 +[#24679]: https://github.com/JuliaLang/julia/issues/24679 +[#24684]: https://github.com/JuliaLang/julia/issues/24684 +[#24713]: https://github.com/JuliaLang/julia/issues/24713 +[#24715]: https://github.com/JuliaLang/julia/issues/24715 +[#24774]: https://github.com/JuliaLang/julia/issues/24774 +[#24781]: https://github.com/JuliaLang/julia/issues/24781 +[#24785]: https://github.com/JuliaLang/julia/issues/24785 +[#24786]: https://github.com/JuliaLang/julia/issues/24786 +[#24808]: https://github.com/JuliaLang/julia/issues/24808 +[#24831]: https://github.com/JuliaLang/julia/issues/24831 +[#24839]: https://github.com/JuliaLang/julia/issues/24839 +[#24844]: https://github.com/JuliaLang/julia/issues/24844 +[#24869]: https://github.com/JuliaLang/julia/issues/24869 +[#25002]: https://github.com/JuliaLang/julia/issues/25002 +[#25012]: https://github.com/JuliaLang/julia/issues/25012 +[#25021]: https://github.com/JuliaLang/julia/issues/25021 +[#25029]: https://github.com/JuliaLang/julia/issues/25029 +[#25030]: https://github.com/JuliaLang/julia/issues/25030 +[#25037]: https://github.com/JuliaLang/julia/issues/25037 +[#25046]: https://github.com/JuliaLang/julia/issues/25046 +[#25047]: https://github.com/JuliaLang/julia/issues/25047 +[#25056]: https://github.com/JuliaLang/julia/issues/25056 +[#25057]: https://github.com/JuliaLang/julia/issues/25057 +[#25058]: https://github.com/JuliaLang/julia/issues/25058 +[#25067]: https://github.com/JuliaLang/julia/issues/25067 +[#25088]: https://github.com/JuliaLang/julia/issues/25088 +[#25162]: https://github.com/JuliaLang/julia/issues/25162 +[#25165]: https://github.com/JuliaLang/julia/issues/25165 +[#25168]: https://github.com/JuliaLang/julia/issues/25168 +[#25184]: https://github.com/JuliaLang/julia/issues/25184 +[#25197]: https://github.com/JuliaLang/julia/issues/25197 +[#25210]: https://github.com/JuliaLang/julia/issues/25210 +[#25231]: https://github.com/JuliaLang/julia/issues/25231 +[#25249]: https://github.com/JuliaLang/julia/issues/25249 +[#25277]: https://github.com/JuliaLang/julia/issues/25277 +[#25278]: https://github.com/JuliaLang/julia/issues/25278 +[#25311]: https://github.com/JuliaLang/julia/issues/25311 +[#25321]: https://github.com/JuliaLang/julia/issues/25321 +[#25368]: https://github.com/JuliaLang/julia/issues/25368 +[#25391]: https://github.com/JuliaLang/julia/issues/25391 +[#25424]: https://github.com/JuliaLang/julia/issues/25424 +[#25429]: https://github.com/JuliaLang/julia/issues/25429 +[#25457]: https://github.com/JuliaLang/julia/issues/25457 +[#25459]: https://github.com/JuliaLang/julia/issues/25459 +[#25472]: https://github.com/JuliaLang/julia/issues/25472 +[#25496]: https://github.com/JuliaLang/julia/issues/25496 +[#25501]: https://github.com/JuliaLang/julia/issues/25501 +[#25522]: https://github.com/JuliaLang/julia/issues/25522 +[#25532]: https://github.com/JuliaLang/julia/issues/25532 +[#25545]: https://github.com/JuliaLang/julia/issues/25545 +[#25564]: https://github.com/JuliaLang/julia/issues/25564 +[#25567]: https://github.com/JuliaLang/julia/issues/25567 +[#25571]: https://github.com/JuliaLang/julia/issues/25571 +[#25616]: https://github.com/JuliaLang/julia/issues/25616 +[#25622]: https://github.com/JuliaLang/julia/issues/25622 +[#25631]: https://github.com/JuliaLang/julia/issues/25631 +[#25633]: https://github.com/JuliaLang/julia/issues/25633 +[#25634]: https://github.com/JuliaLang/julia/issues/25634 +[#25654]: https://github.com/JuliaLang/julia/issues/25654 +[#25655]: https://github.com/JuliaLang/julia/issues/25655 +[#25662]: https://github.com/JuliaLang/julia/issues/25662 +[#25667]: https://github.com/JuliaLang/julia/issues/25667 +[#25668]: https://github.com/JuliaLang/julia/issues/25668 +[#25697]: https://github.com/JuliaLang/julia/issues/25697 +[#25701]: https://github.com/JuliaLang/julia/issues/25701 +[#25725]: https://github.com/JuliaLang/julia/issues/25725 +[#25745]: https://github.com/JuliaLang/julia/issues/25745 +[#25763]: https://github.com/JuliaLang/julia/issues/25763 +[#25786]: https://github.com/JuliaLang/julia/issues/25786 +[#25812]: https://github.com/JuliaLang/julia/issues/25812 +[#25815]: https://github.com/JuliaLang/julia/issues/25815 +[#25830]: https://github.com/JuliaLang/julia/issues/25830 +[#25845]: https://github.com/JuliaLang/julia/issues/25845 +[#25854]: https://github.com/JuliaLang/julia/issues/25854 +[#25858]: https://github.com/JuliaLang/julia/issues/25858 +[#25872]: https://github.com/JuliaLang/julia/issues/25872 +[#25896]: https://github.com/JuliaLang/julia/issues/25896 +[#25944]: https://github.com/JuliaLang/julia/issues/25944 +[#25947]: https://github.com/JuliaLang/julia/issues/25947 +[#25979]: https://github.com/JuliaLang/julia/issues/25979 +[#25980]: https://github.com/JuliaLang/julia/issues/25980 +[#25990]: https://github.com/JuliaLang/julia/issues/25990 +[#25998]: https://github.com/JuliaLang/julia/issues/25998 +[#26009]: https://github.com/JuliaLang/julia/issues/26009 +[#26071]: https://github.com/JuliaLang/julia/issues/26071 +[#26080]: https://github.com/JuliaLang/julia/issues/26080 +[#26093]: https://github.com/JuliaLang/julia/issues/26093 +[#26149]: https://github.com/JuliaLang/julia/issues/26149 +[#26154]: https://github.com/JuliaLang/julia/issues/26154 +[#26156]: https://github.com/JuliaLang/julia/issues/26156 +[#26161]: https://github.com/JuliaLang/julia/issues/26161 +[#26206]: https://github.com/JuliaLang/julia/issues/26206 +[#26212]: https://github.com/JuliaLang/julia/issues/26212 +[#26262]: https://github.com/JuliaLang/julia/issues/26262 +[#26283]: https://github.com/JuliaLang/julia/issues/26283 +[#26284]: https://github.com/JuliaLang/julia/issues/26284 +[#26286]: https://github.com/JuliaLang/julia/issues/26286 +[#26347]: https://github.com/JuliaLang/julia/issues/26347 +[#26436]: https://github.com/JuliaLang/julia/issues/26436 +[#26442]: https://github.com/JuliaLang/julia/issues/26442 +[#26486]: https://github.com/JuliaLang/julia/issues/26486 +[#26559]: https://github.com/JuliaLang/julia/issues/26559 +[#26576]: https://github.com/JuliaLang/julia/issues/26576 +[#26600]: https://github.com/JuliaLang/julia/issues/26600 +[#26660]: https://github.com/JuliaLang/julia/issues/26660 +[#26670]: https://github.com/JuliaLang/julia/issues/26670 +[#26733]: https://github.com/JuliaLang/julia/issues/26733 +[#26775]: https://github.com/JuliaLang/julia/issues/26775 +[#26858]: https://github.com/JuliaLang/julia/issues/26858 +[#26862]: https://github.com/JuliaLang/julia/issues/26862 +[#26932]: https://github.com/JuliaLang/julia/issues/26932 +[#26935]: https://github.com/JuliaLang/julia/issues/26935 +[#26980]: https://github.com/JuliaLang/julia/issues/26980 +[#26997]: https://github.com/JuliaLang/julia/issues/26997 +[#27067]: https://github.com/JuliaLang/julia/issues/27067 +[#27071]: https://github.com/JuliaLang/julia/issues/27071 +[#27075]: https://github.com/JuliaLang/julia/issues/27075 +[#27100]: https://github.com/JuliaLang/julia/issues/27100 +[#27121]: https://github.com/JuliaLang/julia/issues/27121 +[#27159]: https://github.com/JuliaLang/julia/issues/27159 +[#27164]: https://github.com/JuliaLang/julia/issues/27164 +[#27189]: https://github.com/JuliaLang/julia/issues/27189 +[#27212]: https://github.com/JuliaLang/julia/issues/27212 +[#27248]: https://github.com/JuliaLang/julia/issues/27248 +[#27309]: https://github.com/JuliaLang/julia/issues/27309 +[#27401]: https://github.com/JuliaLang/julia/issues/27401 +[#27447]: https://github.com/JuliaLang/julia/issues/27447 +[#27459]: https://github.com/JuliaLang/julia/issues/27459 +[#27470]: https://github.com/JuliaLang/julia/issues/27470 +[#27473]: https://github.com/JuliaLang/julia/issues/27473 +[#27554]: https://github.com/JuliaLang/julia/issues/27554 +[#27560]: https://github.com/JuliaLang/julia/issues/27560 +[#27616]: https://github.com/JuliaLang/julia/issues/27616 +[#27635]: https://github.com/JuliaLang/julia/issues/27635 +[#27641]: https://github.com/JuliaLang/julia/issues/27641 +[#27711]: https://github.com/JuliaLang/julia/issues/27711 +[#27726]: https://github.com/JuliaLang/julia/issues/27726 +[#27746]: https://github.com/JuliaLang/julia/issues/27746 +[#27856]: https://github.com/JuliaLang/julia/issues/27856 +[#27859]: https://github.com/JuliaLang/julia/issues/27859 +[#27908]: https://github.com/JuliaLang/julia/issues/27908 +[#27944]: https://github.com/JuliaLang/julia/issues/27944 +[#28045]: https://github.com/JuliaLang/julia/issues/28045 +[#28065]: https://github.com/JuliaLang/julia/issues/28065 +[#28155]: https://github.com/JuliaLang/julia/issues/28155 +[#28266]: https://github.com/JuliaLang/julia/issues/28266 +[#28302]: https://github.com/JuliaLang/julia/issues/28302 + +Julia v0.6.0 Release Notes +========================== + +New language features +--------------------- + + * New type system capabilities ([#8974], [#18457]) + + + Type parameter constraints can refer to previous parameters, e.g. + `type Foo{R<:Real, A<:AbstractArray{R}}`. Can also be used in method definitions. + + + New syntax `Array{T} where T<:Integer`, indicating a union of types over all + specified values of `T` (represented by a `UnionAll` type). This provides behavior + similar to parametric methods or `typealias`, but can be used anywhere a type is + accepted. This syntax can also be used in method definitions, e.g. + `function inv(M::Matrix{T}) where T<:AbstractFloat`. + Anonymous functions can have type parameters via the syntax + `((x::Array{T}) where T<:Real) -> 2x`. + + + Implicit type parameters, e.g. `Vector{<:Real}` is equivalent to + `Vector{T} where T<:Real`, and similarly for `Vector{>:Int}` ([#20414]). + + + Much more accurate subtype and type intersection algorithms. Method sorting and + identification of equivalent and ambiguous methods are improved as a result. + +Language changes +---------------- + + * "Inner constructor" syntax for parametric types is deprecated. For example, + in this definition: + ``` + type Foo{T,S<:Real} + x + Foo(x) = new(x) + end + ``` + the syntax `Foo(x) = new(x)` actually defined a constructor for `Foo{T,S}`, + i.e. the case where the type parameters are specified. For clarity, this + definition now must be written as `Foo{T,S}(x) where {T,S<:Real} = new(x)` + ([#11310], [#20308]). + + * The keywords used to define types have changed ([#19157], [#20418]). + + + `immutable` changes to `struct` + + + `type` changes to `mutable struct` + + + `abstract` changes to `abstract type ... end` + + + `bitstype 32 Char` changes to `primitive type Char 32 end` + + In 0.6, `immutable` and `type` are still allowed as synonyms without a deprecation + warning. + + * Multi-line and single-line nonstandard command literals have been added. A + nonstandard command literal is like a nonstandard string literal, but the + syntax uses backquotes (``` ` ```) instead of double quotes, and the + resulting macro called is suffixed with `_cmd`. For instance, the syntax + ``` q`xyz` ``` is equivalent to `@q_cmd "xyz"` ([#18644]). + + * Nonstandard string and command literals can now be qualified with their + module. For instance, `Base.r"x"` is now parsed as `Base.@r_str "x"`. + Previously, this syntax parsed as an implicit multiplication ([#18690]). + + * For every binary operator `⨳`, `a .⨳ b` is now automatically equivalent to + the `broadcast` call `(⨳).(a, b)`. Hence, one no longer defines methods + for `.*` etcetera. This also means that "dot operations" automatically + fuse into a single loop, along with other dot calls `f.(x)` ([#17623]). + Similarly for unary operators ([#20249]). + + * Newly defined methods are no longer callable from the same dynamic runtime + scope they were defined in ([#17057]). + + * `isa` is now parsed as an infix operator with the same precedence as `in` + ([#19677]). + + * `@.` is now parsed as `@__dot__`, and can be used to add dots to + every function call, operator, and assignment in an expression ([#20321]). + + * The identifier `_` can be assigned, but accessing its value is deprecated, + allowing this syntax to be used in the future for discarding values ([#9343], + [#18251], [#20328]). + + * The `typealias` keyword is deprecated, and should be replaced with + `Vector{T} = Array{T,1}` or a `const` assignment ([#20500]). + + * Experimental feature: `x^n` for integer literals `n` (e.g. `x^3` + or `x^-3`) is now lowered to `Base.literal_pow(^, x, Val{n})`, to enable + compile-time specialization for literal integer exponents ([#20530], [#20889]). + +Breaking changes +---------------- + +This section lists changes that do not have deprecation warnings. + + * `readline`, `readlines` and `eachline` return lines without line endings by default. + You *must* use `readline(s, chomp=false)`, etc. to get the old behavior where + returned lines include trailing end-of-line character(s) ([#19944]). + + * `String`s no longer have a `.data` field (as part of a significant performance + improvement). Use `Vector{UInt8}(str)` to access a string as a byte array. + However, allocating the `Vector` object has overhead. You can also use + `codeunit(str, i)` to access the `i`th byte of a `String`. + Use `sizeof(str)` instead of `length(str.data)`, and `pointer(str)` instead of + `pointer(str.data)` ([#19449]). + + * Operations between `Float16` and `Integers` now return `Float16` instead of `Float32` ([#17261]). + + * Keyword arguments are processed left-to-right: if the same keyword is specified more than + once, the rightmost occurrence takes precedence ([#17785]). + + * The `lgamma(z)` function now uses a different (more standard) branch cut + for `real(z) < 0`, which differs from `log(gamma(z))` by multiples of 2π + in the imaginary part ([#18330]). + + * `broadcast` now handles tuples, and treats any argument that is not a tuple + or an array as a "scalar" ([#16986]). + + * `broadcast` now produces a `BitArray` instead of `Array{Bool}` for + functions yielding a boolean result. If you want `Array{Bool}`, use + `broadcast!` or `.=` ([#17623]). + + * Broadcast `A[I...] .= X` with entirely scalar indices `I` is deprecated as + its behavior will change in the future. Use `A[I...] = X` instead. + + * Operations like `.+` and `.*` on `Range` objects are now generic + `broadcast` calls (see [above](#language-changes)) and produce an `Array`. + If you want a `Range` result, use `+` and `*`, etcetera ([#17623]). + + * `broadcast` now treats `Ref` (except for `Ptr`) arguments as 0-dimensional + arrays ([#18965]). + + * `broadcast` now handles missing data (`Nullable`s) allowing operations to + be lifted over mixtures of `Nullable`s and scalars, as if the `Nullable` + were like an array with zero or one element ([#16961], [#19787]). + + * The runtime now enforces when new method definitions can take effect ([#17057]). + The flip-side of this is that new method definitions should now reliably actually + take effect, and be called when evaluating new code ([#265]). + + * The array-scalar methods of `/`, `\`, `*`, `+`, and `-` now follow broadcast promotion + rules. (Likewise for the now-deprecated array-scalar methods of `div`, `mod`, `rem`, + `&`, `|`, and `xor`; see "Deprecated or removed" below.) ([#19692]). + + * `broadcast!(f, A)` now calls `f()` for each element of `A`, rather than doing `fill!(A, f())` ([#19722]). + + * `rmprocs` now throws an exception if requested workers have not been completely + removed before `waitfor` seconds. With a `waitfor=0`, `rmprocs` returns immediately + without waiting for worker exits. + + * `quadgk` has been moved from Base into a separate package ([#19741]). + + * The `Collections` module has been removed, and all functions defined therein have been + moved to the `DataStructures` package ([#19800]). + + * The `RepString` type has been moved to the + [LegacyStrings.jl package](https://github.com/JuliaArchive/LegacyStrings.jl). + + * In macro calls with parentheses, e.g. `@m(a=1)`, assignments are now parsed as + `=` expressions, instead of as `kw` expressions ([#7669]). + + * When used as an infix operator, `~` is now parsed as a call to an ordinary operator + with assignment precedence, instead of as a macro call ([#20406]). + + * (µ "micro" and ɛ "latin epsilon") are considered equivalent to + the corresponding Greek characters in identifiers. `\varepsilon` + now tab-completes to U+03B5 (greek small letter epsilon) ([#19464]). + + * `retry` now inputs the keyword arguments `delays` and `check` instead of + `n` and `max_delay`. The previous functionality can be achieved setting + `delays` to `ExponentialBackOff` ([#19331]). + + * `transpose(::AbstractVector)` now always returns a `RowVector` view of the input (which is a + special 1×n-sized `AbstractMatrix`), not a `Matrix`, etc. In particular, for + `v::AbstractVector` we now have `(v.').' === v` and `v.' * v` is a scalar ([#19670]). + + * Parametric types with "unspecified" parameters, such as `Array`, are now represented + as `UnionAll` types instead of `DataType`s ([#18457]). + + * `Union` types have two fields, `a` and `b`, instead of a single `types` field. + The empty type `Union{}` is represented by a singleton of type `TypeofBottom` ([#18457]). + + * The type `NTuple{N}` now refers to tuples where every element has the same type + (since it is shorthand for `NTuple{N,T} where T`). To get the old behavior of matching + any tuple, use `NTuple{N,Any}` ([#18457]). + + * `FloatRange` has been replaced by `StepRangeLen`, and the internal + representation of `LinSpace` has changed. Aside from changes in + the internal field names, this leads to several differences in + behavior ([#18777]): + + + Both `StepRangeLen` and `LinSpace` can represent ranges of + arbitrary object types---they are no longer limited to + floating-point numbers. + + + For ranges that produce `Float64`, `Float32`, or `Float16` + numbers, `StepRangeLen` can be used to produce values with + little or no roundoff error due to internal arithmetic that is + typically twice the precision of the output result. + + + To take advantage of this precision, `linspace(start, stop, + len)` now returns a range of type `StepRangeLen` rather than + `LinSpace` when `start` and `stop` are + `FloatNN`. `LinSpace(start, stop, len)` always returns a + `LinSpace`. + + + `StepRangeLen(a, step, len)` constructs an ordinary-precision range + using the values and types of `a` and `step` as given, whereas + `range(a, step, len)` will attempt to match inputs `a::FloatNN` + and `step::FloatNN` to rationals and construct a `StepRangeLen` + that internally uses twice-precision arithmetic. These two + outcomes exhibit differences in both precision and speed. + + * `A=>B` expressions are now parsed as calls instead of using `=>` as the + expression head ([#20327]). + + * The `count` function no longer sums non-boolean values ([#20404]) + + * The generic `getindex(::AbstractString, ::AbstractVector)` method's signature has been + tightened to `getindex(::AbstractString, ::AbstractVector{<:Integer})`. Consequently, + indexing into `AbstractString`s with non-`AbstractVector{<:Integer}` `AbstractVector`s + now throws a `MethodError` in the absence of an appropriate specialization. + (Previously such cases failed less explicitly with the exception of + `AbstractVector{Bool}`, which now throws an `ArgumentError` noting that + logical indexing into strings is not supported.) ([#20248]) + + * Bessel, Hankel, Airy, error, Dawson, eta, zeta, digamma, inverse digamma, + trigamma, and polygamma special functions have been moved from Base to + the + [SpecialFunctions.jl package](https://github.com/JuliaMath/SpecialFunctions.jl) + ([#20427]). Note that `airy`, `airyx` and `airyprime` have been deprecated + in favor of more specific functions (`airyai`, `airybi`, `airyaiprime`, + `airybiprimex`, `airyaix`, `airybix`, `airyaiprimex`, `airybiprimex`) + ([#18050]). + + * When a macro is called in the module in which that macro is defined, global variables + in the macro are now correctly resolved in the macro definition environment. Breakage + from this change commonly manifests as undefined variable errors that do not occur + under 0.5. Fixing such breakage typically requires sprinkling additional `esc`s in + the offending macro ([#15850]). + + * `write` on an `IOBuffer` now returns a signed integer in order to be + consistent with other buffers ([#20609]). + + * The `<:Integer` division fallback `/(::Integer, ::Integer)`, which formerly + inappropriately took precedence over other division methods for some + mixed-integer-type division calls, has been removed ([#19779]). + + * `@async`, `@spawn`, `@spawnat`, `@fetch` and `@fetchfrom` no longer implicitly + localize variables. Previously, the expression would be wrapped in an implicit + `let` block ([#19594]). + + * `parse` no longer accepts IPv4 addresses including leading zeros, octal, or hexadecimal. + Convert IPv4 addresses including octal or hexadecimal to decimal, and remove leading + zeros in decimal addresses ([#19811]). + + * Closures shipped for remote execution via `@spawn` or `remotecall` now automatically + serialize globals defined under Main. For details, please refer to the paragraph + on "Global variables" under the "Parallel computing" chapter in the manual ([#19594]). + + * `homedir` now determines the user's home directory via `libuv`'s `uv_os_homedir`, + rather than from environment variables ([#19636]). + + * Workers now listen on an ephemeral port assigned by the OS. Previously workers would + listen on the first free port available from 9009 ([#21818]). + + +Library improvements +-------------------- + + * A new `@views` macro was added to convert a whole expression or block of code to + use views for all slices ([#20164]). + + * `max`, `min`, and related functions (`minmax`, `maximum`, `minimum`, `extrema`) + now return `NaN` for `NaN` arguments ([#12563]). + + * `oneunit(x)` function to return a dimensionful version of `one(x)` + (which is clarified to mean a dimensionless quantity if `x` is dimensionful) ([#20268]). + + * The `chop` and `chomp` functions now return a `SubString` ([#18339]). + + * Numbered stackframes printed in stacktraces can now be opened in an editor by + entering the corresponding number in the REPL and pressing `^Q` ([#19680]). + + * The REPL now supports something called *prompt pasting* ([#17599]). + This activates when pasting text that starts with `julia> ` into the REPL. + In that case, only expressions starting with `julia> ` are parsed, the rest are removed. + This makes it possible to paste a chunk of code that has been copied from a REPL session + without having to scrub away prompts and outputs. + This can be disabled or enabled at will with `Base.REPL.enable_promptpaste(::Bool)`. + + * The function `print_with_color` can now take a color + represented by an integer between 0 and 255 inclusive + as its first argument ([#18473]). For a number-to-color mapping, please refer to + [this chart](https://upload.wikimedia.org/wikipedia/commons/1/15/Xterm_256color_chart.svg). + It is also possible to use numbers as colors in environment variables that customizes colors in the REPL. + For example, to get orange warning messages, simply set `ENV["JULIA_WARN_COLOR"] = 208`. + Please note that not all terminals support 256 colors. + + * The function `print_with_color` no longer prints text in bold by default ([#18628]). + Instead, the function now take a keyword argument `bold::Bool` + which determines whether to print in bold or not. On some terminals, printing a color in non bold + results in slightly darker colors being printed than when printing in bold. + Therefore, light versions of the colors are now supported. + For the available colors see the help entry on `print_with_color`. + + * The default text style for REPL input and answers has been changed from bold to normal ([#11250]). + They can be changed back to bold by setting the environment variables + `JULIA_INPUT_COLOR` and `JULIA_ANSWER_COLOR` to `"bold"`. + For example, one way of doing this is adding `ENV["JULIA_INPUT_COLOR"] = :bold` + and `ENV["JULIA_ANSWER_COLOR"] = :bold` to the `.juliarc.jl` file. See the + [manual section on customizing colors](https://docs.julialang.org/en/latest/manual/interacting-with-julia#Customizing-Colors-1) + for more information. + + * The default color for info messages has been changed from blue to cyan + ([#18442]), and for warning messages from red to yellow ([#18453]). This + can be changed back to the original colors by setting the environment + variables `JULIA_INFO_COLOR` to `"blue"` and `JULIA_WARN_COLOR` to `"red"`. + + * Iteration utilities that wrap iterators and return other iterators (`enumerate`, `zip`, `rest`, + `countfrom`, `take`, `drop`, `cycle`, `repeated`, `product`, `flatten`, `partition`) have been + moved to the module `Base.Iterators` ([#18839]). + + * BitArrays can now be constructed from arbitrary iterables, in particular from generator expressions, + e.g. `BitArray(isodd(x) for x = 1:100)` ([#19018]). + + * `hcat`, `vcat`, and `hvcat` now work with `UniformScaling` objects, so + you can now do e.g. `[A I]` and it will concatenate an appropriately sized + identity matrix ([#19305]). + + * New `accumulate` and `accumulate!` functions were added, which generalize `cumsum` and `cumprod`. + Also known as a [scan](https://en.wikipedia.org/wiki/Prefix_sum) operation ([#18931]). + + * `reshape` now allows specifying one dimension with a `Colon()` (`:`) for the new shape, in which case + that dimension's length will be computed such that its product with all the other dimensions is equal + to the length of the original array ([#19919]). + + * The new `to_indices` function provides a uniform interface for index conversions, + taking an array and a tuple of indices as arguments and returning a tuple of + integers and/or arrays of supported scalar indices. It will throw an `ArgumentError` + for any unsupported indices, and the returned arrays should be iterated over (and + not indexed into) to support more efficient logical indexing ([#19730]). + + + Using colons (`:`) to represent a collection of indices is deprecated. They now must be + explicitly converted to a specialized array of integers with the `to_indices` function. +    As a result, the type of `SubArray`s that represent views over colon indices has changed. + + + Logical indexing is now more efficient. Logical arrays are converted by `to_indices` to + a lazy, iterable collection of indices that doesn't support indexing. A deprecation + provides indexing support with O(n) lookup. + + + The performance of indexing with `CartesianIndex`es is also improved in many situations. + + * A new `titlecase` function was added, to capitalize the first character of each word within a string ([#19469]). + + * `any` and `all` now always short-circuit, and `mapreduce` never short-circuits ([#19543]). + That is, not every member of the input iterable will be visited if a `true` (in the case of `any`) or + `false` (in the case of `all`) value is found, and `mapreduce` will visit all members of the iterable. + + * Additional methods for `ones` and `zeros` functions were added + to support the same signature as the `similar` function ([#19635]). + + * `count` now has a `count(itr)` method equivalent to `count(identity, itr)` ([#20403]). + + * Methods for `map` and `filter` with `Nullable` arguments have been implemented; + the semantics are as if the `Nullable` were a container with zero or one elements ([#16961]). + + * New `@test_warn` and `@test_nowarn` macros were added in the `Base.Test` module to + test for the presence or absence of warning messages ([#19903]). + + * `logging` can now be used to redirect `info`, `warn`, and `error` messages + either universally or on a per-module/function basis ([#16213]). + + * New function `Base.invokelatest(f, args...)` to call the latest version + of a function in circumstances where an older version may be called + instead (e.g. in a function calling `eval`) ([#19784]). + + * A new `iszero(x)` function was added, to quickly check whether `x` is zero + (or is all zeros, for an array) ([#19950]). + + * `notify` now returns a count of tasks woken up ([#19841]). + + * A new nonstandard string literal `raw"..."` was added, + for creating strings with no interpolation or unescaping ([#19900]). + + * A new `Dates.Time` type was added that supports representing the time of day + with up to nanosecond resolution ([#12274]). + + * Raising one or negative one to a negative integer power formerly threw a `DomainError`. + One raised to any negative integer power now yields one, negative one raised to any + negative even integer power now yields one, and negative one raised to any negative + odd integer power now yields negative one. Similarly, raising `true` to any negative + integer power now yields `true` rather than throwing a `DomainError` ([#18342]). + + * A new `@macroexpand` macro was added as a convenient alternative to the `macroexpand` function ([#18660]). + + * `invoke` now supports keyword arguments ([#20345]). + + * A new `ConjArray` type was added, as a wrapper type for lazy complex conjugation of arrays. + Currently, it is used by default for the new `RowVector` type only, and + enforces that both `transpose(vec)` and `ctranspose(vec)` are views not copies ([#20047]). + + * `rem` now accepts a `RoundingMode` argument via `rem(x, y, r::RoundingMode)`, yielding + `x - y*round(x/y, r)` without intermediate rounding. In particular, `rem(x, y, RoundNearest)` + yields a value in the interval `[-abs(y)/2, abs(y)/2]`), which corresponds to the IEE754 + `remainder` function. Similarly, `rem2pi(x, r::RoundingMode)` now exists as well, yielding + `rem(x, 2pi, r::RoundingMode)` but with greater accuracy ([#10946]). + + * `map[!]` and `broadcast[!]` now have dedicated methods for sparse/structured + vectors/matrices. Specifically, `map[!]` and `broadcast[!]` over combinations including + one or more `SparseVector`, `SparseMatrixCSC`, `Diagonal`, `Bidiagonal`, `Tridiagonal`, + or `SymTridiagonal`, and any number of `broadcast` scalars, `Vector`s, or `Matrix`s, + now efficiently yield `SparseVector`s or `SparseMatrix`s as appropriate ([#19239], + [#19371], [#19518], [#19438], [#19690], [#19724], [#19926], [#19934], [#20009]). + + * The operators `!` and `∘` (`\circ` at the REPL and in most code editors) now + respectively perform predicate function negation and function composition. For example, + `map(!iszero, (0, 1))` is now equivalent to `map(x -> !iszero(x), (0, 1))` and + `map(uppercase ∘ hex, 250:255)` is now equivalent to + `map(x -> uppercase(hex(x)), 250:255)` ([#17155]). + + * `enumerate` now supports the two-argument form `enumerate(::IndexStyle, iterable)`. + This form allows specification of the returned indices' style. For example, + `enumerate(IndexLinear, iterable)` yields linear indices and + `enumerate(IndexCartesian, iterable)` yields cartesian indices ([#16378]). + +Compiler/Runtime improvements +----------------------------- + + * `ccall` is now implemented as a macro, + removing the need for special code-generator support for `Intrinsics` ([#18754]). + + * `ccall` gained limited support for a `llvmcall` calling-convention. + This can replace many uses of `llvmcall` with a simpler, shorter declaration ([#18754]). + + * All `Intrinsics` are now `Builtin` functions instead and have proper error checking + and fall-back static compilation support ([#18754]). + +Deprecated or removed +--------------------- + + * `ipermutedims(A::AbstractArray, p)` has been deprecated in favor of + `permutedims(A, invperm(p))` ([#18891]). + + * Linear indexing is now only supported when there is exactly one + non-cartesian index provided. Allowing a trailing index at dimension `d` to + linearly access the higher dimensions from array `A` (beyond `size(A, d)`) + has been deprecated as a stricter constraint during bounds checking. + Instead, `reshape` the array such that its dimensionality matches the + number of indices ([#20079]). + + * `Multimedia.@textmime "mime"` has been deprecated. Instead define + `Multimedia.istextmime(::MIME"mime") = true` ([#18441]). + + * `isdefined(a::Array, i::Int)` has been deprecated in favor of `isassigned` ([#18346]). + + * The three-argument `SubArray` constructor (which accepts `dims::Tuple` as its third + argument) has been deprecated in favor of the two-argument equivalent (the + `dims::Tuple` argument being superfluous) ([#19259]). + + * `is` has been deprecated in favor of `===` (which used to be an alias for `is`) ([#17758]). + + * Ambiguous methods for addition and subtraction between `UniformScaling`s and `Number`s, + for example `(+)(J::UniformScaling, x::Number)`, have been deprecated in favor of + unambiguous, explicit equivalents, for example `J.λ + x` ([#17607]). + + * `num` and `den` have been deprecated in favor of `numerator` and `denominator` respectively ([#19233],[#19246]). + + * `delete!(ENV::EnvDict, k::AbstractString, def)` has been deprecated in favor of + `pop!(ENV, k, def)`. Be aware that `pop!` returns `k` or `def`, whereas `delete!` + returns `ENV` or `def` ([#18012]). + + * infix operator `$` has been deprecated in favor of infix `⊻` or function `xor` ([#18977]). + + * The single-argument form of `write` (`write(x)`, with implicit `STDOUT` output stream), + has been deprecated in favor of the explicit equivalent `write(STDOUT, x)` ([#17654]). + + * `Dates.recur` has been deprecated in favor of `filter` ([#19288]) + + * A number of ambiguous `convert` operations between `Number`s (especially `Real`s) + and `Date`, `DateTime`, and `Period` types have been deprecated in favor of + unambiguous `convert` and explicit constructor calls. Additionally, ambiguous colon + construction of `<:Period` ranges without step specification, for example + `Dates.Hour(1):Dates.Hour(2)`, has been deprecated in favor of such construction + including step specification, for example `Dates.Hour(1):Dates.Hour(1):Dates.Hour(2)` + ([#19920]). + + * `cummin` and `cummax` have been deprecated in favor of `accumulate` ([#18931]). + + * The `Array` constructor syntax `Array(T, dims...)` has been deprecated + in favor of the forms `Array{T,N}(dims...)` (where `N` is known, or + particularly `Vector{T}(dims...)` for `N = 1` and `Matrix{T}(dims...)` for `N = 2`), + and `Array{T}(dims...)` (where `N` is not known). Likewise for `SharedArray`s ([#19989]). + + * `sumabs` and `sumabs2` have been deprecated in favor of `sum(abs, x)` and `sum(abs2, x)`, respectively. + `maxabs` and `minabs` have similarly been deprecated in favor of `maximum(abs, x)` and `minimum(abs, x)`. + Likewise for the in-place counterparts of these functions ([#19598]). + + * The array-reducing form of `isinteger` (`isinteger(x::AbstractArray)`) has been + deprecated in favor of `all(isinteger, x)` ([#19925]). + + * `produce`, `consume` and iteration over a Task object have been deprecated in favor of + using Channels for inter-task communication ([#19841]). + + * The `negate` keyword has been deprecated from all functions in the `Dates` adjuster + API (`adjust`, `tonext`, `toprev`, `Date`, `Time`, and `DateTime`). Instead use + predicate function negation via the `!` operator + (see [Library Improvements](#library-improvements)) ([#20213]). + + * `@test_approx_eq x y` has been deprecated in favor of `@test isapprox(x,y)` or `@test x ≈ y` ([#4615]). + + * `Matrix()` and `Matrix{T}()` have been deprecated in favor of the explicit forms + `Matrix(0, 0)` and `Matrix{T}(0, 0)` ([#20330]). + + * Vectorized functions have been deprecated in favor of dot syntax ([#17302], [#17265], + [#18558], [#19711], [#19712], [#19791], [#19802], [#19931], [#20543], [#20228]). + + * All methods of character predicates (`isalnum`, `isalpha`, `iscntrl`, `isdigit`, + `isnumber`, `isgraph`, `islower`, `isprint`, `ispunct`, `isspace`, `isupper`, + `isxdigit`) that accept `AbstractStrings` have been deprecated in favor of `all`. + For example, `isnumber("123")` should now be expressed `all(isnumber, "123")` + ([#20342]). + + * A few names related to indexing traits have been changed: `LinearIndexing` and + `linearindexing` have been deprecated in favor of `IndexStyle`. `LinearFast` has + been deprecated in favor of `IndexLinear`, and `LinearSlow` has been deprecated in + favor of `IndexCartesian` ([#16378]). + + * The two-argument forms of `map` (`map!(f, A)`) and `asyncmap!` (`asyncmap!(f, A)`) + have been deprecated in anticipation of future semantic changes ([#19721]). + + * `unsafe_wrap(String, ...)` has been deprecated in favor of `unsafe_string` ([#19449]). + + * `zeros` and `ones` methods accepting an element type as the first argument and an + array as the second argument, for example `zeros(Float64, [1, 2, 3])`, have been + deprecated in favor of equivalent methods with the second argument instead the + size of the array, for example `zeros(Float64, size([1, 2, 3]))` ([#21183]). + + * `Base.promote_eltype_op` has been deprecated ([#19669], [#19814], [#19937]). + + * `isimag` has been deprecated ([#19949]). + + * The tuple-of-types form of `invoke`, `invoke(f, (types...), ...)`, has been deprecated + in favor of the tuple-type form `invoke(f, Tuple{types...}, ...)` ([#18444]). + + * `Base._promote_array_type` has been deprecated ([#19766]). + + * `broadcast_zpreserving` has been deprecated ([#19533], [#19720]). + + * Methods allowing indexing of tuples by `AbstractArray`s with more than one dimension + have been deprecated. (Indexing a tuple by such a higher-dimensional `AbstractArray` + should yield a tuple with more than one dimension, but tuples are one-dimensional.) + ([#19737]). + + * `@test_approx_eq a b` has been deprecated in favor of `@test a ≈ b` (or, + equivalently, `@test ≈(a, b)` or `@test isapprox(a, b)`). + `@test_approx_eq_eps` has been deprecated in favor of new `@test` syntax: + `@test` now supports the syntax `@test f(args...) key=val ...` for + `@test f(args..., key=val...)`. This syntax allows, for example, writing + `@test a ≈ b atol=c` in place of `@test ≈(a, b, atol=c)` (and hence + `@test_approx_eq_eps a b c`) ([#19901]). + + * `takebuf_array` has been deprecated in favor of `take!`, and `takebuf_string(x)` + has been deprecated in favor of `String(take!(x))` ([#19088]). + + * `convert` methods from `Diagonal` and `Bidiagonal` to subtypes of + `AbstractTriangular` have been deprecated ([#17723]). + + * `Base.LinAlg.arithtype` has been deprecated. If you were using `arithtype` within a + `promote_op` call, instead use `promote_op(Base.LinAlg.matprod, Ts...)`. Otherwise, + consider defining equivalent functionality locally ([#18218]). + + * Special characters (`#{}()[]<>|&*?~;`) should now be quoted in commands. For example, + ``` `export FOO=1\;` ``` should replace ``` `export FOO=1;` ``` and + ``` `cd $dir '&&' $thingie` ``` should replace ``` `cd $dir && $thingie` ``` ([#19786]). + + * Zero-argument `Channel` constructors (`Channel()`, `Channel{T}()`) have been deprecated + in favor of equivalents accepting an explicit `Channel` size + (`Channel(2)`, `Channel{T}(2)`) ([#18832]). + + * The zero-argument constructor `MersenneTwister()` has been + deprecated in favor of the explicit `MersenneTwister(0)` ([#16984]). + + * `Base.promote_type(op::Type, Ts::Type...)` has been removed as part of an overhaul + of `broadcast`'s promotion mechanism. If you need the functionality of that + `Base.promote_type` method, consider defining it locally via + `Core.Compiler.return_type(op, Tuple{Ts...})` ([#18642]). + + * `bitbroadcast` has been deprecated in favor of `broadcast`, which now produces a + `BitArray` instead of `Array{Bool}` for functions yielding a boolean result ([#19771]). + + * To complete the deprecation of histogram-related functions, `midpoints` has been + deprecated. Instead use the + [StatsBase.jl package](https://github.com/JuliaStats/StatsBase.jl)'s + `midpoints` function ([#20058]). + + * Passing a type argument to `LibGit2.cat` has been deprecated in favor of a simpler, + two-argument method for `LibGit2.cat` ([#20435]). + + * The `LibGit2.owner` function for finding the repository which owns a given Git object + has been deprecated in favor of `LibGit2.repository` ([#20135]). + + * The `LibGit2.GitAnyObject` type has been renamed to `LibGit2.GitUnknownObject` to + clarify its intent ([#19935]). + + * The `LibGit2.GitOid` type has been renamed to `LibGit2.GitHash` for clarity ([#19878]). + + * Finalizing `LibGit2` objects with `finalize` has been deprecated in favor of using `close` + ([#19660]). + + * Parsing string dates from a `Dates.DateFormat` object has been deprecated as part of a + larger effort toward faster, more extensible date parsing ([#20952]). + +Command-line option changes +--------------------------- + + * In `polly` builds (`USE_POLLY := 1`), the new flag `--polly={yes|no}` controls whether + `@polly` declarations are respected. (With `--polly=no`, `@polly` declarations are + ignored.) This flag is also available in non-`polly` builds (`USE_POLLY := 0`), + but has no effect ([#18159]). + +Julia v0.5.0 Release Notes +========================== + +New language features +--------------------- + + * Generator expressions: `f(i) for i in 1:n` ([#4470]). This returns an iterator + that computes the specified values on demand. This is useful for computing, e.g. + `sum(f(i) for i in 1:n)` without creating an intermediate array of values. + + * Generators and comprehensions support filtering using `if` ([#550]) and nested + iteration using multiple `for` keywords ([#4867]). + + * Fused broadcasting syntax: ``f.(args...)`` is equivalent to ``broadcast(f, args...)`` ([#15032]), + and nested `f.(g.(args...))` calls are fused into a single `broadcast` loop ([#17300]). + Similarly, the syntax `x .= ...` is equivalent to a `broadcast!(identity, x, ...)` + call and fuses with nested "dot" calls; also, `x .+= y` and similar is now + equivalent to `x .= x .+ y`, rather than `x = x .+ y` ([#17510]). + + * Macro expander functions are now generic, so macros can have multiple definitions + (e.g. for different numbers of arguments, or optional arguments) ([#8846], [#9627]). + However note that the argument types refer to the syntax tree representation, and not + to the types of run time values. + + * Varargs functions like `foo{T}(x::T...)` may now restrict the number + of such arguments using `foo{T,N}(x::Vararg{T,N})` ([#11242]). + + * `x ∈ X` is now a synonym for `x in X` in `for` loops and comprehensions, + as it already was in comparisons ([#13824]). + + * The `PROGRAM_FILE` global is now available for determining the name of the running script ([#14114]). + + * The syntax `x.:sym` (e.g. `Base.:+`) is now supported, while using `x.(:sym)` + or `x.(i)` for field access are deprecated in favor of `getfield` ([#15032]). + + * Function return type syntax `function f()::T` has been added ([#1090]). Values returned + from a function with such a declaration will be converted to the specified type `T`. + + * Many more operators now support `.` prefixes (e.g. `.≤`) ([#17393]). However, + users are discouraged from overloading these, since they are mainly parsed + in order to implement backwards compatibility with planned automatic + broadcasting of dot operators in Julia 0.6 ([#16285]). Explicitly qualified + operator names like `Base.≤` should now use `Base.:≤` (prefixed by `@compat` + if you need 0.4 compatibility via the `Compat` package). + + * User-extensible bounds check elimination is now possible with the new + `@boundscheck` macro ([#14474]). This macro marks bounds checking code blocks, + which the compiler may remove when encountered inside an `@inbounds` call. + +Experimental language features +------------------------------ + + * Support for + [multi-threading](https://docs.julialang.org/en/latest/manual/parallel-computing/#multi-threading-experimental). + Loops with independent iterations can be easily parallelized with the + `Threads.@threads` macro. + + * Support for arrays with indexing starting at values different from 1. + The array types are expected to be defined in packages, but now + Julia provides an API for writing generic algorithms for arbitrary + indexing schemes ([#16260]). + +Language changes +---------------- + + * Each function and closure now has its own type. The captured variables of a closure + are fields of its type. `Function` is now an abstract type, and is the default + supertype of functions and closures. All functions, including anonymous functions, + are generic and support all features (e.g. keyword arguments). Instead of adding + methods to `call`, methods are added by type using the syntax + `(::ftype)(...) = ...`. `call` is deprecated ([#13412]). A significant result of + this language change is that higher order functions can be specialized on their + function arguments, leading to much faster functional programming, typically as + fast as if function arguments were manually inlined. See below for details. + + * Square brackets and commas (e.g. `[x, y]`) no longer concatenate arrays, and always + simply construct a vector of the provided values. If `x` and `y` are arrays, + `[x, y]` will be an array of arrays ([#3737], [#2488], [#8599]). + + * `using` and `import` are now case-sensitive even on case-insensitive filesystems + (common on Mac and Windows) ([#13542]). + + * Relational algebra symbols are now allowed as infix operators ([#8036]): + `⨝`, `⟕`, `⟖`, `⟗` for joins and `▷` for anti-join. + + * A warning is always given when a method is overwritten; previously, this was done + only when the new and old definitions were in separate modules ([#14759]). + + * The `if` keyword cannot be followed immediately by a line break ([#15763]). + + * Juxtaposition of numeric literals ending in `.` (e.g. `1.x`) is no longer + allowed ([#15731]). + + * The built-in `NTuple` type has been removed; `NTuple{N,T}` is now + implemented internally as `Tuple{Vararg{T,N}}` ([#11242]). + + * Use of the syntax `x::T` to declare the type of a local variable is deprecated. + In the future this will always mean type assertion, and declarations should use + `local x::T` instead ([#16071]). + When `x` is global, `x::T = ...` and `global x::T` used to mean type assertion, + but this syntax is now reserved for type declaration ([#964]). + + * Dictionary comprehension syntax `[ a=>b for x in y ]` is deprecated. + Use `Dict(a=>b for x in y)` instead ([#16510]). + + * Parentheses are no longer allowed around iteration specifications, e.g. + `for (i = 1:n)` ([#17668]). + +Breaking changes +---------------- + +This section lists changes that do not have deprecation warnings. + + * All dimensions indexed by scalars are now dropped, whereas previously only + trailing scalar dimensions would be omitted from the result ([#13612]). This + is a very major behavioral change, but should cause obvious failures. To retain + a dimension sliced with a scalar `i` slice with `i:i` instead. + + * The assignment operations `.+=`, `.*=` and so on now generate calls + to `broadcast!` on the left-hand side (or call to `view(a, ...)` on the left-hand side + if the latter is an indexing expression, e.g. `a[...]`). This means that they will fail + if the left-hand side is immutable (or does not support `view`), and will otherwise + change the left-hand side in-place ([#17510], [#17546]). + + * Method ambiguities no longer generate warnings when files are loaded, + nor do they dispatch to an arbitrarily-chosen method; instead, a call that + cannot be resolved to a single method results in a `MethodError` at run time, + rather than the previous definition-time warning ([#6190]). + + * Array comprehensions preserve the dimensions of the input ranges. For example, + `[2x for x in A]` will have the same dimensions as `A` ([#16622]). + + * The result type of an array comprehension depends only on the types of elements + computed, instead of using type inference ([#7258]). If the result is empty, then + type inference is still used to determine the element type. + + * `reshape` is now defined to always share data with the original array. + If a reshaped copy is needed, use `copy(reshape(a))` or `copy!` to a new array of + the desired shape ([#4211]). + + * `mapslices` now re-uses temporary storage. Recipient functions that expect + input slices to be persistent should copy data to other storage ([#17266]). + All usages of `mapslices` should be carefully audited since this change can cause + silent, incorrect behavior, rather than failing noisily. + + * Local variables and arguments are represented in lowered code as numbered `Slot` + objects instead of as symbols ([#15609]). + + * The information that used to be in the `ast` field of the `LambdaStaticData` type + is now divided among the fields `code`, `slotnames`, `slottypes`, `slotflags`, + `gensymtypes`, `rettype`, `nargs`, and `isva` in the `LambdaInfo` type ([#15609]). + + * `A <: B` is parsed as `Expr(:(<:), :A, :B)` in all cases ([#9503]). + This also applies to the `>:` operator. + + * Simple 2-argument comparisons like `A < B` are parsed as calls instead of using the + `:comparison` expression type ([#15524]). The `:comparison` expression type is still + produced in ASTs when comparisons are chained (e.g. `A < B ≤ C`). + + * `map` on a dictionary now expects a function that expects and returns a `Pair`. + The result is now another dictionary instead of an array ([#16622]). + + * Bit shift operations (i.e. `<<`, `>>`, and `>>>`) now handle + negative shift counts differently: Negative counts are interpreted + as shifts in the opposite direction. For example, `4 >> -1 == 4 << + +1 == 8`. Previously, negative counts would implicitly overflow to + large positive counts, always yielding either `0` or `-1`. + +Library improvements +-------------------- + + * Strings ([#16107]): + + * The `UTF8String` and `ASCIIString` types have been merged into a single + `String` type ([#16058]). Use `isascii(s)` to check whether + a string contains only ASCII characters. The `ascii(s)` function now + converts `s` to `String`, raising an `ArgumentError` exception if `s` is + not pure ASCII. + + * The `UTF16String` and `UTF32String` types and corresponding `utf16` and + `utf32` converter functions have been removed from the standard library. + If you need these types, they have been moved to the + [LegacyStrings.jl package](https://github.com/JuliaArchive/LegacyStrings.jl). + In the future, more robust Unicode string support will be provided by the + [StringEncodings.jl package](https://github.com/nalimilan/StringEncodings.jl). + If you only need these types to call wide string APIs (UTF-16 on Windows, + UTF-32 on UNIX), consider using the new `transcode` function (see below) + or the `Cwstring` type as a `ccall` argument type, which also ensures + correct NUL termination of string data. + + * A `transcode(T, src)` function is now exported for converting data + between UTF-xx Unicode encodings ([#17323]). + + * The basic string construction routines are now `string(args...)`, + `String(s)`, `unsafe_string(ptr)` (formerly `bytestring(ptr)`), and + `unsafe_wrap(String, ptr)` (formerly `pointer_to_string`) ([#16731]). + + * Comparisons between `Char`s and `Integer`s are now deprecated ([#16024]): + `'x' == 120` now produces a warning but still evaluates to `true`. In the + future it may evaluate to `false` or the comparison may be an error. To + compare characters with integers you should either convert the integer to + a character value or convert the character to the corresponding code point + first: e.g. `'x' == Char(120)` or `Int('x') == 120`. The former is usually + preferable. + + * Support for Unicode 9 ([#17402]). + + * Arrays and linear algebra: + + * Dimensions indexed by multidimensional arrays add dimensions. More generally, the + dimensionality of the result is the sum of the dimensionalities of the indices ([#15431]). + + * New `normalize` and `normalize!` convenience functions for normalizing + vectors ([#13681]). + + * QR matrix factorization: + + * New method for generic QR with column pivoting ([#13480]). + + * New method for polar decompositions of `AbstractVector`s ([#13681]). + + * A new `SparseVector` type allows for one-dimensional sparse arrays. + Slicing and reshaping sparse matrices now return vectors when + appropriate. The `sparsevec` function returns a one-dimensional sparse + vector instead of a one-column sparse matrix. The `SparseMatrix` module + has been renamed to `SparseArrays` ([#13440]). + + * Rank one update and downdate functions, `lowrankupdate`, `lowrankupdate!`, `lowrankdowndate`, + and `lowrankdowndate!`, have been introduced for dense Cholesky factorizations ([#14243], [#14424]). + + * All `sparse` methods now retain provided numerical zeros as structural nonzeros; to + drop numerical zeros, use `dropzeros!` ([#14798], [#15242]). + + * `setindex!` methods for sparse matrices and vectors no longer purge allocated entries + on zero assignment. To drop stored entries from sparse matrices and vectors, use + `Base.SparseArrays.dropstored!` ([#17404]). + + * Concatenating dense and sparse matrices now returns a sparse matrix ([#15172]). + + * Files and I/O: + + * The `open` function now respects `umask` on UNIX when creating files ([#16466], [#16502]). + + * A new function `walkdir()` returns an iterator that walks the tree of a directory ([#8814], [#13707]). + + ``` + for (root, dirs, files) in walkdir(expanduser("~/.julia/v0.5/Plots/src")) + println("$(length(files)) \t files in $root") + end + 19 files in /Users/me/.julia/v0.5/Plots/src + 15 files in /Users/me/.julia/v0.5/Plots/src/backends + 4 files in /Users/me/.julia/v0.5/Plots/src/deprecated + ``` + + * A new function `chown()` changes the ownership of files ([#15007]). + + * Display properties can now be passed among output functions (e.g. `show`) + using an `IOContext` object ([#13825]). + + * `Cmd(cmd; ...)` now accepts new Windows-specific options `windows_verbatim` + (to alter Windows command-line generation) and `windows_hide` (to + suppress creation of new console windows) ([#13780]). + + * There is now a default no-op `flush(io)` function for all `IO` types ([#16403]). + + * Parallel computing: + + * `pmap` keyword arguments `err_retry=true` and `err_stop=false` are deprecated. + Action to be taken on errors can be specified via the `on_error` keyword argument. + Retry is specified via `retry_n`, `retry_on` and `retry_max_delay` ([#15409], [#15975], [#16663]). + + * The functions `remotecall`, `remotecall_fetch`, and `remotecall_wait` now have the + function argument as the first argument to allow for do-block syntax ([#13338]). + + * Statistics: + + * Improve performance of `quantile` ([#14413]). + + * `extrema` can now operate over a region ([#15550]). + + * `cov` and `cor` don't use keyword arguments anymore and are therefore now type stable ([#13465]). + + * Histogram functionality has been deprecated in `Base`. Use the + [StatsBase.jl package](https://github.com/JuliaStats/StatsBase.jl) + instead ([#6842], [#16450]). + + * Testing: + + * The `Base.Test` module now has a `@testset` feature to bundle + tests together and delay throwing an error until the end ([#13062]). + + * The new features are mirrored in the + [BaseTestNext.jl package](https://github.com/IainNZ/BaseTestNext.jl) + for users who would like to use the new functionality on Julia v0.4. + + * The [BaseTestDeprecated.jl package](https://github.com/IainNZ/BaseTestDeprecated.jl) + provides the old-style `handler` functionality, for compatibility + with code that needs to support both Julia v0.4 and v0.5. + + * Package management: + + * The package system (`Pkg`) is now based on the `libgit2` library, rather + than running the `git` program, increasing performance (especially on + Windows) ([#11196]). + + * Package-development functions like `Pkg.tag` and `Pkg.publish` + have been moved to an external [PkgDev] package ([#13387]). + + * Updating only a subset of the packages is now supported, + e.g. `Pkg.update("Example")` ([#17132]). + + * Miscellaneous: + + * Prime number related functions have been moved from `Base` to the + [Primes.jl package](https://github.com/JuliaMath/Primes.jl) ([#16481]). + + * Most of the combinatorics functions have been moved from `Base` + to the [Combinatorics.jl package](https://github.com/JuliaLang/Combinatorics.jl) ([#13897]). + + * New `foreach` function for calling a function on every element of a collection when + the results are not needed ([#13774]). Compared to `map(f, v)`, which allocates and + returns a result array, `foreach(f, v)` calls `f` on each element of `v`, returning + nothing. + + * The new `Base.StackTraces` module makes stack traces easier to use programmatically ([#14469]). + + * The `libjulia` library is now properly versioned and installed to the public `/lib` + directory, instead of the private `/lib/julia` directory ([#16362]). + + * System reflection is now more consistently exposed from `Sys` and not `Base` + (e.g. constants such as `WORD_SIZE` and `CPU_CORES`). `OS_NAME` has been + replaced by `Sys.KERNEL` and always reports the name of the kernel (as + reported by `uname`). The `@windows_only` and `@osx` family of macros + have been replaced with functions such as `is_windows()` and `is_apple()`. + There is now also a `@static` macro that will evaluate the condition of an + if-statement at compile time, for when a static branch is required ([#16219]). + + * `Date` and `DateTime` values can now be rounded to a specified resolution (e.g., 1 month or + 15 minutes) with `floor`, `ceil`, and `round` ([#17037]). + +[PkgDev]: https://github.com/JuliaLang/PkgDev.jl + +Compiler/Runtime improvements +----------------------------- + + * Machine SIMD types can be represented in Julia as a homogeneous tuple of `VecElement` ([#15244]). + + * The performance of higher-order and anonymous functions has been greatly improved. + For example, `map(x->2x, A)` performs as well as `2.*A`([#13412]). + + * On windows, a DLL of standard library code is now precompiled and used by default, + improving startup time ([#16953]). + + * LLVM has been upgraded to version 3.7.1, improving the quality of generated + code and debug info. However compile times may be slightly longer ([#14623]). + +New architectures +----------------- + + This release greatly improves support for ARM, and introduces support for Power. + + * [ARM](https://github.com/JuliaLang/julia/issues?utf8=%E2%9C%93&q=label%3Aarm): + [#14194], [#14519], [#16645], [#16621] + + * [Power](https://github.com/JuliaLang/julia/issues?utf8=%E2%9C%93&q=label%3Apower): + [#16455], [#16404] + +Deprecated or removed +--------------------- + + * The following function names have been simplified and unified ([#13232]): + + * `get_bigfloat_precision` -> `precision(BigFloat)` + * `set_bigfloat_precision` -> `setprecision` + * `with_bigfloat_precision` -> `setprecision` + + * `get_rounding` -> `rounding` + * `set_rounding` -> `setrounding` + * `with_rounding` -> `setrounding` + + * The method `A_ldiv_B!(SparseMatrixCSC, StridedVecOrMat)` has been deprecated + in favor of versions that require the matrix to be in factored form + ([#13496]). + + * `chol(A,Val{:U/:L})` has been deprecated in favor of `chol(A)` ([#13680]). + + * `rem1(x,y)` is discontinued due to inconsistency for `x==0`. Use `mod1` instead ([#14140]). + + * The `FS` module has been renamed to `Filesystem`. Calling the functions `isreadable`, + `iswritable`, and `isexecutable` on filesystem paths has been deprecated ([#12819]). + + * `RemoteRef` has been deprecated in favor of `RemoteChannel` ([#14458]). + + * `super` has been renamed to `supertype` ([#14335]). + + * `parseip(str)` has been deprecated in favor of `parse(IPAddr, str)` ([#14676]). + + * `readall` has been renamed to `readstring`, and `readbytes` has been renamed to `read` ([#14608], [#14660]). + + * `fieldoffsets(x)` has been deprecated in favor of calling `fieldoffset(x, i)` on each field ([#14777]). + + * `issym` is deprecated in favor of `issymmetric` to match similar functions + (`ishermitian`, ...) ([#15192]). + + * `scale` is deprecated in favor of either `α*A`, `Diagonal(x)*A`, or `A*Diagonal(x)` ([#15258]). + + * `istext` has been renamed to `istextmime` ([#12872], [#15708]). + + * "Functor" types are no longer necessary and have been deprecated ([#15804]). To maintain + performance on older versions of Julia the [Compat.jl package](https://github.com/JuliaLang/Compat.jl/pull/184) + provides a `@functorize` macro. + + * `bitunpack(B)` and `bitpack(A)` have been deprecated in favor of + `Array(B)` and `BitArray(A)`, respectively ([#16010]). + + * `xdump` is removed, and `dump` now simply shows the full representation of a value. + `dump` should not be overloaded, since it is for examining concrete structure ([#4163]). + + * `sprandbool` has been deprecated in favor of `sprand(Bool, ...)` or + `sprand(rng, Bool, ...)` ([#11688], [#16098]). + + * The lowercase `symbol` function has been deprecated in favor of the `Symbol` + constructor ([#16154]). + + * `writemime` is deprecated, and output methods specifying a MIME type are now + methods of `show` ([#14052]). + + * BLAS utility functions `blas_set_num_threads`, `blas_vendor`, and `check_blas` + have been moved to the BLAS module as `BLAS.set_num_threads`, `BLAS.vendor`, + and `BLAS.check` ([#10548], [#16600]). + + * `print_escaped` has been renamed to `escape_string`, `print_unescaped` has been + renamed to `unescape_string`, and `print_joined` has been renamed to `join` ([#16603]). + + * `pointer_to_string` has been renamed to `unsafe_wrap(String, ...)`, and + `pointer_to_array` has been renamed to `unsafe_wrap(Array, ...)` ([#16731]). + + * `sub` and `slice` have been deprecated in favor of `view` ([#16972]). + + * Sparse matrix functions `etree`, `ereach`, `csc_permute`, and `symperm` have been moved + to the [SuiteSparse.jl package](https://github.com/JuliaSparse/SuiteSparse.jl) ([#12231], [#17033]). + + * The no-op `transpose` fallback for non-numeric arrays has been deprecated. Consider introducing suitable + `transpose` methods or calling `permutedims(x, (2, 1))` for matrices and `reshape(x, 1, length(x))` for + vectors. ([#13171], [#17075], [#17374]). + + * The following macros have been deprecated ([#16219]): + * `@windows` is deprecated in favor of `is_windows()` + * `@unix` is deprecated in favor of `is_unix()` + * `@osx` is deprecated in favor of `is_apple()` + * `@linux` is deprecated in favor of `is_linux()` + * `@windows_only` is deprecated in favor of `if is_windows()` + * `@unix_only` is deprecated in favor of `if is_unix()` + * `@osx_only` is deprecated in favor of `if is_apple()` + * `@linux_only` is deprecated in favor of `if is_linux()` + * NOTE: Using `@static` could be useful/necessary when used in a function's local scope. See details at the section entitled [Handling Operating System Variation](https://docs.julialang.org/en/latest/manual/handling-operating-system-variation/#man-handling-operating-system-variation) in the manual. + +Command-line option changes +--------------------------- + + * The `-F` flag to load `~/.juliarc` has been deprecated in favor of + `--startup-file=yes` ([#9482]). + + * The `-f` and `--no-startup` flags to disable loading of `~/.juliarc` have + been deprecated in favor of `--startup-file=no` ([#9482]). + + * The `-P` and `--post-boot` flags for evaluating an expression in "interactive mode" + have been deprecated in favor of `-i -e` ([#16854]). + + * The `--no-history-file` flag to disable loading of `~/.julia_history` has been + deprecated in favor of `--history-file=no` ([#9482]). + +Language tooling improvements +----------------------------- + + * The [Julia debugger](https://github.com/Keno/Gallium.jl) makes its debut + with this release. Install it with `Pkg.add("Gallium")`, and the + [documentation](https://github.com/Keno/Gallium.jl#gallium) should + get you going. The [JuliaCon + talk](https://www.youtube.com/watch?v=e6-hcOHO0tc&list=PLP8iPy9hna6SQPwZUDtAM59-wPzCPyD_S&index=5) + on Gallium shows off various features of the debugger. + + * The [Juno IDE](http://junolab.org) has matured significantly, and now + also includes support for plotting and debugging. + + * [Cxx.jl](https://github.com/Keno/Cxx.jl) provides a convenient FFI for + calling C++ code from Julia. + +Julia v0.4.0 Release Notes +========================== + +New language features +--------------------- + + * Function call overloading: for arbitrary objects `x` (not of type + `Function`), `x(...)` is transformed into `call(x, ...)`, and `call` + can be overloaded as desired. Constructors are now a special case of + this mechanism, which allows e.g. constructors for abstract types. + `T(...)` falls back to `convert(T, x)`, so all `convert` methods implicitly + define a constructor ([#8712], [#2403]). + + * Unicode version 8 is now supported for identifiers etcetera ([#7917], [#12031]). + + * Type parameters now permit any `isbits` type, not just `Int` and `Bool` ([#6081]). + + * Keyword argument names can be computed, using syntax such as `f(; symbol => val)` ([#7704]). + + * The syntax `@generated function` enables generation of specialized methods based on + argument types. At compile time, the function is called with its arguments bound to their + types instead of to their values. The function then returns an expression forming the + body of the function to be called at run time ([#7311]). + + * [Documentation system](https://docs.julialang.org/en/latest/manual/documentation/) + for functions, methods, types and macros in packages and user code ([#8791]). + + * The syntax `function foo end` can be used to introduce a generic function without + yet adding any methods ([#8283]). + + * Incremental precompilation of modules: call `VERSION >= v"0.4.0-dev+6521" && __precompile__()` at the top of a + module file to automatically precompile it when it is imported ([#12491]), or manually + run `Base.compilecache(modulename)`. The resulting precompiled `.ji` file is saved in + `~/.julia/lib/v0.4` ([#8745]). + + * See manual section on `Module initialization and precompilation` (under `Modules`) for + details and errata. In particular, to be safely precompilable a module may need an + `__init__` function to separate code that must be executed at runtime rather than precompile + time. Modules that are *not* precompilable should call `__precompile__(false)`. + + * The precompiled `.ji` file includes a list of dependencies (modules and files that + were imported/included at precompile-time), and the module is automatically recompiled + upon `import` when any of its dependencies have changed. Explicit dependencies + on other files can be declared with `include_dependency(path)` ([#12458]). + + * New option `--output-incremental={yes|no}` added to invoke the equivalent of `Base.compilecache` + from the command line. + + * The syntax `new{parameters...}(...)` can be used in constructors to specify parameters for + the type to be constructed ([#8135]). + + * `++` is now parsed as an infix operator, but does not yet have a default definition ([#11030], [#11686]). + + * Support for inter-task communication using `Channels` ([#12264]). + See https://docs.julialang.org/en/latest/manual/parallel-computing/#channels for details. + + * `RemoteRef`s now point to remote channels. The remote channels can be of length greater than 1. + Default continues to be of length 1 ([#12385]). + See https://docs.julialang.org/en/latest/manual/parallel-computing/#remoterefs-and-abstractchannels for details. + + * `@__LINE__` special macro now available to reflect invocation source line number ([#12727]). + +Language changes +---------------- + + * Tuple types are now written as `Tuple{A, B}` instead of as `(A, B)`. + Tuples of bits types are inlined into structs and arrays, like other + immutable types. + `...` now does splatting inside parentheses, instead of constructing a + variadic tuple type ([#10380]). + Variadic tuple types are written as `Tuple{Vararg{T}}`. + + * Using `[x,y]` to concatenate arrays is deprecated, and in the future will + construct a vector of `x` and `y` instead ([#3737], [#2488], [#8599]). + + * Significant improvements to `ccall` and `cfunction` + + * As a safer alternative to creating pointers (`Ptr`), the managed reference type + `Ref` has been added. A `Ref` points to the data contained by a value in an + abstract sense, and in a way that is GC-safe. For example, `Ref(2)` points to + a storage location that contains the integer `2`, and `Ref(array,3)` points + to the third element of an array. A `Ref` can be automatically converted to a + native pointer when passed to a `ccall`. + + * When passing a by-reference argument to `ccall`, you can declare + the argument type to be `Ref{T}` instead of `Ptr{T}`, and just + pass `x` instead of `&x`. + + * `ccall` is now lowered to call `unsafe_convert(T, cconvert(T, x))` on each + argument. `cconvert` falls back to `convert`, but can be used to convert an + argument to an arbitrarily-different representation more suitable for passing + to C. `unsafe_convert` then handles conversions to `Ptr`. + + * `ccall` and `cfunction` now support correctly passing and returning structs, + following the platform ABI (assuming the C types are mirrored accurately in Julia). + + * `cfunction` arguments of struct-like Julia types are now passed by value. + If `Ref{T}` is used as a `cfunction` argument type, it will look up the + method applicable to `T`, but pass the argument by reference (as Julia functions + usually do). However, this should only be used for objects allocated by Julia + and for `isbits` types. + + * `convert(Ptr,x)` is deprecated for most types, replaced by + `unsafe_convert`. You can still `convert` between pointer types, + and between pointers and `Int` or `UInt`. + + * Module `__init__` methods no longer swallow thrown exceptions; they now + throw an `InitError` wrapping the thrown exception ([#12576]). + + * Unsigned `BigInt` literal syntax has been removed ([#11105]). + Unsigned literals larger than `UInt128` now throw a syntax error. + + * `error(::Exception)` and `error(::Type{Exception})` have been deprecated + in favor of using an explicit `throw` ([#9690]). + + * `Uint` etcetera are renamed to `UInt` ([#8905]). + + * `String` is renamed to `AbstractString` ([#8872]). + + * `FloatingPoint` is renamed to `AbstractFloat` ([#12162]). + + * `None` is deprecated; use `Union{}` instead ([#8423]). + + * `Nothing` (the type of `nothing`) is renamed to `Void` ([#8423]). + + * Arrays can be constructed with the syntax `Array{T}(m,n)` ([#3214], [#10075]). + + * `Dict` literal syntax `[a=>b,c=>d]` is replaced by `Dict(a=>b,c=>d)`, + `{a=>b}` is replaced by `Dict{Any,Any}(a=>b)`, and + `(K=>V)[...]` is replaced by `Dict{K,V}(...)`. + The new syntax has many advantages: all of its components are first-class, + it generalizes to other types of containers, it is easier to guess how to + specify key and value types, and the syntaxes for empty and pre-populated + dicts are synchronized. As part of this change, `=>` is parsed as a normal + operator, and `Base` defines it to construct `Pair` objects ([#6739]). + + * `Char` is no longer a subtype of `Integer` ([#8816]). + Char now supports a more limited set of operations with `Integer` types: + + * comparison / equality + * `Char` + `Int` = `Char` + * `Char` - `Char` = `Int` + + * `round` rounds to the nearest integer using the default rounding mode, + which is ties-to-even by default ([#8750]). + + * A custom triple-quoted string like `x"""..."""` no longer invokes an `x_mstr` + macro. Instead, the string is first unindented and then `x_str` is invoked, + as if the string had been single-quoted ([#10228]). + + * Colons (`:`) within indexing expressions are no longer lowered to the range + `1:end`. Instead, the `:` identifier is passed directly. Custom array types + that implement `getindex` or `setindex!` methods must also extend those + methods to support arguments of type `Colon` ([#10331]). + + * Unions of types should now be written with curly braces instead of parentheses, i.e. + `Union{Type1, Type2}` instead of `Union(Type1, Type2)` ([#11432]). + + * The keyword `local` is no longer allowed in global scope. Use `let` instead of + `begin` to create a new scope from the top level ([#7234], [#10472]). + + * Triple-quoted strings no longer treat tabs as 8 spaces. Instead, the + longest common prefix of spaces and tabs is removed. + + * `global x` in a nested scope is now a syntax error if `x` is local + to the enclosing scope ([#7264]/[#11985]). + + * The default `importall Base.Operators` is deprecated, and relying on it + will give a warning ([#8113]). + + * `remotecall_fetch` and `fetch` now rethrow any uncaught remote exception locally as a + `RemoteException`. Previously they would return the remote exception object. + The worker pid, remote exception and remote backtrace are available in the + thrown `RemoteException`. + + * If any of the enclosed async operations in a `@sync` block throw exceptions, they + are now collected in a `CompositeException` and the `CompositeException` thrown. + + +Command line option changes +--------------------------- + + * The `-i` option now forces the REPL to run after loading the specified script (if any) ([#11347]). + + * New option `--handle-signals={yes|no}` to disable Julia's signal handlers. + + * The `--depwarn={yes|no|error}` option enables/disables syntax and method deprecation warnings, + or turns them into errors ([#9294]). + + * Some command line options are slated for deprecation / removal + - `-f, --no-startup` Don't load ~/.juliarc (deprecated, use --startup-file=no) + - `-F` Load ~/.juliarc (deprecated, use --startup-file=yes)` + - `-P, --post-boot ` Evaluate , but don't disable interactive mode (deprecated, use -i -e instead) + - `--no-history-file` Don't load history file (deprecated, use --history-file=no) + +Compiler/Runtime improvements +----------------------------- + + * Functions may be annotated with metadata (`:meta` expressions) to be used by the compiler ([#8297]). + + * `@inline` before a function definition forces the compiler to inline the function ([#8297]). + + * Loads from heap-allocated immutables are hoisted out of loops in more cases ([#8867]). + + * Accessing fields that are always initialized no longer produces undefined checks ([#8827]). + + * New generational garbage collector which greatly reduces GC overhead for many common workloads ([#5227]). + +Library improvements +-------------------- + + * Build with USE_GPL_LIBS=0 to exclude all GPL libraries and code ([#10870]). + + * Linear algebra + + * The `LinAlg` module is now exported. + + * `sparse(A)` now takes any `AbstractMatrix` A as an argument ([#10031]). + + * Factorization API is now type-stable; functions dispatch on `Val{false}` or `Val{true}` instead of a boolean value ([#9575]). + + * Added generic Cholesky factorization, and the Cholesky factorization is now parametrized by the matrix type ([#7236]). + + * Sparse `cholfact` and `ldltfact` functions now accept a `perm` keyword + for user-provided permutations and a `shift` keyword to factorize + a shifted matrix ([#10844]). + + * New `svds` function for the sparse truncated SVD ([#9425]). + + * `Symmetric` and `Hermitian` immutables are now parametrized by the matrix type ([#7992]). + + * New `ordschur` and `ordschur!` functions for sorting a Schur factorization by the eigenvalues ([#8467],[#9701]). + + * `Givens` type doesn't have a size anymore and is no longer a subtype of `AbstractMatrix` ([#8660]). + + * Large speedup in sparse `\` and splitting of Cholesky and LDLᵀ factorizations into `cholfact` and `ldltfact` ([#10117]). + + * Add sparse least squares to `\` by adding `qrfact` for sparse matrices based on the SPQR library ([#10180]). + + * Split `Triangular` type into `UpperTriangular`, `LowerTriangular`, `UnitUpperTriagular` and `UnitLowerTriangular` ([#9779]) + + * OpenBLAS 64-bit (ILP64) interface is now compiled with a `64_` suffix ([#8734]) to avoid conflicts with external libraries using a 32-bit BLAS ([#4923]). + + * New `vecdot` function, analogous to `vecnorm`, for Euclidean inner products over any iterable container ([#11067]). + + * `p = plan_fft(x)` and similar functions now return a `Base.DFT.Plan` object, rather + than an anonymous function. Calling it via `p(x)` is deprecated in favor of + `p * x` or `p \ x` (for the inverse), and it can also be used with `A_mul_B!` + to employ pre-allocated output arrays ([#12087]). + + * `LU{T,Tridiagonal{T}}` now supports extraction of `L`, `U`, `p`, and `P` factors ([#12137]). + + * Allocations in sparse matrix factorizations are now tracked by Julia's garbage collector ([#12034]). + + * Strings + + * NUL-terminated strings should now be passed to C via the new `Cstring` type, not `Ptr{UInt8}` or `Ptr{Cchar}`, + in order to check whether the string is free of NUL characters (which would cause silent truncation in C). + The analogous type `Cwstring` should be used for NUL-terminated `wchar_t*` strings ([#10994]). + + * `graphemes(s)` returns an iterator over grapheme substrings of `s` ([#9261]). + + * Character predicates such as `islower()`, `isspace()`, etc. use + utf8proc to provide uniform cross-platform behavior and + up-to-date, locale-independent support for Unicode standards + ([#5939]). + + * `reverseind` function to convert indices in reversed strings (e.g. from + reversed regex searches) to indices in the original string ([#9249]). + + * `charwidth(c)` and `strwidth(s)` now return up-to-date cross-platform + results (via utf8proc) ([#10659]): Julia now likes pizza ([#3721]), but some terminals still don't. + + * `is_valid_char(c)`, (now `isvalid(Char,c)` ([#11241])), now correctly handles Unicode "non-characters", which are valid Unicode codepoints ([#11171]). + + * Backreferences in replacement strings in calls to `replace` with a `Regex` pattern are now supported ([#11849]). + Use the `s` string prefix to indicate a replacement string contains a backreference. For example, `replace("ab", r"(.)(.)", s"\2\1")` yields "ba". + + * Capture groups in regular expressions can now be named using PCRE syntax, `(?P...)`. Capture group matches can be accessed by name by indexing a `Match` object with the name of the group ([#11566]). + + * `countlines()` now counts all lines, not just non-empty ([#11947]). + + * Array and AbstractArray improvements + + * New multidimensional iterators and index types for efficient iteration over `AbstractArray`s. Array iteration should generally be written as `for i in eachindex(A) ... end` rather than `for i = 1:length(A) ... end` ([#8432]). + + * New implementation of SubArrays with substantial performance and functionality improvements ([#8501]). + + * AbstractArray subtypes only need to implement `size` and `getindex` + for scalar indices to support indexing; all other indexing behaviors + (including logical indexing, ranges of indices, vectors, colons, etc.) are + implemented in default fallbacks. Similarly, they only need to implement + scalar `setindex!` to support all forms of indexed assingment ([#10525]). + + * AbstractArrays that do not extend `similar` now return an `Array` by + default ([#10525]). + + * Data structures + + * New `sortperm!` function for pre-allocated index arrays ([#8792]). + + * Switch from `O(N)` to `O(log N)` algorithm for `dequeue!(pq, key)` + with `PriorityQueue`. This provides major speedups for large + queues ([#8011]). + + * `PriorityQueue` now includes the order type among its + parameters, `PriorityQueue{KeyType,ValueType,OrderType}`. An + empty queue can be constructed as `pq = + PriorityQueue(KeyType,ValueType)`, if you intend to use the + default `Forward` order, or `pq = PriorityQueue(KeyType, + ValueType, OrderType)` otherwise ([#8011]). + + * Efficient `mean` and `median` for ranges ([#8089]). + + * `deepcopy` recurses through immutable types and makes copies of their mutable fields ([#8560]). + + * `copy(a::DArray)` will now make a copy of a `DArray` ([#9745]). + + * New types + + * Enums are now supported through the `@enum EnumName EnumValue1 + EnumValue2` syntax. Enum member values also support abitrary + value assignment by the `@enum EnumName EnumValue1=1 + EnumValue2=10 EnumValue3=20` syntax ([#10168]). + + * New `Dates` module for calendar dates and other time-interval calculations ([#7654]). + + * New `Nullable` type for missing data ([#8152]). + + * A new `Val{T}` type allows one to dispatch on bits-type values ([#9452]). + + * `linspace` now returns a `LinSpace` object which lazily computes linear interpolation of values between the start and stop values. It "lifts" endpoints which are approximately rational in the same manner as the `colon` operator. + + * Arithmetic + + * `convert` now checks for overflow when truncating integers or converting between + signed and unsigned ([#5413]). + + * Arithmetic is type-preserving for more types; e.g. `(x::Int8) + (y::Int8)` now + yields an `Int8` ([#3759]). + + * Reductions (e.g. `reduce`, `sum`) widen small types (integers smaller than `Int`, and `Float16`). + + * Added optional rounding argument to floating-point constructors ([#8845]). + + * Equality (`==`) and inequality (`<`/`<=`) comparisons are now correct + across all numeric types ([#9133], [#9198]). + + * Rational arithmetic throws errors on overflow ([#8672]). + + * Optional `log` and `log1p` functions implemented in pure Julia (experimental) ([#10008]). + + * The `MathConst` type has been renamed `Irrational` ([#11922]). + + * `isapprox` now has simpler and more sensible default tolerances ([#12393]), supports arrays, and has synonyms `≈` ([U+2248](https://www.fileformat.info/info/unicode/char/2248/index.htm), LaTeX `\approx`) and `≉` ([U+2249](https://www.fileformat.info/info/unicode/char/2249/index.htm), LaTeX `\napprox`) for `isapprox` and `!isapprox`, respectively ([#12472]). + + * Numbers + + * `primes` is now faster and has been extended to generate the primes in a user defined closed interval ([#12025]). + + * The function `primesmask` which generates a prime sieve for a user defined closed interval is now exported ([#12025]). + + * Random numbers + + * Streamlined random number generation APIs [#8246]. + The default `rand` no longer uses global state in the underlying C library, + dSFMT, making it closer to being thread-safe ([#8399], [#8832]). + All APIs can now take an `AbstractRNG` argument ([#8854], [#9065]). The seed argument to `srand` is now optional ([#8320], [#8854]). + The APIs accepting a range argument are extended to accept an arbitrary + `AbstractArray` ([#9049]). + Passing a range of `BigInt` to `rand` or `rand!` is now supported ([#9122]). + There are speed improvements across the board ([#8808], [#8941], [#8958], [#9083]). + + * Significantly faster `randn` ([#9126], [#9132]). + + * The `randexp` and `randexp!` functions are exported ([#9144]). + + * File + + * Added function `readlink` which returns the value of a symbolic link "path" ([#10714]). + + * Added function `ismount` which checks if a directory is a mount point ([#11279]). + + * The `cp` function now accepts keyword arguments `remove_destination` and `follow_symlinks` ([#10888]). + + * The `mv` function now accepts keyword argument `remove_destination` ([#11145]). + + * `Pipe()` creates a bidirectional I/O object that can be passed to `spawn` or `pipeline` + for redirecting process streams ([#12739]). + + * Other improvements + + * You can now tab-complete emoji via their [short names](http://www.emoji-cheat-sheet.com/), using `\:name:` ([#10709]). + + * `gc_enable` subsumes `gc_disable`, and also returns the previous GC state. + + * `assert`, `@assert` now throws an `AssertionError` exception type ([#9734]). + + * `@simd` now rejects invalid control flow (`@goto` / break / continue) in the inner loop body at compile time ([#8624]). + + * The `machinefile` now supports a host count ([#7616]). + + * `code_native` now outputs branch labels ([#8897]). + + * Added `recvfrom` to get source address of UDP packets ([#9418]). + + * `ClusterManager` performance improvements ([#9309]) and support for changing transports([#9434]). + + * Added `Base.get_process_title` / `Base.set_process_title` ([#9957]). + + * `readavailable` now returns a byte vector instead of a string. + + * New `lock` and `unlock` functions, operating on `ReentrantLock`, to lock a stream during + concurrent writes from multiple tasks ([#10679]). + + * `code_llvm` now outputs stripped IR without debug info or other attached metadata. + Use `code_llvm_raw` for the unstripped output ([#10747]). + + * New `withenv(var=>val, ...) do ... end` function to temporarily + modify environment variables ([#10914]). + + * New function `relpath` returns a relative filepath to path either from the current + directory or from an optional start directory ([#10893]). + + * `mktemp` and `mktempdir` now take an optional argument to set which + directory the temporary file or directory is created in. + + * New garbage collector tracked memory allocator functions: `jl_malloc`, `jl_calloc`, + `jl_realloc`, and `jl_free` with libc API ([[#12034]]). + + * `mktempdir` and `mktemp` now have variants that take a function as its + first argument for automated clean-up ([[#9017]]). + +Deprecated or removed +--------------------- + + * several syntax whitespace insensitivities have been deprecated ([#11891]). + ```julia + # function call + f (x) + + # getindex + x [17] + rand(2) [1] + + # function definition + f (x) = x^2 + function foo (x) + x^2 + end + ``` + + * indexing with `Real`s that are not subtypes of `Integer` (`Rational`, `AbstractFloat`, etc.) has been deprecated ([#10458]). + + * `push!(A)` has been deprecated, use `append!` instead of splatting arguments to `push!` ([#10400]). + + * `names` for composite datatypes has been deprecated and + renamed to `fieldnames` ([#10332]). + + * `DArray` functionality has been removed from `Base` and is now a + standalone package under the JuliaParallel umbrella organization ([#10333]). + + * The `Graphics` module has been removed from `Base` and is now a + standalone package ([#10150], [#9862]). + + * The `Woodbury` special matrix type has been removed from `LinAlg` ([#10024]). + + * `median` and `median!` no longer accept a `checknan` keyword argument ([#8605]). + + * `inf` and `nan` are now deprecated in favor of `T(Inf)` and `T(NaN)`, respectively ([#8776]). + + * `oftype(T::Type, x)` is deprecated in favor of `convert(T,x)` (or `T(x)`). + + * `{...}` syntax is deprecated in favor of `Any[...]` ([#8578]). + + * `itrunc`, `ifloor`, `iceil` and `iround` are deprecated in favour of + `trunc{T<:Integer}(T,x)`, `floor{T<:Integer}(T,x)`, etc.. `trunc` is now + always bound-checked;`Base.unsafe_trunc` provides the old unchecked `itrunc` + behaviour ([#9133]). + + * `squeeze` now requires that passed dimension(s) are an `Int` or tuple of `Int`s; + calling `squeeze` with an arbitrary iterator is deprecated ([#9271]). + Additionally, passed dimensions must be unique and correspond to extant + dimensions of the input array. + + * `randbool` is deprecated. Use `rand(Bool)` to produce a random boolean value, and + `bitrand` to produce a random BitArray ([#9105], [#9569]). + + * `beginswith` is renamed to `startswith` ([#9578]). + + * `null` is renamed to `nullspace` ([#9714]). + + * The operators `|>`, `.>`, `>>`, and `.>>` as used for process I/O redirection + are replaced with the `pipeline` function ([#5349], [#12739]). + + * `flipud(A)` and `fliplr(A)` have been deprecated in favor of `flipdim(A, 1)` and + `flipdim(A, 2)`, respectively ([#10446]). + + * Numeric conversion functions whose names are lower-case versions of type + names have been removed. To convert a scalar, use the type name, e.g. + `Int32(x)`. To convert an array to a different element type, use + `Array{T}(x)`, `map(T,x)`, or `round(T,x)`. To parse a string as an integer + or floating-point number, use `parse` ([#1470], [#6211]). + + * Low-level functions from the C library and dynamic linker have been moved to + modules `Libc` and `Libdl`, respectively ([#10328]). + + * The functions `parseint`, `parsefloat`, `float32_isvalid`, + `float64_isvalid`, and the string-argument `BigInt` and `BigFloat` have + been replaced by `parse` and `tryparse` with a type argument. The string + macro `big"xx"` can be used to construct `BigInt` and `BigFloat` literals + ([#3631], [#5704], [#9487], [#10543], [#10955]). + + * the `--int-literals` compiler option is no longer accepted ([#9597]). + + * Instead of `linrange`, use `linspace` ([#9666]). + + * The functions `is_valid_char`, `is_valid_ascii`, `is_valid_utf8`, `is_valid_utf16`, and + `is_valid_utf32` have been replaced by generic `isvalid` methods. + The single argument form `isvalid(value)` can now be used for values of type `Char`, `ASCIIString`, + `UTF8String`, `UTF16String` and `UTF32String`. + The two argument form `isvalid(type, value)` can be used with the above types, with values + of type `Vector{UInt8}`, `Vector{UInt16}`, `Vector{UInt32}`, and `Vector{Char}` ([#11241]). + + * Instead of `utf32(64,123,...)` use `utf32(UInt32[64,123,...])` ([#11379]). + + * `start_timer` and `stop_timer` are replaced by `Timer` and `close`. + + * The following internal julia C functions have been renamed, in order to prevent + potential naming conflicts with C libraries: ([#11741]) + + * `gc_wb*` -> `jl_gc_wb*` + + * `gc_queue_root` -> `jl_gc_queue_root` + + * `allocobj` -> `jl_gc_allocobj` + + * `alloc_[0-3]w` -> `jl_gc_alloc_*w` + + * `diff_gc_total_bytes` -> `jl_gc_diff_total_bytes` + + * `sync_gc_total_bytes` -> `jl_gc_sync_total_bytes` + + * `require(::AbstractString)` and `reload` (see news about addition of `compile`). + + * `cartesianmap` is deprecated in favor of iterating over a `CartesianRange` + +Julia v0.3.0 Release Notes +========================== + +New language features +--------------------- + + * Greatly enhanced performance for passing and returning `Tuple`s ([#4042]). + + * `Tuple`s (of `Integer`s, `Symbol`s, or `Bool`s) can now be used as type + parameters ([#5164]). + + * An additional default "inner" constructor accepting any arguments is now + generated. Constructors that look like `MyType(a, b) = new(a, b)` do not + need to be added manually ([#4026], [#7071]). + + * Expanded array type hierarchy to include an abstract `DenseArray` for + in-memory arrays with standard strided storage ([#987], [#2345], + [#6212]). + + * When reloading code, types whose definitions have not changed can be + ignored in some cases. + + * Binary `~` now parses as a vararg macro call to `@~`. + For example `x~y~z` => `@~ x y z` ([#4882]). + + * Structure fields can now be accessed by index ([#4806]). + + * If a module contains a function `__init__()`, it will be called when + the module is first loaded, and on process startup if a pre-compiled + version of the module is present ([#1268]). + + * Multi-line comments ([#69], [#6128]): `#= .... =#` + + * `--check-bounds=yes|no` compiler option + + * Unicode identifiers are normalized (NFC) so that different encodings + of equivalent strings are treated as the same identifier ([#5462]). + + * The set of characters permitted in identifiers has been restricted based + on Unicode categories. Generally, punctuation, formatting and control + characters, and operator symbols are not allowed in identifiers. + Number-like characters cannot begin identifiers ([#5936]). + + * Define a limited number of infix Unicode operators ([#552], [#6582]): + + | Precedence class | Operators (with synonyms, if any) + | ---------------- | --------------------------------- + | == | ≥ (>=) ≤ (<=) ≡ (===) ≠ (!=) ≢ (!==) .≥ (.>=) .≤ (.<=) .!= (.≠) ∈ (`in`) ∉ (`(x,y)->!in(x, y)`) ∋ (`(x,y)->in(y, x)`) ∌ (`(x,y)->!in(y, x)`) ⊆ (`issubset`) ⊈ (`(x,y)->!issubset(x, y)`) ⊊ (`(x,y)->x⊆y && x!=y`) | + | + | ∪ (`union`) | + | * | ÷ (`div`) ⋅ (`dot`) × (`cross`) ∩ (`intersect`) | + | unary | √ ∛ | + + In addition to these, many of the Unicode operator symbols are parsed + as infix operators and are available for user-defined methods ([#6929]). + + * Improved reporting of syntax errors ([#6179]) + + * `break` inside a `for` loop with multiple ranges now exits the entire loop nest ([#5154]) + + * Local goto statements using the `@goto` and `@label` macros ([#101]). + +REPL improvements +----------------- + + * New native-Julia REPL implementation, eliminating many problems + stemming from the old GNU Readline-based REPL ([#6270]). + + * Tab-substitution of LaTeX math symbols (e.g. `\alpha` by `α`) ([#6911]). + This also works in IJulia and in Emacs ([#6920]). + + * `workspace()` function for obtaining a fresh workspace ([#1195]). + +Library improvements +-------------------- + + * `isequal` now compares all numbers by value, ignoring type ([#6624]). + + * Implement limited shared-memory parallelism with `SharedArray`s ([#5380]). + + * Well-behaved floating-point ranges ([#2333], [#5636]). + Introduced the `FloatRange` type for floating-point ranges with a step, + which will give intuitive/correct results for classically problematic + ranges like `0.1:0.1:0.3`, `0.0:0.7:2.1` or `1.0:1/49:27.0`. + + * `mod2pi` function ([#4799], [#4862]). + + * New functions `minmax` and `extrema` ([#5275]). + + * New macros `@edit`, `@less`, `@code_typed`, `@code_lowered`, `@code_llvm` and `@code_native` that all function like `@which` ([#5832]). + + * `consume(p)` extended to `consume(p, args...)`, allowing it + to optionally pass `args...` back to the producer ([#4775]). + + * `.juliarc.jl` is now loaded for both script and REPL execution ([#5076]). + + * The `Sys` module now includes convenient functions for working with + dynamic library handles; `Sys.dllist` will list out all paths currently + loaded via `dlopen`, and `Sys.dlpath` will lookup a path from a handle + + * `readdlm` treats multiple whitespace characters as a single delimiter + by default (when no delimiter is specified). This is useful for reading + fixed-width or messy whitespace-delimited data ([#5403]). + + * The Airy, Bessel, Hankel, and related functions (`airy*`, + `bessel*`, `hankel*`) now detect errors returned by the underlying + AMOS library, throwing an `AmosException` in that case ([#4967]). + + * `methodswith` now returns an array of `Method`s ([#5464]) rather + than just printing its results. + + * `errno([code])` function to get or set the C library's `errno`. + + * `GitHub` module for interacting with the GitHub API. + + * Package improvements + + * Packages are now installed into `.julia/v0.3` by default (or + whatever the current Julia version is), so that different + versions of Julia can co-exist with incompatible packages. + Existing `.julia` installations are unaffected unless `Pkg.init()` + is run to re-create the package directories ([#3344], [#5737]). + + * `Pkg.submit(pkg[,commit])` function to automatically submit + a GitHub pull request to the package author. + + * Collections improvements + + * `Array` assignment (e.g. `x[:] = y`) ignores singleton dimensions + and allows the last dimension of one side to match all trailing dimensions + of the other ([#4048], [#4383]). + + * `Dict(kv)` constructor for any iterator on `(key,value)` pairs. + + * Multi-key `Dict`s: `D[x,y...]` is now a synonym for `D[(x,y...)]` + for associations `D` ([#4870]). + + * `push!` and `unshift!` can push multiple arguments ([#4782]). + + * `writedlm` and `writecsv` now accept any iterable collection of + iterable rows, in addition to `AbstractArray` arguments, and the + `writedlm` delimiter can be any printable object (e.g. a + `String`) instead of just a `Char`. + + * `isempty` now works for any iterable collection ([#5827]). + + * `unique` now accepts an optional `dim` argument for finding + unique rows or columns of a matrix or regions of a + multidimensional array ([#5811]). + + * `Number` improvements + + * The `ImaginaryUnit` type no longer exists. Instead, `im` is of type + `Complex{Bool}`. Making this work required changing the semantics of + boolean multiplication to approximately, `true * x = x` and + `false * x = zero(x)`, which can itself be considered useful ([#5468]). + + * `big` is now vectorized ([#4766]) + + * `nextpow` and `prevpow` now return the `a^n` values instead of the + exponent `n` ([#4819]) + + * Overflow detection in `parseint` ([#4874]). + + * `rand` now supports arbitrary `Ranges` arguments ([#5059]). + + * `expm1` and `log1p` now support complex arguments ([#3141]). + + * Broadcasting `.//` is now included ([#7094]). + + * `prevfloat` and `nextfloat` now saturate at -Inf and Inf, + respectively, and have otherwise been fixed to follow the IEEE-754 + standard functions `nextDown` and `nextUp` ([#5025]). + + * New function `widen` for widening numeric types and values, and `widemul` + for multiplying to a larger type ([#6169]). + + * `polygamma`, `digamma`, and `trigamma` now accept complex + arguments, and `zeta(s, z)` now provides the Hurwitz zeta ([#7125]). + + * Narrow integer types (< 32 bits) are promoted to `Float64` rather + than to `Float32` by `float(x)` ([#7390]). + + * `String` improvements + + * Triple-quoted regex strings, `r"""..."""` ([#4934]). + + * New string type, `UTF16String` ([#4930]), constructed by + `utf16(s)` from another string, a `Uint16` array or pointer, or + a byte array (possibly prefixed by a byte-order marker to + indicate endian-ness). Its data is internally `NULL`-terminated + for passing to C ([#7016]). + + * `CharString` is renamed to `UTF32String` ([#4943]), and its data + is now internally `NULL`-terminated for passing to C ([#7016]). + `CharString(c::Char...)` is deprecated in favor of `utf32(c...)`, + and `utf32(s)` otherwise has functionality similar to `utf16(s)`. + + * New `WString` and `wstring` synonyms for either `UTF16String` + and `utf16` or `UTF32String` and `utf32`, respectively, depending + on the width of `Cwchar_t` ([#7016]). + + * `normalize_string` function to perform Unicode normalization, + case-folding, and other transformations ([#5576]). + + * `pointer(s, i=1)` for `ByteString`, `UTF16String`, `UTF32String`, + and `SubString`s thereof ([#5703]). + + * `bytestring` is automatically called on `String` arguments for + conversion to `Ptr{Uint8}` in `ccall` ([#5677]). + + * Linear algebra improvements + + * Balancing options for eigenvector calculations for general matrices ([#5428]). + + * Mutating linear algebra functions no longer promote ([#5526]). + + * `condskeel` for Skeel condition numbers ([#5726]). + + * `norm(::Matrix)` no longer calculates a vector norm when the first + dimension is one ([#5545]); it always uses the operator (induced) + matrix norm. + + * New `vecnorm(itr, p=2)` function that computes the norm of + any iterable collection of numbers as if it were a vector of + the same length. This generalizes and replaces `normfro` ([#6057]), + and `norm` is now type-stable ([#6056]). + + * New `UniformScaling` matrix type and identity `I` constant ([#5810]). + + * None of the concrete matrix factorization types are exported from `Base` + by default anymore. + + * Sparse linear algebra + + * 1-d sparse `getindex` has been implemented ([#7047]) + + * Faster sparse `getindex` ([#7131]). + + * Faster sparse `kron` ([#4958]). + + * `sparse(A) \ B` now supports a matrix `B` of right-hand sides ([#5196]). + + * `eigs(A, sigma)` now uses shift-and-invert for nonzero shifts `sigma` and inverse iteration for `which="SM"`. If `sigma==nothing` (the new default), computes ordinary (forward) iterations ([#5776]). + + * `sprand` is faster, and whether any entry is nonzero is now determined independently with the specified probability ([#6726]). + + * Dense linear algebra for special matrix types + + * Interconversions between the special matrix types `Diagonal`, `Bidiagonal`, + `SymTridiagonal`, `Triangular`, and `Triangular`, and `Matrix` are now allowed + for matrices which are representable in both source and destination types ([5e3f074b]). + + * Allow for addition and subtraction over mixed matrix types, automatically promoting + the result to the denser matrix type ([a448e080], [#5927]) + + * new algorithms for linear solvers and eigensystems of `Bidiagonal` + matrices of generic element types ([#5277]) + + * new algorithms for linear solvers, eigensystems and singular systems of `Diagonal` + matrices of generic element types ([#5263]) + + * new algorithms for linear solvers and eigensystems of `Triangular` + matrices of generic element types ([#5255]) + + * specialized `inv` and `det` methods for `Tridiagonal` and `SymTridiagonal` + based on recurrence relations between principal minors ([#5358]) + + * specialized `transpose`, `ctranspose`, `istril`, `istriu` methods for + `Triangular` ([#5255]) and `Bidiagonal` ([#5277]) + + * new LAPACK wrappers + - condition number estimate `cond(A::Triangular)` ([#5255]) + + * parametrize `Triangular` on matrix type ([#7064]) + + * Lyapunov / Sylvester solver ([#7435]) + + * `eigvals` for `Symmetric`, `Tridiagonal` and `Hermitian` matrices now + support additional method signatures: ([#3688], [#6652], [#6678], [#7647]) + - `eigvals(M, el, eu)` finds all eigenvalues in the interval `(el, eu]` + - `eigvals(M, il:iu)` finds the `il`th through the `iu`th eigenvalues (in ascending order) + + * Dense linear algebra for generic matrix element types + + * LU factorization ([#5381] and [#5430]) + + * QR factorization ([#5526]) + + * New function `deleteat!` deletes a specified index or indices and + returns the updated collection + + * The `setenv` function for external processes now accepts a `dir` keyword + argument for specifying the directory to start the child process in ([#4888]). + + * Constructors for collections (`Set`, `Dict`, etc.) now generally accept a + single iterable argument giving the elements of the collection ([#4996], [#4871]) + + * Ranges and arrays with the same elements are now unequal. This allows hashing + and comparing ranges to be faster ([#5778]). + + * Broadcasting now works on arbitrary `AbstractArrays` ([#5387]) + + * Reduction functions that accept a pre-allocated output array, including + `sum!`, `prod!`, `maximum!`, `minimum!`, `all!`, `any!` ([#6197], [#5387]) + + * Faster performance on `fill!` and `copy!` for array types not supporting + efficient linear indexing ([#5671], [#5387]) + + * Changes to range types ([#5585]) + + * `Range` is now the abstract range type, instead of `Ranges` + + * New function `range` for constructing ranges by length + + * `Range` is now `StepRange`, and `Range1` is now `UnitRange`. Their + constructors accept end points instead of lengths. Both are subtypes of a + new abstract type `OrdinalRange`. + + * Ranges now support `BigInt` and general ordinal types. + + * Very large ranges (e.g. `0:typemax(Int)`) can now be constructed, but some + operations (e.g. `length`) will raise an `OverflowError`. + + * Extended API for `cov` and `cor`, which accept keyword arguments `vardim`, + `corrected`, and `mean` ([#6273]) + + * New functions `randsubseq` and `randsubseq!` to create a random subsequence of an array ([#6726]) + + * New macro `@evalpoly` for efficient inline evaluation of polynomials ([#7146]). + + * The signal filtering function `filt` now accepts an optional initial filter state vector. A new in-place function `filt!` is also exported ([#7513]). + + * Significantly faster `cumsum` and `cumprod` ([#7359]). + + * Implement `findmin` and `findmax` over specified array dimensions ([#6716]). + + * Support memory-mapping of files with offsets on Windows ([#7242]). + + * Catch writes to protect memory, such as when trying to modify a mmapped file opened in read-only mode ([#3434]). + +Environment improvements +------------------------ + + * New `--code-coverage` and `--track-allocation` startup features allow one to measure the number of executions or the amount of memory allocated, respectively, at each line of code ([#5423],[#7464]). + + * `Profile.init` now accepts keyword arguments, and returns the current settings when no arguments are supplied ([#7365]). + +Build improvements +------------------ + + * Dependencies are now verified against stored MD5/SHA512 hashes, to ensure + that the correct file has been downloaded and was not modified ([#6773]). + + +Deprecated or removed +--------------------- + + * `convert(Ptr{T1}, x::Array{T2})` is now deprecated unless `T1 == T2` + or `T1 == Void` ([#6073]). (You can still explicitly `convert` + one pointer type into another if needed.) + + * `Sys.shlib_ext` has been renamed to `Sys.dlext` + + * `dense` is deprecated in favor of `full` ([#4759]). + + * The `Stat` type is renamed `StatStruct` ([#4670]). + + * `setrounding`, `rounding` and `setrounding` now take an additional + argument specifying the floating point type to which they apply. The old + behaviour and `[get/set/with]_bigfloat_rounding` functions are deprecated ([#5007]). + + * `cholpfact` and `qrpfact` are deprecated in favor of keyword arguments in + `cholfact(..., pivot=true)` and `qrfact(..., pivot=true)` ([#5330]). + + * `symmetrize!` is deprecated in favor of `Base.LinAlg.copytri!` ([#5427]). + + * `myindexes` has been renamed to `localindexes` ([#5475]). + + * `factorize!` is deprecated in favor of `factorize` ([#5526]). + + * `nnz` counts the number of structural nonzeros in a sparse + matrix. Use `countnz` for the actual number of nonzeros ([#6769]). + + * `setfield` is renamed `setfield!` ([#5748]). + + * `put` and `take` are renamed `put!` and `take!` ([#5511]). + + * `put!` now returns its first argument, the remote reference ([#5819]). + + * `read` methods that modify a passed array are now called `read!` ([#5970]) + + * `infs` and `nans` are deprecated in favor of the more general `fill`. + + * `*` and `div` are no longer supported for `Char`. + + * `Range` is renamed `StepRange` and `Range1` is renamed `UnitRange`. + `Ranges` is renamed `Range`. + + * `bitmix` is replaced by a 2-argument form of `hash`. + + * `readsfrom` and `writesto` are replaced by `open` ([#6948]). + + * `insert!` now throws a `BoundsError` if + `index > length(collection)+1` ([#7373]). + + * No longer exported from `Base`: + * `start_reading`, `stop_reading`, `start_watching` ([#10885]). + +Julia v0.2.0 Release Notes +========================== + +The 0.2 release brings improvements to many areas of Julia. Among the +most visible changes are support for 64-bit Windows, keyword arguments +to functions, immutable types, a redesigned and polished package +manager, a multimedia interface supporting usage of Julia in IPython, +a built-in profiler, and major improvements to Julia's linear algebra, +I/O, and parallel capabilities. These are accompanied by many other +changes adding new features, enhancing the library's consistency, +improving performance, increasing test coverage, easing installation, +and expanding the documentation. While not part of Julia proper, the +package ecosystem has also grown and matured considerably since the +0.1 release. See below for more information about the long list of +changes that improve Julia's usability and performance. + +New language features +--------------------- + + * Keyword & optional function arguments ([#485], [#1817]). + + * Immutable types ([#13]). + + * Triple-quoted string literals ([#70]). + + * New infix operator `in` (e.g. `x in S`), and corresponding function + `in(x,S)`, replacing `contains(S,x)` function ([#2703]). + + * New variable bindings on each for loop and comprehension iteration ([#1571]). + For example, before this change: + + julia> map(f->f(), { ()->i for i=1:3 }) + 3-element Any Array: + 3 + 3 + 3 + + and after: + + julia> map(f->f(), { ()->i for i=1:3 }) + 3-element Any Array: + 1 + 2 + 3 + + * Explicit relative importing ([#2375]). + + * Methods can be added to functions in other modules using dot syntax, + as in `Foo.bar(x) = 0`. + + * `import module: name1, name2, ...` ([#5214]). + + * A semicolon is now allowed after an `import` or `using` statement ([#4130]). + + * In an interactive session (REPL), you can use `;cmd` to run `cmd` via an interactive + shell. For example: + + julia> ;ls + CONTRIBUTING.md Makefile VERSION deps/ julia@ ui/ + DISTRIBUTING.md NEWS.md Windows.inc doc/ src/ usr/ + LICENSE.md README.md base/ etc/ test/ + Make.inc README.windows.md contrib/ examples/ tmp/ + +New library functions +--------------------- + + * Sampling profiler ([#2597]). + + * Functions for examining stages of the compiler's output: + `code_lowered`, `code_typed`, `code_llvm`, and `code_native`. + + * Multimedia I/O API (display, writemime, etcetera) ([#3932]). + + * MPFR-based `BigFloat` ([#2814]), and many new `BigFloat` operations. + + * New half-precision IEEE floating-point type, `Float16` ([#3467]). + + * Support for setting floating-point rounding modes ([#3149]). + + * `methodswith` shows all methods with an argument of specific type. + + * `mapslices` provides a general way to perform operations on slices of arrays ([#2204]). + + * `repeat` function for constructing Arrays with repeated elements ([#3605]). + + * `Collections.PriorityQueue` type and `Collections.heap` functions ([#2920]). + + * `quadgk` 1d-integration routine ([#3140]). + + * `erfinv` and `erfcinv` functions ([#2987]). + + * `varm`, `stdm` ([#2265]). + + * `digamma`, `invdigamma`, `trigamma` and `polygamma` for calculating derivatives of `gamma` function ([#3233]). + + * `logdet` ([#3070]). + + * Names for C-compatible types: `Cchar`, `Clong`, etc. ([#2370]). + + * `cglobal` to access global variables ([#1815]). + + * `unsafe_pointer_to_objref` ([#2468]) and `pointer_from_objref` ([#2515]). + + * `readandwrite` for external processes. + + * I/O functions `readbytes` and `readbytes!` ([#3878]). + + * `flush_cstdio` function ([#3949]). + + * ClusterManager makes it possible to support different types of compute clusters + ([#3649], [#4014]). + + * `rmprocs` for removing processors from a parallel computing session. + The system can also tolerate to some extent processors that die unexpectedly + ([#3050]). + + * `interrupt` for interrupting worker processes ([#3819]). + + * `timedwait` does a polled wait for an event till a specified timeout. + + * `Condition` type with `wait` and `notify` functions for `Task` synchronization. + + * `versioninfo` provides detailed version information, especially useful when + reporting and diagnosing bugs. + + * `detach` for running child processes in a separate process group. + + * `setenv` for passing environment variables to child processes. + + * `ifelse` eagerly-evaluated conditional function, especially useful for + vectorized conditionals. + +Library improvements +-------------------- + + * `isequal` now returns `false` for numbers of different types. + This makes it much easier to define hashing for new numeric types. + Uses of `Dict` with numeric keys might need to change + to account for this increased strictness. + + * A redesigned and rewritten `Pkg` system is much more robust in case of problems. + The basic interface to adding and removing package requirements remains the + same, but great deal of additional functionality for developing packages in-place + was added. See the new [packages chapter] in the manual for further details. + + * Sorting API updates ([#3665]) – see [sorting functions]. + + * The `delete!(d::Dict, key)` function has been split into separate `pop!` + and `delete!` functions ([#3439]). + `pop!(d,key)` removes `key` from `d` and returns the value that was associated with it; + it throws an exception if `d` does not contain `key`. + `delete!(d,key)` removes `key` from `d` and succeeds regardless of whether `d` + contained `key` or not, returning `d` itself in either case. + + * Linear-algebra factorization routines (`lu`, `chol`, etc.) now return + `Factorization` objects (and `lud`, `chold`, etc. are deprecated; [#2212]). + + * A number of improvements to sparse matrix capabilities and sparse linear algebra. + + * More linear algebra fixes and eigensolver hooks + for `SymTridiagonal`, `Tridiagonal` and `Bidiagonal` matrix types + ([#2606], [#2608], [#2609], [#2611], [#2678], [#2713], [#2720], [#2725]). + + * Change `integer_valued`, `real_valued`, and so on to `isinteger`, `isreal`, + and so on, and semantics of the later are now value-based rather than type-based, + unlike MATLAB/Octave ([#3071]). `isbool` and `iscomplex` are eliminated in favor + of a general `iseltype` function. + + * Transitive comparison of floats with rationals ([#3102]). + + * Fast prime generation with `primes` and fast primality testing with `isprime`. + + * `sum` and `cumsum` now use [pairwise summation] for better accuracy ([#4039]). + + * Dot operators (`.+`, `.*` etc.) now broadcast singleton dimensions of array arguments. + This behavior can be applied to any function using `broadcast(f, ...)`. + + * `combinations`, `permutations`, and `partitions` now return iterators instead of a task, + and `integer_partitions` has been renamed to `partitions` ([#3989], [#4055]). + + * `isreadable`/`iswritable` methods added for more IO types ([#3872]). + + * Much faster and improved `readdlm` and `writedlm` ([#3350], [#3468], [#3483]). + + * Faster `matchall` ([#3719]), and various string and regex improvements. + + * Documentation of advanced linear algebra features ([#2807]). + + * Support optional RTLD flags in `dlopen` ([#2380]). + + * `pmap` now works with any iterable collection. + + * Options in `pmap` for retrying or ignoring failed tasks. + + * New `sinpi(x)` and `cospi(x)` functions to compute sine and cosine of `pi*x` + more accurately ([#4112]). + + * New implementations of elementary complex functions + `sqrt`, `log`, `asin`, `acos`, `atan`, `tanh`, `asinh`, `acosh`, `atanh` + with correct branch cuts ([#2891]). + + * Improved behavior of `SubArray` ([#4412], [#4284], [#4044], [#3697], [#3790], + [#3148], [#2844], [#2644] and various other fixes). + + * New convenience functions in graphics API. + + * Improved backtraces on Windows and OS X. + + * Implementation of reduction functions (including `reduce`, `mapreduce`, `sum`, `prod`, + `maximum`, `minimum`, `all`, and `any`) are refactored, with improved type stability, + efficiency, and consistency ([#6116], [#7035], [#7061], [#7106]). + +Deprecated or removed +--------------------- + + * Methods of `min` and `max` that do reductions were renamed to + `minimum` and `maximum`. `min(x)` is now `minimum(x)`, and + `min(x,(),dim)` is now `minimum(x,dim)` ([#4235]). + + * `ComplexPair` was renamed to `Complex` and made `immutable`, + and `Complex128` and so on are now aliases to the new `Complex` type. + + * `!` was added to the name of many mutating functions, + e.g., `push` was renamed `push!` ([#907]). + + * `ref` renamed to `getindex`, and `assign` to `setindex!` ([#1484]). + + * `writeable` renamed to `writable` ([#3874]). + + * `logb` and `ilogb` renamed to `exponent` ([#2516]). + + * `quote_string` became a method of `repr`. + + * `safe_char`, `check_ascii`, and `check_utf8` replaced by + `is_valid_char`, `is_valid_ascii`, and `is_valid_utf8`, respectively. + + * `each_line`, `each_match`, `begins_with`, `ends_with`, `parse_float`, + `parse_int`, and `seek_end` replaced by: `eachline`, `eachmatch`, and so on + (`_` was removed) ([#1539]). + + * `parse_bin(s)` replaced by `parseint(s,2)`; + `parse_oct(s)` replaced by `parseint(s,8)`; + `parse_hex(s)` replaced by `parseint(s,16)`. + + * `findn_nzs` replaced by `findnz` ([#1539]). + + * `DivideByZeroError` replaced by `DivideError`. + + * `addprocs_ssh`, `addprocs_ssh_tunnel`, and `addprocs_local` + replaced by `addprocs` (with keyword options). + + * `remote_call`, `remote_call_fetch`, and `remote_call_wait` + replaced by `remotecall`, `remotecall_fetch`, and `remotecall_wait`. + + * `has` replaced by `in` for sets and by `haskey` for dictionaries. + + * `diagmm` and `diagmm!` replaced by `scale` and `scale!` ([#2916]). + + * `unsafe_ref` and `unsafe_assign` replaced by `unsafe_load` and `unsafe_store!`. + + * `add_each!` and `del_each!` replaced by `union!` and `setdiff!`. + + * `isdenormal` renamed to `issubnormal` ([#3105]). + + * `expr` replaced by direct call to `Expr` constructor. + + * `|`, `&`, `$`, `-`, and `~` for sets replaced by + `union`, `intersect`, `symdiff`, `setdiff`, and `complement` ([#3272]). + + * `square` function removed. + + * `pascal` function removed. + + * `add` and `add!` for `Set` replaced by `push!`. + + * `ls` function deprecated in favor of `readdir` or `;ls` in the REPL. + + * `start_timer` now expects arguments in units of seconds, not milliseconds. + + * Shell redirection operators `|`, `>`, and `<` eliminated in favor of a new + operator `|>` ([#3523]). + + * `amap` is deprecated in favor of new `mapslices` functionality. + + * The `Reverse` iterator was removed since it did not work in many cases. + + * The `gcd` function now returns a non-negative value regardless of + the argument signs, and various other sign problems with `invmod`, + `lcm`, `gcdx`, and `powermod` were fixed ([#4811]). + +Miscellaneous changes +--------------------- + + * `julia-release-*` executables renamed to `julia-*`, + and `libjulia-release` renamed to `libjulia` ([#4177]). + + * Packages will now be installed in `.julia/vX.Y`, where + X.Y is the current Julia version. + +Bugfixes and performance updates +-------------------------------- + +Too numerous to mention. + +[packages chapter]: https://docs.julialang.org/en/latest/manual/packages/ +[sorting functions]: https://docs.julialang.org/en/latest/stdlib/sort/ +[pairwise summation]: https://en.wikipedia.org/wiki/Pairwise_summation +[a448e080]: https://github.com/JuliaLang/julia/commit/a448e080dc736c7fb326426dfcb2528be36973d3 +[5e3f074b]: https://github.com/JuliaLang/julia/commit/5e3f074b9173044a0a4219f9b285879ff7cec041 + +[#13]: https://github.com/JuliaLang/julia/issues/13 +[#69]: https://github.com/JuliaLang/julia/issues/69 +[#70]: https://github.com/JuliaLang/julia/issues/70 +[#101]: https://github.com/JuliaLang/julia/issues/101 +[#265]: https://github.com/JuliaLang/julia/issues/265 +[#485]: https://github.com/JuliaLang/julia/issues/485 +[#550]: https://github.com/JuliaLang/julia/issues/550 +[#552]: https://github.com/JuliaLang/julia/issues/552 +[#907]: https://github.com/JuliaLang/julia/issues/907 +[#964]: https://github.com/JuliaLang/julia/issues/964 +[#987]: https://github.com/JuliaLang/julia/issues/987 +[#1090]: https://github.com/JuliaLang/julia/issues/1090 +[#1195]: https://github.com/JuliaLang/julia/issues/1195 +[#1268]: https://github.com/JuliaLang/julia/issues/1268 +[#1470]: https://github.com/JuliaLang/julia/issues/1470 +[#1484]: https://github.com/JuliaLang/julia/issues/1484 +[#1539]: https://github.com/JuliaLang/julia/issues/1539 +[#1571]: https://github.com/JuliaLang/julia/issues/1571 +[#1815]: https://github.com/JuliaLang/julia/issues/1815 +[#1817]: https://github.com/JuliaLang/julia/issues/1817 +[#2204]: https://github.com/JuliaLang/julia/issues/2204 +[#2212]: https://github.com/JuliaLang/julia/issues/2212 +[#2265]: https://github.com/JuliaLang/julia/issues/2265 +[#2333]: https://github.com/JuliaLang/julia/issues/2333 +[#2345]: https://github.com/JuliaLang/julia/issues/2345 +[#2370]: https://github.com/JuliaLang/julia/issues/2370 +[#2375]: https://github.com/JuliaLang/julia/issues/2375 +[#2380]: https://github.com/JuliaLang/julia/issues/2380 +[#2403]: https://github.com/JuliaLang/julia/issues/2403 +[#2468]: https://github.com/JuliaLang/julia/issues/2468 +[#2488]: https://github.com/JuliaLang/julia/issues/2488 +[#2515]: https://github.com/JuliaLang/julia/issues/2515 +[#2516]: https://github.com/JuliaLang/julia/issues/2516 +[#2597]: https://github.com/JuliaLang/julia/issues/2597 +[#2606]: https://github.com/JuliaLang/julia/issues/2606 +[#2608]: https://github.com/JuliaLang/julia/issues/2608 +[#2609]: https://github.com/JuliaLang/julia/issues/2609 +[#2611]: https://github.com/JuliaLang/julia/issues/2611 +[#2644]: https://github.com/JuliaLang/julia/issues/2644 +[#2678]: https://github.com/JuliaLang/julia/issues/2678 +[#2703]: https://github.com/JuliaLang/julia/issues/2703 +[#2713]: https://github.com/JuliaLang/julia/issues/2713 +[#2720]: https://github.com/JuliaLang/julia/issues/2720 +[#2725]: https://github.com/JuliaLang/julia/issues/2725 +[#2807]: https://github.com/JuliaLang/julia/issues/2807 +[#2814]: https://github.com/JuliaLang/julia/issues/2814 +[#2844]: https://github.com/JuliaLang/julia/issues/2844 +[#2891]: https://github.com/JuliaLang/julia/issues/2891 +[#2916]: https://github.com/JuliaLang/julia/issues/2916 +[#2920]: https://github.com/JuliaLang/julia/issues/2920 +[#2987]: https://github.com/JuliaLang/julia/issues/2987 +[#3050]: https://github.com/JuliaLang/julia/issues/3050 +[#3070]: https://github.com/JuliaLang/julia/issues/3070 +[#3071]: https://github.com/JuliaLang/julia/issues/3071 +[#3102]: https://github.com/JuliaLang/julia/issues/3102 +[#3105]: https://github.com/JuliaLang/julia/issues/3105 +[#3140]: https://github.com/JuliaLang/julia/issues/3140 +[#3141]: https://github.com/JuliaLang/julia/issues/3141 +[#3148]: https://github.com/JuliaLang/julia/issues/3148 +[#3149]: https://github.com/JuliaLang/julia/issues/3149 +[#3214]: https://github.com/JuliaLang/julia/issues/3214 +[#3233]: https://github.com/JuliaLang/julia/issues/3233 +[#3272]: https://github.com/JuliaLang/julia/issues/3272 +[#3344]: https://github.com/JuliaLang/julia/issues/3344 +[#3350]: https://github.com/JuliaLang/julia/issues/3350 +[#3434]: https://github.com/JuliaLang/julia/issues/3434 +[#3439]: https://github.com/JuliaLang/julia/issues/3439 +[#3467]: https://github.com/JuliaLang/julia/issues/3467 +[#3468]: https://github.com/JuliaLang/julia/issues/3468 +[#3483]: https://github.com/JuliaLang/julia/issues/3483 +[#3523]: https://github.com/JuliaLang/julia/issues/3523 +[#3605]: https://github.com/JuliaLang/julia/issues/3605 +[#3631]: https://github.com/JuliaLang/julia/issues/3631 +[#3649]: https://github.com/JuliaLang/julia/issues/3649 +[#3665]: https://github.com/JuliaLang/julia/issues/3665 +[#3688]: https://github.com/JuliaLang/julia/issues/3688 +[#3697]: https://github.com/JuliaLang/julia/issues/3697 +[#3719]: https://github.com/JuliaLang/julia/issues/3719 +[#3721]: https://github.com/JuliaLang/julia/issues/3721 +[#3737]: https://github.com/JuliaLang/julia/issues/3737 +[#3759]: https://github.com/JuliaLang/julia/issues/3759 +[#3790]: https://github.com/JuliaLang/julia/issues/3790 +[#3819]: https://github.com/JuliaLang/julia/issues/3819 +[#3872]: https://github.com/JuliaLang/julia/issues/3872 +[#3874]: https://github.com/JuliaLang/julia/issues/3874 +[#3878]: https://github.com/JuliaLang/julia/issues/3878 +[#3932]: https://github.com/JuliaLang/julia/issues/3932 +[#3949]: https://github.com/JuliaLang/julia/issues/3949 +[#3989]: https://github.com/JuliaLang/julia/issues/3989 +[#4014]: https://github.com/JuliaLang/julia/issues/4014 +[#4026]: https://github.com/JuliaLang/julia/issues/4026 +[#4039]: https://github.com/JuliaLang/julia/issues/4039 +[#4042]: https://github.com/JuliaLang/julia/issues/4042 +[#4044]: https://github.com/JuliaLang/julia/issues/4044 +[#4048]: https://github.com/JuliaLang/julia/issues/4048 +[#4055]: https://github.com/JuliaLang/julia/issues/4055 +[#4112]: https://github.com/JuliaLang/julia/issues/4112 +[#4130]: https://github.com/JuliaLang/julia/issues/4130 +[#4163]: https://github.com/JuliaLang/julia/issues/4163 +[#4177]: https://github.com/JuliaLang/julia/issues/4177 +[#4211]: https://github.com/JuliaLang/julia/issues/4211 +[#4235]: https://github.com/JuliaLang/julia/issues/4235 +[#4284]: https://github.com/JuliaLang/julia/issues/4284 +[#4383]: https://github.com/JuliaLang/julia/issues/4383 +[#4412]: https://github.com/JuliaLang/julia/issues/4412 +[#4470]: https://github.com/JuliaLang/julia/issues/4470 +[#4615]: https://github.com/JuliaLang/julia/issues/4615 +[#4670]: https://github.com/JuliaLang/julia/issues/4670 +[#4759]: https://github.com/JuliaLang/julia/issues/4759 +[#4766]: https://github.com/JuliaLang/julia/issues/4766 +[#4775]: https://github.com/JuliaLang/julia/issues/4775 +[#4782]: https://github.com/JuliaLang/julia/issues/4782 +[#4799]: https://github.com/JuliaLang/julia/issues/4799 +[#4806]: https://github.com/JuliaLang/julia/issues/4806 +[#4811]: https://github.com/JuliaLang/julia/issues/4811 +[#4819]: https://github.com/JuliaLang/julia/issues/4819 +[#4862]: https://github.com/JuliaLang/julia/issues/4862 +[#4867]: https://github.com/JuliaLang/julia/issues/4867 +[#4870]: https://github.com/JuliaLang/julia/issues/4870 +[#4871]: https://github.com/JuliaLang/julia/issues/4871 +[#4874]: https://github.com/JuliaLang/julia/issues/4874 +[#4882]: https://github.com/JuliaLang/julia/issues/4882 +[#4888]: https://github.com/JuliaLang/julia/issues/4888 +[#4923]: https://github.com/JuliaLang/julia/issues/4923 +[#4930]: https://github.com/JuliaLang/julia/issues/4930 +[#4934]: https://github.com/JuliaLang/julia/issues/4934 +[#4943]: https://github.com/JuliaLang/julia/issues/4943 +[#4958]: https://github.com/JuliaLang/julia/issues/4958 +[#4967]: https://github.com/JuliaLang/julia/issues/4967 +[#4996]: https://github.com/JuliaLang/julia/issues/4996 +[#5007]: https://github.com/JuliaLang/julia/issues/5007 +[#5025]: https://github.com/JuliaLang/julia/issues/5025 +[#5059]: https://github.com/JuliaLang/julia/issues/5059 +[#5076]: https://github.com/JuliaLang/julia/issues/5076 +[#5154]: https://github.com/JuliaLang/julia/issues/5154 +[#5164]: https://github.com/JuliaLang/julia/issues/5164 +[#5196]: https://github.com/JuliaLang/julia/issues/5196 +[#5214]: https://github.com/JuliaLang/julia/issues/5214 +[#5227]: https://github.com/JuliaLang/julia/issues/5227 +[#5255]: https://github.com/JuliaLang/julia/issues/5255 +[#5263]: https://github.com/JuliaLang/julia/issues/5263 +[#5275]: https://github.com/JuliaLang/julia/issues/5275 +[#5277]: https://github.com/JuliaLang/julia/issues/5277 +[#5330]: https://github.com/JuliaLang/julia/issues/5330 +[#5349]: https://github.com/JuliaLang/julia/issues/5349 +[#5358]: https://github.com/JuliaLang/julia/issues/5358 +[#5380]: https://github.com/JuliaLang/julia/issues/5380 +[#5381]: https://github.com/JuliaLang/julia/issues/5381 +[#5387]: https://github.com/JuliaLang/julia/issues/5387 +[#5403]: https://github.com/JuliaLang/julia/issues/5403 +[#5413]: https://github.com/JuliaLang/julia/issues/5413 +[#5423]: https://github.com/JuliaLang/julia/issues/5423 +[#5427]: https://github.com/JuliaLang/julia/issues/5427 +[#5428]: https://github.com/JuliaLang/julia/issues/5428 +[#5430]: https://github.com/JuliaLang/julia/issues/5430 +[#5462]: https://github.com/JuliaLang/julia/issues/5462 +[#5464]: https://github.com/JuliaLang/julia/issues/5464 +[#5468]: https://github.com/JuliaLang/julia/issues/5468 +[#5475]: https://github.com/JuliaLang/julia/issues/5475 +[#5511]: https://github.com/JuliaLang/julia/issues/5511 +[#5526]: https://github.com/JuliaLang/julia/issues/5526 +[#5545]: https://github.com/JuliaLang/julia/issues/5545 +[#5576]: https://github.com/JuliaLang/julia/issues/5576 +[#5585]: https://github.com/JuliaLang/julia/issues/5585 +[#5636]: https://github.com/JuliaLang/julia/issues/5636 +[#5671]: https://github.com/JuliaLang/julia/issues/5671 +[#5677]: https://github.com/JuliaLang/julia/issues/5677 +[#5703]: https://github.com/JuliaLang/julia/issues/5703 +[#5704]: https://github.com/JuliaLang/julia/issues/5704 +[#5726]: https://github.com/JuliaLang/julia/issues/5726 +[#5737]: https://github.com/JuliaLang/julia/issues/5737 +[#5748]: https://github.com/JuliaLang/julia/issues/5748 +[#5776]: https://github.com/JuliaLang/julia/issues/5776 +[#5778]: https://github.com/JuliaLang/julia/issues/5778 +[#5810]: https://github.com/JuliaLang/julia/issues/5810 +[#5811]: https://github.com/JuliaLang/julia/issues/5811 +[#5819]: https://github.com/JuliaLang/julia/issues/5819 +[#5827]: https://github.com/JuliaLang/julia/issues/5827 +[#5832]: https://github.com/JuliaLang/julia/issues/5832 +[#5927]: https://github.com/JuliaLang/julia/issues/5927 +[#5936]: https://github.com/JuliaLang/julia/issues/5936 +[#5939]: https://github.com/JuliaLang/julia/issues/5939 +[#5970]: https://github.com/JuliaLang/julia/issues/5970 +[#6056]: https://github.com/JuliaLang/julia/issues/6056 +[#6057]: https://github.com/JuliaLang/julia/issues/6057 +[#6073]: https://github.com/JuliaLang/julia/issues/6073 +[#6081]: https://github.com/JuliaLang/julia/issues/6081 +[#6116]: https://github.com/JuliaLang/julia/issues/6116 +[#6128]: https://github.com/JuliaLang/julia/issues/6128 +[#6169]: https://github.com/JuliaLang/julia/issues/6169 +[#6179]: https://github.com/JuliaLang/julia/issues/6179 +[#6190]: https://github.com/JuliaLang/julia/issues/6190 +[#6197]: https://github.com/JuliaLang/julia/issues/6197 +[#6211]: https://github.com/JuliaLang/julia/issues/6211 +[#6212]: https://github.com/JuliaLang/julia/issues/6212 +[#6270]: https://github.com/JuliaLang/julia/issues/6270 +[#6273]: https://github.com/JuliaLang/julia/issues/6273 +[#6582]: https://github.com/JuliaLang/julia/issues/6582 +[#6624]: https://github.com/JuliaLang/julia/issues/6624 +[#6652]: https://github.com/JuliaLang/julia/issues/6652 +[#6678]: https://github.com/JuliaLang/julia/issues/6678 +[#6716]: https://github.com/JuliaLang/julia/issues/6716 +[#6726]: https://github.com/JuliaLang/julia/issues/6726 +[#6739]: https://github.com/JuliaLang/julia/issues/6739 +[#6769]: https://github.com/JuliaLang/julia/issues/6769 +[#6773]: https://github.com/JuliaLang/julia/issues/6773 +[#6842]: https://github.com/JuliaLang/julia/issues/6842 +[#6911]: https://github.com/JuliaLang/julia/issues/6911 +[#6920]: https://github.com/JuliaLang/julia/issues/6920 +[#6929]: https://github.com/JuliaLang/julia/issues/6929 +[#6948]: https://github.com/JuliaLang/julia/issues/6948 +[#7016]: https://github.com/JuliaLang/julia/issues/7016 +[#7035]: https://github.com/JuliaLang/julia/issues/7035 +[#7047]: https://github.com/JuliaLang/julia/issues/7047 +[#7061]: https://github.com/JuliaLang/julia/issues/7061 +[#7064]: https://github.com/JuliaLang/julia/issues/7064 +[#7071]: https://github.com/JuliaLang/julia/issues/7071 +[#7094]: https://github.com/JuliaLang/julia/issues/7094 +[#7106]: https://github.com/JuliaLang/julia/issues/7106 +[#7125]: https://github.com/JuliaLang/julia/issues/7125 +[#7131]: https://github.com/JuliaLang/julia/issues/7131 +[#7146]: https://github.com/JuliaLang/julia/issues/7146 +[#7234]: https://github.com/JuliaLang/julia/issues/7234 +[#7236]: https://github.com/JuliaLang/julia/issues/7236 +[#7242]: https://github.com/JuliaLang/julia/issues/7242 +[#7258]: https://github.com/JuliaLang/julia/issues/7258 +[#7264]: https://github.com/JuliaLang/julia/issues/7264 +[#7311]: https://github.com/JuliaLang/julia/issues/7311 +[#7359]: https://github.com/JuliaLang/julia/issues/7359 +[#7365]: https://github.com/JuliaLang/julia/issues/7365 +[#7373]: https://github.com/JuliaLang/julia/issues/7373 +[#7390]: https://github.com/JuliaLang/julia/issues/7390 +[#7435]: https://github.com/JuliaLang/julia/issues/7435 +[#7464]: https://github.com/JuliaLang/julia/issues/7464 +[#7513]: https://github.com/JuliaLang/julia/issues/7513 +[#7616]: https://github.com/JuliaLang/julia/issues/7616 +[#7647]: https://github.com/JuliaLang/julia/issues/7647 +[#7654]: https://github.com/JuliaLang/julia/issues/7654 +[#7669]: https://github.com/JuliaLang/julia/issues/7669 +[#7704]: https://github.com/JuliaLang/julia/issues/7704 +[#7917]: https://github.com/JuliaLang/julia/issues/7917 +[#7992]: https://github.com/JuliaLang/julia/issues/7992 +[#8011]: https://github.com/JuliaLang/julia/issues/8011 +[#8036]: https://github.com/JuliaLang/julia/issues/8036 +[#8089]: https://github.com/JuliaLang/julia/issues/8089 +[#8113]: https://github.com/JuliaLang/julia/issues/8113 +[#8135]: https://github.com/JuliaLang/julia/issues/8135 +[#8152]: https://github.com/JuliaLang/julia/issues/8152 +[#8246]: https://github.com/JuliaLang/julia/issues/8246 +[#8283]: https://github.com/JuliaLang/julia/issues/8283 +[#8297]: https://github.com/JuliaLang/julia/issues/8297 +[#8320]: https://github.com/JuliaLang/julia/issues/8320 +[#8399]: https://github.com/JuliaLang/julia/issues/8399 +[#8423]: https://github.com/JuliaLang/julia/issues/8423 +[#8432]: https://github.com/JuliaLang/julia/issues/8432 +[#8467]: https://github.com/JuliaLang/julia/issues/8467 +[#8501]: https://github.com/JuliaLang/julia/issues/8501 +[#8560]: https://github.com/JuliaLang/julia/issues/8560 +[#8578]: https://github.com/JuliaLang/julia/issues/8578 +[#8599]: https://github.com/JuliaLang/julia/issues/8599 +[#8605]: https://github.com/JuliaLang/julia/issues/8605 +[#8624]: https://github.com/JuliaLang/julia/issues/8624 +[#8660]: https://github.com/JuliaLang/julia/issues/8660 +[#8672]: https://github.com/JuliaLang/julia/issues/8672 +[#8712]: https://github.com/JuliaLang/julia/issues/8712 +[#8734]: https://github.com/JuliaLang/julia/issues/8734 +[#8745]: https://github.com/JuliaLang/julia/issues/8745 +[#8750]: https://github.com/JuliaLang/julia/issues/8750 +[#8776]: https://github.com/JuliaLang/julia/issues/8776 +[#8791]: https://github.com/JuliaLang/julia/issues/8791 +[#8792]: https://github.com/JuliaLang/julia/issues/8792 +[#8808]: https://github.com/JuliaLang/julia/issues/8808 +[#8814]: https://github.com/JuliaLang/julia/issues/8814 +[#8816]: https://github.com/JuliaLang/julia/issues/8816 +[#8827]: https://github.com/JuliaLang/julia/issues/8827 +[#8832]: https://github.com/JuliaLang/julia/issues/8832 +[#8845]: https://github.com/JuliaLang/julia/issues/8845 +[#8846]: https://github.com/JuliaLang/julia/issues/8846 +[#8854]: https://github.com/JuliaLang/julia/issues/8854 +[#8867]: https://github.com/JuliaLang/julia/issues/8867 +[#8872]: https://github.com/JuliaLang/julia/issues/8872 +[#8897]: https://github.com/JuliaLang/julia/issues/8897 +[#8905]: https://github.com/JuliaLang/julia/issues/8905 +[#8941]: https://github.com/JuliaLang/julia/issues/8941 +[#8958]: https://github.com/JuliaLang/julia/issues/8958 +[#8974]: https://github.com/JuliaLang/julia/issues/8974 +[#9017]: https://github.com/JuliaLang/julia/issues/9017 +[#9049]: https://github.com/JuliaLang/julia/issues/9049 +[#9065]: https://github.com/JuliaLang/julia/issues/9065 +[#9083]: https://github.com/JuliaLang/julia/issues/9083 +[#9105]: https://github.com/JuliaLang/julia/issues/9105 +[#9122]: https://github.com/JuliaLang/julia/issues/9122 +[#9126]: https://github.com/JuliaLang/julia/issues/9126 +[#9132]: https://github.com/JuliaLang/julia/issues/9132 +[#9133]: https://github.com/JuliaLang/julia/issues/9133 +[#9144]: https://github.com/JuliaLang/julia/issues/9144 +[#9198]: https://github.com/JuliaLang/julia/issues/9198 +[#9249]: https://github.com/JuliaLang/julia/issues/9249 +[#9261]: https://github.com/JuliaLang/julia/issues/9261 +[#9271]: https://github.com/JuliaLang/julia/issues/9271 +[#9294]: https://github.com/JuliaLang/julia/issues/9294 +[#9309]: https://github.com/JuliaLang/julia/issues/9309 +[#9343]: https://github.com/JuliaLang/julia/issues/9343 +[#9418]: https://github.com/JuliaLang/julia/issues/9418 +[#9425]: https://github.com/JuliaLang/julia/issues/9425 +[#9434]: https://github.com/JuliaLang/julia/issues/9434 +[#9452]: https://github.com/JuliaLang/julia/issues/9452 +[#9482]: https://github.com/JuliaLang/julia/issues/9482 +[#9487]: https://github.com/JuliaLang/julia/issues/9487 +[#9503]: https://github.com/JuliaLang/julia/issues/9503 +[#9569]: https://github.com/JuliaLang/julia/issues/9569 +[#9575]: https://github.com/JuliaLang/julia/issues/9575 +[#9578]: https://github.com/JuliaLang/julia/issues/9578 +[#9597]: https://github.com/JuliaLang/julia/issues/9597 +[#9627]: https://github.com/JuliaLang/julia/issues/9627 +[#9666]: https://github.com/JuliaLang/julia/issues/9666 +[#9690]: https://github.com/JuliaLang/julia/issues/9690 +[#9701]: https://github.com/JuliaLang/julia/issues/9701 +[#9714]: https://github.com/JuliaLang/julia/issues/9714 +[#9734]: https://github.com/JuliaLang/julia/issues/9734 +[#9745]: https://github.com/JuliaLang/julia/issues/9745 +[#9779]: https://github.com/JuliaLang/julia/issues/9779 +[#9862]: https://github.com/JuliaLang/julia/issues/9862 +[#9957]: https://github.com/JuliaLang/julia/issues/9957 +[#10008]: https://github.com/JuliaLang/julia/issues/10008 +[#10024]: https://github.com/JuliaLang/julia/issues/10024 +[#10031]: https://github.com/JuliaLang/julia/issues/10031 +[#10075]: https://github.com/JuliaLang/julia/issues/10075 +[#10117]: https://github.com/JuliaLang/julia/issues/10117 +[#10150]: https://github.com/JuliaLang/julia/issues/10150 +[#10168]: https://github.com/JuliaLang/julia/issues/10168 +[#10180]: https://github.com/JuliaLang/julia/issues/10180 +[#10228]: https://github.com/JuliaLang/julia/issues/10228 +[#10328]: https://github.com/JuliaLang/julia/issues/10328 +[#10331]: https://github.com/JuliaLang/julia/issues/10331 +[#10332]: https://github.com/JuliaLang/julia/issues/10332 +[#10333]: https://github.com/JuliaLang/julia/issues/10333 +[#10380]: https://github.com/JuliaLang/julia/issues/10380 +[#10400]: https://github.com/JuliaLang/julia/issues/10400 +[#10446]: https://github.com/JuliaLang/julia/issues/10446 +[#10458]: https://github.com/JuliaLang/julia/issues/10458 +[#10472]: https://github.com/JuliaLang/julia/issues/10472 +[#10525]: https://github.com/JuliaLang/julia/issues/10525 +[#10543]: https://github.com/JuliaLang/julia/issues/10543 +[#10548]: https://github.com/JuliaLang/julia/issues/10548 +[#10659]: https://github.com/JuliaLang/julia/issues/10659 +[#10679]: https://github.com/JuliaLang/julia/issues/10679 +[#10709]: https://github.com/JuliaLang/julia/issues/10709 +[#10714]: https://github.com/JuliaLang/julia/issues/10714 +[#10747]: https://github.com/JuliaLang/julia/issues/10747 +[#10844]: https://github.com/JuliaLang/julia/issues/10844 +[#10870]: https://github.com/JuliaLang/julia/issues/10870 +[#10885]: https://github.com/JuliaLang/julia/issues/10885 +[#10888]: https://github.com/JuliaLang/julia/issues/10888 +[#10893]: https://github.com/JuliaLang/julia/issues/10893 +[#10914]: https://github.com/JuliaLang/julia/issues/10914 +[#10946]: https://github.com/JuliaLang/julia/issues/10946 +[#10955]: https://github.com/JuliaLang/julia/issues/10955 +[#10994]: https://github.com/JuliaLang/julia/issues/10994 +[#11030]: https://github.com/JuliaLang/julia/issues/11030 +[#11067]: https://github.com/JuliaLang/julia/issues/11067 +[#11105]: https://github.com/JuliaLang/julia/issues/11105 +[#11145]: https://github.com/JuliaLang/julia/issues/11145 +[#11171]: https://github.com/JuliaLang/julia/issues/11171 +[#11196]: https://github.com/JuliaLang/julia/issues/11196 +[#11241]: https://github.com/JuliaLang/julia/issues/11241 +[#11242]: https://github.com/JuliaLang/julia/issues/11242 +[#11250]: https://github.com/JuliaLang/julia/issues/11250 +[#11279]: https://github.com/JuliaLang/julia/issues/11279 +[#11310]: https://github.com/JuliaLang/julia/issues/11310 +[#11347]: https://github.com/JuliaLang/julia/issues/11347 +[#11379]: https://github.com/JuliaLang/julia/issues/11379 +[#11432]: https://github.com/JuliaLang/julia/issues/11432 +[#11566]: https://github.com/JuliaLang/julia/issues/11566 +[#11686]: https://github.com/JuliaLang/julia/issues/11686 +[#11688]: https://github.com/JuliaLang/julia/issues/11688 +[#11741]: https://github.com/JuliaLang/julia/issues/11741 +[#11849]: https://github.com/JuliaLang/julia/issues/11849 +[#11891]: https://github.com/JuliaLang/julia/issues/11891 +[#11922]: https://github.com/JuliaLang/julia/issues/11922 +[#11947]: https://github.com/JuliaLang/julia/issues/11947 +[#11985]: https://github.com/JuliaLang/julia/issues/11985 +[#12025]: https://github.com/JuliaLang/julia/issues/12025 +[#12031]: https://github.com/JuliaLang/julia/issues/12031 +[#12034]: https://github.com/JuliaLang/julia/issues/12034 +[#12087]: https://github.com/JuliaLang/julia/issues/12087 +[#12137]: https://github.com/JuliaLang/julia/issues/12137 +[#12162]: https://github.com/JuliaLang/julia/issues/12162 +[#12231]: https://github.com/JuliaLang/julia/issues/12231 +[#12264]: https://github.com/JuliaLang/julia/issues/12264 +[#12274]: https://github.com/JuliaLang/julia/issues/12274 +[#12385]: https://github.com/JuliaLang/julia/issues/12385 +[#12393]: https://github.com/JuliaLang/julia/issues/12393 +[#12458]: https://github.com/JuliaLang/julia/issues/12458 +[#12472]: https://github.com/JuliaLang/julia/issues/12472 +[#12491]: https://github.com/JuliaLang/julia/issues/12491 +[#12563]: https://github.com/JuliaLang/julia/issues/12563 +[#12576]: https://github.com/JuliaLang/julia/issues/12576 +[#12727]: https://github.com/JuliaLang/julia/issues/12727 +[#12739]: https://github.com/JuliaLang/julia/issues/12739 +[#12819]: https://github.com/JuliaLang/julia/issues/12819 +[#12872]: https://github.com/JuliaLang/julia/issues/12872 +[#13062]: https://github.com/JuliaLang/julia/issues/13062 +[#13171]: https://github.com/JuliaLang/julia/issues/13171 +[#13232]: https://github.com/JuliaLang/julia/issues/13232 +[#13338]: https://github.com/JuliaLang/julia/issues/13338 +[#13387]: https://github.com/JuliaLang/julia/issues/13387 +[#13412]: https://github.com/JuliaLang/julia/issues/13412 +[#13440]: https://github.com/JuliaLang/julia/issues/13440 +[#13465]: https://github.com/JuliaLang/julia/issues/13465 +[#13480]: https://github.com/JuliaLang/julia/issues/13480 +[#13496]: https://github.com/JuliaLang/julia/issues/13496 +[#13542]: https://github.com/JuliaLang/julia/issues/13542 +[#13612]: https://github.com/JuliaLang/julia/issues/13612 +[#13680]: https://github.com/JuliaLang/julia/issues/13680 +[#13681]: https://github.com/JuliaLang/julia/issues/13681 +[#13707]: https://github.com/JuliaLang/julia/issues/13707 +[#13774]: https://github.com/JuliaLang/julia/issues/13774 +[#13780]: https://github.com/JuliaLang/julia/issues/13780 +[#13824]: https://github.com/JuliaLang/julia/issues/13824 +[#13825]: https://github.com/JuliaLang/julia/issues/13825 +[#13897]: https://github.com/JuliaLang/julia/issues/13897 +[#14052]: https://github.com/JuliaLang/julia/issues/14052 +[#14114]: https://github.com/JuliaLang/julia/issues/14114 +[#14140]: https://github.com/JuliaLang/julia/issues/14140 +[#14194]: https://github.com/JuliaLang/julia/issues/14194 +[#14243]: https://github.com/JuliaLang/julia/issues/14243 +[#14335]: https://github.com/JuliaLang/julia/issues/14335 +[#14413]: https://github.com/JuliaLang/julia/issues/14413 +[#14424]: https://github.com/JuliaLang/julia/issues/14424 +[#14458]: https://github.com/JuliaLang/julia/issues/14458 +[#14469]: https://github.com/JuliaLang/julia/issues/14469 +[#14474]: https://github.com/JuliaLang/julia/issues/14474 +[#14519]: https://github.com/JuliaLang/julia/issues/14519 +[#14608]: https://github.com/JuliaLang/julia/issues/14608 +[#14623]: https://github.com/JuliaLang/julia/issues/14623 +[#14660]: https://github.com/JuliaLang/julia/issues/14660 +[#14676]: https://github.com/JuliaLang/julia/issues/14676 +[#14759]: https://github.com/JuliaLang/julia/issues/14759 +[#14777]: https://github.com/JuliaLang/julia/issues/14777 +[#14798]: https://github.com/JuliaLang/julia/issues/14798 +[#15007]: https://github.com/JuliaLang/julia/issues/15007 +[#15032]: https://github.com/JuliaLang/julia/issues/15032 +[#15172]: https://github.com/JuliaLang/julia/issues/15172 +[#15192]: https://github.com/JuliaLang/julia/issues/15192 +[#15242]: https://github.com/JuliaLang/julia/issues/15242 +[#15244]: https://github.com/JuliaLang/julia/issues/15244 +[#15258]: https://github.com/JuliaLang/julia/issues/15258 +[#15409]: https://github.com/JuliaLang/julia/issues/15409 +[#15431]: https://github.com/JuliaLang/julia/issues/15431 +[#15524]: https://github.com/JuliaLang/julia/issues/15524 +[#15550]: https://github.com/JuliaLang/julia/issues/15550 +[#15609]: https://github.com/JuliaLang/julia/issues/15609 +[#15708]: https://github.com/JuliaLang/julia/issues/15708 +[#15731]: https://github.com/JuliaLang/julia/issues/15731 +[#15763]: https://github.com/JuliaLang/julia/issues/15763 +[#15804]: https://github.com/JuliaLang/julia/issues/15804 +[#15850]: https://github.com/JuliaLang/julia/issues/15850 +[#15975]: https://github.com/JuliaLang/julia/issues/15975 +[#16010]: https://github.com/JuliaLang/julia/issues/16010 +[#16024]: https://github.com/JuliaLang/julia/issues/16024 +[#16058]: https://github.com/JuliaLang/julia/issues/16058 +[#16071]: https://github.com/JuliaLang/julia/issues/16071 +[#16098]: https://github.com/JuliaLang/julia/issues/16098 +[#16107]: https://github.com/JuliaLang/julia/issues/16107 +[#16154]: https://github.com/JuliaLang/julia/issues/16154 +[#16213]: https://github.com/JuliaLang/julia/issues/16213 +[#16219]: https://github.com/JuliaLang/julia/issues/16219 +[#16260]: https://github.com/JuliaLang/julia/issues/16260 +[#16285]: https://github.com/JuliaLang/julia/issues/16285 +[#16362]: https://github.com/JuliaLang/julia/issues/16362 +[#16378]: https://github.com/JuliaLang/julia/issues/16378 +[#16403]: https://github.com/JuliaLang/julia/issues/16403 +[#16404]: https://github.com/JuliaLang/julia/issues/16404 +[#16450]: https://github.com/JuliaLang/julia/issues/16450 +[#16455]: https://github.com/JuliaLang/julia/issues/16455 +[#16466]: https://github.com/JuliaLang/julia/issues/16466 +[#16481]: https://github.com/JuliaLang/julia/issues/16481 +[#16502]: https://github.com/JuliaLang/julia/issues/16502 +[#16510]: https://github.com/JuliaLang/julia/issues/16510 +[#16600]: https://github.com/JuliaLang/julia/issues/16600 +[#16603]: https://github.com/JuliaLang/julia/issues/16603 +[#16621]: https://github.com/JuliaLang/julia/issues/16621 +[#16622]: https://github.com/JuliaLang/julia/issues/16622 +[#16645]: https://github.com/JuliaLang/julia/issues/16645 +[#16663]: https://github.com/JuliaLang/julia/issues/16663 +[#16731]: https://github.com/JuliaLang/julia/issues/16731 +[#16854]: https://github.com/JuliaLang/julia/issues/16854 +[#16953]: https://github.com/JuliaLang/julia/issues/16953 +[#16961]: https://github.com/JuliaLang/julia/issues/16961 +[#16972]: https://github.com/JuliaLang/julia/issues/16972 +[#16984]: https://github.com/JuliaLang/julia/issues/16984 +[#16986]: https://github.com/JuliaLang/julia/issues/16986 +[#17033]: https://github.com/JuliaLang/julia/issues/17033 +[#17037]: https://github.com/JuliaLang/julia/issues/17037 +[#17057]: https://github.com/JuliaLang/julia/issues/17057 +[#17075]: https://github.com/JuliaLang/julia/issues/17075 +[#17132]: https://github.com/JuliaLang/julia/issues/17132 +[#17155]: https://github.com/JuliaLang/julia/issues/17155 +[#17261]: https://github.com/JuliaLang/julia/issues/17261 +[#17265]: https://github.com/JuliaLang/julia/issues/17265 +[#17266]: https://github.com/JuliaLang/julia/issues/17266 +[#17300]: https://github.com/JuliaLang/julia/issues/17300 +[#17302]: https://github.com/JuliaLang/julia/issues/17302 +[#17323]: https://github.com/JuliaLang/julia/issues/17323 +[#17374]: https://github.com/JuliaLang/julia/issues/17374 +[#17393]: https://github.com/JuliaLang/julia/issues/17393 +[#17402]: https://github.com/JuliaLang/julia/issues/17402 +[#17404]: https://github.com/JuliaLang/julia/issues/17404 +[#17510]: https://github.com/JuliaLang/julia/issues/17510 +[#17546]: https://github.com/JuliaLang/julia/issues/17546 +[#17599]: https://github.com/JuliaLang/julia/issues/17599 +[#17607]: https://github.com/JuliaLang/julia/issues/17607 +[#17623]: https://github.com/JuliaLang/julia/issues/17623 +[#17654]: https://github.com/JuliaLang/julia/issues/17654 +[#17668]: https://github.com/JuliaLang/julia/issues/17668 +[#17723]: https://github.com/JuliaLang/julia/issues/17723 +[#17758]: https://github.com/JuliaLang/julia/issues/17758 +[#17785]: https://github.com/JuliaLang/julia/issues/17785 +[#18012]: https://github.com/JuliaLang/julia/issues/18012 +[#18050]: https://github.com/JuliaLang/julia/issues/18050 +[#18159]: https://github.com/JuliaLang/julia/issues/18159 +[#18218]: https://github.com/JuliaLang/julia/issues/18218 +[#18251]: https://github.com/JuliaLang/julia/issues/18251 +[#18330]: https://github.com/JuliaLang/julia/issues/18330 +[#18339]: https://github.com/JuliaLang/julia/issues/18339 +[#18342]: https://github.com/JuliaLang/julia/issues/18342 +[#18346]: https://github.com/JuliaLang/julia/issues/18346 +[#18441]: https://github.com/JuliaLang/julia/issues/18441 +[#18442]: https://github.com/JuliaLang/julia/issues/18442 +[#18444]: https://github.com/JuliaLang/julia/issues/18444 +[#18453]: https://github.com/JuliaLang/julia/issues/18453 +[#18457]: https://github.com/JuliaLang/julia/issues/18457 +[#18473]: https://github.com/JuliaLang/julia/issues/18473 +[#18558]: https://github.com/JuliaLang/julia/issues/18558 +[#18628]: https://github.com/JuliaLang/julia/issues/18628 +[#18642]: https://github.com/JuliaLang/julia/issues/18642 +[#18644]: https://github.com/JuliaLang/julia/issues/18644 +[#18660]: https://github.com/JuliaLang/julia/issues/18660 +[#18690]: https://github.com/JuliaLang/julia/issues/18690 +[#18754]: https://github.com/JuliaLang/julia/issues/18754 +[#18777]: https://github.com/JuliaLang/julia/issues/18777 +[#18832]: https://github.com/JuliaLang/julia/issues/18832 +[#18839]: https://github.com/JuliaLang/julia/issues/18839 +[#18891]: https://github.com/JuliaLang/julia/issues/18891 +[#18931]: https://github.com/JuliaLang/julia/issues/18931 +[#18965]: https://github.com/JuliaLang/julia/issues/18965 +[#18977]: https://github.com/JuliaLang/julia/issues/18977 +[#19018]: https://github.com/JuliaLang/julia/issues/19018 +[#19088]: https://github.com/JuliaLang/julia/issues/19088 +[#19157]: https://github.com/JuliaLang/julia/issues/19157 +[#19233]: https://github.com/JuliaLang/julia/issues/19233 +[#19239]: https://github.com/JuliaLang/julia/issues/19239 +[#19246]: https://github.com/JuliaLang/julia/issues/19246 +[#19259]: https://github.com/JuliaLang/julia/issues/19259 +[#19288]: https://github.com/JuliaLang/julia/issues/19288 +[#19305]: https://github.com/JuliaLang/julia/issues/19305 +[#19331]: https://github.com/JuliaLang/julia/issues/19331 +[#19371]: https://github.com/JuliaLang/julia/issues/19371 +[#19438]: https://github.com/JuliaLang/julia/issues/19438 +[#19449]: https://github.com/JuliaLang/julia/issues/19449 +[#19464]: https://github.com/JuliaLang/julia/issues/19464 +[#19469]: https://github.com/JuliaLang/julia/issues/19469 +[#19518]: https://github.com/JuliaLang/julia/issues/19518 +[#19533]: https://github.com/JuliaLang/julia/issues/19533 +[#19543]: https://github.com/JuliaLang/julia/issues/19543 +[#19594]: https://github.com/JuliaLang/julia/issues/19594 +[#19598]: https://github.com/JuliaLang/julia/issues/19598 +[#19635]: https://github.com/JuliaLang/julia/issues/19635 +[#19636]: https://github.com/JuliaLang/julia/issues/19636 +[#19660]: https://github.com/JuliaLang/julia/issues/19660 +[#19669]: https://github.com/JuliaLang/julia/issues/19669 +[#19670]: https://github.com/JuliaLang/julia/issues/19670 +[#19677]: https://github.com/JuliaLang/julia/issues/19677 +[#19680]: https://github.com/JuliaLang/julia/issues/19680 +[#19690]: https://github.com/JuliaLang/julia/issues/19690 +[#19692]: https://github.com/JuliaLang/julia/issues/19692 +[#19711]: https://github.com/JuliaLang/julia/issues/19711 +[#19712]: https://github.com/JuliaLang/julia/issues/19712 +[#19720]: https://github.com/JuliaLang/julia/issues/19720 +[#19721]: https://github.com/JuliaLang/julia/issues/19721 +[#19722]: https://github.com/JuliaLang/julia/issues/19722 +[#19724]: https://github.com/JuliaLang/julia/issues/19724 +[#19730]: https://github.com/JuliaLang/julia/issues/19730 +[#19737]: https://github.com/JuliaLang/julia/issues/19737 +[#19741]: https://github.com/JuliaLang/julia/issues/19741 +[#19766]: https://github.com/JuliaLang/julia/issues/19766 +[#19771]: https://github.com/JuliaLang/julia/issues/19771 +[#19779]: https://github.com/JuliaLang/julia/issues/19779 +[#19784]: https://github.com/JuliaLang/julia/issues/19784 +[#19786]: https://github.com/JuliaLang/julia/issues/19786 +[#19787]: https://github.com/JuliaLang/julia/issues/19787 +[#19791]: https://github.com/JuliaLang/julia/issues/19791 +[#19800]: https://github.com/JuliaLang/julia/issues/19800 +[#19802]: https://github.com/JuliaLang/julia/issues/19802 +[#19811]: https://github.com/JuliaLang/julia/issues/19811 +[#19814]: https://github.com/JuliaLang/julia/issues/19814 +[#19841]: https://github.com/JuliaLang/julia/issues/19841 +[#19878]: https://github.com/JuliaLang/julia/issues/19878 +[#19900]: https://github.com/JuliaLang/julia/issues/19900 +[#19901]: https://github.com/JuliaLang/julia/issues/19901 +[#19903]: https://github.com/JuliaLang/julia/issues/19903 +[#19919]: https://github.com/JuliaLang/julia/issues/19919 +[#19920]: https://github.com/JuliaLang/julia/issues/19920 +[#19925]: https://github.com/JuliaLang/julia/issues/19925 +[#19926]: https://github.com/JuliaLang/julia/issues/19926 +[#19931]: https://github.com/JuliaLang/julia/issues/19931 +[#19934]: https://github.com/JuliaLang/julia/issues/19934 +[#19935]: https://github.com/JuliaLang/julia/issues/19935 +[#19937]: https://github.com/JuliaLang/julia/issues/19937 +[#19944]: https://github.com/JuliaLang/julia/issues/19944 +[#19949]: https://github.com/JuliaLang/julia/issues/19949 +[#19950]: https://github.com/JuliaLang/julia/issues/19950 +[#19989]: https://github.com/JuliaLang/julia/issues/19989 +[#20009]: https://github.com/JuliaLang/julia/issues/20009 +[#20047]: https://github.com/JuliaLang/julia/issues/20047 +[#20058]: https://github.com/JuliaLang/julia/issues/20058 +[#20079]: https://github.com/JuliaLang/julia/issues/20079 +[#20135]: https://github.com/JuliaLang/julia/issues/20135 +[#20164]: https://github.com/JuliaLang/julia/issues/20164 +[#20213]: https://github.com/JuliaLang/julia/issues/20213 +[#20228]: https://github.com/JuliaLang/julia/issues/20228 +[#20248]: https://github.com/JuliaLang/julia/issues/20248 +[#20249]: https://github.com/JuliaLang/julia/issues/20249 +[#20268]: https://github.com/JuliaLang/julia/issues/20268 +[#20308]: https://github.com/JuliaLang/julia/issues/20308 +[#20321]: https://github.com/JuliaLang/julia/issues/20321 +[#20327]: https://github.com/JuliaLang/julia/issues/20327 +[#20328]: https://github.com/JuliaLang/julia/issues/20328 +[#20330]: https://github.com/JuliaLang/julia/issues/20330 +[#20342]: https://github.com/JuliaLang/julia/issues/20342 +[#20345]: https://github.com/JuliaLang/julia/issues/20345 +[#20403]: https://github.com/JuliaLang/julia/issues/20403 +[#20404]: https://github.com/JuliaLang/julia/issues/20404 +[#20406]: https://github.com/JuliaLang/julia/issues/20406 +[#20414]: https://github.com/JuliaLang/julia/issues/20414 +[#20418]: https://github.com/JuliaLang/julia/issues/20418 +[#20427]: https://github.com/JuliaLang/julia/issues/20427 +[#20435]: https://github.com/JuliaLang/julia/issues/20435 +[#20500]: https://github.com/JuliaLang/julia/issues/20500 +[#20530]: https://github.com/JuliaLang/julia/issues/20530 +[#20543]: https://github.com/JuliaLang/julia/issues/20543 +[#20609]: https://github.com/JuliaLang/julia/issues/20609 +[#20889]: https://github.com/JuliaLang/julia/issues/20889 +[#20952]: https://github.com/JuliaLang/julia/issues/20952 +[#21183]: https://github.com/JuliaLang/julia/issues/21183 +[#21818]: https://github.com/JuliaLang/julia/issues/21818 diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..77cbc92 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,85 @@ +The Julia language is licensed under the MIT License. The "language" consists +of the compiler (the contents of src/), most of the standard library (base/), +and some utilities (most of the rest of the files in this repository). See below +for exceptions. + +> Copyright (c) 2009-2019: Jeff Bezanson, Stefan Karpinski, Viral B. Shah, +> and other contributors: +> +> https://github.com/JuliaLang/julia/contributors +> +> Permission is hereby granted, free of charge, to any person obtaining +> a copy of this software and associated documentation files (the +> "Software"), to deal in the Software without restriction, including +> without limitation the rights to use, copy, modify, merge, publish, +> distribute, sublicense, and/or sell copies of the Software, and to +> permit persons to whom the Software is furnished to do so, subject to +> the following conditions: +> +> The above copyright notice and this permission notice shall be +> included in all copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +> EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +> MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +> NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +> LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +> OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +> WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Julia includes code from the following projects, which have their own licenses: + +- [crc32c.c](https://stackoverflow.com/questions/17645167/implementing-sse-4-2s-crc32c-in-software) (CRC-32c checksum code by Mark Adler) [[ZLib](https://opensource.org/licenses/Zlib)]. +- [LDC](https://github.com/ldc-developers/ldc/blob/master/LICENSE) (for ccall/cfunction ABI definitions) [BSD-3]. The portion of code that Julia uses from LDC is [BSD-3] licensed. +- [LLVM](https://releases.llvm.org/3.9.0/LICENSE.TXT) (for parts of src/jitlayers.cpp and src/disasm.cpp) [BSD-3, effectively] +- [MUSL](https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT) (for getopt implementation on Windows) [MIT] +- [MINGW](https://sourceforge.net/p/mingw/mingw-org-wsl/ci/legacy/tree/mingwrt/mingwex/dirname.c) (for dirname implementation on Windows) [MIT] +- [NetBSD](https://www.netbsd.org/about/redistribution.html) (for setjmp, longjmp, and strptime implementations on Windows) [BSD-3] +- [Python](https://docs.python.org/2/license.html) (for strtod implementation on Windows) [BSD-3, effectively] + +The following components included in Julia `Base` have their own separate licenses: + +- base/grisu/* [BSD-3] (see [double-conversion](https://github.com/google/double-conversion/blob/master/LICENSE)) +- base/special/{exp,rem_pio2,hyperbolic}.jl [Freely distributable with preserved copyright notice] (see [FDLIBM](https://www.netlib.org/fdlibm)) + +The Julia language links to the following external libraries, which have their +own licenses: + +- [FEMTOLISP](https://github.com/JeffBezanson/femtolisp) [BSD-3] +- [LIBUNWIND](https://git.savannah.gnu.org/gitweb/?p=libunwind.git;a=blob_plain;f=LICENSE;hb=master) [MIT] +- [LIBUV](https://github.com/joyent/libuv/blob/master/LICENSE) [MIT] +- [LLVM](https://releases.llvm.org/6.0.0/LICENSE.TXT) [BSD-3, effectively] +- [UTF8PROC](https://github.com/JuliaStrings/utf8proc) [MIT] + +The following components included in `stdlib` have their own separate licenses: + +- stdlib/SuiteSparse/umfpack.jl (see [SUITESPARSE](http://suitesparse.com)) +- stdlib/SuiteSparse/cholmod.jl (see [SUITESPARSE](http://suitesparse.com)) + +Julia's `stdlib` uses the following external libraries, which have their own licenses: + +- [DSFMT](http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/LICENSE.txt) [BSD-3] +- [OPENLIBM](https://github.com/JuliaMath/openlibm/blob/master/LICENSE.md) [MIT, BSD-2, ISC] +- [GMP](https://gmplib.org/manual/Copying.html#Copying) [LGPL3+ or GPL2+] +- [LIBGIT2](https://github.com/libgit2/libgit2/blob/development/COPYING) [GPL2+ with unlimited linking exception] +- [CURL](https://curl.haxx.se/docs/copyright.html) [MIT/X derivative] +- [LIBSSH2](https://github.com/libssh2/libssh2/blob/master/COPYING) [BSD-3] +- [MBEDTLS](https://tls.mbed.org/how-to-get) [either GPLv2 or Apache 2.0] +- [MPFR](https://www.mpfr.org/mpfr-current/mpfr.html#Copying) [LGPL3+] +- [OPENBLAS](https://raw.github.com/xianyi/OpenBLAS/master/LICENSE) [BSD-3] +- [LAPACK](https://netlib.org/lapack/LICENSE.txt) [BSD-3] +- [PCRE](https://www.pcre.org/licence.txt) [BSD-3] +- [SUITESPARSE](http://suitesparse.com) [mix of LGPL2+ and GPL2+; see individual module licenses] + +Julia's build process uses the following external tools: + +- [PATCHELF](https://nixos.org/patchelf.html) +- [OBJCONV](https://www.agner.org/optimize/#objconv) + +Julia bundles the following external programs and libraries: + +- [7-Zip](https://www.7-zip.org/license.txt) +- [ZLIB](https://zlib.net/zlib_license.html) + +On some platforms, distributions of Julia contain SSL certificate authority certificates, +released under the [Mozilla Public License](https://en.wikipedia.org/wiki/Mozilla_Public_License). diff --git a/Make.inc b/Make.inc new file mode 100644 index 0000000..2f0d52f --- /dev/null +++ b/Make.inc @@ -0,0 +1,1410 @@ +# -*- mode: makefile-gmake -*- +# vi:syntax=make + +## Note: +## It is generally preferable to change these options, for +## your local machine, in a file named `Make.user` in the toplevel +## and build directories +## +## For developers, take care to not insert comments on the same line as +## variable declarations. The spaces between the variable value and the +## comment will be included in the value. + +# Julia precompilation options +# Set to zero to turn off extra precompile (e.g. for the REPL) +JULIA_PRECOMPILE ?= 1 + +FORCE_ASSERTIONS ?= 0 + +# OPENBLAS build options +OPENBLAS_TARGET_ARCH:= +OPENBLAS_SYMBOLSUFFIX:= +OPENBLAS_LIBNAMESUFFIX:= + +# If OPENBLAS_TARGET_ARCH is set, we default to disabling OPENBLAS_DYNAMIC_ARCH +ifneq ($(OPENBLAS_TARGET_ARCH),) +OPENBLAS_DYNAMIC_ARCH:=0 +else +OPENBLAS_DYNAMIC_ARCH:=1 +endif +OPENBLAS_USE_THREAD:=1 + +# Flags for using libraries available on the system instead of building them. +# Please read the notes around usage of SYSTEM flags in README.md +# Issues resulting from use of SYSTEM versions will generally not be accepted. +USE_SYSTEM_LLVM:=0 +USE_SYSTEM_LIBUNWIND:=0 +DISABLE_LIBUNWIND:=0 +USE_SYSTEM_PCRE:=0 +USE_SYSTEM_LIBM:=0 +USE_SYSTEM_OPENLIBM:=0 +UNTRUSTED_SYSTEM_LIBM:=0 +USE_SYSTEM_DSFMT:=0 +USE_SYSTEM_BLAS:=0 +USE_SYSTEM_LAPACK:=0 +USE_SYSTEM_GMP:=0 +USE_SYSTEM_MPFR:=0 +USE_SYSTEM_SUITESPARSE:=0 +USE_SYSTEM_LIBUV:=0 +USE_SYSTEM_UTF8PROC:=0 +USE_SYSTEM_MBEDTLS:=0 +USE_SYSTEM_LIBSSH2:=0 +USE_SYSTEM_CURL:=0 +USE_SYSTEM_LIBGIT2:=0 +USE_SYSTEM_PATCHELF:=0 +USE_SYSTEM_ZLIB:=0 +USE_SYSTEM_P7ZIP:=0 + +# Link to the LLVM shared library +USE_LLVM_SHLIB := 1 + +## Settings for various Intel tools +# Set to 1 to use MKL +USE_INTEL_MKL ?= 0 +# Set to 1 to use Intel LIBM +USE_INTEL_LIBM ?= 0 +# Set to 1 to enable profiling with Intel VTune Amplifier +USE_INTEL_JITEVENTS ?= 0 +# Set to 1 to use Intel C, C++, and FORTRAN compilers +USEICC ?= 0 +USEIFC ?= 0 + +# Enable threading with one thread +JULIA_THREADS := 1 + +ifeq ($(USE_MKL), 1) +$(warning "The julia make variable USE_MKL has been renamed to USE_INTEL_MKL") +USE_INTEL_MKL := 1 +endif + +# Set to 1 to enable profiling with OProfile +USE_OPROFILE_JITEVENTS ?= 0 + +# Set to 1 to enable profiling with perf +USE_PERF_JITEVENTS ?= 0 + +# libc++ is standard on OS X 10.9, but not for earlier releases +USE_LIBCPP := 0 + +# assume we don't have LIBSSP support in our compiler, will enable later if likely true +HAVE_SSP := 0 + +# GC debugging options +WITH_GC_VERIFY := 0 +WITH_GC_DEBUG_ENV := 0 + +# Prevent picking up $ARCH from the environment variables +ARCH:= + +# pick up BUILDROOT from O= if it isn't already set (from recursive make) +ifeq ($(BUILDROOT),) +ifeq ("$(origin O)", "command line") + BUILDROOT := $(abspath $O) + BUILDDIR := $(abspath $(BUILDROOT)/$(shell $(JULIAHOME)/contrib/relative_path.sh $(JULIAHOME) $(SRCDIR))) + $(info $(shell printf '\033[32;1mBuilding into $(BUILDROOT)\033[0m')) # use printf to expand the escape sequences +else + BUILDROOT:=$(JULIAHOME) +endif +endif +export BUILDROOT +unexport O + +# Make sure the user didn't try to specify a path that will confuse the shell / make +METACHARACTERS := ][?*{}() $$%:;&|!\#,\\`\": +ifneq (,$(findstring ',$(value BUILDROOT))) +$(error cowardly refusing to build into directory with a single-quote in the path) +endif +ifneq (,$(findstring ',$(value JULIAHOME))) +$(error cowardly refusing to build from source directory with a single-quote in the path) +endif +ifneq (,$(shell echo '$(value BUILDROOT)' | grep '[$(METACHARACTERS)]')) +$(error cowardly refusing to build into directory with a shell-metacharacter in the path\ + (got: $(value BUILDROOT))) +endif +ifneq (,$(shell echo '$(value JULIAHOME)' | grep '[$(METACHARACTERS)]')) +$(error cowardly refusing to build from source directory with a shell-metacharacter in the path\ + (got: $(value JULIAHOME))) +endif + +# we include twice to pickup user definitions better +# include from JULIAHOME first so that BUILDROOT can override +MAYBE_HOST := +ifneq ($(BUILDING_HOST_TOOLS),1) +MAKE_USER_FNAME = Make.user +else +MAYBE_HOST := /host +MAKE_USER_FNAME = Make.host.user +endif + +ifeq (exists, $(shell [ -e $(JULIAHOME)/$(MAKE_USER_FNAME) ] && echo exists )) +include $(JULIAHOME)/$(MAKE_USER_FNAME) +endif +ifeq (exists, $(shell [ -e $(BUILDROOT)/$(MAKE_USER_FNAME) ] && echo exists )) +include $(BUILDROOT)/$(MAKE_USER_FNAME) +endif + +# disable automatic Makefile rules +.SUFFIXES: + +# find out if git repository is available +ifeq ($(shell [ -e $(JULIAHOME)/.git ] && echo true || echo "Warning: git information unavailable; versioning information limited" >&2), true) +NO_GIT := 0 +else +NO_GIT := 1 +endif + +# Julia's Semantic Versioning system labels the three decimal places in a version number as +# the major, minor and patch versions. Typically the major version would be incremented +# whenever a backwards-incompatible change is made, the minor version would be incremented +# whenever major backwards-compatible changes are made, and the patch version would be +# incremented whenever smaller changes are made. However, before v1.0.0, the major +# version number is always zero and the meanings shift down a place; the minor version +# number becomes the major version number, the patch version number becomes the minor +# version number, and there is no patch version number to speak of. In this case, the +# version v0.4.1 has backwards-compatible changes as compared to v0.4.0, and the +# version v0.5.0 has major backwards-incompatible changes as compared to v0.4.X. +JULIA_VERSION := $(shell cat $(JULIAHOME)/VERSION) +JULIA_MAJOR_VERSION := $(shell echo $(JULIA_VERSION) | cut -d'-' -f 1 | cut -d'.' -f 1) +JULIA_MINOR_VERSION := $(shell echo $(JULIA_VERSION) | cut -d'-' -f 1 | cut -d'.' -f 2) +JULIA_PATCH_VERSION := $(shell echo $(JULIA_VERSION) | cut -d'-' -f 1 | cut -d'.' -f 3) + +# libjulia's SONAME will follow the format libjulia.so.$(SOMAJOR). Before v1.0.0, +# SOMAJOR will be a two-decimal value, e.g. libjulia.so.0.5, whereas at and beyond +# v1.0.0, SOMAJOR will be simply the major version number, e.g. libjulia.so.1 +# The file itself will ultimately symlink to libjulia.so.$(SOMAJOR).$(SOMINOR) +ifeq ($(JULIA_MAJOR_VERSION),0) +SOMAJOR := $(JULIA_MAJOR_VERSION).$(JULIA_MINOR_VERSION) +SOMINOR := $(JULIA_PATCH_VERSION) +else +SOMAJOR := $(JULIA_MAJOR_VERSION) +SOMINOR := $(JULIA_MINOR_VERSION) +endif + +ifneq ($(NO_GIT), 1) +JULIA_COMMIT := $(shell git -C $(JULIAHOME) rev-parse --short=10 HEAD) +else +JULIA_COMMIT := $(JULIA_VERSION) +endif + +# Override `JULIA_COMMIT` to `JULIA_VERSION` if we're on a tagged commit +ifeq ($(shell git -C $(JULIAHOME) describe --tags --exact-match > /dev/null 2>&1 && echo true),true) +JULIA_COMMIT := $(JULIA_VERSION) +endif + +# Whether to use GPL libraries or not. +USE_GPL_LIBS ?= 1 + +# Whether to install Julia as a framework on Darwin (Apple) platforms. +DARWIN_FRAMEWORK ?= 0 + +# Override in Make.user to customize the framework: +FRAMEWORK_NAME ?= Julia +FRAMEWORK_VERSION ?= $(JULIA_MAJOR_VERSION).$(JULIA_MINOR_VERSION) + +# The prefix for all code sign identifiers. +DARWIN_CODESIGN_ID_BASE ?= org.julialang.julia +# The codesign id for the ui/repl (also embedded in Info.plist). +darwin_codesign_id_julia_ui := $(DARWIN_CODESIGN_ID_BASE).ui +# The prefix for all deps. +darwin_codesign_id_julia_deps := $(DARWIN_CODESIGN_ID_BASE).deps + +# Directories and file structure of a Darwin framework: +framework_directory:=$(FRAMEWORK_NAME).framework +framework_versions:=$(framework_directory)/Versions +framework_currver:=$(framework_versions)/$(FRAMEWORK_VERSION) +framework_dylib:=$(framework_currver)/$(FRAMEWORK_NAME) +framework_headers:=$(framework_currver)/Headers +framework_documentation:=$(framework_currver)/Documentation +framework_resources:=$(framework_currver)/Resources +framework_frameworks:=$(framework_currver)/Frameworks +framework_modules:=$(framework_currver)/Modules +framework_helpers:=$(framework_currver)/Helpers +framework_infoplist:=$(framework_resources)/Info.plist + +# Directories where said libraries get installed to +prefix ?= $(BUILDROOT)/julia-$(JULIA_COMMIT) +ifeq ($(DARWIN_FRAMEWORK), 1) +bindir := $(prefix)/$(framework_helpers) +libdir := $(prefix)/$(framework_currver) +libexecdir := $(prefix)/$(framework_helpers) +datarootdir := $(prefix)/$(framework_resources) +docdir := $(prefix)/$(framework_documentation) +mandir := $(datarootdir)/man +man1dir := $(mandir)/man1 +includedir := $(prefix)/$(framework_headers) +sysconfdir := $(prefix)/$(framework_resources) +else +bindir := $(prefix)/bin +libdir := $(prefix)/lib +libexecdir := $(prefix)/libexec +datarootdir := $(prefix)/share +docdir := $(datarootdir)/doc/julia +mandir := $(datarootdir)/man +man1dir := $(mandir)/man1 +includedir := $(prefix)/include +sysconfdir := $(prefix)/etc +endif + +# Directories where things get built into +build_prefix := $(BUILDROOT)/usr$(MAYBE_HOST) +ifeq ($(BUILDING_HOST_TOOLS), 1) +build_staging := $(BUILDROOT)/usr-host-staging +else +build_staging := $(build_prefix)-staging +endif +build_bindir := $(build_prefix)/bin +build_depsbindir := $(build_prefix)/tools +build_libdir := $(build_prefix)/lib +build_libexecdir := $(build_prefix)/libexec +build_datarootdir := $(build_prefix)/share +build_mandir := $(build_datarootdir)/man +build_man1dir := $(build_mandir)/man1 +build_includedir := $(build_prefix)/include +build_sysconfdir := $(build_prefix)/etc + +# This used for debian packaging, to conform to library layout guidelines +ifeq ($(MULTIARCH_INSTALL), 1) +MULTIARCH := $(shell gcc -print-multiarch) +libdir := $(prefix)/lib/$(MULTIARCH) +build_libdir := $(build_prefix)/lib/$(MULTIARCH) +endif + +# Private library directories +ifeq ($(DARWIN_FRAMEWORK), 1) +private_libdir := $(prefix)/$(framework_frameworks) +else +private_libdir := $(libdir)/julia +endif +build_private_libdir := $(build_libdir)/julia + +# Calculate relative paths to libdir, private_libdir, datarootdir, and sysconfdir +build_libdir_rel := $(shell $(JULIAHOME)/contrib/relative_path.sh $(build_bindir) $(build_libdir)) +libdir_rel := $(shell $(JULIAHOME)/contrib/relative_path.sh $(bindir) $(libdir)) +build_private_libdir_rel := $(shell $(JULIAHOME)/contrib/relative_path.sh $(build_bindir) $(build_private_libdir)) +private_libdir_rel := $(shell $(JULIAHOME)/contrib/relative_path.sh $(bindir) $(private_libdir)) +datarootdir_rel := $(shell $(JULIAHOME)/contrib/relative_path.sh $(bindir) $(datarootdir)) +libexecdir_rel := $(shell $(JULIAHOME)/contrib/relative_path.sh $(bindir) $(libexecdir)) +docdir_rel := $(shell $(JULIAHOME)/contrib/relative_path.sh $(bindir) $(docdir)) +sysconfdir_rel := $(shell $(JULIAHOME)/contrib/relative_path.sh $(bindir) $(sysconfdir)) +includedir_rel := $(shell $(JULIAHOME)/contrib/relative_path.sh $(bindir) $(includedir)) + +INSTALL_F := $(JULIAHOME)/contrib/install.sh 644 +INSTALL_M := $(JULIAHOME)/contrib/install.sh 755 + +# LLVM Options +LLVMROOT := $(build_prefix) +LLVM_ASSERTIONS := 0 +LLVM_DEBUG := 0 +# set to 1 to get clang and compiler-rt +BUILD_LLVM_CLANG := 0 +# set to 1 to get lldb (often does not work, no chance with llvm3.2 and earlier) +# see http://lldb.llvm.org/build.html for dependencies +BUILD_LLDB := 0 + +# Options to enable Polly and its code-generation options +USE_POLLY := 0 +USE_POLLY_OPENMP := 0 # Enable OpenMP code-generation +USE_POLLY_ACC := 0 # Enable GPU code-generation + +# Cross-compile +#XC_HOST := i686-w64-mingw32 +#XC_HOST := x86_64-w64-mingw32 + +# Path to cmake (override in Make.user if needed) +CMAKE ?= cmake +CMAKE_GENERATOR ?= make + +# Point pkg-config to only look at our libraries, overriding whatever +# the user may have unwittingly set. To pass PKG_CONFIG_* variables +# through to the buildsystem, these must be set either on the command +# line, or through `override` directives within Make.user +export PKG_CONFIG_PATH = $(JULIAHOME)/usr/lib/pkgconfig +export PKG_CONFIG_LIBDIR = $(JULIAHOME)/usr/lib/pkgconfig + +# Figure out OS and architecture +BUILD_OS := $(shell uname) + +ifneq (,$(findstring CYGWIN,$(BUILD_OS))) +XC_HOST ?= $(shell uname -m)-w64-mingw32 +endif + +ifeq ($(XC_HOST),) +CROSS_COMPILE:= +# delayed expansion of $(CC), since it won't be computed until later +HOSTCC = $(CC) +else +HOSTCC ?= gcc +OPENBLAS_DYNAMIC_ARCH := 1 +override CROSS_COMPILE:=$(XC_HOST)- +ifneq (,$(findstring mingw,$(XC_HOST))) +override OS := WINNT +else ifneq (,$(findstring emscripten,$(XC_HOST))) +override OS := emscripten +override CROSS_COMPILE:= +else +ifeq (,$(OS)) +$(error "unknown XC_HOST variable set, please set OS") +endif +endif +endif + +JLDOWNLOAD := $(JULIAHOME)/deps/tools/jldownload +JLCHECKSUM := $(JULIAHOME)/deps/tools/jlchecksum + +# Figure out OS and architecture +OS := $(BUILD_OS) + +ifneq (,$(findstring MINGW,$(OS))) +override OS := WINNT +endif +ifneq (,$(findstring MINGW,$(BUILD_OS))) +override BUILD_OS := WINNT +endif +ifneq (,$(findstring MSYS,$(OS))) +override OS := WINNT +endif +ifneq (,$(findstring MSYS,$(BUILD_OS))) +override BUILD_OS := WINNT +endif + +ifeq ($(BUILD_OS), WINNT) +BUILD_EXE := .exe +else ifneq (,$(findstring CYGWIN,$(BUILD_OS))) +BUILD_EXE := .exe +else +BUILD_EXE := +endif +ifeq ($(OS), WINNT) +fPIC := +EXE := .exe +else +fPIC := -fPIC +EXE := +endif + +JULIACODEGEN := LLVM + +# flag for disabling assertions +ifeq ($(FORCE_ASSERTIONS), 1) +# C++ code needs to include LLVM header with the same assertion flag as LLVM +# Use this flag to re-enable assertion in our code after all the LLVM headers are included +CXX_DISABLE_ASSERTION := +DISABLE_ASSERTIONS := +else +CXX_DISABLE_ASSERTION := -DJL_NDEBUG +DISABLE_ASSERTIONS := -DNDEBUG -DJL_NDEBUG +endif + +# Compiler specific stuff + +ifeq ($(USEMSVC), 1) +USEGCC := 0 +USECLANG := 0 +USEICC := 0 +else +ifeq ($(USECLANG), 1) +USEGCC := 0 +USEICC := 0 +else +ifeq ($(USEICC), 1) +USEGCC := 0 +USECLANG := 0 +else # default to gcc +USEGCC := 1 +USECLANG := 0 +USEICC := 0 +endif +endif +endif + +ifeq ($(USEIFC), 1) +FC := ifort +else +FC := $(CROSS_COMPILE)gfortran +endif + +STDLIBCPP_FLAG := + +ifeq ($(OS), FreeBSD) +USEGCC := 0 +USECLANG := 1 +endif + +ifeq ($(OS), Darwin) +DARWINVER := $(shell uname -r | cut -b 1-2) +DARWINVER_GTE13 := $(shell expr `uname -r | cut -b 1-2` \>= 13) +ifeq ($(DARWINVER), 10) # Snow Leopard specific configuration +USEGCC := 1 +USECLANG := 0 +OPENBLAS_TARGET_ARCH:=NEHALEM +OPENBLAS_DYNAMIC_ARCH:=0 +USE_SYSTEM_LIBUNWIND:=1 +else +ifeq ($(DARWINVER_GTE13),1) +USE_LIBCPP := 1 +STDLIBCPP_FLAG := -stdlib=libstdc++ +else +USE_LIBCPP := 0 +endif +USEGCC := 0 +USECLANG := 1 +endif +endif + +ifeq ($(USEGCC),1) +ifeq ($(USE_LIBCPP),1) +$(error USE_LIBCPP only supported with clang. Try setting USE_LIBCPP=0) +endif +ifeq ($(SANITIZE),1) +$(error Address Sanitizer only supported with clang. Try setting SANITIZE=0) +endif +CC := $(CROSS_COMPILE)gcc +CXX := $(CROSS_COMPILE)g++ +JCFLAGS := -std=gnu99 -pipe $(fPIC) -fno-strict-aliasing -D_FILE_OFFSET_BITS=64 +# AArch64 needs this flag to generate the .eh_frame used by libunwind +JCPPFLAGS := -fasynchronous-unwind-tables +JCXXFLAGS := -pipe $(fPIC) -fno-rtti +ifneq ($(OS), WINNT) +# Do not enable on windows to avoid warnings from libuv. +JCXXFLAGS += -pedantic +endif +DEBUGFLAGS := -O0 -ggdb2 -DJL_DEBUG_BUILD -fstack-protector-all +SHIPFLAGS := -O3 -ggdb2 -falign-functions +endif + +ifeq ($(USECLANG),1) +CC := $(CROSS_COMPILE)clang +CXX := $(CROSS_COMPILE)clang++ +JCFLAGS := -pipe $(fPIC) -fno-strict-aliasing -D_FILE_OFFSET_BITS=64 +# AArch64 needs this flag to generate the .eh_frame used by libunwind +JCPPFLAGS := -fasynchronous-unwind-tables +JCXXFLAGS := -pipe $(fPIC) -fno-rtti -pedantic +DEBUGFLAGS := -O0 -g -DJL_DEBUG_BUILD -fstack-protector-all +SHIPFLAGS := -O3 -g +ifeq ($(OS), Darwin) +ifeq ($(USE_LIBCPP), 1) +MACOSX_VERSION_MIN := 10.8 +CC += -stdlib=libc++ -mmacosx-version-min=$(MACOSX_VERSION_MIN) +CXX += -stdlib=libc++ -mmacosx-version-min=$(MACOSX_VERSION_MIN) +FC += -mmacosx-version-min=$(MACOSX_VERSION_MIN) +else +MACOSX_VERSION_MIN := 10.6 +CC += $(STDLIBCPP_FLAG) -mmacosx-version-min=$(MACOSX_VERSION_MIN) +CXX += $(STDLIBCPP_FLAG) -mmacosx-version-min=$(MACOSX_VERSION_MIN) +endif +# export MACOSX_DEPLOYMENT_TARGET so that ld picks it up, especially for deps +export MACOSX_DEPLOYMENT_TARGET=$(MACOSX_VERSION_MIN) +JCPPFLAGS += -D_LARGEFILE_SOURCE -D_DARWIN_USE_64_BIT_INODE=1 +endif +endif + +ifeq ($(USEICC),1) +ifeq ($(USE_LIBCPP),1) +$(error USE_LIBCPP only supported with clang. Try setting USE_LIBCPP=0) +endif +ifeq ($(SANITIZE),1) +$(error Address Sanitizer only supported with clang. Try setting SANITIZE=0) +endif +CC := icc +CXX := icpc +JCFLAGS := -std=gnu11 -pipe $(fPIC) -fno-strict-aliasing -D_FILE_OFFSET_BITS=64 -fp-model precise -fp-model except -no-ftz +JCPPFLAGS := +JCXXFLAGS := -pipe $(fPIC) -fno-rtti +DEBUGFLAGS := -O0 -g -DJL_DEBUG_BUILD -fstack-protector-all +SHIPFLAGS := -O3 -g -falign-functions +endif + +ifeq ($(USECCACHE), 1) +# Expand CC, CXX and FC here already because we want the original definition and not the ccache version. +CC_ARG := $(CC) +CXX_ARG := $(CXX) +FC_ARG := $(FC) +# Expand CC, CXX and FC here already to avoid recursive referencing. +CC_FULL := ccache $(CC) +CXX_FULL := ccache $(CXX) +FC_FULL := ccache $(FC) +# Add an extra indirection to make CC/CXX/FC non-simple vars +# (because of how -m$(BINARY) is added later on). +CC := $(CC_FULL) +CXX := $(CXX_FULL) +FC := $(FC_FULL) +CC_BASE := ccache +CXX_BASE := ccache +FC_BASE := ccache +ifeq ($(USECLANG),1) +# ccache and Clang don't do well together +# http://petereisentraut.blogspot.be/2011/05/ccache-and-clang.html +CC += -Qunused-arguments +CXX += -Qunused-arguments +# http://petereisentraut.blogspot.be/2011/09/ccache-and-clang-part-2.html +export CCACHE_CPP2 := yes +endif +else #USECCACHE +CC_BASE := $(shell echo $(CC) | cut -d' ' -f1) +CC_ARG := $(shell echo $(CC) | cut -s -d' ' -f2-) +CXX_BASE := $(shell echo $(CXX) | cut -d' ' -f1) +CXX_ARG := $(shell echo $(CXX) | cut -s -d' ' -f2-) +FC_BASE := $(shell echo $(FC) | cut -d' ' -f1) +FC_ARG := $(shell echo $(FC) | cut -s -d' ' -f2-) +endif + +JFFLAGS := -O2 $(fPIC) +ifneq ($(USEMSVC),1) +CPP := $(CC) -E +AR := $(CROSS_COMPILE)ar +AS := $(CROSS_COMPILE)as +LD := $(CROSS_COMPILE)ld +else #USEMSVC +CPP := $(CC) -EP +AR := lib +ifeq ($(ARCH),x86_64) +AS := ml64 +else +AS := ml +endif #ARCH +LD := link +endif #USEMSVC +RANLIB := $(CROSS_COMPILE)ranlib +OBJCOPY := $(CROSS_COMPILE)objcopy + +# file extensions +ifeq ($(OS), WINNT) + SHLIB_EXT := dll +else ifeq ($(OS), Darwin) + SHLIB_EXT := dylib +else + SHLIB_EXT := so +endif + +# On Windows, we want shared library files to end up in $(build_bindir), instead of $(build_libdir) +ifeq ($(OS),WINNT) +build_shlibdir := $(build_bindir) +else +build_shlibdir := $(build_libdir) +endif + +ifeq ($(OS), FreeBSD) +LOCALBASE ?= /usr/local +else +LOCALBASE ?= /usr +endif + +ifeq (exists, $(shell [ -e $(JULIAHOME)/$(MAKE_USER_FNAME) ] && echo exists )) +include $(JULIAHOME)/$(MAKE_USER_FNAME) +endif +ifeq (exists, $(shell [ -e $(BUILDROOT)/$(MAKE_USER_FNAME) ] && echo exists )) +include $(BUILDROOT)/$(MAKE_USER_FNAME) +endif + +# A bit of a kludge to work around libraries linking to FreeBSD's outdated system libgcc_s +# Instead, let's link to the libgcc_s corresponding to the installation of gfortran +ifeq ($(OS),FreeBSD) +ifneq (,$(findstring gfortran,$(FC))) + +# First let's figure out what version of GCC we're dealing with +_GCCMAJOR := $(shell $(FC) -dumpversion | cut -d'.' -f1) +_GCCMINOR := $(shell $(FC) -dumpversion | cut -d'.' -f2) + +# The ports system uses major and minor for GCC < 5 (e.g. gcc49 for GCC 4.9), otherwise major only +ifeq ($(_GCCMAJOR),4) + _GCCVER := $(_GCCMAJOR)$(_GCCMINOR) +else + _GCCVER := $(_GCCMAJOR) +endif + +# Allow the user to specify this in Make.user +GCCPATH ?= $(LOCALBASE)/lib/gcc$(_GCCVER) + +# We're going to copy over the libraries we need from GCCPATH into build_libdir, then +# tell everyone to look for them there. At install time, the build_libdir added into +# the RPATH here is removed by patchelf. +LDFLAGS += -L$(build_libdir) -Wl,-rpath,$(build_libdir) + +endif # gfortran +endif # FreeBSD + +ifneq ($(CC_BASE)$(CXX_BASE),$(shell echo $(CC) | cut -d' ' -f1)$(shell echo $(CXX) | cut -d' ' -f1)) + $(error Forgot override directive on CC or CXX in Make.user? Cowardly refusing to build) +endif + +ifeq ($(DARWIN_FRAMEWORK),1) +ifneq ($(OS), Darwin) + $(error Darwin framework cannot be enabled for non-Darwin OS) +endif +endif + +ifeq ($(SANITIZE),1) +ifeq ($(SANITIZE_MEMORY),1) +SANITIZE_OPTS := -fsanitize=memory -fsanitize-memory-track-origins -fno-omit-frame-pointer +SANITIZE_LDFLAGS := $(SANITIZE_OPTS) +else +SANITIZE_OPTS := -fsanitize=address -mllvm -asan-stack=0 +SANITIZE_LDFLAGS := -fsanitize=address +endif +JCXXFLAGS += $(SANITIZE_OPTS) +JCFLAGS += $(SANITIZE_OPTS) +JLDFLAGS += $(SANITIZE_LDFLAGS) +endif + +TAR := $(shell which gtar 2>/dev/null || which tar 2>/dev/null) +TAR_TEST := $(shell $(TAR) --help 2>&1 | egrep 'bsdtar|strip-components') +ifeq (,$(findstring components,$(TAR_TEST))) +ifneq (bsdtar,$(findstring bsdtar,$(TAR_TEST))) +$(error "please install either GNU tar or bsdtar") +endif +endif + +ifeq ($(WITH_GC_VERIFY), 1) +JCXXFLAGS += -DGC_VERIFY +JCFLAGS += -DGC_VERIFY +endif + +ifeq ($(WITH_GC_DEBUG_ENV), 1) +JCXXFLAGS += -DGC_DEBUG_ENV +JCFLAGS += -DGC_DEBUG_ENV +endif + +# =========================================================================== + +# Select the cpu architecture to target, or automatically detects the user's compiler +# ARCH is the first element of the triple, and gives the CPU class (e.g. x86_64) +# MARCH is the CPU type, and accepts anything that can be passed to the gcc -march flag +# it is set equal to ARCH (for cases where the two are the same, such as i686) +# it can be set to native to optimize all code for the user's machine (not just the JIT code) +# if MARCH is set newer than the native processor, be forewarned that the compile might fail +# JULIA_CPU_TARGET is the JIT-only complement to MARCH. Setting it explicitly is not generally necessary, +# since it is set equal to MARCH by default + +BUILD_MACHINE := $(shell $(HOSTCC) -dumpmachine) + +# Clang spells mingw `-windows-gnu`, but autotools, etc +# don't recognize that, so canonicalize to mingw32 +BUILD_MACHINE := $(subst windows-gnu,mingw32,$(BUILD_MACHINE)) + +ifeq ($(ARCH),) +override ARCH := $(shell $(CC) -dumpmachine | sed "s/\([^-]*\).*$$/\1/") +else +ifneq ($(XC_HOST),) +XC_HOST := $(ARCH)$(shell echo $(XC_HOST) | sed "s/[^-]*\(.*\)$$/\1/") +MARCH := $(subst _,-,$(ARCH)) +else # insert ARCH into HOST +XC_HOST := $(ARCH)$(shell echo $(BUILD_MACHINE) | sed "s/[^-]*\(.*\)$$/\1/") +endif +endif + +ifeq ($(ARCH),mingw32) +$(error "the mingw32 compiler you are using fails the openblas testsuite. please see the README.windows document for a replacement") +else ifeq (cygwin, $(shell $(CC) -dumpmachine | cut -d\- -f3)) +$(error "cannot build julia with cygwin-target compilers. set XC_HOST to i686-w64-mingw32 or x86_64-w64-mingw32 for mingw cross-compile") +else ifeq (msys, $(shell $(CC) -dumpmachine | cut -d\- -f3)) +$(error "cannot build julia with msys-target compilers. please see the README.windows document for instructions on setting up mingw-w64 compilers") +endif + +ifeq ($(BUILD_OS),Darwin) +## Mac is a rather cool 64-bit user-space on 32-bit kernel architecture, so to determine arch we detect +## the capabilities of the hardware, rather than the compiler or kernel, and make a substitution +BUILD_ARCH := $(shell echo $(BUILD_MACHINE) | sed "s/\([^-]*\).*$$/\1/") +ifeq ($(BUILD_ARCH),x86_64) +BUILD_ARCH := i686 +else ifeq ($(BUILD_ARCH),i386) +BUILD_ARCH := i686 +endif +ifeq ($(BUILD_ARCH),i686) +ifeq ($(shell sysctl -n hw.cpu64bit_capable),1) +BUILD_ARCH := x86_64 +endif +BUILD_MACHINE := $(BUILD_ARCH)$(shell echo $(BUILD_MACHINE) | sed "s/[^-]*\(.*\)$$/\1/") +endif +ifeq ($(BUILD_OS),$(OS)) +ARCH := $(BUILD_OS) +endif +endif + +# Detect common pre-SSE2 JULIA_CPU_TARGET values known not to work (#7185) +ifeq ($(MARCH),) +ifneq ($(findstring $(ARCH),i386 i486 i586 i686),) +MARCH := pentium4 +endif +endif + +ifneq ($(findstring $(MARCH),i386 i486 i586 i686 pentium pentium2 pentium3),) +$(error Pre-SSE2 CPU targets not supported. To create a generic 32-bit x86 binary, \ +pass 'MARCH=pentium4'.) +endif + +# We map amd64 to x86_64 for compatibility with systems that identify 64-bit systems as such +ifeq ($(ARCH),amd64) +override ARCH := x86_64 +endif + +ifeq ($(ARCH),i386) +BINARY:=32 +ISX86:=1 +else ifeq ($(ARCH),i387) +BINARY:=32 +ISX86:=1 +else ifeq ($(ARCH),i486) +BINARY:=32 +ISX86:=1 +else ifeq ($(ARCH),i586) +BINARY:=32 +ISX86:=1 +else ifeq ($(ARCH),i686) +BINARY:=32 +ISX86:=1 +else ifeq ($(ARCH),x86_64) +BINARY:=64 +ISX86:=1 +else +# For all other architectures (ARM, PPC, AArch64, etc.) +ISX86:=0 +endif + +# If we are running on powerpc64le or ppc64le, set certain options automatically +ifneq (,$(filter $(ARCH), powerpc64le ppc64le)) +JCFLAGS += -fsigned-char +OPENBLAS_DYNAMIC_ARCH:=0 +OPENBLAS_TARGET_ARCH:=POWER8 +BINARY:=64 +# GCC doesn't do -march= on ppc64le +MARCH= +endif + +# If we are running on powerpc64 or ppc64, fail out dramatically +ifneq (,$(filter $(ARCH), powerpc64 ppc64)) +$(error Big-endian PPC64 is not supported, to ignore this error, set ARCH=ppc64le) +endif + +# File name of make binary-dist result +ifeq ($(JULIA_BINARYDIST_FILENAME),) +DIST_OS:=$(shell echo $(OS) | tr '[:upper:]' '[:lower:]') +ifeq (WINNT,$(OS)) +DIST_OS:=win +endif +ifeq (Linux,$(OS)) +DIST_OS:=linux +endif +ifeq (Darwin,$(OS)) +DIST_OS:=mac +endif +DIST_ARCH:=$(ARCH) +ifneq (,$(filter $(ARCH), powerpc64le ppc64le)) +DIST_ARCH:=ppc64le +endif +ifeq (1,$(ISX86)) +DIST_ARCH:=$(BINARY) +endif +ifneq (,$(findstring arm,$(ARCH))) +DIST_ARCH:=arm +endif + +JULIA_BINARYDIST_FILENAME := julia-$(JULIA_COMMIT)-$(DIST_OS)$(DIST_ARCH) +endif + +# If we are running on ARM, set certain options automatically +ifneq (,$(findstring arm,$(ARCH))) +JCFLAGS += -fsigned-char +USE_BLAS64:=0 +OPENBLAS_DYNAMIC_ARCH:=0 +OPENBLAS_TARGET_ARCH:=ARMV7 +endif + +# If we are running on aarch64 (e.g. ARMv8 or ARM64), set certain options automatically +ifneq (,$(findstring aarch64,$(ARCH))) +OPENBLAS_DYNAMIC_ARCH:=0 +OPENBLAS_TARGET_ARCH:=ARMV8 +endif + +# Set MARCH-specific flags +ifneq ($(MARCH),) +CC += -march=$(MARCH) +CXX += -march=$(MARCH) +FC += -march=$(MARCH) +JULIA_CPU_TARGET ?= $(MARCH) +ifeq ($(OS),Darwin) +# on Darwin, the standalone `as` program doesn't know +# how to handle AVX instructions, but it does know how +# to dispatch to the clang assembler (if we ask it to) +ifeq ($(USECLANG),1) +CC += -integrated-as +CXX += -integrated-as +else +CC += -Wa,-q +CXX += -Wa,-q +endif +FC += -Wa,-q +AS += -q +endif +endif + +JULIA_CPU_TARGET ?= native + +ifneq ($(OS),WINNT) +# Windows headers with this configuration conflicts with LLVM +# (Symbol renames are done with macros) +# We mainly need this on linux for cgmemmgr so don't worry about windows for now... +JCXXFLAGS += -D_FILE_OFFSET_BITS=64 +endif + +# Set some ARCH-specific flags +ifneq ($(USEICC),1) +ifeq ($(ISX86),1) +CC += -m$(BINARY) +CXX += -m$(BINARY) +FC += -m$(BINARY) +CC_ARG += -m$(BINARY) +CXX_ARG += -m$(BINARY) +FC_ARG += -m$(BINARY) +endif +endif + +ifeq ($(OS),WINNT) +ifneq ($(ARCH),x86_64) +ifneq ($(USECLANG),1) +JCFLAGS += -mincoming-stack-boundary=2 +JCXXFLAGS += -mincoming-stack-boundary=2 +endif +endif +endif + +ifeq ($(USEGCC),1) +ifeq ($(ISX86),1) + SHIPFLAGS += -momit-leaf-frame-pointer +endif +endif + +ifeq ($(OS),WINNT) +LIBUNWIND:= +else ifneq ($(DISABLE_LIBUNWIND), 0) +LIBUNWIND:= +else +ifeq ($(USE_SYSTEM_LIBUNWIND), 1) +ifneq ($(OS),Darwin) +LIBUNWIND:=-lunwind-generic -lunwind +# Only for linux since we want to use not yet released libunwind features +JCFLAGS+=-DSYSTEM_LIBUNWIND +JCPPFLAGS+=-DSYSTEM_LIBUNWIND +endif +else +ifeq ($(OS),Darwin) +LIBUNWIND:=$(build_libdir)/libosxunwind.a +JCPPFLAGS+=-DLIBOSXUNWIND +else +LIBUNWIND:=$(build_libdir)/libunwind-generic.a $(build_libdir)/libunwind.a +endif +endif +endif + +ifeq ($(origin LLVM_CONFIG), undefined) +ifeq ($(USE_SYSTEM_LLVM), 1) +LLVM_CONFIG := llvm-config$(EXE) +else +LLVM_CONFIG := $(build_depsbindir)/llvm-config$(EXE) +endif +endif # LLVM_CONFIG undefined + +ifeq ($(USE_SYSTEM_LLVM), 1) +JCPPFLAGS+=-DSYSTEM_LLVM +endif # SYSTEM_LLVM + +ifeq ($(BUILD_OS),$(OS)) +LLVM_CONFIG_HOST := $(LLVM_CONFIG) +else +LLVM_CONFIG_HOST := $(basename $(LLVM_CONFIG))-host$(BUILD_EXE) +ifeq (exists, $(shell [ -f '$(LLVM_CONFIG_HOST)' ] && echo exists )) +ifeq ($(shell $(LLVM_CONFIG_HOST) --version),3.3) +# llvm-config-host <= 3.3 is broken, use llvm-config instead (in an emulator) +# use delayed expansion (= not :=) because spawn isn't defined until later +LLVM_CONFIG_HOST = $(call spawn,$(LLVM_CONFIG)) +endif +else +# llvm-config-host does not exist (cmake build) +LLVM_CONFIG_HOST = $(call spawn,$(LLVM_CONFIG)) +endif +endif + +ifeq ($(USE_SYSTEM_PCRE), 1) +PCRE_CONFIG := pcre2-config +else +PCRE_CONFIG := $(build_depsbindir)/pcre2-config +endif + +ifeq ($(USE_SYSTEM_PATCHELF), 1) +PATCHELF := patchelf +else +PATCHELF := $(build_depsbindir)/patchelf +endif + +# On aarch64 and powerpc64le, we assume the page size is 64K. Our binutils linkers +# and such already assume this, but `patchelf` seems to be behind the times. We +# explicitly tell it to use this large page size so that when we rewrite rpaths and +# such, we don't accidentally create incorrectly-aligned sections in our ELF files. +ifneq (,$(filter $(ARCH),aarch64 powerpc64le)) +PATCHELF += --page-size=65536 +endif + +# Use ILP64 BLAS interface when building openblas from source on 64-bit architectures +ifeq ($(BINARY), 64) +ifeq ($(USE_SYSTEM_BLAS), 1) +ifeq ($(USE_INTEL_MKL), 1) +USE_BLAS64 ?= 1 +else # non MKL system blas is most likely LP64 +USE_BLAS64 ?= 0 +endif +else +USE_BLAS64 ?= 1 +endif +endif + +ifeq ($(USE_SYSTEM_BLAS), 1) +ifeq ($(OS), Darwin) +USE_BLAS64 := 0 +USE_SYSTEM_LAPACK := 0 +LIBBLAS := -L$(build_libdir) -lgfortblas +LIBBLASNAME := libgfortblas +else +LIBBLAS ?= -lblas +LIBBLASNAME ?= libblas +endif +else +LIBBLAS := -L$(build_shlibdir) -lopenblas +LIBBLASNAME := libopenblas +endif + +# OpenBLAS builds LAPACK as part of its build. +# We only need to build LAPACK if we are not using OpenBLAS. +ifeq ($(USE_SYSTEM_BLAS), 0) +LIBLAPACK := $(LIBBLAS) +LIBLAPACKNAME := $(LIBBLASNAME) +else +ifeq ($(USE_SYSTEM_LAPACK), 1) +LIBLAPACK ?= -llapack +LIBLAPACKNAME ?= liblapack +else +LIBLAPACK := -L$(build_shlibdir) -llapack $(LIBBLAS) +LIBLAPACKNAME := liblapack +endif +endif + +ifeq ($(USE_SYSTEM_LIBM), 1) +LIBM := -lm +LIBMNAME := libm +else +LIBM := -lopenlibm +LIBMNAME := libopenlibm +endif + +ifeq ($(USE_SYSTEM_LIBUV), 1) + LIBUV := $(LOCALBASE)/lib/libuv-julia.a + LIBUV_INC := $(LOCALBASE)/include +else + LIBUV := $(build_libdir)/libuv.a + LIBUV_INC := $(build_includedir) +endif + +ifeq ($(USE_SYSTEM_UTF8PROC), 1) + LIBUTF8PROC := -lutf8proc + UTF8PROC_INC := $(LOCALBASE)/include +else + LIBUTF8PROC := $(build_libdir)/libutf8proc.a + UTF8PROC_INC := $(build_includedir) +endif + +# BinaryBuilder options. We default to "on" for all the projects listed in BB_PROJECTS, +# but only if contrib/normalize_triplet.py works for our requested triplet. +ifeq ($(shell python $(JULIAHOME)/contrib/normalize_triplet.py $(or $(XC_HOST),$(XC_HOST),$(BUILD_MACHINE)) >/dev/null 2>/dev/null; echo $$?),0) +USE_BINARYBUILDER ?= 1 +else +ifneq ($(shell python $(JULIAHOME)/contrib/normalize_triplet.py x86_64-linux-gnu),x86_64-linux-gnu) +$(warning python normalize_triplet.py appears to be non-functional, so BinaryBuilder disabled) +endif +USE_BINARYBUILDER ?= 0 +endif + +# This is the set of projects that BinaryBuilder dependencies are hooked up for. +BB_PROJECTS := OPENBLAS LLVM SUITESPARSE OPENLIBM GMP MBEDTLS LIBSSH2 MPFR CURL LIBGIT2 PCRE LIBUV LIBUNWIND DSFMT OBJCONV ZLIB P7ZIP +define SET_BB_DEFAULT +# First, check to see if BB is disabled on a global setting +ifeq ($$(USE_BINARYBUILDER),0) +USE_BINARYBUILDER_$(1) ?= 0 +else +# If it's not, check to see if it's disabled by a USE_SYSTEM_xxx flag +ifeq ($$(USE_SYSTEM_$(1)),1) +USE_BINARYBUILDER_$(1) ?= 0 +else +USE_BINARYBUILDER_$(1) ?= 1 +endif +endif +endef +$(foreach proj,$(BB_PROJECTS),$(eval $(call SET_BB_DEFAULT,$(proj)))) + + + +# Use the Assertions build +BINARYBUILDER_LLVM_ASSERTS ?= 0 + + + +# OS specific stuff + +# install_name_tool +ifeq ($(OS), Darwin) + # must end with a / and have no trailing spaces + INSTALL_NAME_ID_DIR := @rpath/ + INSTALL_NAME_CMD := install_name_tool -id $(INSTALL_NAME_ID_DIR) + INSTALL_NAME_CHANGE_CMD := install_name_tool -change +ifneq (,$(findstring LLVM,$(shell dsymutil --version))) + DSYMUTIL := dsymutil +else ifeq ($(shell test `dsymutil -v | cut -d\- -f2 | cut -d. -f1` -gt 102 && echo yes), yes) + DSYMUTIL := dsymutil +else + DSYMUTIL := true -ignore +endif +else + INSTALL_NAME_ID_DIR := + INSTALL_NAME_CMD := true -ignore + INSTALL_NAME_CHANGE_CMD := true -ignore + DSYMUTIL := true -ignore +endif + +# shared library runtime paths +ifneq (,$(filter $(OS),WINNT emscripten)) + RPATH := + RPATH_ORIGIN := + RPATH_ESCAPED_ORIGIN := + RPATH_LIB := +else ifeq ($(OS), Darwin) + RPATH := -Wl,-rpath,'@executable_path/$(build_libdir_rel)' + RPATH_ORIGIN := -Wl,-rpath,'@loader_path/' + RPATH_ESCAPED_ORIGIN := $(RPATH_ORIGIN) + RPATH_LIB := -Wl,-rpath,'@loader_path/julia/' -Wl,-rpath,'@loader_path/' +else + RPATH := -Wl,-rpath,'$$ORIGIN/$(build_libdir_rel)' -Wl,-rpath,'$$ORIGIN/$(build_private_libdir_rel)' -Wl,-rpath-link,$(build_shlibdir) -Wl,-z,origin + RPATH_ORIGIN := -Wl,-rpath,'$$ORIGIN' -Wl,-z,origin + RPATH_ESCAPED_ORIGIN := -Wl,-rpath,'\$$\$$ORIGIN' -Wl,-z,origin -Wl,-rpath-link,$(build_shlibdir) + RPATH_LIB := -Wl,-rpath,'$$ORIGIN/julia' -Wl,-rpath,'$$ORIGIN' -Wl,-z,origin +endif + +# --whole-archive +ifeq ($(OS), Darwin) + WHOLE_ARCHIVE := -Xlinker -all_load + NO_WHOLE_ARCHIVE := +else ifneq ($(USEMSVC), 1) + WHOLE_ARCHIVE := -Wl,--whole-archive + NO_WHOLE_ARCHIVE := -Wl,--no-whole-archive +endif + +ifeq ($(OS), Linux) +OSLIBS += -Wl,--no-as-needed -ldl -lrt -lpthread -Wl,--export-dynamic,--as-needed,--no-whole-archive +# Detect if ifunc is supported +IFUNC_DETECT_SRC := 'void (*f0(void))(void) { return (void(*)(void))0L; }; void f(void) __attribute__((ifunc("f0")));' +ifeq (supported, $(shell echo $(IFUNC_DETECT_SRC) | $(CC) -Werror -x c - -S -o /dev/null > /dev/null 2>&1 && echo supported)) +JCPPFLAGS += -DJULIA_HAS_IFUNC_SUPPORT=1 +endif +JLDFLAGS := -Wl,-Bdynamic +ifneq ($(SANITIZE),1) +ifneq ($(SANITIZE_MEMORY),1) +ifneq ($(LLVM_SANITIZE),1) +OSLIBS += -Wl,--version-script=$(JULIAHOME)/src/julia.expmap +JLDFLAGS += -Wl,-no-undefined +endif +endif +endif +ifeq (-Bsymbolic-functions, $(shell $(LD) --help | grep -o -e "-Bsymbolic-functions")) +JLIBLDFLAGS := -Wl,-Bsymbolic-functions +else +JLIBLDFLAGS := +endif +else ifneq ($(OS), Darwin) +JLIBLDFLAGS := +endif + +ifeq ($(OS), FreeBSD) +JLDFLAGS := -Wl,-Bdynamic +OSLIBS += -lelf -lkvm -lrt -lpthread + +# Tweak order of libgcc_s in DT_NEEDED, +# make it loaded first to +# prevent from linking to outdated system libs. +# See #21788 +OSLIBS += -lgcc_s + +OSLIBS += -Wl,--export-dynamic -Wl,--version-script=$(JULIAHOME)/src/julia.expmap \ + $(NO_WHOLE_ARCHIVE) $(LIBUNWIND) +endif + +ifeq ($(OS), Darwin) +SHLIB_EXT := dylib +OSLIBS += -framework CoreFoundation $(LIBUNWIND) +WHOLE_ARCHIVE := -Xlinker -all_load +NO_WHOLE_ARCHIVE := +JLDFLAGS := +HAVE_SSP := 1 +JLIBLDFLAGS := -Wl,-compatibility_version,$(SOMAJOR) -Wl,-current_version,$(JULIA_MAJOR_VERSION).$(JULIA_MINOR_VERSION).$(JULIA_PATCH_VERSION) +endif + +ifeq ($(OS), WINNT) +ifneq ($(USEMSVC), 1) +HAVE_SSP := 1 +OSLIBS += -Wl,--export-all-symbols -Wl,--version-script=$(JULIAHOME)/src/julia.expmap \ + $(NO_WHOLE_ARCHIVE) -lpsapi -lkernel32 -lws2_32 -liphlpapi -lwinmm -ldbghelp -luserenv -lsecur32 +JLDFLAGS := -Wl,--stack,8388608 +ifeq ($(ARCH),i686) +JLDFLAGS += -Wl,--large-address-aware +endif +else #USEMSVC +OSLIBS += kernel32.lib ws2_32.lib psapi.lib advapi32.lib iphlpapi.lib shell32.lib winmm.lib userenv.lib secur32.lib +JLDFLAGS := -stack:8388608 +endif +JCPPFLAGS += -D_WIN32_WINNT=0x0502 +UNTRUSTED_SYSTEM_LIBM := 1 +endif + +# Threads +ifneq ($(JULIA_THREADS), 0) +JCPPFLAGS += -DJULIA_NUM_THREADS=$(JULIA_THREADS) +endif + +# Intel VTune Amplifier +ifeq ($(USE_INTEL_JITEVENTS), 1) +JCPPFLAGS += -DJL_USE_INTEL_JITEVENTS +endif + +# OProfile +ifeq ($(USE_OPROFILE_JITEVENTS), 1) +JCPPFLAGS += -DJL_USE_OPROFILE_JITEVENTS +endif + +ifeq ($(DISABLE_LIBUNWIND), 1) +JCPPFLAGS += -DJL_DISABLE_LIBUNWIND +endif + +# perf +ifeq ($(USE_PERF_JITEVENTS), 1) +JCPPFLAGS += -DJL_USE_PERF_JITEVENTS +endif + + +# Intel libraries + +ifeq ($(USE_INTEL_LIBM), 1) +USE_SYSTEM_LIBM := 1 +LIBM := -L$(MKLROOT)/../compiler/lib/intel64 -limf +LIBMNAME := libimf +endif + +ifeq ($(USE_INTEL_MKL), 1) +ifeq ($(USE_BLAS64), 1) +export MKL_INTERFACE_LAYER := ILP64 +MKLLIB := $(MKLROOT)/lib/intel64 +else +MKLLIB := $(MKLROOT)/lib/ia32 +endif +USE_SYSTEM_BLAS:=1 +USE_SYSTEM_LAPACK:=1 +LIBBLASNAME := libmkl_rt +LIBLAPACKNAME := libmkl_rt +MKL_LDFLAGS := -L$(MKLLIB) -lmkl_rt +ifneq ($(strip $(MKLLIB)),) + ifeq ($(OS), Linux) + RPATH_MKL := -Wl,-rpath,$(MKLLIB) + RPATH += $(RPATH_MKL) + MKL_LDFLAGS += $(RPATH_MKL) + endif +endif +LIBBLAS := $(MKL_LDFLAGS) +LIBLAPACK := $(MKL_LDFLAGS) +endif + +ifeq ($(HAVE_SSP),1) +JCPPFLAGS += -DHAVE_SSP=1 +ifeq ($(USEGCC),1) +OSLIBS += -lssp +endif +endif + +# ATLAS + +# ATLAS must have been previously installed to usr/lib/libatlas +# (built as a shared library, for your platform, single threaded) +USE_ATLAS := 0 +ATLAS_LIBDIR := $(build_libdir) +#or ATLAS_LIBDIR := /path/to/system/atlas/lib + +ifeq ($(USE_ATLAS), 1) +USE_BLAS64 := 0 +USE_SYSTEM_BLAS := 1 +USE_SYSTEM_LAPACK := 1 +LIBBLAS := -L$(ATLAS_LIBDIR) -lsatlas +LIBLAPACK := $(LIBBLAS) +LIBBLASNAME := libsatlas +LIBLAPACKNAME := $(LIBBLASNAME) +endif + +# Renaming OpenBLAS symbols, see #4923 and #8734 +ifeq ($(USE_SYSTEM_BLAS), 0) +ifeq ($(USE_BLAS64), 1) +OPENBLAS_SYMBOLSUFFIX := 64_ +OPENBLAS_LIBNAMESUFFIX := 64_ +LIBBLAS := -L$(build_shlibdir) -lopenblas$(OPENBLAS_LIBNAMESUFFIX) +LIBLAPACK := $(LIBBLAS) +LIBBLASNAME := $(LIBBLASNAME)$(OPENBLAS_LIBNAMESUFFIX) +LIBLAPACKNAME := $(LIBBLASNAME) +endif +endif + +# Custom libcxx +ifeq ($(BUILD_CUSTOM_LIBCXX),1) +LDFLAGS += -L$(build_libdir) +CXXLDFLAGS += -L$(build_libdir) -lc++abi -lc++ +ifeq ($(USECLANG),1) +CXXLDFLAGS += -stdlib=libc++ +else +ifeq ($(USEGCC),1) +$(error BUILD_CUSTOM_LIBCXX is currently only supported with Clang. Try setting BUILD_CUSTOM_LIBCXX=0) +endif +endif # Clang +CUSTOM_LD_LIBRARY_PATH := LD_LIBRARY_PATH="$(build_libdir)" +ifeq ($(USEICC),1) +CXXFLAGS += -cxxlib-nostd -static-intel +CLDFLAGS += -static-intel +LDFLAGS += -cxxlib-nostd -static-intel +endif +endif + +# Some special restrictions on BB usage: +ifeq ($(USE_SYSTEM_BLAS),1) +# Since the names don't line up (`BLAS` vs. `OPENBLAS`), manually gate: +USE_BINARYBUILDER_OPENBLAS := 0 +# Disable BB SuiteSparse if we're using system BLAS +USE_BINARYBUILDER_SUITESPARSE := 0 +endif + +ifeq ($(USE_SYSTEM_LIBM),1) +# If we're using system libm, disable BB OpenLibm +USE_BINARYBUILDER_OPENLIBM := 0 +endif + +# Make tricks + +define dir_target +$$(abspath $(1)): + @mkdir -p $$@ +endef + +ifeq ($(BUILD_OS), WINNT) +define mingw_to_dos +$(subst /,\\,$(subst $(shell $(2) pwd),$(shell $(2) cmd //C cd),$(abspath $(1)))) +endef +endif + +define symlink_target # (from, to-dir, to-name) +CLEAN_TARGETS += clean-$$(abspath $(2)/$(3)) +clean-$$(abspath $(2)/$(3)): +ifeq ($(BUILD_OS), WINNT) + -cmd //C rmdir $$(call mingw_to_dos,$(2)/$(3),cd $(2) &&) +else + -rm -r $$(abspath $(2)/$(3)) +endif +$$(abspath $(2)/$(3)): | $$(abspath $(2)) +ifeq ($$(BUILD_OS), WINNT) + @cmd //C mklink //J $$(call mingw_to_dos,$(2)/$(3),cd $(2) &&) $$(call mingw_to_dos,$(1),) +else ifneq (,$$(findstring CYGWIN,$$(BUILD_OS))) + @cmd /C mklink /J $$(call cygpath_w,$(2)/$(3)) $$(call cygpath_w,$(1)) +else ifdef JULIA_VAGRANT_BUILD + @rm -r $$@ + @cp -R $$(abspath $(1)) $$@.tmp + @mv $$@.tmp $$@ +else + @ln -sf $$(abspath $(1)) $$@ +endif +endef + +# many of the following targets must be = not := because the expansion of the makefile functions (and $1) shouldn't happen until later +ifeq ($(BUILD_OS), WINNT) # MSYS +spawn = $(1) +cygpath_w = $(1) +else ifneq (,$(findstring CYGWIN,$(BUILD_OS))) # Cygwin +spawn = $(1) +cygpath_w = `cygpath -w $(1)` +else +ifeq ($(OS), WINNT) # unix-to-Windows cross-compile +spawn = wine $(1) +cygpath_w = `winepath -w $(1)` +else # not Windows +spawn = $(1) +cygpath_w = $(1) +endif +endif + +exec = $(shell $(call spawn,$(1))) + +JULIA_BUILD_MODE := release +JULIA_LIBSUFFIX:= +ifeq (,$(findstring release,$(MAKECMDGOALS))) +ifneq (,$(findstring debug,$(MAKECMDGOALS))) +JULIA_BUILD_MODE := debug +JULIA_LIBSUFFIX:=-debug +endif +endif + +JULIA_EXECUTABLE_debug := $(build_bindir)/julia-debug$(EXE) +JULIA_EXECUTABLE_release := $(build_bindir)/julia$(EXE) +JULIA_EXECUTABLE := $(JULIA_EXECUTABLE_$(JULIA_BUILD_MODE)) + +# Colors for make +ifndef VERBOSE +VERBOSE := 0 +endif + +WARNCOLOR:="\033[33;1m" +ENDCOLOR:="\033[0m" + +ifeq ($(VERBOSE), 0) + +QUIET_MAKE = -s + +CCCOLOR:="\033[34m" +LINKCOLOR:="\033[34;1m" +PERLCOLOR:="\033[35m" +FLISPCOLOR:="\033[32m" +JULIACOLOR:="\033[32;1m" + +SRCCOLOR:="\033[33m" +BINCOLOR:="\033[37;1m" +JULCOLOR:="\033[34;1m" + +GOAL=$(subst ','\'',$(subst $(abspath $(JULIAHOME))/,,$(abspath $@))) + +PRINT_CC = printf ' %b %b\n' $(CCCOLOR)CC$(ENDCOLOR) $(SRCCOLOR)$(GOAL)$(ENDCOLOR); $(1) +PRINT_ANALYZE = printf ' %b %b\n' $(CCCOLOR)ANALYZE$(ENDCOLOR) $(SRCCOLOR)$(GOAL)$(ENDCOLOR); $(1) +PRINT_LINK = printf ' %b %b\n' $(LINKCOLOR)LINK$(ENDCOLOR) $(BINCOLOR)$(GOAL)$(ENDCOLOR); $(1) +PRINT_PERL = printf ' %b %b\n' $(PERLCOLOR)PERL$(ENDCOLOR) $(BINCOLOR)$(GOAL)$(ENDCOLOR); $(1) +PRINT_FLISP = printf ' %b %b\n' $(FLISPCOLOR)FLISP$(ENDCOLOR) $(BINCOLOR)$(GOAL)$(ENDCOLOR); $(1) +PRINT_JULIA = printf ' %b %b\n' $(JULIACOLOR)JULIA$(ENDCOLOR) $(BINCOLOR)$(GOAL)$(ENDCOLOR); $(1) + +else +QUIET_MAKE = +PRINT_CC = echo '$(subst ','\'',$(1))'; $(1) +PRINT_ANALYZE = echo '$(subst ','\'',$(1))'; $(1) +PRINT_LINK = echo '$(subst ','\'',$(1))'; $(1) +PRINT_PERL = echo '$(subst ','\'',$(1))'; $(1) +PRINT_FLISP = echo '$(subst ','\'',$(1))'; $(1) +PRINT_JULIA = echo '$(subst ','\'',$(1))'; $(1) + +endif + +define newline # a literal \n + + +endef + +# Makefile debugging trick: +# call print-VARIABLE to see the runtime value of any variable +# (hardened against any special characters appearing in the output) +print-%: + @echo '$*=$(subst ','\'',$(subst $(newline),\n,$($*)))' + +# Literal values that are hard to use in Makefiles otherwise: +COMMA:=, +SPACE:=$(eval) $(eval) diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..575f5e3 --- /dev/null +++ b/Makefile @@ -0,0 +1,618 @@ +JULIAHOME := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) +include $(JULIAHOME)/Make.inc + +VERSDIR := v`cut -d. -f1-2 < $(JULIAHOME)/VERSION` + +default: $(JULIA_BUILD_MODE) # contains either "debug" or "release" +all: debug release + +# sort is used to remove potential duplicates +DIRS := $(sort $(build_bindir) $(build_depsbindir) $(build_libdir) $(build_private_libdir) $(build_libexecdir) $(build_includedir) $(build_includedir)/julia $(build_sysconfdir)/julia $(build_datarootdir)/julia $(build_datarootdir)/julia/stdlib $(build_man1dir)) +ifneq ($(BUILDROOT),$(JULIAHOME)) +BUILDDIRS := $(BUILDROOT) $(addprefix $(BUILDROOT)/,base src src/flisp src/support src/clangsa ui doc deps stdlib test test/embedding test/llvmpasses) +BUILDDIRMAKE := $(addsuffix /Makefile,$(BUILDDIRS)) $(BUILDROOT)/sysimage.mk +DIRS := $(DIRS) $(BUILDDIRS) +$(BUILDDIRMAKE): | $(BUILDDIRS) + @# add Makefiles to the build directories for convenience (pointing back to the source location of each) + @echo '# -- This file is automatically generated in julia/Makefile -- #' > $@ + @echo 'BUILDROOT=$(BUILDROOT)' >> $@ + @echo 'include $(JULIAHOME)$(patsubst $(BUILDROOT)%,%,$@)' >> $@ +julia-deps: | $(BUILDDIRMAKE) +configure-y: | $(BUILDDIRMAKE) +configure: +ifeq ("$(origin O)", "command line") + @if [ "$$(ls '$(BUILDROOT)' 2> /dev/null)" ]; then \ + echo 'WARNING: configure called on non-empty directory $(BUILDROOT)'; \ + read -p "Proceed [y/n]? " answer; \ + else \ + answer=y;\ + fi; \ + [ $$answer = 'y' ] && $(MAKE) configure-$$answer +else + $(error "cannot rerun configure from within a build directory") +endif +else +configure: + $(error "must specify O=builddir to run the Julia `make configure` target") +endif + +$(foreach dir,$(DIRS),$(eval $(call dir_target,$(dir)))) +$(foreach link,base $(JULIAHOME)/test,$(eval $(call symlink_target,$(link),$$(build_datarootdir)/julia,$(notdir $(link))))) + +julia_flisp.boot.inc.phony: julia-deps + @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT)/src julia_flisp.boot.inc.phony + +# Build the HTML docs (skipped if already exists, notably in tarballs) +$(BUILDROOT)/doc/_build/html/en/index.html: $(shell find $(BUILDROOT)/base $(BUILDROOT)/doc \( -path $(BUILDROOT)/doc/_build -o -path $(BUILDROOT)/doc/deps -o -name *_constants.jl -o -name *_h.jl -o -name version_git.jl \) -prune -o -type f -print) + @$(MAKE) docs + +julia-symlink: julia-ui-$(JULIA_BUILD_MODE) +ifeq ($(OS),WINNT) + @echo '@"%~dp0"\'"$(shell $(JULIAHOME)/contrib/relative_path.sh "$(BUILDROOT)" "$(JULIA_EXECUTABLE)" | tr / '\\')" '%*' > $(BUILDROOT)/julia.bat + chmod a+x $(BUILDROOT)/julia.bat +else +ifndef JULIA_VAGRANT_BUILD + @ln -sf "$(shell $(JULIAHOME)/contrib/relative_path.sh "$(BUILDROOT)" "$(JULIA_EXECUTABLE)")" $(BUILDROOT)/julia +endif +endif + +julia-deps: | $(DIRS) $(build_datarootdir)/julia/base $(build_datarootdir)/julia/test + @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT)/deps + +julia-stdlib: | $(DIRS) + @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT)/stdlib + +julia-base: julia-deps $(build_sysconfdir)/julia/startup.jl $(build_man1dir)/julia.1 $(build_datarootdir)/julia/julia-config.jl + @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT)/base + +julia-libccalltest: julia-deps + @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT)/src libccalltest + +julia-libllvmcalltest: julia-deps + @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT)/src libllvmcalltest + +julia-src-release julia-src-debug : julia-src-% : julia-deps julia_flisp.boot.inc.phony + @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT)/src libjulia-$* + +julia-ui-release julia-ui-debug : julia-ui-% : julia-src-% + @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT)/ui julia-$* + +julia-sysimg-ji : julia-stdlib julia-base julia-ui-$(JULIA_BUILD_MODE) | $(build_private_libdir) + @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT) -f sysimage.mk sysimg-ji JULIA_EXECUTABLE='$(JULIA_EXECUTABLE)' + +julia-sysimg-release julia-sysimg-debug : julia-sysimg-% : julia-sysimg-ji julia-ui-% + @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT) -f sysimage.mk sysimg-$* + +julia-debug julia-release : julia-% : julia-sysimg-% julia-symlink julia-libccalltest julia-libllvmcalltest julia-base-cache + +debug release : % : julia-% + +docs: julia-sysimg-$(JULIA_BUILD_MODE) + @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT)/doc JULIA_EXECUTABLE='$(call spawn,$(JULIA_EXECUTABLE_$(JULIA_BUILD_MODE)))' + +check-whitespace: +ifneq ($(NO_GIT), 1) + @$(JULIAHOME)/contrib/check-whitespace.sh +else + $(warn "Skipping whitespace check because git is unavailable") +endif + +release-candidate: release testall + @$(JULIA_EXECUTABLE) $(JULIAHOME)/contrib/add_license_to_files.jl #add license headers + @#Check documentation + @$(JULIA_EXECUTABLE) $(JULIAHOME)/doc/NEWS-update.jl #Add missing cross-references to NEWS.md + @$(MAKE) -C $(BUILDROOT)/doc html doctest=true linkcheck=true + @$(MAKE) -C $(BUILDROOT)/doc pdf + + @# Check to see if the above make invocations changed anything important + @if [ -n "$$(git status --porcelain)" ]; then \ + echo "Git repository dirty; Verify and commit changes to the repository, then retry"; \ + exit 1; \ + fi + + @#Check that netload tests work + @#for test in test/netload/*.jl; do julia $$test; if [ $$? -ne 0 ]; then exit 1; fi; done + @echo + @echo To complete the release candidate checklist: + @echo + + @echo 1. Remove deprecations in base/deprecated.jl + @echo 2. Update references to the julia version in the source directories, such as in README.md + @echo 3. Bump VERSION + @echo 4. Increase SOMAJOR and SOMINOR if needed. + @echo 5. Create tag, push to github "\(git tag v\`cat VERSION\` && git push --tags\)" #"` # These comments deal with incompetent syntax highlighting rules + @echo 6. Clean out old .tar.gz files living in deps/, "\`git clean -fdx\`" seems to work #"` + @echo 7. Replace github release tarball with tarballs created from make light-source-dist and make full-source-dist + @echo 8. Check that 'make && make install && make test' succeed with unpacked tarballs even without Internet access. + @echo 9. Follow packaging instructions in DISTRIBUTING.md to create binary packages for all platforms + @echo 10. Upload to AWS, update https://julialang.org/downloads and http://status.julialang.org/stable links + @echo 11. Update checksums on AWS for tarball and packaged binaries + @echo 12. Announce on mailing lists + @echo 13. Change master to release-0.X in base/version.jl and base/version_git.sh as in 4cb1e20 + @echo + +$(build_man1dir)/julia.1: $(JULIAHOME)/doc/man/julia.1 | $(build_man1dir) + @echo Copying in usr/share/man/man1/julia.1 + @mkdir -p $(build_man1dir) + @cp $< $@ + +$(build_sysconfdir)/julia/startup.jl: $(JULIAHOME)/etc/startup.jl | $(build_sysconfdir)/julia + @echo Creating usr/etc/julia/startup.jl + @cp $< $@ + +$(build_datarootdir)/julia/julia-config.jl: $(JULIAHOME)/contrib/julia-config.jl | $(build_datarootdir)/julia + $(INSTALL_M) $< $(dir $@) + +$(build_depsbindir)/stringreplace: $(JULIAHOME)/contrib/stringreplace.c | $(build_depsbindir) + @$(call PRINT_CC, $(HOSTCC) -o $(build_depsbindir)/stringreplace $(JULIAHOME)/contrib/stringreplace.c) + +julia-base-cache: julia-sysimg-$(JULIA_BUILD_MODE) | $(DIRS) $(build_datarootdir)/julia + @$(call spawn,JULIA_BINDIR=$(call cygpath_w,$(build_bindir)) $(JULIA_EXECUTABLE) --startup-file=no $(call cygpath_w,$(JULIAHOME)/etc/write_base_cache.jl) \ + $(call cygpath_w,$(build_datarootdir)/julia/base.cache)) + +# public libraries, that are installed in $(prefix)/lib +JL_TARGETS := julia +ifeq ($(BUNDLE_DEBUG_LIBS),1) +JL_TARGETS += julia-debug +endif + +# private libraries, that are installed in $(prefix)/lib/julia +JL_PRIVATE_LIBS-0 := libccalltest libllvmcalltest +ifeq ($(USE_GPL_LIBS), 1) +JL_PRIVATE_LIBS-0 += libsuitesparse_wrapper +JL_PRIVATE_LIBS-$(USE_SYSTEM_SUITESPARSE) += libamd libcamd libccolamd libcholmod libcolamd libumfpack libspqr libsuitesparseconfig +endif +JL_PRIVATE_LIBS-$(USE_SYSTEM_PCRE) += libpcre2-8 +JL_PRIVATE_LIBS-$(USE_SYSTEM_DSFMT) += libdSFMT +JL_PRIVATE_LIBS-$(USE_SYSTEM_GMP) += libgmp +JL_PRIVATE_LIBS-$(USE_SYSTEM_MPFR) += libmpfr +JL_PRIVATE_LIBS-$(USE_SYSTEM_LIBSSH2) += libssh2 +JL_PRIVATE_LIBS-$(USE_SYSTEM_MBEDTLS) += libmbedtls libmbedcrypto libmbedx509 +JL_PRIVATE_LIBS-$(USE_SYSTEM_CURL) += libcurl +JL_PRIVATE_LIBS-$(USE_SYSTEM_LIBGIT2) += libgit2 +ifeq ($(OS),WINNT) +JL_PRIVATE_LIBS-$(USE_SYSTEM_ZLIB) += zlib +else +JL_PRIVATE_LIBS-$(USE_SYSTEM_ZLIB) += libz +endif +ifeq ($(USE_LLVM_SHLIB),1) +JL_PRIVATE_LIBS-$(USE_SYSTEM_LLVM) += libLLVM libLLVM-6 +endif + +ifeq ($(USE_SYSTEM_LIBM),0) +JL_PRIVATE_LIBS-$(USE_SYSTEM_OPENLIBM) += libopenlibm +endif + +JL_PRIVATE_LIBS-$(USE_SYSTEM_BLAS) += $(LIBBLASNAME) +ifneq ($(LIBLAPACKNAME),$(LIBBLASNAME)) +JL_PRIVATE_LIBS-$(USE_SYSTEM_LAPACK) += $(LIBLAPACKNAME) +endif + +ifeq ($(OS),Darwin) +ifeq ($(USE_SYSTEM_BLAS),1) +ifeq ($(USE_SYSTEM_LAPACK),0) +JL_PRIVATE_LIBS-0 += libgfortblas +endif +endif +endif + +# On FreeBSD, /lib/libgcc_s.so.1 is incompatible with Fortran; to use Fortran on FreeBSD, +# we need to link to the libgcc_s that ships with the same GCC version used by libgfortran. +# To work around this, we copy the GCC libraries we need, namely libgfortran, libgcc_s, +# and libquadmath, into our build library directory, $(build_libdir). We also add them to +# JL_PRIVATE_LIBS-0 so that they know where they need to live at install time. +ifeq ($(OS),FreeBSD) +define std_so +julia-deps: | $$(build_libdir)/$(1).so +$$(build_libdir)/$(1).so: | $$(build_libdir) + $$(INSTALL_M) $$(GCCPATH)/$(1).so* $$(build_libdir) +JL_PRIVATE_LIBS-0 += $(1) +endef + +$(eval $(call std_so,libgfortran)) +$(eval $(call std_so,libgcc_s)) +$(eval $(call std_so,libquadmath)) +endif # FreeBSD + +ifeq ($(OS),WINNT) +# find the standard .dll folders +ifeq ($(XC_HOST),) +STD_LIB_PATH ?= $(PATH) +else +STD_LIB_PATH := $(shell LANG=C $(CC) -print-search-dirs | grep programs | sed -e "s/^programs: =//") +STD_LIB_PATH += :$(shell LANG=C $(CC) -print-search-dirs | grep libraries | sed -e "s/^libraries: =//") +ifneq (,$(findstring CYGWIN,$(BUILD_OS))) # the cygwin-mingw32 compiler lies about it search directory paths +STD_LIB_PATH := $(shell echo '$(STD_LIB_PATH)' | sed -e "s!/lib/!/bin/!g") +endif +endif + +pathsearch = $(firstword $(wildcard $(addsuffix /$(1),$(subst :, ,$(2))))) + +define std_dll +julia-deps-libs: | $$(build_bindir)/lib$(1).dll $$(build_depsbindir)/lib$(1).dll +$$(build_bindir)/lib$(1).dll: | $$(build_bindir) + cp $$(or $$(call pathsearch,lib$(1).dll,$$(STD_LIB_PATH)),$$(error can't find lib$1.dll)) $$(build_bindir) +$$(build_depsbindir)/lib$(1).dll: | $$(build_depsbindir) + cp $$(or $$(call pathsearch,lib$(1).dll,$$(STD_LIB_PATH)),$$(error can't find lib$1.dll)) $$(build_depsbindir) +JL_TARGETS += $(1) +endef +julia-deps: julia-deps-libs + +# Given a list of space-separated libraries, return the first library name that is +# correctly found through `pathsearch`. +define select_std_dll +$(firstword $(foreach name,$(1),$(if $(call pathsearch,lib$(name).dll,$(STD_LIB_PATH)),$(name),))) +endef + +$(eval $(call std_dll,$(call select_std_dll,gfortran-3 gfortran-4 gfortran-5))) +$(eval $(call std_dll,quadmath-0)) +$(eval $(call std_dll,stdc++-6)) +ifeq ($(ARCH),i686) +$(eval $(call std_dll,gcc_s_sjlj-1)) +else +$(eval $(call std_dll,gcc_s_seh-1)) +endif +$(eval $(call std_dll,ssp-0)) +$(eval $(call std_dll,winpthread-1)) +$(eval $(call std_dll,atomic-1)) +endif + + +define stringreplace + $(build_depsbindir)/stringreplace $$(strings -t x - $1 | grep '$2' | awk '{print $$1;}') '$3' 255 "$(call cygpath_w,$1)" +endef + +# Run fixup-libgfortran on all platforms but Windows and FreeBSD. On FreeBSD we +# pull in the GCC libraries earlier and use them for the build to make sure we +# don't inadvertently link to /lib/libgcc_s.so.1, which is incompatible with +# libgfortran, and on Windows we copy them in earlier as well. +ifeq (,$(findstring $(OS),FreeBSD WINNT)) +julia-base: $(build_libdir)/libgfortran*.$(SHLIB_EXT)* +$(build_libdir)/libgfortran*.$(SHLIB_EXT)*: | $(build_libdir) julia-deps + -$(CUSTOM_LD_LIBRARY_PATH) PATH="$(PATH):$(build_depsbindir)" PATCHELF="$(PATCHELF)" $(JULIAHOME)/contrib/fixup-libgfortran.sh --verbose $(build_libdir) +JL_PRIVATE_LIBS-0 += libgfortran libgcc_s libquadmath +endif + + +install: $(build_depsbindir)/stringreplace $(BUILDROOT)/doc/_build/html/en/index.html +ifeq ($(BUNDLE_DEBUG_LIBS),1) + @$(MAKE) $(QUIET_MAKE) all +else + @$(MAKE) $(QUIET_MAKE) release +endif + @for subdir in $(bindir) $(datarootdir)/julia/stdlib/$(VERSDIR) $(docdir) $(man1dir) $(includedir)/julia $(libdir) $(private_libdir) $(sysconfdir) $(libexecdir); do \ + mkdir -p $(DESTDIR)$$subdir; \ + done + + $(INSTALL_M) $(build_bindir)/julia $(DESTDIR)$(bindir)/ +ifeq ($(BUNDLE_DEBUG_LIBS),1) + $(INSTALL_M) $(build_bindir)/julia-debug $(DESTDIR)$(bindir)/ +endif +ifeq ($(OS),WINNT) + -$(INSTALL_M) $(filter-out $(build_bindir)/libjulia-debug.dll,$(wildcard $(build_bindir)/*.dll)) $(DESTDIR)$(bindir)/ + -$(INSTALL_M) $(build_libdir)/libjulia.dll.a $(DESTDIR)$(libdir)/ + + # We have a single exception; we want 7z.dll to live in libexec, not bin, so that 7z.exe can find it. + -mv $(DESTDIR)$(bindir)/7z.dll $(DESTDIR)$(libexecdir)/ +ifeq ($(BUNDLE_DEBUG_LIBS),1) + -$(INSTALL_M) $(build_bindir)/libjulia-debug.dll $(DESTDIR)$(bindir)/ + -$(INSTALL_M) $(build_libdir)/libjulia-debug.dll.a $(DESTDIR)$(libdir)/ +endif + -$(INSTALL_M) $(build_bindir)/libopenlibm.dll.a $(DESTDIR)$(libdir)/ +else + +# Copy over .dSYM directories directly for Darwin +ifneq ($(DARWIN_FRAMEWORK),1) +ifeq ($(OS),Darwin) + -cp -a $(build_libdir)/libjulia.*.dSYM $(DESTDIR)$(libdir) + -cp -a $(build_private_libdir)/sys.dylib.dSYM $(DESTDIR)$(private_libdir) +ifeq ($(BUNDLE_DEBUG_LIBS),1) + -cp -a $(build_libdir)/libjulia-debug.*.dSYM $(DESTDIR)$(libdir) + -cp -a $(build_private_libdir)/sys-debug.dylib.dSYM $(DESTDIR)$(private_libdir) +endif +endif + + for suffix in $(JL_TARGETS) ; do \ + for lib in $(build_libdir)/lib$${suffix}.*$(SHLIB_EXT)*; do \ + if [ "$${lib##*.}" != "dSYM" ]; then \ + $(INSTALL_M) $$lib $(DESTDIR)$(libdir) ; \ + fi \ + done \ + done +else + # libjulia in Darwin framework has special location and name + $(INSTALL_M) $(build_libdir)/libjulia.$(SOMAJOR).$(SOMINOR).dylib $(DESTDIR)$(prefix)/$(framework_dylib) + @$(DSYMUTIL) -o $(DESTDIR)$(prefix)/$(framework_resources)/$(FRAMEWORK_NAME).dSYM $(DESTDIR)$(prefix)/$(framework_dylib) + @$(DSYMUTIL) -o $(DESTDIR)$(prefix)/$(framework_resources)/sys.dylib.dSYM $(build_private_libdir)/sys.dylib +ifeq ($(BUNDLE_DEBUG_LIBS),1) + $(INSTALL_M) $(build_libdir)/libjulia-debug.$(SOMAJOR).$(SOMINOR).dylib $(DESTDIR)$(prefix)/$(framework_dylib)_debug + @$(DSYMUTIL) -o $(DESTDIR)$(prefix)/$(framework_resources)/$(FRAMEWORK_NAME)_debug.dSYM $(DESTDIR)$(prefix)/$(framework_dylib)_debug + @$(DSYMUTIL) -o $(DESTDIR)$(prefix)/$(framework_resources)/sys-debug.dylib.dSYM $(build_private_libdir)/sys-debug.dylib +endif +endif + + for suffix in $(JL_PRIVATE_LIBS-0) ; do \ + for lib in $(build_libdir)/$${suffix}.*$(SHLIB_EXT)*; do \ + if [ "$${lib##*.}" != "dSYM" ]; then \ + $(INSTALL_M) $$lib $(DESTDIR)$(private_libdir) ; \ + fi \ + done \ + done + for suffix in $(JL_PRIVATE_LIBS-1) ; do \ + lib=$(build_private_libdir)/$${suffix}.$(SHLIB_EXT); \ + $(INSTALL_M) $$lib $(DESTDIR)$(private_libdir) ; \ + done +endif + # Install `7z` into libexec/ + $(INSTALL_M) $(build_bindir)/7z$(EXE) $(DESTDIR)$(libexecdir)/ + + # Copy public headers + cp -R -L $(build_includedir)/julia/* $(DESTDIR)$(includedir)/julia + # Copy system image + $(INSTALL_M) $(build_private_libdir)/sys.$(SHLIB_EXT) $(DESTDIR)$(private_libdir) +ifeq ($(BUNDLE_DEBUG_LIBS),1) + $(INSTALL_M) $(build_private_libdir)/sys-debug.$(SHLIB_EXT) $(DESTDIR)$(private_libdir) +endif + + # Copy in all .jl sources as well + mkdir -p $(DESTDIR)$(datarootdir)/julia/base $(DESTDIR)$(datarootdir)/julia/test + cp -R -L $(JULIAHOME)/base/* $(DESTDIR)$(datarootdir)/julia/base + cp -R -L $(JULIAHOME)/test/* $(DESTDIR)$(datarootdir)/julia/test + cp -R -L $(build_datarootdir)/julia/* $(DESTDIR)$(datarootdir)/julia + # Copy documentation + cp -R -L $(BUILDROOT)/doc/_build/html $(DESTDIR)$(docdir)/ + # Remove various files which should not be installed + -rm -f $(DESTDIR)$(datarootdir)/julia/base/version_git.sh + -rm -f $(DESTDIR)$(datarootdir)/julia/test/Makefile + -rm -f $(DESTDIR)$(datarootdir)/julia/stdlib/$(VERSDIR)/*/source-extracted + -rm -f $(DESTDIR)$(datarootdir)/julia/stdlib/$(VERSDIR)/*/build-configured + -rm -f $(DESTDIR)$(datarootdir)/julia/stdlib/$(VERSDIR)/*/build-compiled + -rm -f $(DESTDIR)$(datarootdir)/julia/stdlib/$(VERSDIR)/*/build-checked + # Copy in beautiful new man page + $(INSTALL_F) $(build_man1dir)/julia.1 $(DESTDIR)$(man1dir)/ + # Copy icon and .desktop file + mkdir -p $(DESTDIR)$(datarootdir)/icons/hicolor/scalable/apps/ + $(INSTALL_F) $(JULIAHOME)/contrib/julia.svg $(DESTDIR)$(datarootdir)/icons/hicolor/scalable/apps/ + -touch -c $(DESTDIR)$(datarootdir)/icons/hicolor/ + mkdir -p $(DESTDIR)$(datarootdir)/applications/ + $(INSTALL_F) $(JULIAHOME)/contrib/julia.desktop $(DESTDIR)$(datarootdir)/applications/ + # Install appdata file + mkdir -p $(DESTDIR)$(datarootdir)/appdata/ + $(INSTALL_F) $(JULIAHOME)/contrib/julia.appdata.xml $(DESTDIR)$(datarootdir)/appdata/ + + # Update RPATH entries and JL_SYSTEM_IMAGE_PATH if $(private_libdir_rel) != $(build_private_libdir_rel) +ifneq ($(private_libdir_rel),$(build_private_libdir_rel)) +ifeq ($(OS), Darwin) +ifneq ($(DARWIN_FRAMEWORK),1) + for j in $(JL_TARGETS) ; do \ + install_name_tool -rpath @executable_path/$(build_private_libdir_rel) @executable_path/$(private_libdir_rel) $(DESTDIR)$(bindir)/$$j; \ + install_name_tool -add_rpath @executable_path/$(build_libdir_rel) @executable_path/$(libdir_rel) $(DESTDIR)$(bindir)/$$j; \ + done +endif +else ifneq (,$(findstring $(OS),Linux FreeBSD)) + for j in $(JL_TARGETS) ; do \ + $(PATCHELF) --set-rpath '$$ORIGIN/$(private_libdir_rel):$$ORIGIN/$(libdir_rel)' $(DESTDIR)$(bindir)/$$j; \ + done +endif + + # Overwrite JL_SYSTEM_IMAGE_PATH in julia library + if [ $(DARWIN_FRAMEWORK) == 0 ]; then \ + RELEASE_TARGET=$(DESTDIR)$(libdir)/libjulia.$(SHLIB_EXT); \ + DEBUG_TARGET=$(DESTDIR)$(libdir)/libjulia-debug.$(SHLIB_EXT); \ + else \ + RELEASE_TARGET=$(DESTDIR)$(prefix)/$(framework_dylib); \ + DEBUG_TARGET=$(DESTDIR)$(prefix)/$(framework_dylib)_debug; \ + fi; \ + $(call stringreplace,$${RELEASE_TARGET},sys.$(SHLIB_EXT)$$,$(private_libdir_rel)/sys.$(SHLIB_EXT)); \ + if [ $(BUNDLE_DEBUG_LIBS) == 1 ]; then \ + $(call stringreplace,$${DEBUG_TARGET},sys-debug.$(SHLIB_EXT)$$,$(private_libdir_rel)/sys-debug.$(SHLIB_EXT)); \ + fi; + +endif + # On FreeBSD, remove the build's libdir from each library's RPATH +ifeq ($(OS),FreeBSD) + $(JULIAHOME)/contrib/fixup-rpath.sh "$(PATCHELF)" $(DESTDIR)$(libdir) $(build_libdir) + $(JULIAHOME)/contrib/fixup-rpath.sh "$(PATCHELF)" $(DESTDIR)$(private_libdir) $(build_libdir) + $(JULIAHOME)/contrib/fixup-rpath.sh "$(PATCHELF)" $(DESTDIR)$(bindir) $(build_libdir) + # Set libgfortran's RPATH to ORIGIN instead of GCCPATH. It's only libgfortran that + # needs to be fixed here, as libgcc_s and libquadmath don't have RPATHs set. If we + # don't set libgfortran's RPATH, it won't be able to find its friends on systems + # that don't have the exact GCC port installed used for the build. + for lib in $(DESTDIR)$(private_libdir)/libgfortran*$(SHLIB_EXT)*; do \ + $(PATCHELF) --set-rpath '$$ORIGIN' $$lib; \ + done +endif + + mkdir -p $(DESTDIR)$(sysconfdir) + cp -R $(build_sysconfdir)/julia $(DESTDIR)$(sysconfdir)/ + +ifeq ($(DARWIN_FRAMEWORK),1) + $(MAKE) -C $(JULIAHOME)/contrib/mac/framework frameworknoinstall +endif + +distclean: + -rm -fr $(BUILDROOT)/julia-*.tar.gz $(BUILDROOT)/julia*.exe $(BUILDROOT)/julia-*.7z $(BUILDROOT)/julia-$(JULIA_COMMIT) + +binary-dist: distclean +ifeq ($(USE_SYSTEM_BLAS),0) +ifeq ($(ISX86),1) +ifneq ($(OPENBLAS_DYNAMIC_ARCH),1) + @echo OpenBLAS must be rebuilt with OPENBLAS_DYNAMIC_ARCH=1 to use binary-dist target + @false +endif +endif +endif +ifneq ($(prefix),$(abspath julia-$(JULIA_COMMIT))) + $(error prefix must not be set for make binary-dist) +endif +ifneq ($(DESTDIR),) + $(error DESTDIR must not be set for make binary-dist) +endif + @$(MAKE) -C $(BUILDROOT) -f $(JULIAHOME)/Makefile install + cp $(JULIAHOME)/LICENSE.md $(BUILDROOT)/julia-$(JULIA_COMMIT) +ifeq ($(OS), Linux) + -$(JULIAHOME)/contrib/fixup-libstdc++.sh $(DESTDIR)$(libdir) $(DESTDIR)$(private_libdir) + + # Copy over any bundled ca certs we picked up from the system during buildi + -cp $(build_datarootdir)/julia/cert.pem $(DESTDIR)$(datarootdir)/julia/ +endif + # Copy in startup.jl files per-platform for binary distributions as well + # Note that we don't install to sysconfdir: we always install to $(DESTDIR)$(prefix)/etc. + # If you want to make a distribution with a hardcoded path, you take care of installation +ifeq ($(OS), Darwin) + -cat $(JULIAHOME)/contrib/mac/startup.jl >> $(DESTDIR)$(prefix)/etc/julia/startup.jl +endif + +ifeq ($(OS), WINNT) + cd $(BUILDROOT)/julia-$(JULIA_COMMIT)/bin && rm -f llvm* llc.exe lli.exe opt.exe LTO.dll bugpoint.exe macho-dump.exe + + # create file listing for uninstall. note: must have Windows path separators and line endings. + cd $(BUILDROOT)/julia-$(JULIA_COMMIT) && find * | sed -e 's/\//\\/g' -e 's/$$/\r/g' > etc/uninstall.log + + # build nsis package + cd $(BUILDROOT) && $(call spawn,$(JULIAHOME)/dist-extras/nsis/makensis.exe) -NOCD -DVersion=$(JULIA_VERSION) -DArch=$(ARCH) -DCommit=$(JULIA_COMMIT) -DJULIAHOME="$(call cygpath_w,$(JULIAHOME))" $(call cygpath_w,$(JULIAHOME)/contrib/windows/build-installer.nsi) | iconv -f latin1 + + # compress nsis installer and combine with 7zip self-extracting header + cd $(BUILDROOT) && $(JULIAHOME)/usr/bin/7z a -mx=9 "julia-install-$(JULIA_COMMIT)-$(ARCH).7z" julia-installer.exe + cd $(BUILDROOT) && cat $(JULIAHOME)/contrib/windows/7zS.sfx $(JULIAHOME)/contrib/windows/7zSFX-config.txt "julia-install-$(JULIA_COMMIT)-$(ARCH).7z" > "$(JULIA_BINARYDIST_FILENAME).exe" + chmod a+x "$(BUILDROOT)/$(JULIA_BINARYDIST_FILENAME).exe" + -rm -f $(BUILDROOT)/julia-install-$(JULIA_COMMIT)-$(ARCH).7z + -rm -f $(BUILDROOT)/julia-installer.exe +else + cd $(BUILDROOT) && $(TAR) zcvf $(JULIA_BINARYDIST_FILENAME).tar.gz julia-$(JULIA_COMMIT) +endif + rm -fr $(BUILDROOT)/julia-$(JULIA_COMMIT) + +app: + $(MAKE) -C contrib/mac/app + @mv contrib/mac/app/$(JULIA_BINARYDIST_FILENAME).dmg $(BUILDROOT) + +darwinframework: + $(MAKE) -C $(JULIAHOME)/contrib/mac/framework + +light-source-dist.tmp: $(BUILDROOT)/doc/_build/html/en/index.html +ifneq ($(BUILDROOT),$(JULIAHOME)) + $(error make light-source-dist does not work in out-of-tree builds) +endif + # Save git information + -@$(MAKE) -C $(JULIAHOME)/base version_git.jl.phony + + # Create file light-source-dist.tmp to hold all the filenames that go into the tarball + echo "base/version_git.jl" > light-source-dist.tmp + + # Download all stdlibs and include the tarball filenames in light-source-dist.tmp + @$(MAKE) -C stdlib getall NO_GIT=1 + -ls stdlib/srccache/*.tar.gz >> light-source-dist.tmp + + # Exclude git, github and CI config files + git ls-files | sed -E -e '/^\..+/d' -e '/\/\..+/d' -e '/appveyor.yml/d' >> light-source-dist.tmp + find doc/_build/html >> light-source-dist.tmp + +# Make tarball with only Julia code + stdlib tarballs +light-source-dist: light-source-dist.tmp + # Prefix everything with "julia-$(commit-sha)/" or "julia-$(version)/" and then create tarball + # To achieve prefixing, we temporarily create a symlink in the source directory that points back + # to the source directory. + sed -e "s_.*_julia-${JULIA_COMMIT}/&_" light-source-dist.tmp > light-source-dist.tmp1 + ln -s . julia-${JULIA_COMMIT} + tar -cz --no-recursion -T light-source-dist.tmp1 -f julia-$(JULIA_VERSION)_$(JULIA_COMMIT).tar.gz + rm julia-${JULIA_COMMIT} + +source-dist: + @echo \'source-dist\' target is deprecated: use \'full-source-dist\' instead. + +# Make tarball with Julia code plus all dependencies +full-source-dist: light-source-dist.tmp + # Get all the dependencies downloaded + @$(MAKE) -C deps getall NO_GIT=1 + + # Create file full-source-dist.tmp to hold all the filenames that go into the tarball + cp light-source-dist.tmp full-source-dist.tmp + -ls deps/srccache/*.tar.gz deps/srccache/*.tar.bz2 deps/srccache/*.tar.xz deps/srccache/*.tgz deps/srccache/*.zip deps/srccache/*.pem >> full-source-dist.tmp + + # Prefix everything with "julia-$(commit-sha)/" or "julia-$(version)/" and then create tarball + # To achieve prefixing, we temporarily create a symlink in the source directory that points back + # to the source directory. + sed -e "s_.*_julia-${JULIA_COMMIT}/&_" full-source-dist.tmp > full-source-dist.tmp1 + ln -s . julia-${JULIA_COMMIT} + tar -cz --no-recursion -T full-source-dist.tmp1 -f julia-$(JULIA_VERSION)_$(JULIA_COMMIT)-full.tar.gz + rm julia-${JULIA_COMMIT} + +clean: | $(CLEAN_TARGETS) + @-$(MAKE) -C $(BUILDROOT)/base clean + @-$(MAKE) -C $(BUILDROOT)/doc clean + @-$(MAKE) -C $(BUILDROOT)/src clean + @-$(MAKE) -C $(BUILDROOT)/ui clean + @-$(MAKE) -C $(BUILDROOT)/test clean + @-$(MAKE) -C $(BUILDROOT)/stdlib clean + -rm -f $(BUILDROOT)/julia + -rm -f $(BUILDROOT)/*.tar.gz + -rm -f $(build_depsbindir)/stringreplace \ + $(BUILDROOT)/light-source-dist.tmp $(BUILDROOT)/light-source-dist.tmp1 \ + $(BUILDROOT)/full-source-dist.tmp $(BUILDROOT)/full-source-dist.tmp1 + -rm -fr $(build_private_libdir) +# Teporarily add this line to the Makefile to remove extras + -rm -fr $(build_datarootdir)/julia/extras + +cleanall: clean + @-$(MAKE) -C $(BUILDROOT)/src clean-flisp clean-support + @-$(MAKE) -C $(BUILDROOT)/deps clean-libuv + -rm -fr $(build_prefix) $(build_staging) + +distcleanall: cleanall + @-$(MAKE) -C $(BUILDROOT)/stdlib distclean + @-$(MAKE) -C $(BUILDROOT)/deps distcleanall + @-$(MAKE) -C $(BUILDROOT)/doc cleanall + +.PHONY: default debug release check-whitespace release-candidate \ + julia-debug julia-release julia-stdlib julia-deps julia-deps-libs \ + julia-ui-release julia-ui-debug julia-src-release julia-src-debug \ + julia-symlink julia-base julia-sysimg julia-sysimg-ji julia-sysimg-release julia-sysimg-debug \ + test testall testall1 test clean distcleanall cleanall clean-* \ + run-julia run-julia-debug run-julia-release run \ + install binary-dist light-source-dist.tmp light-source-dist \ + dist full-source-dist source-dist + +test: check-whitespace $(JULIA_BUILD_MODE) + @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT)/test default JULIA_BUILD_MODE=$(JULIA_BUILD_MODE) + +JULIA_SYSIMG=$(build_private_libdir)/sys$(JULIA_LIBSUFFIX).$(SHLIB_EXT) + +testall: check-whitespace $(JULIA_BUILD_MODE) + cp $(JULIA_SYSIMG) $(BUILDROOT)/local.$(SHLIB_EXT) + $(call spawn,$(JULIA_EXECUTABLE) -J $(call cygpath_w,$(BUILDROOT)/local.$(SHLIB_EXT)) -e 'true') + rm $(BUILDROOT)/local.$(SHLIB_EXT) + @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT)/test all JULIA_BUILD_MODE=$(JULIA_BUILD_MODE) + +testall1: check-whitespace $(JULIA_BUILD_MODE) + @env JULIA_CPU_THREADS=1 $(MAKE) $(QUIET_MAKE) -C $(BUILDROOT)/test all JULIA_BUILD_MODE=$(JULIA_BUILD_MODE) + +test-%: check-whitespace $(JULIA_BUILD_MODE) + @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT)/test $* JULIA_BUILD_MODE=$(JULIA_BUILD_MODE) + +# download target for some hardcoded windows dependencies +.PHONY: win-extras wine_path +win-extras: + @$(MAKE) -C $(BUILDROOT)/deps install-p7zip + mkdir -p $(JULIAHOME)/dist-extras + cd $(JULIAHOME)/dist-extras && \ + $(JLDOWNLOAD) https://sourceforge.net/projects/nsis/files/NSIS%203/3.04/nsis-3.04-setup.exe && \ + $(JLCHECKSUM) nsis-3.04-setup.exe && \ + $(call spawn,$(JULIAHOME)/usr/bin/7z.exe) x -y -onsis nsis-3.04-setup.exe && \ + chmod a+x ./nsis/makensis.exe + +# various statistics about the build that may interest the user +ifeq ($(USE_SYSTEM_LLVM), 1) +LLVM_SIZE := llvm-size$(EXE) +else +LLVM_SIZE := $(build_depsbindir)/llvm-size$(EXE) +endif +build-stats: + @echo $(JULCOLOR)' ==> ./julia binary sizes'$(ENDCOLOR) + $(call spawn,$(LLVM_SIZE) -A $(call cygpath_w,$(build_private_libdir)/sys.$(SHLIB_EXT)) \ + $(call cygpath_w,$(build_shlibdir)/libjulia.$(SHLIB_EXT)) \ + $(call cygpath_w,$(build_bindir)/julia$(EXE))) + @echo $(JULCOLOR)' ==> ./julia launch speedtest'$(ENDCOLOR) + @time $(call spawn,$(build_bindir)/julia$(EXE) -e '') + @time $(call spawn,$(build_bindir)/julia$(EXE) -e '') + @time $(call spawn,$(build_bindir)/julia$(EXE) -e '') diff --git a/NEWS.md b/NEWS.md new file mode 100644 index 0000000..7a5997b --- /dev/null +++ b/NEWS.md @@ -0,0 +1,175 @@ +Julia v1.3 Release Notes +======================== + +New language features +--------------------- + +* Support for Unicode 12.1.0 ([#32002]). +* Methods can now be added to an abstract type ([#31916]). +* Support for unicode bold digits and double-struck digits 0 through 9 as valid identifiers ([#32838]). +* Added the syntax `var"#str#"` for printing and parsing non-standard variable names ([#32408]). + +Language changes +---------------- + + +Multi-threading changes +----------------------- + +* New experimental `Threads.@spawn` macro that runs a task on any available thread ([#32600]). +* All system-level I/O operations (e.g. files and sockets) are now thread-safe. + This does not include subtypes of `IO` that are entirely in-memory, such as `IOBuffer`, + although it specifically does include `BufferStream`. + ([#32309], [#32174], [#31981], [#32421]). +* The global random number generator (`GLOBAL_RNG`) is now thread-safe (and thread-local) ([#32407]). +* New `Channel(f::Function, spawn=true)` keyword argument to schedule the created Task on + any available thread, matching the behavior of `Threads.@spawn` ([#32872]). +* Simplified the `Channel` constructor, which is now easier to read and more idiomatic julia. + Use of the keyword arguments `csize` and `ctype` is now discouraged ([#30855], [#32818]). + +Build system changes +-------------------- + + +New library functions +--------------------- + +* `findfirst`, `findlast`, `findnext` and `findprev` now accept a character as first argument + to search for that character in a string passed as the second argument ([#31664]). +* New `findall(pattern, string)` method where `pattern` is a string or regex ([#31834]). +* `count(pattern, string)` gives the number of things `findall` would match ([#32849]). +* `istaskfailed` is now documented and exported, like its siblings `istaskdone` and `istaskstarted` ([#32300]). +* `RefArray` and `RefValue` objects now accept index `CartesianIndex()` in `getindex` and `setindex!` ([#32653]) +* Added `sincosd(x)` to simultaneously compute the sine and cosine of `x`, where `x` is in degrees ([#30134]). +* The function `nonmissingtype`, which removes `Missing` from type unions, is now exported ([#31562]). + +Standard library changes +------------------------ + +* `Pkg` won't clobber pre-compilation files as often when switching environments ([#32651]) +* `Pkg` can now download and install binary artifacts through the `Pkg.Artifacts` + submodule and supporting functions. ([#32918]) +* When `wait` (or `@sync`, or `fetch`) is called on a failing `Task`, the exception is propagated as a + `TaskFailedException` wrapping the task. + This makes it possible to see the location of the original failure inside the task (as well as the + location of the `wait` call, as before) ([#32814]). +* `Regex` can now be multiplied (`*`) and exponentiated (`^`), like strings ([#23422]). +* `Cmd` interpolation (``` `$(x::Cmd) a b c` ``` where) now propagates `x`'s process flags + (environment, flags, working directory, etc) if `x` is the first interpolant and errors + otherwise ([#24353]). +* Zero-dimensional arrays are now consistently preserved in the return values of mathematical + functions that operate on the array(s) as a whole (and are not explicitly broadcasted across their elements). + Previously, the functions `+`, `-`, `*`, `/`, `conj`, `real` and `imag` returned the unwrapped element + when operating over zero-dimensional arrays ([#32122]). +* `IPAddr` subtypes now behave like scalars when used in broadcasting ([#32133]). +* `Pair` is now treated as a scalar for broadcasting ([#32209]). +* `clamp` can now handle missing values ([#31066]). +* `empty` now accepts a `NamedTuple` ([#32534]). +* `mod` now accepts a unit range as the second argument to easily perform offset modular arithmetic to ensure the result is inside the range ([#32628]). +* `nothing` can now be `print`ed, and interpolated into strings etc. as the string `"nothing"`. It is still not permitted to be interpolated into Cmds (i.e. ``echo `$(nothing)` `` will still error without running anything.) ([#32148]) +* When `open` is called with a function, command, and keyword argument (e.g. ```open(`ls`, read=true) do f ...```) + it now correctly throws a `ProcessFailedException` like other similar calls ([#32193]). +* `mktemp` and `mktempdir` now try, by default, to remove temporary paths they create before the process exits ([#32851]). +* Added argument `keep` to `unescape_string` ([#27125]). + +#### Libdl + +* `dlopen()` can now be invoked in `do`-block syntax, similar to `open()`. + +#### LinearAlgebra + +* The BLAS submodule no longer exports `dot`, which conflicts with that in LinearAlgebra ([#31838]). +* `diagm` and `spdiagm` now accept optional `m,n` initial arguments to specify a size ([#31654]). +* `Hessenberg` factorizations `H` now support efficient shifted solves `(H+µI) \ b` and determinants, and use a specialized tridiagonal factorization for Hermitian matrices. There is also a new `UpperHessenberg` matrix type ([#31853]). +* Added keyword argument `alg` to `svd` and `svd!` that allows one to switch between different SVD algorithms ([#31057]). +* Five-argument `mul!(C, A, B, α, β)` now implements inplace multiplication fused with addition _C = A B α + C β_ ([#23919]). + +#### SparseArrays + +* `SparseMatrixCSC(m,n,colptr,rowval,nzval)` perform consistency checks for arguments: + `colptr` must be properly populated and lengths of `colptr`, `rowval`, and `nzval` + must be compatible with `m`, `n`, and `eltype(colptr)`. +* `sparse(I, J, V, m, n)` verifies lengths of `I`, `J`, `V` are equal and compatible with + `eltype(I)` and `m`, `n`. + +#### Dates + +* `DateTime` and `Time` formatting/parsing now supports 12-hour clocks with AM/PM via `I` and `p` codes, similar to `strftime` ([#32308]). + +#### Statistics + +* `mean` now accepts both a function argument and a `dims` keyword ([#31576]). + +#### Sockets + +* `Sockets.recvfrom` now returns both host and port as an InetAddr ([#32729]). +* Added `InetAddr` constructor from `AbstractString`, representing IP address, and `Integer`, + representing port number ([#31459]). + +#### Miscellaneous + +* `foldr` and `mapfoldr` now work on any iterator that supports `Iterators.reverse`, not just arrays ([#31781]). + +Deprecated or removed +--------------------- + +* `@spawn expr` from the `Distributed` standard library should be replaced with `@spawnat :any expr` ([#32600]). +* `Threads.Mutex` and `Threads.RecursiveSpinLock` have been removed; use `ReentrantLock` (preferred) or + `Threads.SpinLock` instead ([#32875]). + +External dependencies +--------------------- + +Tooling Improvements +--------------------- + +* The `ClangSA.jl` static analysis package has been imported, which makes use of + the clang static analyzer to validate GC invariants in Julia's C code. The analysis + may be run using `make -C src analyzegc`. + + +[#23422]: https://github.com/JuliaLang/julia/issues/23422 +[#23919]: https://github.com/JuliaLang/julia/issues/23919 +[#24353]: https://github.com/JuliaLang/julia/issues/24353 +[#27125]: https://github.com/JuliaLang/julia/issues/27125 +[#30134]: https://github.com/JuliaLang/julia/issues/30134 +[#30855]: https://github.com/JuliaLang/julia/issues/30855 +[#31057]: https://github.com/JuliaLang/julia/issues/31057 +[#31066]: https://github.com/JuliaLang/julia/issues/31066 +[#31459]: https://github.com/JuliaLang/julia/issues/31459 +[#31562]: https://github.com/JuliaLang/julia/issues/31562 +[#31576]: https://github.com/JuliaLang/julia/issues/31576 +[#31654]: https://github.com/JuliaLang/julia/issues/31654 +[#31664]: https://github.com/JuliaLang/julia/issues/31664 +[#31781]: https://github.com/JuliaLang/julia/issues/31781 +[#31834]: https://github.com/JuliaLang/julia/issues/31834 +[#31838]: https://github.com/JuliaLang/julia/issues/31838 +[#31853]: https://github.com/JuliaLang/julia/issues/31853 +[#31916]: https://github.com/JuliaLang/julia/issues/31916 +[#31981]: https://github.com/JuliaLang/julia/issues/31981 +[#32002]: https://github.com/JuliaLang/julia/issues/32002 +[#32122]: https://github.com/JuliaLang/julia/issues/32122 +[#32133]: https://github.com/JuliaLang/julia/issues/32133 +[#32148]: https://github.com/JuliaLang/julia/issues/32148 +[#32174]: https://github.com/JuliaLang/julia/issues/32174 +[#32193]: https://github.com/JuliaLang/julia/issues/32193 +[#32209]: https://github.com/JuliaLang/julia/issues/32209 +[#32300]: https://github.com/JuliaLang/julia/issues/32300 +[#32308]: https://github.com/JuliaLang/julia/issues/32308 +[#32309]: https://github.com/JuliaLang/julia/issues/32309 +[#32407]: https://github.com/JuliaLang/julia/issues/32407 +[#32408]: https://github.com/JuliaLang/julia/issues/32408 +[#32421]: https://github.com/JuliaLang/julia/issues/32421 +[#32534]: https://github.com/JuliaLang/julia/issues/32534 +[#32600]: https://github.com/JuliaLang/julia/issues/32600 +[#32628]: https://github.com/JuliaLang/julia/issues/32628 +[#32653]: https://github.com/JuliaLang/julia/issues/32653 +[#32729]: https://github.com/JuliaLang/julia/issues/32729 +[#32814]: https://github.com/JuliaLang/julia/issues/32814 +[#32818]: https://github.com/JuliaLang/julia/issues/32818 +[#32838]: https://github.com/JuliaLang/julia/issues/32838 +[#32849]: https://github.com/JuliaLang/julia/issues/32849 +[#32851]: https://github.com/JuliaLang/julia/issues/32851 +[#32872]: https://github.com/JuliaLang/julia/issues/32872 +[#32875]: https://github.com/JuliaLang/julia/issues/32875 +[#32918]: https://github.com/JuliaLang/julia/issues/32918 diff --git a/README.md b/README.md new file mode 100644 index 0000000..1077267 --- /dev/null +++ b/README.md @@ -0,0 +1,149 @@ + + + +Build status: +[![travis][travis-img]](https://travis-ci.org/JuliaLang/julia) +[![appveyor][appveyor-img]](https://ci.appveyor.com/project/JuliaLang/julia/branch/master) + +Code coverage: +[![coveralls][coveralls-img]](https://coveralls.io/r/JuliaLang/julia?branch=master) +[![codecov][codecov-img]](https://codecov.io/github/JuliaLang/julia?branch=master) + +[travis-img]: https://img.shields.io/travis/JuliaLang/julia/master.svg?label=Linux+/+macOS +[appveyor-img]: https://img.shields.io/appveyor/ci/JuliaLang/julia/master.svg?label=Windows +[coveralls-img]: https://img.shields.io/coveralls/github/JuliaLang/julia/master.svg?label=coveralls +[codecov-img]: https://img.shields.io/codecov/c/github/JuliaLang/julia/master.svg?label=codecov + +## The Julia Language + +Julia is a high-level, high-performance dynamic language for technical +computing. The main homepage for Julia can be found at +[julialang.org](https://julialang.org/). This is the GitHub +repository of Julia source code, including instructions for compiling +and installing Julia, below. + +## Resources + +- **Homepage:** +- **Binaries:** +- **Source code:** +- **Documentation:** +- **Packages:** +- **Discussion forum:** +- **Slack:** (get an invite from ) +- **YouTube:** +- **Code coverage:** + +New developers may find the notes in +[CONTRIBUTING](https://github.com/JuliaLang/julia/blob/master/CONTRIBUTING.md) +helpful to start contributing to the Julia codebase. + +### External Resources + +- [**StackOverflow**](https://stackoverflow.com/questions/tagged/julia-lang) +- [**Twitter**](https://twitter.com/JuliaLanguage) +- [**Meetup**](https://julia.meetup.com/) + +## Binary Installation + +If you would rather not compile the latest Julia from source, +platform-specific tarballs with pre-compiled binaries are also +[available for download](https://julialang.org/downloads/). The +downloads page also provides details on the +[different tiers of support](https://julialang.org/downloads/#support-tiers) +for OS and platform combinations. + +If everything works correctly, you will see a Julia banner and an +interactive prompt into which you can enter expressions for +evaluation. You can read about [getting +started](https://julialang.org/manual/getting-started) in the manual. + +**Note**: Although some system package managers provide Julia, such +installations are neither maintained nor endorsed by the Julia +project. They may be outdated, broken and/or unmaintained. We +recommend you use the official Julia binaries instead. + +## Building Julia + +First, make sure you have all the [required +dependencies](https://github.com/JuliaLang/julia/blob/master/doc/build/build.md#required-build-tools-and-external-libraries) installed. +Then, acquire the source code by cloning the git repository: + + git clone git://github.com/JuliaLang/julia.git + +By default you will be building the latest unstable version of +Julia. However, most users should use the most recent stable version +of Julia. You can get this version by changing to the Julia directory +and running: + + git checkout v1.1.1 + +Now run `make` to build the `julia` executable. + +Building Julia requires 2GiB of disk space and approximately 4GiB of virtual memory. + +**Note:** The build process will fail badly if any of the build directory's parent directories have spaces or other shell meta-characters such as `$` or `:` in their names (this is due to a limitation in GNU make). + +Once it is built, you can run the `julia` executable after you enter your julia directory and run + + ./julia + +Your first test of Julia determines whether your build is working +properly. From the UNIX/Windows command prompt inside the `julia` +source directory, type `make testall`. You should see output that +lists a series of running tests; if they complete without error, you +should be in good shape to start using Julia. + +You can read about [getting +started](https://docs.julialang.org/en/v1/manual/getting-started/) +in the manual. + +In case this default build path did not work, detailed build instructions +are included in the [build documentation](https://github.com/JuliaLang/julia/blob/master/doc/build). + +### Uninstalling Julia + +Julia does not install anything outside the directory it was cloned +into. Julia can be completely uninstalled by deleting this +directory. Julia packages are installed in `~/.julia` by default, and +can be uninstalled by deleting `~/.julia`. + +## Source Code Organization + +The Julia source code is organized as follows: + + base/ source code for the Base module (part of Julia's standard library) + stdlib/ source code for other standard library packages + contrib/ editor support for Julia source, miscellaneous scripts + deps/ external dependencies + doc/src/manual source for the user manual + doc/build detailed notes for building Julia + src/ source for Julia language core + test/ test suites + ui/ source for various front ends + usr/ binaries and shared libraries loaded by Julia's standard libraries + +## Terminal, Editors and IDEs + +The Julia REPL is quite powerful. See the section in the manual on +[the Julia REPL](https://docs.julialang.org/en/latest/stdlib/REPL/) +for more details. + +Support for editing Julia is available for many +[widely used editors](https://github.com/JuliaEditorSupport): +[Emacs](https://github.com/JuliaEditorSupport/julia-emacs), +[Vim](https://github.com/JuliaEditorSupport/julia-vim), +[Sublime Text](https://github.com/JuliaEditorSupport/Julia-sublime), and many +others. + +Supported IDEs include: [Juno](http://junolab.org/) (Atom plugin), +[julia-vscode](https://github.com/JuliaEditorSupport/julia-vscode) (VS +Code plugin), and +[julia-intellij](https://github.com/JuliaEditorSupport/julia-intellij) +(IntelliJ IDEA plugin). The popular [Jupyter](https://jupyter.org/) +notebook interface is available through +[IJulia](https://github.com/JuliaLang/IJulia.jl). diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..3a3cd8c --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +1.3.1 diff --git a/base/.gitignore b/base/.gitignore new file mode 100644 index 0000000..49b6f3f --- /dev/null +++ b/base/.gitignore @@ -0,0 +1,9 @@ +/pcre_h.jl +/errno_h.jl +/build_h.jl +/build_h.jl.phony +/file_constants.jl +/uv_constants.jl +/version_git.jl +/version_git.jl.phony +/userimg.jl diff --git a/base/Base.jl b/base/Base.jl new file mode 100644 index 0000000..f521209 --- /dev/null +++ b/base/Base.jl @@ -0,0 +1,408 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +baremodule Base + +using Core.Intrinsics, Core.IR + +const is_primary_base_module = ccall(:jl_module_parent, Ref{Module}, (Any,), Base) === Core.Main +ccall(:jl_set_istopmod, Cvoid, (Any, Bool), Base, is_primary_base_module) + +# Try to help prevent users from shooting them-selves in the foot +# with ambiguities by defining a few common and critical operations +# (and these don't need the extra convert code) +getproperty(x::Module, f::Symbol) = getfield(x, f) +setproperty!(x::Module, f::Symbol, v) = setfield!(x, f, v) +getproperty(x::Type, f::Symbol) = getfield(x, f) +setproperty!(x::Type, f::Symbol, v) = setfield!(x, f, v) +getproperty(x::Tuple, f::Int) = getfield(x, f) +setproperty!(x::Tuple, f::Int, v) = setfield!(x, f, v) # to get a decent error + +getproperty(x, f::Symbol) = getfield(x, f) +setproperty!(x, f::Symbol, v) = setfield!(x, f, convert(fieldtype(typeof(x), f), v)) + +function include_relative end +function include(mod::Module, path::AbstractString) + local result + if INCLUDE_STATE === 1 + result = _include1(mod, path) + elseif INCLUDE_STATE === 2 + result = _include(mod, path) + elseif INCLUDE_STATE === 3 + result = include_relative(mod, path) + end + result +end +function include(path::AbstractString) + local result + if INCLUDE_STATE === 1 + result = _include1(Base, path) + elseif INCLUDE_STATE === 2 + result = _include(Base, path) + else + # to help users avoid error (accidentally evaluating into Base), this is not allowed + error("Base.include(string) is discontinued, use `include(fname)` or `Base.include(@__MODULE__, fname)` instead.") + end + result +end +const _included_files = Array{Tuple{Module,String},1}() +function _include1(mod::Module, path) + Core.Compiler.push!(_included_files, (mod, ccall(:jl_prepend_cwd, Any, (Any,), path))) + Core.include(mod, path) +end +let SOURCE_PATH = "" + # simple, race-y TLS, relative include + global _include + function _include(mod::Module, path) + prev = SOURCE_PATH + path = normpath(joinpath(dirname(prev), path)) + push!(_included_files, (mod, abspath(path))) + SOURCE_PATH = path + result = Core.include(mod, path) + SOURCE_PATH = prev + result + end +end +INCLUDE_STATE = 1 # include = Core.include + +include("coreio.jl") + +eval(x) = Core.eval(Base, x) +eval(m::Module, x) = Core.eval(m, x) + +# init core docsystem +import Core: @doc, @__doc__, WrappedException, @int128_str, @uint128_str, @big_str, @cmd +if isdefined(Core, :Compiler) + import Core.Compiler.CoreDocs + Core.atdoc!(CoreDocs.docm) +end + +include("exports.jl") + +if false + # simple print definitions for debugging. enable these if something + # goes wrong during bootstrap before printing code is available. + # otherwise, they just just eventually get (noisily) overwritten later + global show, print, println + show(io::IO, x) = Core.show(io, x) + print(io::IO, a...) = Core.print(io, a...) + println(io::IO, x...) = Core.println(io, x...) +end + +""" + time_ns() + +Get the time in nanoseconds. The time corresponding to 0 is undefined, and wraps every 5.8 years. +""" +time_ns() = ccall(:jl_hrtime, UInt64, ()) + +start_base_include = time_ns() + +## Load essential files and libraries +include("essentials.jl") +include("ctypes.jl") +include("gcutils.jl") +include("generator.jl") +include("reflection.jl") +include("options.jl") + +# core operations & types +include("promotion.jl") +include("tuple.jl") +include("expr.jl") +include("pair.jl") +include("traits.jl") +include("range.jl") +include("error.jl") + +# core numeric operations & types +include("bool.jl") +include("number.jl") +include("int.jl") +include("operators.jl") +include("pointer.jl") +include("refvalue.jl") +include("refpointer.jl") +include("checked.jl") +using .Checked + +# array structures +include("indices.jl") +include("array.jl") +include("abstractarray.jl") +include("subarray.jl") +include("views.jl") +include("baseext.jl") + +include("ntuple.jl") + +include("abstractdict.jl") + +include("iterators.jl") +using .Iterators: zip, enumerate +using .Iterators: Flatten, Filter, product # for generators + +include("namedtuple.jl") + +# numeric operations +include("hashing.jl") +include("rounding.jl") +using .Rounding +include("float.jl") +include("twiceprecision.jl") +include("complex.jl") +include("rational.jl") +include("multinverses.jl") +using .MultiplicativeInverses +include("abstractarraymath.jl") +include("arraymath.jl") + +# SIMD loops +include("simdloop.jl") +using .SimdLoop + +# map-reduce operators +include("reduce.jl") + +## core structures +include("reshapedarray.jl") +include("reinterpretarray.jl") +include("bitarray.jl") +include("bitset.jl") + +if !isdefined(Core, :Compiler) + include("docs/core.jl") + Core.atdoc!(CoreDocs.docm) +end + +include("multimedia.jl") +using .Multimedia + +# Some type +include("some.jl") + +include("dict.jl") +include("abstractset.jl") +include("set.jl") + +include("char.jl") +include("strings/basic.jl") +include("strings/string.jl") +include("strings/substring.jl") + +# For OS specific stuff +include(string((length(Core.ARGS)>=2 ? Core.ARGS[2] : ""), "build_h.jl")) # include($BUILDROOT/base/build_h.jl) +include(string((length(Core.ARGS)>=2 ? Core.ARGS[2] : ""), "version_git.jl")) # include($BUILDROOT/base/version_git.jl) + +include("osutils.jl") +include("c.jl") + +# Core I/O +include("io.jl") +include("iobuffer.jl") + +# strings & printing +include("intfuncs.jl") +include("strings/strings.jl") +include("parse.jl") +include("shell.jl") +include("regex.jl") +include("show.jl") +include("arrayshow.jl") +include("methodshow.jl") + +# multidimensional arrays +include("cartesian.jl") +using .Cartesian +include("multidimensional.jl") +include("permuteddimsarray.jl") +using .PermutedDimsArrays + +include("broadcast.jl") +using .Broadcast +using .Broadcast: broadcasted, broadcasted_kwsyntax, materialize, materialize! + +# missing values +include("missing.jl") + +# version +include("version.jl") + +# system & environment +include("sysinfo.jl") +include("libc.jl") +using .Libc: getpid, gethostname, time + +const DL_LOAD_PATH = String[] +if Sys.isapple() + if Base.DARWIN_FRAMEWORK + push!(DL_LOAD_PATH, "@loader_path/Frameworks") + else + push!(DL_LOAD_PATH, "@loader_path/julia") + end + push!(DL_LOAD_PATH, "@loader_path") +end + +include("env.jl") + +# Scheduling +include("linked_list.jl") +include("condition.jl") +include("threads.jl") +include("lock.jl") +include("task.jl") +include("weakkeydict.jl") + +# Logging +include("logging.jl") +using .CoreLogging + +# functions defined in Random +function rand end +function randn end + +# I/O +include("libuv.jl") +include("asyncevent.jl") +include("iostream.jl") +include("stream.jl") +include("filesystem.jl") +using .Filesystem +include("cmd.jl") +include("process.jl") +include("grisu/grisu.jl") +include("secretbuffer.jl") + +# core math functions +include("floatfuncs.jl") +include("math.jl") +using .Math +const (√)=sqrt +const (∛)=cbrt + +INCLUDE_STATE = 2 # include = _include (from lines above) + +# reduction along dims +include("reducedim.jl") # macros in this file relies on string.jl +include("accumulate.jl") + +# basic data structures +include("ordering.jl") +using .Order + +# Combinatorics +include("sort.jl") +using .Sort + +# Fast math +include("fastmath.jl") +using .FastMath + +function deepcopy_internal end + +# enums +include("Enums.jl") +using .Enums + +# BigInts and BigFloats +include("gmp.jl") +using .GMP + +include("mpfr.jl") +using .MPFR + +include("combinatorics.jl") + +# more hashing definitions +include("hashing2.jl") + +# irrational mathematical constants +include("irrationals.jl") +include("mathconstants.jl") +using .MathConstants: ℯ, π, pi + +# (s)printf macros +include("printf.jl") +# import .Printf + +# metaprogramming +include("meta.jl") + +# concurrency and parallelism +include("channels.jl") + +# utilities +include("deepcopy.jl") +include("download.jl") +include("summarysize.jl") +include("errorshow.jl") + +# Stack frames and traces +include("stacktraces.jl") +using .StackTraces + +include("initdefs.jl") + +# worker threads +include("threadcall.jl") + +# code loading +include("uuid.jl") +include("loading.jl") + +# misc useful functions & macros +include("util.jl") + +include("asyncmap.jl") + +# experimental API's +include("experimental.jl") + +# deprecated functions +include("deprecated.jl") + +# Some basic documentation +include("docs/basedocs.jl") + +include("client.jl") + +# Documentation -- should always be included last in sysimg. +include("docs/Docs.jl") +using .Docs +if isdefined(Core, :Compiler) && is_primary_base_module + Docs.loaddocs(Core.Compiler.CoreDocs.DOCS) +end + +end_base_include = time_ns() + +if is_primary_base_module +function __init__() + # try to ensuremake sure OpenBLAS does not set CPU affinity (#1070, #9639) + if !haskey(ENV, "OPENBLAS_MAIN_FREE") && !haskey(ENV, "GOTOBLAS_MAIN_FREE") + ENV["OPENBLAS_MAIN_FREE"] = "1" + end + # And try to prevent openblas from starting too many threads, unless/until specifically requested + if !haskey(ENV, "OPENBLAS_NUM_THREADS") && !haskey(ENV, "OMP_NUM_THREADS") + cpu_threads = Sys.CPU_THREADS::Int + if cpu_threads > 8 # always at most 8 + ENV["OPENBLAS_NUM_THREADS"] = "8" + elseif haskey(ENV, "JULIA_CPU_THREADS") # or exactly as specified + ENV["OPENBLAS_NUM_THREADS"] = cpu_threads + end # otherwise, trust that openblas will pick CPU_THREADS anyways, without any intervention + end + # for the few uses of Libc.rand in Base: + Libc.srand() + # Base library init + reinit_stdio() + Multimedia.reinit_displays() # since Multimedia.displays uses stdout as fallback + # initialize loading + init_depot_path() + init_load_path() + nothing +end + +INCLUDE_STATE = 3 # include = include_relative +end + +const tot_time_stdlib = RefValue(0.0) + +end # baremodule Base diff --git a/base/Enums.jl b/base/Enums.jl new file mode 100644 index 0000000..ba9fa43 --- /dev/null +++ b/base/Enums.jl @@ -0,0 +1,206 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +module Enums + +import Core.Intrinsics.bitcast +export Enum, @enum + +function namemap end + +""" + Enum{T<:Integer} + +The abstract supertype of all enumerated types defined with [`@enum`](@ref). +""" +abstract type Enum{T<:Integer} end + +basetype(::Type{<:Enum{T}}) where {T<:Integer} = T + +(::Type{T})(x::Enum{T2}) where {T<:Integer,T2<:Integer} = T(bitcast(T2, x))::T +Base.cconvert(::Type{T}, x::Enum{T2}) where {T<:Integer,T2<:Integer} = T(x) +Base.write(io::IO, x::Enum{T}) where {T<:Integer} = write(io, T(x)) +Base.read(io::IO, ::Type{T}) where {T<:Enum} = T(read(io, basetype(T))) + +Base.isless(x::T, y::T) where {T<:Enum} = isless(basetype(T)(x), basetype(T)(y)) + +Base.Symbol(x::Enum) = namemap(typeof(x))[Integer(x)]::Symbol + +Base.print(io::IO, x::Enum) = print(io, Symbol(x)) + +function Base.show(io::IO, x::Enum) + sym = Symbol(x) + if !get(io, :compact, false) + from = get(io, :module, Main) + def = typeof(x).name.module + if from === nothing || !Base.isvisible(sym, def, from) + show(io, def) + print(io, ".") + end + end + print(io, sym) +end + +function Base.show(io::IO, ::MIME"text/plain", x::Enum) + print(io, x, "::") + show(IOContext(io, :compact => true), typeof(x)) + print(io, " = ") + show(io, Integer(x)) +end + +function Base.show(io::IO, ::MIME"text/plain", t::Type{<:Enum}) + print(io, "Enum ") + Base.show_datatype(io, t) + print(io, ":") + for x in instances(t) + print(io, "\n", Symbol(x), " = ") + show(io, Integer(x)) + end +end + +# generate code to test whether expr is in the given set of values +function membershiptest(expr, values) + lo, hi = extrema(values) + if length(values) == hi - lo + 1 + :($lo <= $expr <= $hi) + elseif length(values) < 20 + foldl((x1,x2)->:($x1 || ($expr == $x2)), values[2:end]; init=:($expr == $(values[1]))) + else + :($expr in $(Set(values))) + end +end + +# give Enum types scalar behavior in broadcasting +Base.broadcastable(x::Enum) = Ref(x) + +@noinline enum_argument_error(typename, x) = throw(ArgumentError(string("invalid value for Enum $(typename): $x"))) + +""" + @enum EnumName[::BaseType] value1[=x] value2[=y] + +Create an `Enum{BaseType}` subtype with name `EnumName` and enum member values of +`value1` and `value2` with optional assigned values of `x` and `y`, respectively. +`EnumName` can be used just like other types and enum member values as regular values, such as + +# Examples +```jldoctest fruitenum +julia> @enum Fruit apple=1 orange=2 kiwi=3 + +julia> f(x::Fruit) = "I'm a Fruit with value: \$(Int(x))" +f (generic function with 1 method) + +julia> f(apple) +"I'm a Fruit with value: 1" + +julia> Fruit(1) +apple::Fruit = 1 +``` + +Values can also be specified inside a `begin` block, e.g. + +```julia +@enum EnumName begin + value1 + value2 +end +``` + +`BaseType`, which defaults to [`Int32`](@ref), must be a primitive subtype of `Integer`. +Member values can be converted between the enum type and `BaseType`. `read` and `write` +perform these conversions automatically. + +To list all the instances of an enum use `instances`, e.g. + +```jldoctest fruitenum +julia> instances(Fruit) +(apple, orange, kiwi) +``` +""" +macro enum(T, syms...) + if isempty(syms) + throw(ArgumentError("no arguments given for Enum $T")) + end + basetype = Int32 + typename = T + if isa(T, Expr) && T.head === :(::) && length(T.args) == 2 && isa(T.args[1], Symbol) + typename = T.args[1] + basetype = Core.eval(__module__, T.args[2]) + if !isa(basetype, DataType) || !(basetype <: Integer) || !isbitstype(basetype) + throw(ArgumentError("invalid base type for Enum $typename, $T=::$basetype; base type must be an integer primitive type")) + end + elseif !isa(T, Symbol) + throw(ArgumentError("invalid type expression for enum $T")) + end + values = basetype[] + seen = Set{Symbol}() + namemap = Dict{basetype,Symbol}() + lo = hi = 0 + i = zero(basetype) + hasexpr = false + + if length(syms) == 1 && syms[1] isa Expr && syms[1].head === :block + syms = syms[1].args + end + for s in syms + s isa LineNumberNode && continue + if isa(s, Symbol) + if i == typemin(basetype) && !isempty(values) + throw(ArgumentError("overflow in value \"$s\" of Enum $typename")) + end + elseif isa(s, Expr) && + (s.head === :(=) || s.head === :kw) && + length(s.args) == 2 && isa(s.args[1], Symbol) + i = Core.eval(__module__, s.args[2]) # allow exprs, e.g. uint128"1" + if !isa(i, Integer) + throw(ArgumentError("invalid value for Enum $typename, $s; values must be integers")) + end + i = convert(basetype, i) + s = s.args[1] + hasexpr = true + else + throw(ArgumentError(string("invalid argument for Enum ", typename, ": ", s))) + end + if !Base.isidentifier(s) + throw(ArgumentError("invalid name for Enum $typename; \"$s\" is not a valid identifier")) + end + if hasexpr && haskey(namemap, i) + throw(ArgumentError("both $s and $(namemap[i]) have value $i in Enum $typename; values must be unique")) + end + namemap[i] = s + push!(values, i) + if s in seen + throw(ArgumentError("name \"$s\" in Enum $typename is not unique")) + end + push!(seen, s) + if length(values) == 1 + lo = hi = i + else + lo = min(lo, i) + hi = max(hi, i) + end + i += oneunit(i) + end + blk = quote + # enum definition + Base.@__doc__(primitive type $(esc(typename)) <: Enum{$(basetype)} $(sizeof(basetype) * 8) end) + function $(esc(typename))(x::Integer) + $(membershiptest(:x, values)) || enum_argument_error($(Expr(:quote, typename)), x) + return bitcast($(esc(typename)), convert($(basetype), x)) + end + Enums.namemap(::Type{$(esc(typename))}) = $(esc(namemap)) + Base.typemin(x::Type{$(esc(typename))}) = $(esc(typename))($lo) + Base.typemax(x::Type{$(esc(typename))}) = $(esc(typename))($hi) + let insts = ntuple(i->$(esc(typename))($values[i]), $(length(values))) + Base.instances(::Type{$(esc(typename))}) = insts + end + end + if isa(typename, Symbol) + for (i, sym) in namemap + push!(blk.args, :(const $(esc(sym)) = $(esc(typename))($i))) + end + end + push!(blk.args, :nothing) + blk.head = :toplevel + return blk +end + +end # module diff --git a/base/Makefile b/base/Makefile new file mode 100644 index 0000000..cd8c952 --- /dev/null +++ b/base/Makefile @@ -0,0 +1,243 @@ +SRCDIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) +BUILDDIR := . +JULIAHOME := $(abspath $(SRCDIR)/..) +include $(JULIAHOME)/deps/Versions.make +include $(JULIAHOME)/Make.inc + +TAGGED_RELEASE_BANNER := "" + +ifneq ($(USEMSVC), 1) +CPP_STDOUT := $(CPP) -P +else +CPP_STDOUT := $(CPP) -E +endif + +all: $(addprefix $(BUILDDIR)/,pcre_h.jl errno_h.jl build_h.jl.phony file_constants.jl uv_constants.jl version_git.jl.phony) + +PCRE_CONST := 0x[0-9a-fA-F]+|[0-9]+|\([\-0-9]+\) +ifeq ($(USE_SYSTEM_PCRE), 1) + PCRE_INCL_PATH := $(shell $(PCRE_CONFIG) --prefix)/include/pcre2.h +else + PCRE_INCL_PATH := $(build_includedir)/pcre2.h +endif + +$(BUILDDIR)/pcre_h.jl: $(PCRE_INCL_PATH) + @$(call PRINT_PERL, $(CPP) -D PCRE2_CODE_UNIT_WIDTH=8 -dM $< | perl -nle '/^\s*#define\s+PCRE2_(\w*)\s*\(?($(PCRE_CONST))\)?u?\s*$$/ and print "const $$1 = $$2 % UInt32"' | LC_ALL=C sort > $@) + +$(BUILDDIR)/errno_h.jl: + @$(call PRINT_PERL, echo '#include ' | $(CPP) -dM - | perl -nle 'print "const $$1 = Int32($$2)" if /^#define\s+(E\w+)\s+(\d+)\s*$$/' | LC_ALL=C sort > $@) + +$(BUILDDIR)/file_constants.jl: $(SRCDIR)/../src/file_constants.h + @$(call PRINT_PERL, $(CPP_STDOUT) -DJULIA $< | perl -nle 'print "$$1 0o$$2" if /^(\s*const\s+[A-z_]+\s+=)\s+(0[0-9]*)\s*$$/; print "$$1" if /^\s*(const\s+[A-z_]+\s+=\s+([1-9]|0x)[0-9A-z]*)\s*$$/' > $@) + +$(BUILDDIR)/uv_constants.jl: $(SRCDIR)/../src/uv_constants.h $(build_includedir)/uv/errno.h + @$(call PRINT_PERL, $(CPP_STDOUT) "-I$(LIBUV_INC)" -DJULIA $< | tail -n 16 > $@) + +$(BUILDDIR)/build_h.jl.phony: + @echo "# This file is automatically generated in base/Makefile" > $@ +ifeq ($(XC_HOST),) + @echo "const MACHINE = \"$(BUILD_MACHINE)\"" >> $@ +else + @echo "const MACHINE = \"$(XC_HOST)\"" >> $@ +endif + @echo "const libm_name = \"$(LIBMNAME)\"" >> $@ + @echo "const libblas_name = \"$(LIBBLASNAME)\"" >> $@ + @echo "const liblapack_name = \"$(LIBLAPACKNAME)\"" >> $@ +ifeq ($(USE_BLAS64), 1) + @echo "const USE_BLAS64 = true" >> $@ +else + @echo "const USE_BLAS64 = false" >> $@ +endif +ifeq ($(USE_GPL_LIBS), 1) + @echo "const USE_GPL_LIBS = true" >> $@ +else + @echo "const USE_GPL_LIBS = false" >> $@ +endif + @echo "const libllvm_version_string = \"$$($(LLVM_CONFIG_HOST) --version)\"" >> $@ + @echo "const VERSION_STRING = \"$(JULIA_VERSION)\"" >> $@ + @echo "const TAGGED_RELEASE_BANNER = \"$(TAGGED_RELEASE_BANNER)\"" >> $@ +ifeq ($(OS),WINNT) + @printf 'const SYSCONFDIR = "%s"\n' '$(subst /,\\,$(sysconfdir_rel))' >> $@ + @printf 'const DATAROOTDIR = "%s"\n' '$(subst /,\\,$(datarootdir_rel))' >> $@ + @printf 'const DOCDIR = "%s"\n' '$(subst /,\\,$(docdir_rel))' >> $@ + @printf 'const LIBDIR = "%s"\n' '$(subst /,\\,$(libdir_rel))' >> $@ + @printf 'const LIBEXECDIR = "%s"\n' '$(subst /,\\,$(libexecdir_rel))' >> $@ + @printf 'const PRIVATE_LIBDIR = "%s"\n' '$(subst /,\\,$(private_libdir_rel))' >> $@ + @printf 'const INCLUDEDIR = "%s"\n' '$(subst /,\\,$(includedir_rel))' >> $@ +else + @echo "const SYSCONFDIR = \"$(sysconfdir_rel)\"" >> $@ + @echo "const DATAROOTDIR = \"$(datarootdir_rel)\"" >> $@ + @echo "const DOCDIR = \"$(docdir_rel)\"" >> $@ + @echo "const LIBDIR = \"$(libdir_rel)\"" >> $@ + @echo "const LIBEXECDIR = \"$(libexecdir_rel)\"" >> $@ + @echo "const PRIVATE_LIBDIR = \"$(private_libdir_rel)\"" >> $@ + @echo "const INCLUDEDIR = \"$(includedir_rel)\"" >> $@ +endif +ifeq ($(DARWIN_FRAMEWORK), 1) + @echo "const DARWIN_FRAMEWORK = true" >> $@ + @echo "const DARWIN_FRAMEWORK_NAME = \"$(FRAMEWORK_NAME)\"" >> $@ +else + @echo "const DARWIN_FRAMEWORK = false" >> $@ +endif + + @# This to ensure that we always rebuild this file, but only when it is modified do we touch build_h.jl, + @# ensuring we rebuild the system image as infrequently as possible + @if ! cmp -s $@ build_h.jl; then \ + $(call PRINT_PERL,) \ + mv $@ build_h.jl; \ + else \ + rm -f $@; \ + fi + +$(BUILDDIR)/version_git.jl.phony: $(SRCDIR)/version_git.sh +ifneq ($(NO_GIT), 1) + sh $< $(SRCDIR) > $@ + @# This to avoid touching git_version.jl when it is not modified, + @# so that the system image does not need to be rebuilt. + @if ! cmp -s $@ version_git.jl; then \ + $(call PRINT_PERL,) \ + mv $@ version_git.jl; \ + else \ + rm -f $@; \ + fi +else +ifeq ($(shell [ -f $(BUILDDIR)/version_git.jl ] && echo "true"), true) + @# Give warning if boilerplate git is used + @if grep -q "Default output if git is not available" $(BUILDDIR)/version_git.jl; then \ + echo "WARNING: Using boilerplate git version info" >&2; \ + fi +else + $(warning "WARNING: Generating boilerplate git version info") + @sh $(SRCDIR)/version_git.sh $(SRCDIR) NO_GIT > $(BUILDDIR)/version_git.jl +endif +endif + +ifeq (,$(filter $(OS), WINNT emscripten)) +# For any USE_SYSTEM_* libraries that will be dynamically loaded by libjulia, +# put a symlink in the private libdir. +# System package managers may want to install these links manually, +# but if not, this will try to create the proper symlink to the right minor version. +all: symlink_system_libraries +SYMLINK_SYSTEM_LIBRARIES := + +# if it's a symlink, pick up the symlink target instead, for one level of indirection +resolve_path = \ + $1_wd="`pwd`" && \ + $1_=`readlink -n $${$1} || true` && \ + if [ -n "$${$1_}" ]; then $1_wd=`dirname "$${$1}"`; $1="$${$1_}"; fi +## if it's a relative path, make it an absolute path +resolve_path += && \ + if [ -z "`echo $${$1} | grep '^/'`" ]; then $1=$${$1_wd}/$${$1}; fi +ifeq ($(OS), Darwin) +# try to use the install_name id instead (unless it is an @rpath or such) +# if it's a relative path, make it an absolute path using the working directory from $1, +# while in theory incorrect, this has been observed to be a common build mistake for many libraries +resolve_path += && \ + $1_=`otool -D $${$1} | tail -n +2 | sed -e 's/^@.*$$//'` && \ + if [ -n "$${$1_}" ]; then \ + $1_wd=`dirname "$${$1}"`; $1=$${$1_}; \ + if [ -z "`echo $${$1} | grep '^/'`" ]; then $1=$${$1_wd}/$${$1}; fi; \ + fi +else +# try to use the SO_NAME (if the named file exists) +resolve_path += && \ + $1_=`objdump -p "$${$1}" | awk '/SONAME/ {print $$2}'` && \ + if [ -n "$${$1_}" ]; then \ + $1_=$$(dirname "$${$1}")/$${$1_}; \ + if [ -e "$${$1_}" ]; then $1=$${$1_}; fi; \ + fi +endif + +## debug code: `make resolve-path P=` +#resolve_path += && \ +# echo "$${$1_wd} $${$1}" +#resolve-path: +# $(call resolve_path,P) && \ +# echo "$$P" + +define symlink_system_library +symlink_$1: $$(build_private_libdir)/$1.$$(SHLIB_EXT) +$$(build_private_libdir)/$1.$$(SHLIB_EXT): + REALPATH=`$$(call spawn,$$(build_depsbindir)/libwhich) -p $$(notdir $$@)` && \ + $$(call resolve_path,REALPATH) && \ + [ -e "$$$$REALPATH" ] && \ + ([ ! -e "$$@" ] || rm "$$@") && \ + echo ln -sf "$$$$REALPATH" "$$@" && \ + ln -sf "$$$$REALPATH" "$$@" +ifneq ($2,) +ifneq ($$(USE_SYSTEM_$2),0) +SYMLINK_SYSTEM_LIBRARIES += symlink_$1 +endif +endif +endef + +# the following excludes: libuv.a, libutf8proc.a + +$(eval $(call symlink_system_library,$(LIBMNAME))) +ifneq ($(USE_SYSTEM_LIBM),0) +SYMLINK_SYSTEM_LIBRARIES += symlink_$(LIBMNAME) +else ifneq ($(USE_SYSTEM_OPENLIBM),0) +SYMLINK_SYSTEM_LIBRARIES += symlink_$(LIBMNAME) +endif + +$(eval $(call symlink_system_library,libpcre2-8,PCRE)) +$(eval $(call symlink_system_library,libdSFMT,DSFMT)) +$(eval $(call symlink_system_library,$(LIBBLASNAME),BLAS)) +ifneq ($(LIBLAPACKNAME),$(LIBBLASNAME)) +$(eval $(call symlink_system_library,$(LIBLAPACKNAME),LAPACK)) +endif +$(eval $(call symlink_system_library,libgmp,GMP)) +$(eval $(call symlink_system_library,libmpfr,MPFR)) +$(eval $(call symlink_system_library,libmbedtls,MBEDTLS)) +$(eval $(call symlink_system_library,libmbedcrypto,MBEDTLS)) +$(eval $(call symlink_system_library,libmbedx509,MBEDTLS)) +$(eval $(call symlink_system_library,libssh2,LIBSSH2)) +$(eval $(call symlink_system_library,libcurl,CURL)) +$(eval $(call symlink_system_library,libgit2,LIBGIT2)) +$(eval $(call symlink_system_library,libamd,SUITESPARSE)) +$(eval $(call symlink_system_library,libcamd,SUITESPARSE)) +$(eval $(call symlink_system_library,libccolamd,SUITESPARSE)) +$(eval $(call symlink_system_library,libcholmod,SUITESPARSE)) +$(eval $(call symlink_system_library,libcolamd,SUITESPARSE)) +$(eval $(call symlink_system_library,libumfpack,SUITESPARSE)) +$(eval $(call symlink_system_library,libspqr,SUITESPARSE)) +$(eval $(call symlink_system_library,libsuitesparseconfig,SUITESPARSE)) +# EXCLUDED LIBRARIES (installed/used, but not vendored for use with dlopen): +# libunwind +endif # WINNT + +symlink_libLLVM: $(build_private_libdir)/libLLVM.$(SHLIB_EXT) +ifneq ($(USE_SYSTEM_LLVM),0) +LLVM_CONFIG_HOST_LIBS := $(shell $(LLVM_CONFIG_HOST) --libfiles) +# HACK: llvm-config doesn't correctly point to shared libs on all platforms +# https://github.com/JuliaLang/julia/issues/29981 +else +LLVM_CONFIG_HOST_LIBS := $(shell $(LLVM_CONFIG_HOST) --libdir)/libLLVM.$(SHLIB_EXT) +endif +$(build_private_libdir)/libLLVM.$(SHLIB_EXT): + REALPATH=$(LLVM_CONFIG_HOST_LIBS) && \ + $(call resolve_path,REALPATH) && \ + [ -e "$$REALPATH" ] && \ + ([ ! -e "$@" ] || rm "$@") && \ + echo ln -sf "$$REALPATH" "$@" && \ + ln -sf "$$REALPATH" "$@" +ifneq ($(USE_SYSTEM_LLVM),0) +ifneq ($(USE_LLVM_SHLIB),0) +SYMLINK_SYSTEM_LIBRARIES += symlink_libLLVM +endif +endif + +symlink_system_libraries: $(SYMLINK_SYSTEM_LIBRARIES) + +.PHONY: $(BUILDDIR)/build_h.jl.phony $(BUILDDIR)/version_git.jl.phony clean all symlink_* + +clean: + -rm -f $(BUILDDIR)/pcre_h.jl + -rm -f $(BUILDDIR)/errno_h.jl + -rm -f $(BUILDDIR)/build_h.jl + -rm -f $(BUILDDIR)/build_h.jl.phony + -rm -f $(BUILDDIR)/uv_constants.jl + -rm -f $(BUILDDIR)/file_constants.jl + -rm -f $(BUILDDIR)/version_git.jl + -rm -f $(BUILDDIR)/version_git.jl.phony + -rm -f $(build_private_libdir)/lib*.$(SHLIB_EXT)* diff --git a/base/abstractarray.jl b/base/abstractarray.jl new file mode 100644 index 0000000..c450fc6 --- /dev/null +++ b/base/abstractarray.jl @@ -0,0 +1,2228 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## Basic functions ## + +""" + AbstractArray{T,N} + +Supertype for `N`-dimensional arrays (or array-like types) with elements of type `T`. +[`Array`](@ref) and other types are subtypes of this. See the manual section on the +[`AbstractArray` interface](@ref man-interface-array). +""" +AbstractArray + +convert(::Type{T}, a::T) where {T<:AbstractArray} = a +convert(::Type{AbstractArray{T}}, a::AbstractArray) where {T} = AbstractArray{T}(a) +convert(::Type{AbstractArray{T,N}}, a::AbstractArray{<:Any,N}) where {T,N} = AbstractArray{T,N}(a) + +""" + size(A::AbstractArray, [dim]) + +Return a tuple containing the dimensions of `A`. Optionally you can specify a +dimension to just get the length of that dimension. + +Note that `size` may not be defined for arrays with non-standard indices, in which case [`axes`](@ref) +may be useful. See the manual chapter on [arrays with custom indices](@ref man-custom-indices). + +# Examples +```jldoctest +julia> A = fill(1, (2,3,4)); + +julia> size(A) +(2, 3, 4) + +julia> size(A, 2) +3 +``` +""" +size(t::AbstractArray{T,N}, d) where {T,N} = d::Integer <= N ? size(t)[d] : 1 + +""" + axes(A, d) + +Return the valid range of indices for array `A` along dimension `d`. + +See also [`size`](@ref), and the manual chapter on [arrays with custom indices](@ref man-custom-indices). + +# Examples +```jldoctest +julia> A = fill(1, (5,6,7)); + +julia> axes(A, 2) +Base.OneTo(6) +``` +""" +function axes(A::AbstractArray{T,N}, d) where {T,N} + @_inline_meta + d::Integer <= N ? axes(A)[d] : OneTo(1) +end + +""" + axes(A) + +Return the tuple of valid indices for array `A`. + +# Examples +```jldoctest +julia> A = fill(1, (5,6,7)); + +julia> axes(A) +(Base.OneTo(5), Base.OneTo(6), Base.OneTo(7)) +``` +""" +function axes(A) + @_inline_meta + map(OneTo, size(A)) +end + +""" + has_offset_axes(A) + has_offset_axes(A, B, ...) + +Return `true` if the indices of `A` start with something other than 1 along any axis. +If multiple arguments are passed, equivalent to `has_offset_axes(A) | has_offset_axes(B) | ...`. +""" +has_offset_axes(A) = _tuple_any(x->first(x)!=1, axes(A)) +has_offset_axes(A...) = _tuple_any(has_offset_axes, A) +has_offset_axes(::Colon) = false + +require_one_based_indexing(A...) = !has_offset_axes(A...) || throw(ArgumentError("offset arrays are not supported but got an array with index other than 1")) + +# Performance optimization: get rid of a branch on `d` in `axes(A, d)` +# for d=1. 1d arrays are heavily used, and the first dimension comes up +# in other applications. +axes1(A::AbstractArray{<:Any,0}) = OneTo(1) +axes1(A::AbstractArray) = (@_inline_meta; axes(A)[1]) +axes1(iter) = OneTo(length(iter)) + +unsafe_indices(A) = axes(A) +unsafe_indices(r::AbstractRange) = (OneTo(unsafe_length(r)),) # Ranges use checked_sub for size + +keys(a::AbstractArray) = CartesianIndices(axes(a)) +keys(a::AbstractVector) = LinearIndices(a) + +""" + keytype(T::Type{<:AbstractArray}) + keytype(A::AbstractArray) + +Return the key type of an array. This is equal to the +`eltype` of the result of `keys(...)`, and is provided +mainly for compatibility with the dictionary interface. + +# Examples +```jldoctest +julia> keytype([1, 2, 3]) == Int +true + +julia> keytype([1 2; 3 4]) +CartesianIndex{2} +``` + +!!! compat "Julia 1.2" + For arrays, this function requires at least Julia 1.2. +""" +keytype(a::AbstractArray) = keytype(typeof(a)) + +keytype(A::Type{<:AbstractArray}) = CartesianIndex{ndims(A)} +keytype(A::Type{<:AbstractVector}) = Int + +valtype(a::AbstractArray) = valtype(typeof(a)) + +""" + valtype(T::Type{<:AbstractArray}) + valtype(A::AbstractArray) + +Return the value type of an array. This is identical to `eltype` and is +provided mainly for compatibility with the dictionary interface. + +# Examples +```jldoctest +julia> valtype(["one", "two", "three"]) +String +``` + +!!! compat "Julia 1.2" + For arrays, this function requires at least Julia 1.2. +""" +valtype(A::Type{<:AbstractArray}) = eltype(A) + +prevind(::AbstractArray, i::Integer) = Int(i)-1 +nextind(::AbstractArray, i::Integer) = Int(i)+1 + +eltype(::Type{<:AbstractArray{E}}) where {E} = @isdefined(E) ? E : Any +elsize(A::AbstractArray) = elsize(typeof(A)) + +""" + ndims(A::AbstractArray) -> Integer + +Return the number of dimensions of `A`. + +# Examples +```jldoctest +julia> A = fill(1, (3,4,5)); + +julia> ndims(A) +3 +``` +""" +ndims(::AbstractArray{T,N}) where {T,N} = N +ndims(::Type{<:AbstractArray{T,N}}) where {T,N} = N + +""" + length(collection) -> Integer + +Return the number of elements in the collection. + +Use [`lastindex`](@ref) to get the last valid index of an indexable collection. + +# Examples +```jldoctest +julia> length(1:5) +5 + +julia> length([1, 2, 3, 4]) +4 + +julia> length([1 2; 3 4]) +4 +``` +""" +length + +""" + length(A::AbstractArray) + +Return the number of elements in the array, defaults to `prod(size(A))`. + +# Examples +```jldoctest +julia> length([1, 2, 3, 4]) +4 + +julia> length([1 2; 3 4]) +4 +``` +""" +length(t::AbstractArray) = (@_inline_meta; prod(size(t))) + +# `eachindex` is mostly an optimization of `keys` +eachindex(itrs...) = keys(itrs...) + +# eachindex iterates over all indices. IndexCartesian definitions are later. +eachindex(A::AbstractVector) = (@_inline_meta(); axes1(A)) + +@noinline function throw_eachindex_mismatch(::IndexLinear, A...) + throw(DimensionMismatch("all inputs to eachindex must have the same indices, got $(join(LinearIndices.(A), ", ", " and "))")) +end +@noinline function throw_eachindex_mismatch(::IndexCartesian, A...) + throw(DimensionMismatch("all inputs to eachindex must have the same axes, got $(join(axes.(A), ", ", " and "))")) +end + +""" + eachindex(A...) + +Create an iterable object for visiting each index of an `AbstractArray` `A` in an efficient +manner. For array types that have opted into fast linear indexing (like `Array`), this is +simply the range `1:length(A)`. For other array types, return a specialized Cartesian +range to efficiently index into the array with indices specified for every dimension. For +other iterables, including strings and dictionaries, return an iterator object +supporting arbitrary index types (e.g. unevenly spaced or non-integer indices). + +If you supply more than one `AbstractArray` argument, `eachindex` will create an +iterable object that is fast for all arguments (a [`UnitRange`](@ref) +if all inputs have fast linear indexing, a [`CartesianIndices`](@ref) +otherwise). +If the arrays have different sizes and/or dimensionalities, `eachindex` will return an +iterable that spans the largest range along each dimension. + +# Examples +```jldoctest +julia> A = [1 2; 3 4]; + +julia> for i in eachindex(A) # linear indexing + println(i) + end +1 +2 +3 +4 + +julia> for i in eachindex(view(A, 1:2, 1:1)) # Cartesian indexing + println(i) + end +CartesianIndex(1, 1) +CartesianIndex(2, 1) +``` +""" +eachindex(A::AbstractArray) = (@_inline_meta(); eachindex(IndexStyle(A), A)) + +function eachindex(A::AbstractArray, B::AbstractArray) + @_inline_meta + eachindex(IndexStyle(A,B), A, B) +end +function eachindex(A::AbstractArray, B::AbstractArray...) + @_inline_meta + eachindex(IndexStyle(A,B...), A, B...) +end +eachindex(::IndexLinear, A::AbstractArray) = (@_inline_meta; OneTo(length(A))) +eachindex(::IndexLinear, A::AbstractVector) = (@_inline_meta; axes1(A)) +function eachindex(::IndexLinear, A::AbstractArray, B::AbstractArray...) + @_inline_meta + indsA = eachindex(IndexLinear(), A) + _all_match_first(X->eachindex(IndexLinear(), X), indsA, B...) || + throw_eachindex_mismatch(IndexLinear(), A, B...) + indsA +end +function _all_match_first(f::F, inds, A, B...) where F<:Function + @_inline_meta + (inds == f(A)) & _all_match_first(f, inds, B...) +end +_all_match_first(f::F, inds) where F<:Function = true + +# keys with an IndexStyle +keys(s::IndexStyle, A::AbstractArray, B::AbstractArray...) = eachindex(s, A, B...) + +""" + lastindex(collection) -> Integer + lastindex(collection, d) -> Integer + +Return the last index of `collection`. If `d` is given, return the last index of `collection` along dimension `d`. + +The syntaxes `A[end]` and `A[end, end]` lower to `A[lastindex(A)]` and +`A[lastindex(A, 1), lastindex(A, 2)]`, respectively. + +# Examples +```jldoctest +julia> lastindex([1,2,4]) +3 + +julia> lastindex(rand(3,4,5), 2) +4 +``` +""" +lastindex(a::AbstractArray) = (@_inline_meta; last(eachindex(IndexLinear(), a))) +lastindex(a::AbstractArray, d) = (@_inline_meta; last(axes(a, d))) + +""" + firstindex(collection) -> Integer + firstindex(collection, d) -> Integer + +Return the first index of `collection`. If `d` is given, return the first index of `collection` along dimension `d`. + +# Examples +```jldoctest +julia> firstindex([1,2,4]) +1 + +julia> firstindex(rand(3,4,5), 2) +1 +``` +""" +firstindex(a::AbstractArray) = (@_inline_meta; first(eachindex(IndexLinear(), a))) +firstindex(a::AbstractArray, d) = (@_inline_meta; first(axes(a, d))) + +first(a::AbstractArray) = a[first(eachindex(a))] + +""" + first(coll) + +Get the first element of an iterable collection. Return the start point of an +[`AbstractRange`](@ref) even if it is empty. + +# Examples +```jldoctest +julia> first(2:2:10) +2 + +julia> first([1; 2; 3; 4]) +1 +``` +""" +function first(itr) + x = iterate(itr) + x === nothing && throw(ArgumentError("collection must be non-empty")) + x[1] +end + +""" + last(coll) + +Get the last element of an ordered collection, if it can be computed in O(1) time. This is +accomplished by calling [`lastindex`](@ref) to get the last index. Return the end +point of an [`AbstractRange`](@ref) even if it is empty. + +# Examples +```jldoctest +julia> last(1:2:10) +9 + +julia> last([1; 2; 3; 4]) +4 +``` +""" +last(a) = a[end] + +""" + strides(A) + +Return a tuple of the memory strides in each dimension. + +# Examples +```jldoctest +julia> A = fill(1, (3,4,5)); + +julia> strides(A) +(1, 3, 12) +``` +""" +function strides end + +""" + stride(A, k::Integer) + +Return the distance in memory (in number of elements) between adjacent elements in dimension `k`. + +# Examples +```jldoctest +julia> A = fill(1, (3,4,5)); + +julia> stride(A,2) +3 + +julia> stride(A,3) +12 +``` +""" +stride(A::AbstractArray, k::Integer) = strides(A)[k] + +@inline size_to_strides(s, d, sz...) = (s, size_to_strides(s * d, sz...)...) +size_to_strides(s, d) = (s,) +size_to_strides(s) = () + + +function isassigned(a::AbstractArray, i::Integer...) + try + a[i...] + true + catch e + if isa(e, BoundsError) || isa(e, UndefRefError) + return false + else + rethrow() + end + end +end + +# used to compute "end" for last index +function trailingsize(A, n) + s = 1 + for i=n:ndims(A) + s *= size(A,i) + end + return s +end +function trailingsize(inds::Indices, n) + s = 1 + for i=n:length(inds) + s *= unsafe_length(inds[i]) + end + return s +end +# This version is type-stable even if inds is heterogeneous +function trailingsize(inds::Indices) + @_inline_meta + prod(map(unsafe_length, inds)) +end + +## Bounds checking ## + +# The overall hierarchy is +# `checkbounds(A, I...)` -> +# `checkbounds(Bool, A, I...)` -> +# `checkbounds_indices(Bool, IA, I)`, which recursively calls +# `checkindex` for each dimension +# +# See the "boundscheck" devdocs for more information. +# +# Note this hierarchy has been designed to reduce the likelihood of +# method ambiguities. We try to make `checkbounds` the place to +# specialize on array type, and try to avoid specializations on index +# types; conversely, `checkindex` is intended to be specialized only +# on index type (especially, its last argument). + +""" + checkbounds(Bool, A, I...) + +Return `true` if the specified indices `I` are in bounds for the given +array `A`. Subtypes of `AbstractArray` should specialize this method +if they need to provide custom bounds checking behaviors; however, in +many cases one can rely on `A`'s indices and [`checkindex`](@ref). + +See also [`checkindex`](@ref). + +# Examples +```jldoctest +julia> A = rand(3, 3); + +julia> checkbounds(Bool, A, 2) +true + +julia> checkbounds(Bool, A, 3, 4) +false + +julia> checkbounds(Bool, A, 1:3) +true + +julia> checkbounds(Bool, A, 1:3, 2:4) +false +``` +""" +function checkbounds(::Type{Bool}, A::AbstractArray, I...) + @_inline_meta + checkbounds_indices(Bool, axes(A), I) +end + +# Linear indexing is explicitly allowed when there is only one (non-cartesian) index +function checkbounds(::Type{Bool}, A::AbstractArray, i) + @_inline_meta + checkindex(Bool, eachindex(IndexLinear(), A), i) +end +# As a special extension, allow using logical arrays that match the source array exactly +function checkbounds(::Type{Bool}, A::AbstractArray{<:Any,N}, I::AbstractArray{Bool,N}) where N + @_inline_meta + axes(A) == axes(I) +end + +""" + checkbounds(A, I...) + +Throw an error if the specified indices `I` are not in bounds for the given array `A`. +""" +function checkbounds(A::AbstractArray, I...) + @_inline_meta + checkbounds(Bool, A, I...) || throw_boundserror(A, I) + nothing +end + +""" + checkbounds_indices(Bool, IA, I) + +Return `true` if the "requested" indices in the tuple `I` fall within +the bounds of the "permitted" indices specified by the tuple +`IA`. This function recursively consumes elements of these tuples, +usually in a 1-for-1 fashion, + + checkbounds_indices(Bool, (IA1, IA...), (I1, I...)) = checkindex(Bool, IA1, I1) & + checkbounds_indices(Bool, IA, I) + +Note that [`checkindex`](@ref) is being used to perform the actual +bounds-check for a single dimension of the array. + +There are two important exceptions to the 1-1 rule: linear indexing and +CartesianIndex{N}, both of which may "consume" more than one element +of `IA`. + +See also [`checkbounds`](@ref). +""" +function checkbounds_indices(::Type{Bool}, IA::Tuple, I::Tuple) + @_inline_meta + checkindex(Bool, IA[1], I[1]) & checkbounds_indices(Bool, tail(IA), tail(I)) +end +function checkbounds_indices(::Type{Bool}, ::Tuple{}, I::Tuple) + @_inline_meta + checkindex(Bool, OneTo(1), I[1]) & checkbounds_indices(Bool, (), tail(I)) +end +checkbounds_indices(::Type{Bool}, IA::Tuple, ::Tuple{}) = (@_inline_meta; all(x->unsafe_length(x)==1, IA)) +checkbounds_indices(::Type{Bool}, ::Tuple{}, ::Tuple{}) = true + +throw_boundserror(A, I) = (@_noinline_meta; throw(BoundsError(A, I))) + +# check along a single dimension +""" + checkindex(Bool, inds::AbstractUnitRange, index) + +Return `true` if the given `index` is within the bounds of +`inds`. Custom types that would like to behave as indices for all +arrays can extend this method in order to provide a specialized bounds +checking implementation. + +# Examples +```jldoctest +julia> checkindex(Bool, 1:20, 8) +true + +julia> checkindex(Bool, 1:20, 21) +false +``` +""" +checkindex(::Type{Bool}, inds::AbstractUnitRange, i) = + throw(ArgumentError("unable to check bounds for indices of type $(typeof(i))")) +checkindex(::Type{Bool}, inds::AbstractUnitRange, i::Real) = (first(inds) <= i) & (i <= last(inds)) +checkindex(::Type{Bool}, inds::AbstractUnitRange, ::Colon) = true +checkindex(::Type{Bool}, inds::AbstractUnitRange, ::Slice) = true +function checkindex(::Type{Bool}, inds::AbstractUnitRange, r::AbstractRange) + @_propagate_inbounds_meta + isempty(r) | (checkindex(Bool, inds, first(r)) & checkindex(Bool, inds, last(r))) +end +checkindex(::Type{Bool}, indx::AbstractUnitRange, I::AbstractVector{Bool}) = indx == axes1(I) +checkindex(::Type{Bool}, indx::AbstractUnitRange, I::AbstractArray{Bool}) = false +function checkindex(::Type{Bool}, inds::AbstractUnitRange, I::AbstractArray) + @_inline_meta + b = true + for i in I + b &= checkindex(Bool, inds, i) + end + b +end + +# See also specializations in multidimensional + +## Constructors ## + +# default arguments to similar() +""" + similar(array, [element_type=eltype(array)], [dims=size(array)]) + +Create an uninitialized mutable array with the given element type and size, based upon the +given source array. The second and third arguments are both optional, defaulting to the +given array's `eltype` and `size`. The dimensions may be specified either as a single tuple +argument or as a series of integer arguments. + +Custom AbstractArray subtypes may choose which specific array type is best-suited to return +for the given element type and dimensionality. If they do not specialize this method, the +default is an `Array{element_type}(undef, dims...)`. + +For example, `similar(1:10, 1, 4)` returns an uninitialized `Array{Int,2}` since ranges are +neither mutable nor support 2 dimensions: + +```julia-repl +julia> similar(1:10, 1, 4) +1×4 Array{Int64,2}: + 4419743872 4374413872 4419743888 0 +``` + +Conversely, `similar(trues(10,10), 2)` returns an uninitialized `BitVector` with two +elements since `BitArray`s are both mutable and can support 1-dimensional arrays: + +```julia-repl +julia> similar(trues(10,10), 2) +2-element BitArray{1}: + 0 + 0 +``` + +Since `BitArray`s can only store elements of type [`Bool`](@ref), however, if you request a +different element type it will create a regular `Array` instead: + +```julia-repl +julia> similar(falses(10), Float64, 2, 4) +2×4 Array{Float64,2}: + 2.18425e-314 2.18425e-314 2.18425e-314 2.18425e-314 + 2.18425e-314 2.18425e-314 2.18425e-314 2.18425e-314 +``` + +""" +similar(a::AbstractArray{T}) where {T} = similar(a, T) +similar(a::AbstractArray, ::Type{T}) where {T} = similar(a, T, to_shape(axes(a))) +similar(a::AbstractArray{T}, dims::Tuple) where {T} = similar(a, T, to_shape(dims)) +similar(a::AbstractArray{T}, dims::DimOrInd...) where {T} = similar(a, T, to_shape(dims)) +similar(a::AbstractArray, ::Type{T}, dims::DimOrInd...) where {T} = similar(a, T, to_shape(dims)) +# Similar supports specifying dims as either Integers or AbstractUnitRanges or any mixed combination +# thereof. Ideally, we'd just convert Integers to OneTos and then call a canonical method with the axes, +# but we don't want to require all AbstractArray subtypes to dispatch on Base.OneTo. So instead we +# define this method to convert supported axes to Ints, with the expectation that an offset array +# package will define a method with dims::Tuple{Union{Integer, UnitRange}, Vararg{Union{Integer, UnitRange}}} +similar(a::AbstractArray, ::Type{T}, dims::Tuple{Union{Integer, OneTo}, Vararg{Union{Integer, OneTo}}}) where {T} = similar(a, T, to_shape(dims)) +# similar creates an Array by default +similar(a::AbstractArray, ::Type{T}, dims::Dims{N}) where {T,N} = Array{T,N}(undef, dims) + +to_shape(::Tuple{}) = () +to_shape(dims::Dims) = dims +to_shape(dims::DimsOrInds) = map(to_shape, dims)::DimsOrInds +# each dimension +to_shape(i::Int) = i +to_shape(i::Integer) = Int(i) +to_shape(r::OneTo) = Int(last(r)) +to_shape(r::AbstractUnitRange) = r + +""" + similar(storagetype, axes) + +Create an uninitialized mutable array analogous to that specified by +`storagetype`, but with `axes` specified by the last +argument. `storagetype` might be a type or a function. + +**Examples**: + + similar(Array{Int}, axes(A)) + +creates an array that "acts like" an `Array{Int}` (and might indeed be +backed by one), but which is indexed identically to `A`. If `A` has +conventional indexing, this will be identical to +`Array{Int}(undef, size(A))`, but if `A` has unconventional indexing then the +indices of the result will match `A`. + + similar(BitArray, (axes(A, 2),)) + +would create a 1-dimensional logical array whose indices match those +of the columns of `A`. +""" +similar(::Type{T}, dims::DimOrInd...) where {T<:AbstractArray} = similar(T, dims) +similar(::Type{T}, shape::Tuple{Union{Integer, OneTo}, Vararg{Union{Integer, OneTo}}}) where {T<:AbstractArray} = similar(T, to_shape(shape)) +similar(::Type{T}, dims::Dims) where {T<:AbstractArray} = T(undef, dims) + +""" + empty(v::AbstractVector, [eltype]) + +Create an empty vector similar to `v`, optionally changing the `eltype`. + +# Examples + +```jldoctest +julia> empty([1.0, 2.0, 3.0]) +0-element Array{Float64,1} + +julia> empty([1.0, 2.0, 3.0], String) +0-element Array{String,1} +``` +""" +empty(a::AbstractVector{T}, ::Type{U}=T) where {T,U} = Vector{U}() + +# like empty, but should return a mutable collection, a Vector by default +emptymutable(a::AbstractVector{T}, ::Type{U}=T) where {T,U} = Vector{U}() +emptymutable(itr, ::Type{U}) where {U} = Vector{U}() + +""" + copy!(dst, src) -> dst + +In-place [`copy`](@ref) of `src` into `dst`, discarding any pre-existing +elements in `dst`. +If `dst` and `src` are of the same type, `dst == src` should hold after +the call. If `dst` and `src` are multidimensional arrays, they must have +equal [`axes`](@ref). +See also [`copyto!`](@ref). + +!!! compat "Julia 1.1" + This method requires at least Julia 1.1. In Julia 1.0 this method + is available from the `Future` standard library as `Future.copy!`. +""" +copy!(dst::AbstractVector, src::AbstractVector) = append!(empty!(dst), src) + +function copy!(dst::AbstractArray, src::AbstractArray) + axes(dst) == axes(src) || throw(ArgumentError( + "arrays must have the same axes for copy! (consider using `copyto!`)")) + copyto!(dst, src) +end + +## from general iterable to any array + +function copyto!(dest::AbstractArray, src) + destiter = eachindex(dest) + y = iterate(destiter) + for x in src + y === nothing && + throw(ArgumentError("destination has fewer elements than required")) + dest[y[1]] = x + y = iterate(destiter, y[2]) + end + return dest +end + +function copyto!(dest::AbstractArray, dstart::Integer, src) + i = Int(dstart) + for x in src + dest[i] = x + i += 1 + end + return dest +end + +# copy from an some iterable object into an AbstractArray +function copyto!(dest::AbstractArray, dstart::Integer, src, sstart::Integer) + if (sstart < 1) + throw(ArgumentError(string("source start offset (",sstart,") is < 1"))) + end + y = iterate(src) + for j = 1:(sstart-1) + if y === nothing + throw(ArgumentError(string("source has fewer elements than required, ", + "expected at least ",sstart,", got ",j-1))) + end + y = iterate(src, y[2]) + end + if y === nothing + throw(ArgumentError(string("source has fewer elements than required, ", + "expected at least ",sstart,", got ",sstart-1))) + end + i = Int(dstart) + while y !== nothing + val, st = y + dest[i] = val + i += 1 + y = iterate(src, st) + end + return dest +end + +# this method must be separate from the above since src might not have a length +function copyto!(dest::AbstractArray, dstart::Integer, src, sstart::Integer, n::Integer) + n < 0 && throw(ArgumentError(string("tried to copy n=", n, " elements, but n should be nonnegative"))) + n == 0 && return dest + dmax = dstart + n - 1 + inds = LinearIndices(dest) + if (dstart ∉ inds || dmax ∉ inds) | (sstart < 1) + sstart < 1 && throw(ArgumentError(string("source start offset (",sstart,") is < 1"))) + throw(BoundsError(dest, dstart:dmax)) + end + y = iterate(src) + for j = 1:(sstart-1) + if y === nothing + throw(ArgumentError(string("source has fewer elements than required, ", + "expected at least ",sstart,", got ",j-1))) + end + y = iterate(src, y[2]) + end + i = Int(dstart) + while i <= dmax && y !== nothing + val, st = y + @inbounds dest[i] = val + y = iterate(src, st) + i += 1 + end + i <= dmax && throw(BoundsError(dest, i)) + return dest +end + +## copy between abstract arrays - generally more efficient +## since a single index variable can be used. + +copyto!(dest::AbstractArray, src::AbstractArray) = + copyto!(IndexStyle(dest), dest, IndexStyle(src), src) + +function copyto!(::IndexStyle, dest::AbstractArray, ::IndexStyle, src::AbstractArray) + destinds, srcinds = LinearIndices(dest), LinearIndices(src) + isempty(srcinds) || (checkbounds(Bool, destinds, first(srcinds)) && checkbounds(Bool, destinds, last(srcinds))) || + throw(BoundsError(dest, srcinds)) + @inbounds for i in srcinds + dest[i] = src[i] + end + return dest +end + +function copyto!(::IndexStyle, dest::AbstractArray, ::IndexCartesian, src::AbstractArray) + destinds, srcinds = LinearIndices(dest), LinearIndices(src) + isempty(srcinds) || (checkbounds(Bool, destinds, first(srcinds)) && checkbounds(Bool, destinds, last(srcinds))) || + throw(BoundsError(dest, srcinds)) + i = 0 + @inbounds for a in src + dest[i+=1] = a + end + return dest +end + +function copyto!(dest::AbstractArray, dstart::Integer, src::AbstractArray) + copyto!(dest, dstart, src, first(LinearIndices(src)), length(src)) +end + +function copyto!(dest::AbstractArray, dstart::Integer, src::AbstractArray, sstart::Integer) + srcinds = LinearIndices(src) + checkbounds(Bool, srcinds, sstart) || throw(BoundsError(src, sstart)) + copyto!(dest, dstart, src, sstart, last(srcinds)-sstart+1) +end + +function copyto!(dest::AbstractArray, dstart::Integer, + src::AbstractArray, sstart::Integer, + n::Integer) + n == 0 && return dest + n < 0 && throw(ArgumentError(string("tried to copy n=", n, " elements, but n should be nonnegative"))) + destinds, srcinds = LinearIndices(dest), LinearIndices(src) + (checkbounds(Bool, destinds, dstart) && checkbounds(Bool, destinds, dstart+n-1)) || throw(BoundsError(dest, dstart:dstart+n-1)) + (checkbounds(Bool, srcinds, sstart) && checkbounds(Bool, srcinds, sstart+n-1)) || throw(BoundsError(src, sstart:sstart+n-1)) + @inbounds for i = 0:(n-1) + dest[dstart+i] = src[sstart+i] + end + return dest +end + +function copy(a::AbstractArray) + @_propagate_inbounds_meta + copymutable(a) +end + +function copyto!(B::AbstractVecOrMat{R}, ir_dest::AbstractRange{Int}, jr_dest::AbstractRange{Int}, + A::AbstractVecOrMat{S}, ir_src::AbstractRange{Int}, jr_src::AbstractRange{Int}) where {R,S} + if length(ir_dest) != length(ir_src) + throw(ArgumentError(string("source and destination must have same size (got ", + length(ir_src)," and ",length(ir_dest),")"))) + end + if length(jr_dest) != length(jr_src) + throw(ArgumentError(string("source and destination must have same size (got ", + length(jr_src)," and ",length(jr_dest),")"))) + end + @boundscheck checkbounds(B, ir_dest, jr_dest) + @boundscheck checkbounds(A, ir_src, jr_src) + jdest = first(jr_dest) + for jsrc in jr_src + idest = first(ir_dest) + for isrc in ir_src + @inbounds B[idest,jdest] = A[isrc,jsrc] + idest += step(ir_dest) + end + jdest += step(jr_dest) + end + return B +end + + +""" + copymutable(a) + +Make a mutable copy of an array or iterable `a`. For `a::Array`, +this is equivalent to `copy(a)`, but for other array types it may +differ depending on the type of `similar(a)`. For generic iterables +this is equivalent to `collect(a)`. + +# Examples +```jldoctest +julia> tup = (1, 2, 3) +(1, 2, 3) + +julia> Base.copymutable(tup) +3-element Array{Int64,1}: + 1 + 2 + 3 +``` +""" +function copymutable(a::AbstractArray) + @_propagate_inbounds_meta + copyto!(similar(a), a) +end +copymutable(itr) = collect(itr) + +zero(x::AbstractArray{T}) where {T} = fill!(similar(x), zero(T)) + +## iteration support for arrays by iterating over `eachindex` in the array ## +# Allows fast iteration by default for both IndexLinear and IndexCartesian arrays + +# While the definitions for IndexLinear are all simple enough to inline on their +# own, IndexCartesian's CartesianIndices is more complicated and requires explicit +# inlining. +function iterate(A::AbstractArray, state=(eachindex(A),)) + y = iterate(state...) + y === nothing && return nothing + A[y[1]], (state[1], tail(y)...) +end + +isempty(a::AbstractArray) = (length(a) == 0) + + +## range conversions ## + +map(::Type{T}, r::StepRange) where {T<:Real} = T(r.start):T(r.step):T(last(r)) +map(::Type{T}, r::UnitRange) where {T<:Real} = T(r.start):T(last(r)) +map(::Type{T}, r::StepRangeLen) where {T<:AbstractFloat} = convert(StepRangeLen{T}, r) +function map(::Type{T}, r::LinRange) where T<:AbstractFloat + LinRange(T(r.start), T(r.stop), length(r)) +end + +## unsafe/pointer conversions ## + +# note: the following type definitions don't mean any AbstractArray is convertible to +# a data Ref. they just map the array element type to the pointer type for +# convenience in cases that work. +pointer(x::AbstractArray{T}) where {T} = unsafe_convert(Ptr{T}, x) +function pointer(x::AbstractArray{T}, i::Integer) where T + @_inline_meta + unsafe_convert(Ptr{T}, x) + (i - first(LinearIndices(x)))*elsize(x) +end + +## Approach: +# We only define one fallback method on getindex for all argument types. +# That dispatches to an (inlined) internal _getindex function, where the goal is +# to transform the indices such that we can call the only getindex method that +# we require the type A{T,N} <: AbstractArray{T,N} to define; either: +# getindex(::A, ::Int) # if IndexStyle(A) == IndexLinear() OR +# getindex(::A{T,N}, ::Vararg{Int, N}) where {T,N} # if IndexCartesian() +# If the subtype hasn't defined the required method, it falls back to the +# _getindex function again where an error is thrown to prevent stack overflows. +""" + getindex(A, inds...) + +Return a subset of array `A` as specified by `inds`, where each `ind` may be an +`Int`, an [`AbstractRange`](@ref), or a [`Vector`](@ref). See the manual section on +[array indexing](@ref man-array-indexing) for details. + +# Examples +```jldoctest +julia> A = [1 2; 3 4] +2×2 Array{Int64,2}: + 1 2 + 3 4 + +julia> getindex(A, 1) +1 + +julia> getindex(A, [2, 1]) +2-element Array{Int64,1}: + 3 + 1 + +julia> getindex(A, 2:4) +3-element Array{Int64,1}: + 3 + 2 + 4 +``` +""" +function getindex(A::AbstractArray, I...) + @_propagate_inbounds_meta + error_if_canonical_getindex(IndexStyle(A), A, I...) + _getindex(IndexStyle(A), A, to_indices(A, I)...) +end +function unsafe_getindex(A::AbstractArray, I...) + @_inline_meta + @inbounds r = getindex(A, I...) + r +end + +error_if_canonical_getindex(::IndexLinear, A::AbstractArray, ::Int) = + error("getindex not defined for ", typeof(A)) +error_if_canonical_getindex(::IndexCartesian, A::AbstractArray{T,N}, ::Vararg{Int,N}) where {T,N} = + error("getindex not defined for ", typeof(A)) +error_if_canonical_getindex(::IndexStyle, ::AbstractArray, ::Any...) = nothing + +## Internal definitions +_getindex(::IndexStyle, A::AbstractArray, I...) = + error("getindex for $(typeof(A)) with types $(typeof(I)) is not supported") + +## IndexLinear Scalar indexing: canonical method is one Int +_getindex(::IndexLinear, A::AbstractArray, i::Int) = (@_propagate_inbounds_meta; getindex(A, i)) +function _getindex(::IndexLinear, A::AbstractArray, I::Vararg{Int,M}) where M + @_inline_meta + @boundscheck checkbounds(A, I...) # generally _to_linear_index requires bounds checking + @inbounds r = getindex(A, _to_linear_index(A, I...)) + r +end +_to_linear_index(A::AbstractArray, i::Int) = i +_to_linear_index(A::AbstractVector, i::Int, I::Int...) = i +_to_linear_index(A::AbstractArray) = 1 +_to_linear_index(A::AbstractArray, I::Int...) = (@_inline_meta; _sub2ind(A, I...)) + +## IndexCartesian Scalar indexing: Canonical method is full dimensionality of Ints +function _getindex(::IndexCartesian, A::AbstractArray, I::Vararg{Int,M}) where M + @_inline_meta + @boundscheck checkbounds(A, I...) # generally _to_subscript_indices requires bounds checking + @inbounds r = getindex(A, _to_subscript_indices(A, I...)...) + r +end +function _getindex(::IndexCartesian, A::AbstractArray{T,N}, I::Vararg{Int, N}) where {T,N} + @_propagate_inbounds_meta + getindex(A, I...) +end +_to_subscript_indices(A::AbstractArray, i::Int) = (@_inline_meta; _unsafe_ind2sub(A, i)) +_to_subscript_indices(A::AbstractArray{T,N}) where {T,N} = (@_inline_meta; fill_to_length((), 1, Val(N))) +_to_subscript_indices(A::AbstractArray{T,0}) where {T} = () +_to_subscript_indices(A::AbstractArray{T,0}, i::Int) where {T} = () +_to_subscript_indices(A::AbstractArray{T,0}, I::Int...) where {T} = () +function _to_subscript_indices(A::AbstractArray{T,N}, I::Int...) where {T,N} + @_inline_meta + J, Jrem = IteratorsMD.split(I, Val(N)) + _to_subscript_indices(A, J, Jrem) +end +_to_subscript_indices(A::AbstractArray, J::Tuple, Jrem::Tuple{}) = + __to_subscript_indices(A, axes(A), J, Jrem) +function __to_subscript_indices(A::AbstractArray, + ::Tuple{AbstractUnitRange,Vararg{AbstractUnitRange}}, J::Tuple, Jrem::Tuple{}) + @_inline_meta + (J..., map(first, tail(_remaining_size(J, axes(A))))...) +end +_to_subscript_indices(A, J::Tuple, Jrem::Tuple) = J # already bounds-checked, safe to drop +_to_subscript_indices(A::AbstractArray{T,N}, I::Vararg{Int,N}) where {T,N} = I +_remaining_size(::Tuple{Any}, t::Tuple) = t +_remaining_size(h::Tuple, t::Tuple) = (@_inline_meta; _remaining_size(tail(h), tail(t))) +_unsafe_ind2sub(::Tuple{}, i) = () # _ind2sub may throw(BoundsError()) in this case +_unsafe_ind2sub(sz, i) = (@_inline_meta; _ind2sub(sz, i)) + +## Setindex! is defined similarly. We first dispatch to an internal _setindex! +# function that allows dispatch on array storage + +""" + setindex!(A, X, inds...) + A[inds...] = X + +Store values from array `X` within some subset of `A` as specified by `inds`. +The syntax `A[inds...] = X` is equivalent to `setindex!(A, X, inds...)`. + +# Examples +```jldoctest +julia> A = zeros(2,2); + +julia> setindex!(A, [10, 20], [1, 2]); + +julia> A[[3, 4]] = [30, 40]; + +julia> A +2×2 Array{Float64,2}: + 10.0 30.0 + 20.0 40.0 +``` +""" +function setindex!(A::AbstractArray, v, I...) + @_propagate_inbounds_meta + error_if_canonical_setindex(IndexStyle(A), A, I...) + _setindex!(IndexStyle(A), A, v, to_indices(A, I)...) +end +function unsafe_setindex!(A::AbstractArray, v, I...) + @_inline_meta + @inbounds r = setindex!(A, v, I...) + r +end + +error_if_canonical_setindex(::IndexLinear, A::AbstractArray, ::Int) = + error("setindex! not defined for ", typeof(A)) +error_if_canonical_setindex(::IndexCartesian, A::AbstractArray{T,N}, ::Vararg{Int,N}) where {T,N} = + error("setindex! not defined for ", typeof(A)) +error_if_canonical_setindex(::IndexStyle, ::AbstractArray, ::Any...) = nothing + +## Internal definitions +_setindex!(::IndexStyle, A::AbstractArray, v, I...) = + error("setindex! for $(typeof(A)) with types $(typeof(I)) is not supported") + +## IndexLinear Scalar indexing +_setindex!(::IndexLinear, A::AbstractArray, v, i::Int) = (@_propagate_inbounds_meta; setindex!(A, v, i)) +function _setindex!(::IndexLinear, A::AbstractArray, v, I::Vararg{Int,M}) where M + @_inline_meta + @boundscheck checkbounds(A, I...) + @inbounds r = setindex!(A, v, _to_linear_index(A, I...)) + r +end + +# IndexCartesian Scalar indexing +function _setindex!(::IndexCartesian, A::AbstractArray{T,N}, v, I::Vararg{Int, N}) where {T,N} + @_propagate_inbounds_meta + setindex!(A, v, I...) +end +function _setindex!(::IndexCartesian, A::AbstractArray, v, I::Vararg{Int,M}) where M + @_inline_meta + @boundscheck checkbounds(A, I...) + @inbounds r = setindex!(A, v, _to_subscript_indices(A, I...)...) + r +end + +""" + parent(A) + +Returns the "parent array" of an array view type (e.g., `SubArray`), or the array itself if +it is not a view. + +# Examples +```jldoctest +julia> A = [1 2; 3 4] +2×2 Array{Int64,2}: + 1 2 + 3 4 + +julia> V = view(A, 1:2, :) +2×2 view(::Array{Int64,2}, 1:2, :) with eltype Int64: + 1 2 + 3 4 + +julia> parent(V) +2×2 Array{Int64,2}: + 1 2 + 3 4 +``` +""" +parent(a::AbstractArray) = a + +## rudimentary aliasing detection ## +""" + Base.unalias(dest, A) + +Return either `A` or a copy of `A` in a rough effort to prevent modifications to `dest` from +affecting the returned object. No guarantees are provided. + +Custom arrays that wrap or use fields containing arrays that might alias against other +external objects should provide a [`Base.dataids`](@ref) implementation. + +This function must return an object of exactly the same type as `A` for performance and type +stability. Mutable custom arrays for which [`copy(A)`](@ref) is not `typeof(A)` should +provide a [`Base.unaliascopy`](@ref) implementation. + +See also [`Base.mightalias`](@ref). +""" +unalias(dest, A::AbstractArray) = mightalias(dest, A) ? unaliascopy(A) : A +unalias(dest, A::AbstractRange) = A +unalias(dest, A) = A + +""" + Base.unaliascopy(A) + +Make a preventative copy of `A` in an operation where `A` [`Base.mightalias`](@ref) against +another array in order to preserve consistent semantics as that other array is mutated. + +This must return an object of the same type as `A` to preserve optimal performance in the +much more common case where aliasing does not occur. By default, +`unaliascopy(A::AbstractArray)` will attempt to use [`copy(A)`](@ref), but in cases where +`copy(A)` is not a `typeof(A)`, then the array should provide a custom implementation of +`Base.unaliascopy(A)`. +""" +unaliascopy(A::Array) = copy(A) +unaliascopy(A::AbstractArray)::typeof(A) = (@_noinline_meta; _unaliascopy(A, copy(A))) +_unaliascopy(A::T, C::T) where {T} = C +_unaliascopy(A, C) = throw(ArgumentError(""" + an array of type `$(typeof(A).name)` shares memory with another argument and must + make a preventative copy of itself in order to maintain consistent semantics, + but `copy(A)` returns a new array of type `$(typeof(C))`. To fix, implement: + `Base.unaliascopy(A::$(typeof(A).name))::typeof(A)`""")) +unaliascopy(A) = A + +""" + Base.mightalias(A::AbstractArray, B::AbstractArray) + +Perform a conservative test to check if arrays `A` and `B` might share the same memory. + +By default, this simply checks if either of the arrays reference the same memory +regions, as identified by their [`Base.dataids`](@ref). +""" +mightalias(A::AbstractArray, B::AbstractArray) = !isbits(A) && !isbits(B) && !_isdisjoint(dataids(A), dataids(B)) +mightalias(x, y) = false + +_isdisjoint(as::Tuple{}, bs::Tuple{}) = true +_isdisjoint(as::Tuple{}, bs::Tuple{UInt}) = true +_isdisjoint(as::Tuple{}, bs::Tuple) = true +_isdisjoint(as::Tuple{UInt}, bs::Tuple{}) = true +_isdisjoint(as::Tuple{UInt}, bs::Tuple{UInt}) = as[1] != bs[1] +_isdisjoint(as::Tuple{UInt}, bs::Tuple) = !(as[1] in bs) +_isdisjoint(as::Tuple, bs::Tuple{}) = true +_isdisjoint(as::Tuple, bs::Tuple{UInt}) = !(bs[1] in as) +_isdisjoint(as::Tuple, bs::Tuple) = !(as[1] in bs) && _isdisjoint(tail(as), bs) + +""" + Base.dataids(A::AbstractArray) + +Return a tuple of `UInt`s that represent the mutable data segments of an array. + +Custom arrays that would like to opt-in to aliasing detection of their component +parts can specialize this method to return the concatenation of the `dataids` of +their component parts. A typical definition for an array that wraps a parent is +`Base.dataids(C::CustomArray) = dataids(C.parent)`. +""" +dataids(A::AbstractArray) = (UInt(objectid(A)),) +dataids(A::Array) = (UInt(pointer(A)),) +dataids(::AbstractRange) = () +dataids(x) = () + +## get (getindex with a default value) ## + +RangeVecIntList{A<:AbstractVector{Int}} = Union{Tuple{Vararg{Union{AbstractRange, AbstractVector{Int}}}}, + AbstractVector{UnitRange{Int}}, AbstractVector{AbstractRange{Int}}, AbstractVector{A}} + +get(A::AbstractArray, i::Integer, default) = checkbounds(Bool, A, i) ? A[i] : default +get(A::AbstractArray, I::Tuple{}, default) = checkbounds(Bool, A) ? A[] : default +get(A::AbstractArray, I::Dims, default) = checkbounds(Bool, A, I...) ? A[I...] : default + +function get!(X::AbstractVector{T}, A::AbstractVector, I::Union{AbstractRange,AbstractVector{Int}}, default::T) where T + # 1d is not linear indexing + ind = findall(in(axes1(A)), I) + X[ind] = A[I[ind]] + Xind = axes1(X) + X[first(Xind):first(ind)-1] = default + X[last(ind)+1:last(Xind)] = default + X +end +function get!(X::AbstractArray{T}, A::AbstractArray, I::Union{AbstractRange,AbstractVector{Int}}, default::T) where T + # Linear indexing + ind = findall(in(1:length(A)), I) + X[ind] = A[I[ind]] + fill!(view(X, 1:first(ind)-1), default) + fill!(view(X, last(ind)+1:length(X)), default) + X +end + +get(A::AbstractArray, I::AbstractRange, default) = get!(similar(A, typeof(default), index_shape(I)), A, I, default) + +function get!(X::AbstractArray{T}, A::AbstractArray, I::RangeVecIntList, default::T) where T + fill!(X, default) + dst, src = indcopy(size(A), I) + X[dst...] = A[src...] + X +end + +get(A::AbstractArray, I::RangeVecIntList, default) = + get!(similar(A, typeof(default), index_shape(I...)), A, I, default) + +## structured matrix methods ## +replace_in_print_matrix(A::AbstractMatrix,i::Integer,j::Integer,s::AbstractString) = s +replace_in_print_matrix(A::AbstractVector,i::Integer,j::Integer,s::AbstractString) = s + +## Concatenation ## +eltypeof(x) = typeof(x) +eltypeof(x::AbstractArray) = eltype(x) + +promote_eltypeof() = Bottom +promote_eltypeof(v1, vs...) = promote_type(eltypeof(v1), promote_eltypeof(vs...)) + +promote_eltype() = Bottom +promote_eltype(v1, vs...) = promote_type(eltype(v1), promote_eltype(vs...)) + +#TODO: ERROR CHECK +_cat(catdim::Integer) = Vector{Any}() + +typed_vcat(::Type{T}) where {T} = Vector{T}() +typed_hcat(::Type{T}) where {T} = Vector{T}() + +## cat: special cases +vcat(X::T...) where {T} = T[ X[i] for i=1:length(X) ] +vcat(X::T...) where {T<:Number} = T[ X[i] for i=1:length(X) ] +hcat(X::T...) where {T} = T[ X[j] for i=1:1, j=1:length(X) ] +hcat(X::T...) where {T<:Number} = T[ X[j] for i=1:1, j=1:length(X) ] + +vcat(X::Number...) = hvcat_fill(Vector{promote_typeof(X...)}(undef, length(X)), X) +hcat(X::Number...) = hvcat_fill(Matrix{promote_typeof(X...)}(undef, 1,length(X)), X) +typed_vcat(::Type{T}, X::Number...) where {T} = hvcat_fill(Vector{T}(undef, length(X)), X) +typed_hcat(::Type{T}, X::Number...) where {T} = hvcat_fill(Matrix{T}(undef, 1,length(X)), X) + +vcat(V::AbstractVector...) = typed_vcat(promote_eltype(V...), V...) +vcat(V::AbstractVector{T}...) where {T} = typed_vcat(T, V...) + +# FIXME: this alias would better be Union{AbstractVector{T}, Tuple{Vararg{T}}} +# and method signatures should do AbstractVecOrTuple{<:T} when they want covariance, +# but that solution currently fails (see #27188 and #27224) +AbstractVecOrTuple{T} = Union{AbstractVector{<:T}, Tuple{Vararg{T}}} + +function _typed_vcat(::Type{T}, V::AbstractVecOrTuple{AbstractVector}) where T + n::Int = 0 + for Vk in V + n += length(Vk) + end + a = similar(V[1], T, n) + pos = 1 + for k=1:length(V) + Vk = V[k] + p1 = pos+length(Vk)-1 + a[pos:p1] = Vk + pos = p1+1 + end + a +end + +typed_hcat(::Type{T}, A::AbstractVecOrMat...) where {T} = _typed_hcat(T, A) + +hcat(A::AbstractVecOrMat...) = typed_hcat(promote_eltype(A...), A...) +hcat(A::AbstractVecOrMat{T}...) where {T} = typed_hcat(T, A...) + +function _typed_hcat(::Type{T}, A::AbstractVecOrTuple{AbstractVecOrMat}) where T + nargs = length(A) + nrows = size(A[1], 1) + ncols = 0 + dense = true + for j = 1:nargs + Aj = A[j] + if size(Aj, 1) != nrows + throw(ArgumentError("number of rows of each array must match (got $(map(x->size(x,1), A)))")) + end + dense &= isa(Aj,Array) + nd = ndims(Aj) + ncols += (nd==2 ? size(Aj,2) : 1) + end + B = similar(A[1], T, nrows, ncols) + pos = 1 + if dense + for k=1:nargs + Ak = A[k] + n = length(Ak) + copyto!(B, pos, Ak, 1, n) + pos += n + end + else + for k=1:nargs + Ak = A[k] + p1 = pos+(isa(Ak,AbstractMatrix) ? size(Ak, 2) : 1)-1 + B[:, pos:p1] = Ak + pos = p1+1 + end + end + return B +end + +vcat(A::AbstractVecOrMat...) = typed_vcat(promote_eltype(A...), A...) +vcat(A::AbstractVecOrMat{T}...) where {T} = typed_vcat(T, A...) + +function _typed_vcat(::Type{T}, A::AbstractVecOrTuple{AbstractVecOrMat}) where T + nargs = length(A) + nrows = sum(a->size(a, 1), A)::Int + ncols = size(A[1], 2) + for j = 2:nargs + if size(A[j], 2) != ncols + throw(ArgumentError("number of columns of each array must match (got $(map(x->size(x,2), A)))")) + end + end + B = similar(A[1], T, nrows, ncols) + pos = 1 + for k=1:nargs + Ak = A[k] + p1 = pos+size(Ak,1)-1 + B[pos:p1, :] = Ak + pos = p1+1 + end + return B +end + +typed_vcat(::Type{T}, A::AbstractVecOrMat...) where {T} = _typed_vcat(T, A) + +reduce(::typeof(vcat), A::AbstractVector{<:AbstractVecOrMat}) = + _typed_vcat(mapreduce(eltype, promote_type, A), A) + +reduce(::typeof(hcat), A::AbstractVector{<:AbstractVecOrMat}) = + _typed_hcat(mapreduce(eltype, promote_type, A), A) + +## cat: general case + +# helper functions +cat_size(A) = (1,) +cat_size(A::AbstractArray) = size(A) +cat_size(A, d) = 1 +cat_size(A::AbstractArray, d) = size(A, d) + +cat_indices(A, d) = OneTo(1) +cat_indices(A::AbstractArray, d) = axes(A, d) + +cat_similar(A, T, shape) = Array{T}(undef, shape) +cat_similar(A::AbstractArray, T, shape) = similar(A, T, shape) + +cat_shape(dims, shape::Tuple) = shape +@inline cat_shape(dims, shape::Tuple, nshape::Tuple, shapes::Tuple...) = + cat_shape(dims, _cshp(1, dims, shape, nshape), shapes...) + +_cshp(ndim::Int, ::Tuple{}, ::Tuple{}, ::Tuple{}) = () +_cshp(ndim::Int, ::Tuple{}, ::Tuple{}, nshape) = nshape +_cshp(ndim::Int, dims, ::Tuple{}, ::Tuple{}) = ntuple(b -> 1, Val(length(dims))) +@inline _cshp(ndim::Int, dims, shape, ::Tuple{}) = + (shape[1] + dims[1], _cshp(ndim + 1, tail(dims), tail(shape), ())...) +@inline _cshp(ndim::Int, dims, ::Tuple{}, nshape) = + (nshape[1], _cshp(ndim + 1, tail(dims), (), tail(nshape))...) +@inline function _cshp(ndim::Int, ::Tuple{}, shape, ::Tuple{}) + _cs(ndim, shape[1], 1) + (1, _cshp(ndim + 1, (), tail(shape), ())...) +end +@inline function _cshp(ndim::Int, ::Tuple{}, shape, nshape) + next = _cs(ndim, shape[1], nshape[1]) + (next, _cshp(ndim + 1, (), tail(shape), tail(nshape))...) +end +@inline function _cshp(ndim::Int, dims, shape, nshape) + a = shape[1] + b = nshape[1] + next = dims[1] ? a + b : _cs(ndim, a, b) + (next, _cshp(ndim + 1, tail(dims), tail(shape), tail(nshape))...) +end + +_cs(d, a, b) = (a == b ? a : throw(DimensionMismatch( + "mismatch in dimension $d (expected $a got $b)"))) + +function dims2cat(::Val{n}) where {n} + n <= 0 && throw(ArgumentError("cat dimension must be a positive integer, but got $n")) + ntuple(i -> (i == n), Val(n)) +end + +function dims2cat(dims) + if any(dims .<= 0) + throw(ArgumentError("All cat dimensions must be positive integers, but got $dims")) + end + ntuple(in(dims), maximum(dims)) +end + +_cat(dims, X...) = cat_t(promote_eltypeof(X...), X...; dims=dims) + +@inline cat_t(::Type{T}, X...; dims) where {T} = _cat_t(dims, T, X...) +@inline function _cat_t(dims, T::Type, X...) + catdims = dims2cat(dims) + shape = cat_shape(catdims, (), map(cat_size, X)...) + A = cat_similar(X[1], T, shape) + if T <: Number && count(!iszero, catdims) > 1 + fill!(A, zero(T)) + end + return __cat(A, shape, catdims, X...) +end + +function __cat(A, shape::NTuple{N}, catdims, X...) where N + offsets = zeros(Int, N) + inds = Vector{UnitRange{Int}}(undef, N) + concat = copyto!(zeros(Bool, N), catdims) + for x in X + for i = 1:N + if concat[i] + inds[i] = offsets[i] .+ cat_indices(x, i) + offsets[i] += cat_size(x, i) + else + inds[i] = 1:shape[i] + end + end + I::NTuple{N, UnitRange{Int}} = (inds...,) + if x isa AbstractArray + A[I...] = x + else + fill!(view(A, I...), x) + end + end + return A +end + +""" + vcat(A...) + +Concatenate along dimension 1. + +# Examples +```jldoctest +julia> a = [1 2 3 4 5] +1×5 Array{Int64,2}: + 1 2 3 4 5 + +julia> b = [6 7 8 9 10; 11 12 13 14 15] +2×5 Array{Int64,2}: + 6 7 8 9 10 + 11 12 13 14 15 + +julia> vcat(a,b) +3×5 Array{Int64,2}: + 1 2 3 4 5 + 6 7 8 9 10 + 11 12 13 14 15 + +julia> c = ([1 2 3], [4 5 6]) +([1 2 3], [4 5 6]) + +julia> vcat(c...) +2×3 Array{Int64,2}: + 1 2 3 + 4 5 6 +``` +""" +vcat(X...) = cat(X...; dims=Val(1)) +""" + hcat(A...) + +Concatenate along dimension 2. + +# Examples +```jldoctest +julia> a = [1; 2; 3; 4; 5] +5-element Array{Int64,1}: + 1 + 2 + 3 + 4 + 5 + +julia> b = [6 7; 8 9; 10 11; 12 13; 14 15] +5×2 Array{Int64,2}: + 6 7 + 8 9 + 10 11 + 12 13 + 14 15 + +julia> hcat(a,b) +5×3 Array{Int64,2}: + 1 6 7 + 2 8 9 + 3 10 11 + 4 12 13 + 5 14 15 + +julia> c = ([1; 2; 3], [4; 5; 6]) +([1, 2, 3], [4, 5, 6]) + +julia> hcat(c...) +3×2 Array{Int64,2}: + 1 4 + 2 5 + 3 6 +``` +""" +hcat(X...) = cat(X...; dims=Val(2)) + +typed_vcat(T::Type, X...) = cat_t(T, X...; dims=Val(1)) +typed_hcat(T::Type, X...) = cat_t(T, X...; dims=Val(2)) + +""" + cat(A...; dims=dims) + +Concatenate the input arrays along the specified dimensions in the iterable `dims`. For +dimensions not in `dims`, all input arrays should have the same size, which will also be the +size of the output array along that dimension. For dimensions in `dims`, the size of the +output array is the sum of the sizes of the input arrays along that dimension. If `dims` is +a single number, the different arrays are tightly stacked along that dimension. If `dims` is +an iterable containing several dimensions, this allows one to construct block diagonal +matrices and their higher-dimensional analogues by simultaneously increasing several +dimensions for every new input array and putting zero blocks elsewhere. For example, +`cat(matrices...; dims=(1,2))` builds a block diagonal matrix, i.e. a block matrix with +`matrices[1]`, `matrices[2]`, ... as diagonal blocks and matching zero blocks away from the +diagonal. +""" +@inline cat(A...; dims) = _cat(dims, A...) +_cat(catdims, A::AbstractArray{T}...) where {T} = cat_t(T, A...; dims=catdims) + +# The specializations for 1 and 2 inputs are important +# especially when running with --inline=no, see #11158 +vcat(A::AbstractArray) = cat(A; dims=Val(1)) +vcat(A::AbstractArray, B::AbstractArray) = cat(A, B; dims=Val(1)) +vcat(A::AbstractArray...) = cat(A...; dims=Val(1)) +hcat(A::AbstractArray) = cat(A; dims=Val(2)) +hcat(A::AbstractArray, B::AbstractArray) = cat(A, B; dims=Val(2)) +hcat(A::AbstractArray...) = cat(A...; dims=Val(2)) + +typed_vcat(T::Type, A::AbstractArray) = cat_t(T, A; dims=Val(1)) +typed_vcat(T::Type, A::AbstractArray, B::AbstractArray) = cat_t(T, A, B; dims=Val(1)) +typed_vcat(T::Type, A::AbstractArray...) = cat_t(T, A...; dims=Val(1)) +typed_hcat(T::Type, A::AbstractArray) = cat_t(T, A; dims=Val(2)) +typed_hcat(T::Type, A::AbstractArray, B::AbstractArray) = cat_t(T, A, B; dims=Val(2)) +typed_hcat(T::Type, A::AbstractArray...) = cat_t(T, A...; dims=Val(2)) + +# 2d horizontal and vertical concatenation + +function hvcat(nbc::Integer, as...) + # nbc = # of block columns + n = length(as) + mod(n,nbc) != 0 && + throw(ArgumentError("number of arrays $n is not a multiple of the requested number of block columns $nbc")) + nbr = div(n,nbc) + hvcat(ntuple(i->nbc, nbr), as...) +end + +""" + hvcat(rows::Tuple{Vararg{Int}}, values...) + +Horizontal and vertical concatenation in one call. This function is called for block matrix +syntax. The first argument specifies the number of arguments to concatenate in each block +row. + +# Examples +```jldoctest +julia> a, b, c, d, e, f = 1, 2, 3, 4, 5, 6 +(1, 2, 3, 4, 5, 6) + +julia> [a b c; d e f] +2×3 Array{Int64,2}: + 1 2 3 + 4 5 6 + +julia> hvcat((3,3), a,b,c,d,e,f) +2×3 Array{Int64,2}: + 1 2 3 + 4 5 6 + +julia> [a b;c d; e f] +3×2 Array{Int64,2}: + 1 2 + 3 4 + 5 6 + +julia> hvcat((2,2,2), a,b,c,d,e,f) +3×2 Array{Int64,2}: + 1 2 + 3 4 + 5 6 +``` + +If the first argument is a single integer `n`, then all block rows are assumed to have `n` +block columns. +""" +hvcat(rows::Tuple{Vararg{Int}}, xs::AbstractVecOrMat...) = typed_hvcat(promote_eltype(xs...), rows, xs...) +hvcat(rows::Tuple{Vararg{Int}}, xs::AbstractVecOrMat{T}...) where {T} = typed_hvcat(T, rows, xs...) + +function typed_hvcat(::Type{T}, rows::Tuple{Vararg{Int}}, as::AbstractVecOrMat...) where T + nbr = length(rows) # number of block rows + + nc = 0 + for i=1:rows[1] + nc += size(as[i],2) + end + + nr = 0 + a = 1 + for i = 1:nbr + nr += size(as[a],1) + a += rows[i] + end + + out = similar(as[1], T, nr, nc) + + a = 1 + r = 1 + for i = 1:nbr + c = 1 + szi = size(as[a],1) + for j = 1:rows[i] + Aj = as[a+j-1] + szj = size(Aj,2) + if size(Aj,1) != szi + throw(ArgumentError("mismatched height in block row $(i) (expected $szi, got $(size(Aj,1)))")) + end + if c-1+szj > nc + throw(ArgumentError("block row $(i) has mismatched number of columns (expected $nc, got $(c-1+szj))")) + end + out[r:r-1+szi, c:c-1+szj] = Aj + c += szj + end + if c != nc+1 + throw(ArgumentError("block row $(i) has mismatched number of columns (expected $nc, got $(c-1))")) + end + r += szi + a += rows[i] + end + out +end + +hvcat(rows::Tuple{Vararg{Int}}) = [] +typed_hvcat(::Type{T}, rows::Tuple{Vararg{Int}}) where {T} = Vector{T}() + +function hvcat(rows::Tuple{Vararg{Int}}, xs::T...) where T<:Number + nr = length(rows) + nc = rows[1] + + a = Matrix{T}(undef, nr, nc) + if length(a) != length(xs) + throw(ArgumentError("argument count does not match specified shape (expected $(length(a)), got $(length(xs)))")) + end + k = 1 + @inbounds for i=1:nr + if nc != rows[i] + throw(ArgumentError("row $(i) has mismatched number of columns (expected $nc, got $(rows[i]))")) + end + for j=1:nc + a[i,j] = xs[k] + k += 1 + end + end + a +end + +function hvcat_fill(a::Array, xs::Tuple) + k = 1 + nr, nc = size(a,1), size(a,2) + for i=1:nr + @inbounds for j=1:nc + a[i,j] = xs[k] + k += 1 + end + end + a +end + +hvcat(rows::Tuple{Vararg{Int}}, xs::Number...) = typed_hvcat(promote_typeof(xs...), rows, xs...) +hvcat(rows::Tuple{Vararg{Int}}, xs...) = typed_hvcat(promote_eltypeof(xs...), rows, xs...) + +function typed_hvcat(::Type{T}, rows::Tuple{Vararg{Int}}, xs::Number...) where T + nr = length(rows) + nc = rows[1] + for i = 2:nr + if nc != rows[i] + throw(ArgumentError("row $(i) has mismatched number of columns (expected $nc, got $(rows[i]))")) + end + end + len = length(xs) + if nr*nc != len + throw(ArgumentError("argument count $(len) does not match specified shape $((nr,nc))")) + end + hvcat_fill(Matrix{T}(undef, nr, nc), xs) +end + +function typed_hvcat(::Type{T}, rows::Tuple{Vararg{Int}}, as...) where T + nbr = length(rows) # number of block rows + rs = Vector{Any}(undef, nbr) + a = 1 + for i = 1:nbr + rs[i] = typed_hcat(T, as[a:a-1+rows[i]]...) + a += rows[i] + end + T[rs...;] +end + +## Reductions and accumulates ## + +function isequal(A::AbstractArray, B::AbstractArray) + if A === B return true end + if axes(A) != axes(B) + return false + end + for (a, b) in zip(A, B) + if !isequal(a, b) + return false + end + end + return true +end + +function cmp(A::AbstractVector, B::AbstractVector) + for (a, b) in zip(A, B) + if !isequal(a, b) + return isless(a, b) ? -1 : 1 + end + end + return cmp(length(A), length(B)) +end + +isless(A::AbstractVector, B::AbstractVector) = cmp(A, B) < 0 + +function (==)(A::AbstractArray, B::AbstractArray) + if axes(A) != axes(B) + return false + end + anymissing = false + for (a, b) in zip(A, B) + eq = (a == b) + if ismissing(eq) + anymissing = true + elseif !eq + return false + end + end + return anymissing ? missing : true +end + +# _sub2ind and _ind2sub +# fallbacks +function _sub2ind(A::AbstractArray, I...) + @_inline_meta + _sub2ind(axes(A), I...) +end + +function _ind2sub(A::AbstractArray, ind) + @_inline_meta + _ind2sub(axes(A), ind) +end + +# 0-dimensional arrays and indexing with [] +_sub2ind(::Tuple{}) = 1 +_sub2ind(::DimsInteger) = 1 +_sub2ind(::Indices) = 1 +_sub2ind(::Tuple{}, I::Integer...) = (@_inline_meta; _sub2ind_recurse((), 1, 1, I...)) + +# Generic cases +_sub2ind(dims::DimsInteger, I::Integer...) = (@_inline_meta; _sub2ind_recurse(dims, 1, 1, I...)) +_sub2ind(inds::Indices, I::Integer...) = (@_inline_meta; _sub2ind_recurse(inds, 1, 1, I...)) +# In 1d, there's a question of whether we're doing cartesian indexing +# or linear indexing. Support only the former. +_sub2ind(inds::Indices{1}, I::Integer...) = + throw(ArgumentError("Linear indexing is not defined for one-dimensional arrays")) +_sub2ind(inds::Tuple{OneTo}, I::Integer...) = (@_inline_meta; _sub2ind_recurse(inds, 1, 1, I...)) # only OneTo is safe +_sub2ind(inds::Tuple{OneTo}, i::Integer) = i + +_sub2ind_recurse(::Any, L, ind) = ind +function _sub2ind_recurse(::Tuple{}, L, ind, i::Integer, I::Integer...) + @_inline_meta + _sub2ind_recurse((), L, ind+(i-1)*L, I...) +end +function _sub2ind_recurse(inds, L, ind, i::Integer, I::Integer...) + @_inline_meta + r1 = inds[1] + _sub2ind_recurse(tail(inds), nextL(L, r1), ind+offsetin(i, r1)*L, I...) +end + +nextL(L, l::Integer) = L*l +nextL(L, r::AbstractUnitRange) = L*unsafe_length(r) +nextL(L, r::Slice) = L*unsafe_length(r.indices) +offsetin(i, l::Integer) = i-1 +offsetin(i, r::AbstractUnitRange) = i-first(r) + +_ind2sub(::Tuple{}, ind::Integer) = (@_inline_meta; ind == 1 ? () : throw(BoundsError())) +_ind2sub(dims::DimsInteger, ind::Integer) = (@_inline_meta; _ind2sub_recurse(dims, ind-1)) +_ind2sub(inds::Indices, ind::Integer) = (@_inline_meta; _ind2sub_recurse(inds, ind-1)) +_ind2sub(inds::Indices{1}, ind::Integer) = + throw(ArgumentError("Linear indexing is not defined for one-dimensional arrays")) +_ind2sub(inds::Tuple{OneTo}, ind::Integer) = (ind,) + +_ind2sub_recurse(::Tuple{}, ind) = (ind+1,) +function _ind2sub_recurse(indslast::NTuple{1}, ind) + @_inline_meta + (_lookup(ind, indslast[1]),) +end +function _ind2sub_recurse(inds, ind) + @_inline_meta + r1 = inds[1] + indnext, f, l = _div(ind, r1) + (ind-l*indnext+f, _ind2sub_recurse(tail(inds), indnext)...) +end + +_lookup(ind, d::Integer) = ind+1 +_lookup(ind, r::AbstractUnitRange) = ind+first(r) +_div(ind, d::Integer) = div(ind, d), 1, d +_div(ind, r::AbstractUnitRange) = (d = unsafe_length(r); (div(ind, d), first(r), d)) + +# Vectorized forms +function _sub2ind(inds::Indices{1}, I1::AbstractVector{T}, I::AbstractVector{T}...) where T<:Integer + throw(ArgumentError("Linear indexing is not defined for one-dimensional arrays")) +end +_sub2ind(inds::Tuple{OneTo}, I1::AbstractVector{T}, I::AbstractVector{T}...) where {T<:Integer} = + _sub2ind_vecs(inds, I1, I...) +_sub2ind(inds::Union{DimsInteger,Indices}, I1::AbstractVector{T}, I::AbstractVector{T}...) where {T<:Integer} = + _sub2ind_vecs(inds, I1, I...) +function _sub2ind_vecs(inds, I::AbstractVector...) + I1 = I[1] + Iinds = axes1(I1) + for j = 2:length(I) + axes1(I[j]) == Iinds || throw(DimensionMismatch("indices of I[1] ($(Iinds)) does not match indices of I[$j] ($(axes1(I[j])))")) + end + Iout = similar(I1) + _sub2ind!(Iout, inds, Iinds, I) + Iout +end + +function _sub2ind!(Iout, inds, Iinds, I) + @_noinline_meta + for i in Iinds + # Iout[i] = _sub2ind(inds, map(Ij -> Ij[i], I)...) + Iout[i] = sub2ind_vec(inds, i, I) + end + Iout +end + +sub2ind_vec(inds, i, I) = (@_inline_meta; _sub2ind(inds, _sub2ind_vec(i, I...)...)) +_sub2ind_vec(i, I1, I...) = (@_inline_meta; (I1[i], _sub2ind_vec(i, I...)...)) +_sub2ind_vec(i) = () + +function _ind2sub(inds::Union{DimsInteger{N},Indices{N}}, ind::AbstractVector{<:Integer}) where N + M = length(ind) + t = ntuple(n->similar(ind),Val(N)) + for (i,idx) in pairs(IndexLinear(), ind) + sub = _ind2sub(inds, idx) + for j = 1:N + t[j][i] = sub[j] + end + end + t +end + +## iteration utilities ## + +""" + foreach(f, c...) -> Nothing + +Call function `f` on each element of iterable `c`. +For multiple iterable arguments, `f` is called elementwise. +`foreach` should be used instead of `map` when the results of `f` are not +needed, for example in `foreach(println, array)`. + +# Examples +```jldoctest +julia> a = 1:3:7; + +julia> foreach(x -> println(x^2), a) +1 +16 +49 +``` +""" +foreach(f) = (f(); nothing) +foreach(f, itr) = (for x in itr; f(x); end; nothing) +foreach(f, itrs...) = (for z in zip(itrs...); f(z...); end; nothing) + +## map over arrays ## + +## transform any set of dimensions +## dims specifies which dimensions will be transformed. for example +## dims==1:2 will call f on all slices A[:,:,...] +""" + mapslices(f, A; dims) + +Transform the given dimensions of array `A` using function `f`. `f` is called on each slice +of `A` of the form `A[...,:,...,:,...]`. `dims` is an integer vector specifying where the +colons go in this expression. The results are concatenated along the remaining dimensions. +For example, if `dims` is `[1,2]` and `A` is 4-dimensional, `f` is called on `A[:,:,i,j]` +for all `i` and `j`. + +# Examples +```jldoctest +julia> a = reshape(Vector(1:16),(2,2,2,2)) +2×2×2×2 Array{Int64,4}: +[:, :, 1, 1] = + 1 3 + 2 4 + +[:, :, 2, 1] = + 5 7 + 6 8 + +[:, :, 1, 2] = + 9 11 + 10 12 + +[:, :, 2, 2] = + 13 15 + 14 16 + +julia> mapslices(sum, a, dims = [1,2]) +1×1×2×2 Array{Int64,4}: +[:, :, 1, 1] = + 10 + +[:, :, 2, 1] = + 26 + +[:, :, 1, 2] = + 42 + +[:, :, 2, 2] = + 58 +``` +""" +function mapslices(f, A::AbstractArray; dims) + if isempty(dims) + return map(f,A) + end + if !isa(dims, AbstractVector) + dims = [dims...] + end + + dimsA = [axes(A)...] + ndimsA = ndims(A) + alldims = [1:ndimsA;] + + otherdims = setdiff(alldims, dims) + + idx = Any[first(ind) for ind in axes(A)] + itershape = tuple(dimsA[otherdims]...) + for d in dims + idx[d] = Slice(axes(A, d)) + end + + # Apply the function to the first slice in order to determine the next steps + Aslice = A[idx...] + r1 = f(Aslice) + # In some cases, we can re-use the first slice for a dramatic performance + # increase. The slice itself must be mutable and the result cannot contain + # any mutable containers. The following errs on the side of being overly + # strict (#18570 & #21123). + safe_for_reuse = isa(Aslice, StridedArray) && + (isa(r1, Number) || (isa(r1, AbstractArray) && eltype(r1) <: Number)) + + # determine result size and allocate + Rsize = copy(dimsA) + # TODO: maybe support removing dimensions + if !isa(r1, AbstractArray) || ndims(r1) == 0 + # If the result of f on a single slice is a scalar then we add singleton + # dimensions. When adding the dimensions, we have to respect the + # index type of the input array (e.g. in the case of OffsetArrays) + tmp = similar(Aslice, typeof(r1), reduced_indices(Aslice, 1:ndims(Aslice))) + tmp[firstindex(tmp)] = r1 + r1 = tmp + end + nextra = max(0, length(dims)-ndims(r1)) + if eltype(Rsize) == Int + Rsize[dims] = [size(r1)..., ntuple(d->1, nextra)...] + else + Rsize[dims] = [axes(r1)..., ntuple(d->OneTo(1), nextra)...] + end + R = similar(r1, tuple(Rsize...,)) + + ridx = Any[map(first, axes(R))...] + for d in dims + ridx[d] = axes(R,d) + end + + concatenate_setindex!(R, r1, ridx...) + + nidx = length(otherdims) + indices = Iterators.drop(CartesianIndices(itershape), 1) # skip the first element, we already handled it + inner_mapslices!(safe_for_reuse, indices, nidx, idx, otherdims, ridx, Aslice, A, f, R) +end + +@noinline function inner_mapslices!(safe_for_reuse, indices, nidx, idx, otherdims, ridx, Aslice, A, f, R) + if safe_for_reuse + # when f returns an array, R[ridx...] = f(Aslice) line copies elements, + # so we can reuse Aslice + for I in indices + replace_tuples!(nidx, idx, ridx, otherdims, I) + _unsafe_getindex!(Aslice, A, idx...) + concatenate_setindex!(R, f(Aslice), ridx...) + end + else + # we can't guarantee safety (#18524), so allocate new storage for each slice + for I in indices + replace_tuples!(nidx, idx, ridx, otherdims, I) + concatenate_setindex!(R, f(A[idx...]), ridx...) + end + end + + return R +end + +function replace_tuples!(nidx, idx, ridx, otherdims, I) + for i in 1:nidx + idx[otherdims[i]] = ridx[otherdims[i]] = I.I[i] + end +end + +concatenate_setindex!(R, v, I...) = (R[I...] .= (v,); R) +concatenate_setindex!(R, X::AbstractArray, I...) = (R[I...] = X) + +## 1 argument + +function map!(f::F, dest::AbstractArray, A::AbstractArray) where F + for (i,j) in zip(eachindex(dest),eachindex(A)) + val = f(@inbounds A[j]) + @inbounds dest[i] = val + end + return dest +end + +# map on collections +map(f, A::AbstractArray) = collect_similar(A, Generator(f,A)) + +# default to returning an Array for `map` on general iterators +""" + map(f, c...) -> collection + +Transform collection `c` by applying `f` to each element. For multiple collection arguments, +apply `f` elementwise. + +See also: [`mapslices`](@ref) + +# Examples +```jldoctest +julia> map(x -> x * 2, [1, 2, 3]) +3-element Array{Int64,1}: + 2 + 4 + 6 + +julia> map(+, [1, 2, 3], [10, 20, 30]) +3-element Array{Int64,1}: + 11 + 22 + 33 +``` +""" +map(f, A) = collect(Generator(f,A)) + +map(f, ::AbstractDict) = error("map is not defined on dictionaries") +map(f, ::AbstractSet) = error("map is not defined on sets") + +## 2 argument +function map!(f::F, dest::AbstractArray, A::AbstractArray, B::AbstractArray) where F + for (i, j, k) in zip(eachindex(dest), eachindex(A), eachindex(B)) + @inbounds a, b = A[j], B[k] + val = f(a, b) + @inbounds dest[i] = val + end + return dest +end + +## N argument + +@inline ith_all(i, ::Tuple{}) = () +function ith_all(i, as) + @_propagate_inbounds_meta + return (as[1][i], ith_all(i, tail(as))...) +end + +function map_n!(f::F, dest::AbstractArray, As) where F + idxs1 = LinearIndices(As[1]) + @boundscheck LinearIndices(dest) == idxs1 && all(x -> LinearIndices(x) == idxs1, As) + for i = idxs1 + @inbounds I = ith_all(i, As) + val = f(I...) + @inbounds dest[i] = val + end + return dest +end + +""" + map!(function, destination, collection...) + +Like [`map`](@ref), but stores the result in `destination` rather than a new +collection. `destination` must be at least as large as the first collection. + +# Examples +```jldoctest +julia> a = zeros(3); + +julia> map!(x -> x * 2, a, [1, 2, 3]); + +julia> a +3-element Array{Float64,1}: + 2.0 + 4.0 + 6.0 +``` +""" +map!(f::F, dest::AbstractArray, As::AbstractArray...) where {F} = map_n!(f, dest, As) + +map(f) = f() +map(f, iters...) = collect(Generator(f, iters...)) + +# multi-item push!, pushfirst! (built on top of type-specific 1-item version) +# (note: must not cause a dispatch loop when 1-item case is not defined) +push!(A, a, b) = push!(push!(A, a), b) +push!(A, a, b, c...) = push!(push!(A, a, b), c...) +pushfirst!(A, a, b) = pushfirst!(pushfirst!(A, b), a) +pushfirst!(A, a, b, c...) = pushfirst!(pushfirst!(A, c...), a, b) + +## hashing AbstractArray ## + +function hash(A::AbstractArray, h::UInt) + h = hash(AbstractArray, h) + # Axes are themselves AbstractArrays, so hashing them directly would stack overflow + # Instead hash the tuple of firsts and lasts along each dimension + h = hash(map(first, axes(A)), h) + h = hash(map(last, axes(A)), h) + isempty(A) && return h + + # Goal: Hash approximately log(N) entries with a higher density of hashed elements + # weighted towards the end and special consideration for repeated values. Colliding + # hashes will often subsequently be compared by equality -- and equality between arrays + # works elementwise forwards and is short-circuiting. This means that a collision + # between arrays that differ by elements at the beginning is cheaper than one where the + # difference is towards the end. Furthermore, blindly choosing log(N) entries from a + # sparse array will likely only choose the same element repeatedly (zero in this case). + + # To achieve this, we work backwards, starting by hashing the last element of the + # array. After hashing each element, we skip `fibskip` elements, where `fibskip` + # is pulled from the Fibonacci sequence -- Fibonacci was chosen as a simple + # ~O(log(N)) algorithm that ensures we don't hit a common divisor of a dimension + # and only end up hashing one slice of the array (as might happen with powers of + # two). Finally, we find the next distinct value from the one we just hashed. + + # This is a little tricky since skipping an integer number of values inherently works + # with linear indices, but `findprev` uses `keys`. Hoist out the conversion "maps": + ks = keys(A) + key_to_linear = LinearIndices(ks) # Index into this map to compute the linear index + linear_to_key = vec(ks) # And vice-versa + + # Start at the last index + keyidx = last(ks) + linidx = key_to_linear[keyidx] + fibskip = prevfibskip = oneunit(linidx) + n = 0 + while true + n += 1 + # Hash the current key-index and its element + elt = A[keyidx] + h = hash(keyidx=>elt, h) + + # Skip backwards a Fibonacci number of indices -- this is a linear index operation + linidx = key_to_linear[keyidx] + linidx <= fibskip && break + linidx -= fibskip + keyidx = linear_to_key[linidx] + + # Only increase the Fibonacci skip once every N iterations. This was chosen + # to be big enough that all elements of small arrays get hashed while + # obscenely large arrays are still tractable. With a choice of N=4096, an + # entirely-distinct 8000-element array will have ~75% of its elements hashed, + # with every other element hashed in the first half of the array. At the same + # time, hashing a `typemax(Int64)`-length Float64 range takes about a second. + if rem(n, 4096) == 0 + fibskip, prevfibskip = fibskip + prevfibskip, fibskip + end + + # Find a key index with a value distinct from `elt` -- might be `keyidx` itself + keyidx = findprev(!isequal(elt), A, keyidx) + keyidx === nothing && break + end + + return h +end diff --git a/base/abstractarraymath.jl b/base/abstractarraymath.jl new file mode 100644 index 0000000..9c7b098 --- /dev/null +++ b/base/abstractarraymath.jl @@ -0,0 +1,457 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + + ## Basic functions ## + +isreal(x::AbstractArray) = all(isreal,x) +iszero(x::AbstractArray) = all(iszero,x) +isreal(x::AbstractArray{<:Real}) = true + +## Constructors ## + +""" + vec(a::AbstractArray) -> AbstractVector + +Reshape the array `a` as a one-dimensional column vector. Return `a` if it is +already an `AbstractVector`. The resulting array +shares the same underlying data as `a`, so it will only be mutable if `a` is +mutable, in which case modifying one will also modify the other. + +# Examples +```jldoctest +julia> a = [1 2 3; 4 5 6] +2×3 Array{Int64,2}: + 1 2 3 + 4 5 6 + +julia> vec(a) +6-element Array{Int64,1}: + 1 + 4 + 2 + 5 + 3 + 6 + +julia> vec(1:3) +1:3 +``` + +See also [`reshape`](@ref). +""" +vec(a::AbstractArray) = reshape(a,length(a)) +vec(a::AbstractVector) = a + +_sub(::Tuple{}, ::Tuple{}) = () +_sub(t::Tuple, ::Tuple{}) = t +_sub(t::Tuple, s::Tuple) = _sub(tail(t), tail(s)) + +""" + dropdims(A; dims) + +Remove the dimensions specified by `dims` from array `A`. +Elements of `dims` must be unique and within the range `1:ndims(A)`. +`size(A,i)` must equal 1 for all `i` in `dims`. + +# Examples +```jldoctest +julia> a = reshape(Vector(1:4),(2,2,1,1)) +2×2×1×1 Array{Int64,4}: +[:, :, 1, 1] = + 1 3 + 2 4 + +julia> dropdims(a; dims=3) +2×2×1 Array{Int64,3}: +[:, :, 1] = + 1 3 + 2 4 +``` +""" +dropdims(A; dims) = _dropdims(A, dims) +function _dropdims(A::AbstractArray, dims::Dims) + for i in 1:length(dims) + 1 <= dims[i] <= ndims(A) || throw(ArgumentError("dropped dims must be in range 1:ndims(A)")) + length(axes(A, dims[i])) == 1 || throw(ArgumentError("dropped dims must all be size 1")) + for j = 1:i-1 + dims[j] == dims[i] && throw(ArgumentError("dropped dims must be unique")) + end + end + d = () + for i = 1:ndims(A) + if !in(i, dims) + d = tuple(d..., axes(A, i)) + end + end + reshape(A, d::typeof(_sub(axes(A), dims))) +end +_dropdims(A::AbstractArray, dim::Integer) = _dropdims(A, (Int(dim),)) + +## Unary operators ## + +conj(x::AbstractArray{<:Real}) = x +conj!(x::AbstractArray{<:Real}) = x + +real(x::AbstractArray{<:Real}) = x +imag(x::AbstractArray{<:Real}) = zero(x) + ++(x::AbstractArray{<:Number}) = x +*(x::AbstractArray{<:Number,2}) = x + +# index A[:,:,...,i,:,:,...] where "i" is in dimension "d" + +""" + selectdim(A, d::Integer, i) + +Return a view of all the data of `A` where the index for dimension `d` equals `i`. + +Equivalent to `view(A,:,:,...,i,:,:,...)` where `i` is in position `d`. + +# Examples +```jldoctest +julia> A = [1 2 3 4; 5 6 7 8] +2×4 Array{Int64,2}: + 1 2 3 4 + 5 6 7 8 + +julia> selectdim(A, 2, 3) +2-element view(::Array{Int64,2}, :, 3) with eltype Int64: + 3 + 7 +``` +""" +@inline selectdim(A::AbstractArray, d::Integer, i) = _selectdim(A, d, i, setindex(map(Slice, axes(A)), i, d)) +@noinline function _selectdim(A, d, i, idxs) + d >= 1 || throw(ArgumentError("dimension must be ≥ 1, got $d")) + nd = ndims(A) + d > nd && (i == 1 || throw(BoundsError(A, (ntuple(k->Colon(),d-1)..., i)))) + return view(A, idxs...) +end + +""" + reverse(A; dims::Integer) + +Reverse `A` in dimension `dims`. + +# Examples +```jldoctest +julia> b = [1 2; 3 4] +2×2 Array{Int64,2}: + 1 2 + 3 4 + +julia> reverse(b, dims=2) +2×2 Array{Int64,2}: + 2 1 + 4 3 +``` +""" +function reverse(A::AbstractArray; dims::Integer) + nd = ndims(A); d = dims + 1 ≤ d ≤ nd || throw(ArgumentError("dimension $d is not 1 ≤ $d ≤ $nd")) + if isempty(A) + return copy(A) + elseif nd == 1 + return reverse(A) + end + inds = axes(A) + B = similar(A) + nnd = 0 + for i = 1:nd + nnd += Int(length(inds[i])==1 || i==d) + end + indsd = inds[d] + sd = first(indsd)+last(indsd) + if nnd==nd + # reverse along the only non-singleton dimension + for i in indsd + B[i] = A[sd-i] + end + return B + end + let B=B # workaround #15276 + alli = [ axes(B,n) for n in 1:nd ] + for i in indsd + B[[ n==d ? sd-i : alli[n] for n in 1:nd ]...] = selectdim(A, d, i) + end + end + return B +end + +function circshift(a::AbstractArray, shiftamt::Real) + circshift!(similar(a), a, (Integer(shiftamt),)) +end +circshift(a::AbstractArray, shiftamt::DimsInteger) = circshift!(similar(a), a, shiftamt) +""" + circshift(A, shifts) + +Circularly shift, i.e. rotate, the data in an array. The second argument is a tuple or +vector giving the amount to shift in each dimension, or an integer to shift only in the +first dimension. + +# Examples +```jldoctest +julia> b = reshape(Vector(1:16), (4,4)) +4×4 Array{Int64,2}: + 1 5 9 13 + 2 6 10 14 + 3 7 11 15 + 4 8 12 16 + +julia> circshift(b, (0,2)) +4×4 Array{Int64,2}: + 9 13 1 5 + 10 14 2 6 + 11 15 3 7 + 12 16 4 8 + +julia> circshift(b, (-1,0)) +4×4 Array{Int64,2}: + 2 6 10 14 + 3 7 11 15 + 4 8 12 16 + 1 5 9 13 + +julia> a = BitArray([true, true, false, false, true]) +5-element BitArray{1}: + 1 + 1 + 0 + 0 + 1 + +julia> circshift(a, 1) +5-element BitArray{1}: + 1 + 1 + 1 + 0 + 0 + +julia> circshift(a, -1) +5-element BitArray{1}: + 1 + 0 + 0 + 1 + 1 +``` + +See also [`circshift!`](@ref). +""" +function circshift(a::AbstractArray, shiftamt) + circshift!(similar(a), a, map(Integer, (shiftamt...,))) +end + +## Other array functions ## + +""" + repeat(A::AbstractArray, counts::Integer...) + +Construct an array by repeating array `A` a given number of times in each dimension, specified by `counts`. + +# Examples +```jldoctest +julia> repeat([1, 2, 3], 2) +6-element Array{Int64,1}: + 1 + 2 + 3 + 1 + 2 + 3 + +julia> repeat([1, 2, 3], 2, 3) +6×3 Array{Int64,2}: + 1 1 1 + 2 2 2 + 3 3 3 + 1 1 1 + 2 2 2 + 3 3 3 +``` +""" +repeat(a::AbstractArray, counts::Integer...) = repeat(a, outer = counts) + +function repeat(a::AbstractVecOrMat, m::Integer, n::Integer=1) + o, p = size(a,1), size(a,2) + b = similar(a, o*m, p*n) + for j=1:n + d = (j-1)*p+1 + R = d:d+p-1 + for i=1:m + c = (i-1)*o+1 + b[c:c+o-1, R] = a + end + end + return b +end + +function repeat(a::AbstractVector, m::Integer) + o = length(a) + b = similar(a, o*m) + for i=1:m + c = (i-1)*o+1 + b[c:c+o-1] = a + end + return b +end + +""" + repeat(A::AbstractArray; inner=ntuple(x->1, ndims(A)), outer=ntuple(x->1, ndims(A))) + +Construct an array by repeating the entries of `A`. The i-th element of `inner` specifies +the number of times that the individual entries of the i-th dimension of `A` should be +repeated. The i-th element of `outer` specifies the number of times that a slice along the +i-th dimension of `A` should be repeated. If `inner` or `outer` are omitted, no repetition +is performed. + +# Examples +```jldoctest +julia> repeat(1:2, inner=2) +4-element Array{Int64,1}: + 1 + 1 + 2 + 2 + +julia> repeat(1:2, outer=2) +4-element Array{Int64,1}: + 1 + 2 + 1 + 2 + +julia> repeat([1 2; 3 4], inner=(2, 1), outer=(1, 3)) +4×6 Array{Int64,2}: + 1 2 1 2 1 2 + 1 2 1 2 1 2 + 3 4 3 4 3 4 + 3 4 3 4 3 4 +``` +""" +function repeat(A::AbstractArray; inner = nothing, outer = nothing) + return _repeat_inner_outer(A, inner, outer) +end + +# we have optimized implementations of these cases above +_repeat_inner_outer(A::AbstractVecOrMat, ::Nothing, r::Union{Tuple{Integer},Tuple{Integer,Integer}}) = repeat(A, r...) +_repeat_inner_outer(A::AbstractVecOrMat, ::Nothing, r::Integer) = repeat(A, r) + +_repeat_inner_outer(A, ::Nothing, ::Nothing) = A +_repeat_inner_outer(A, ::Nothing, outer) = _repeat(A, ntuple(n->1, Val(ndims(A))), rep_kw2tup(outer)) +_repeat_inner_outer(A, inner, ::Nothing) = _repeat(A, rep_kw2tup(inner), ntuple(n->1, Val(ndims(A)))) +_repeat_inner_outer(A, inner, outer) = _repeat(A, rep_kw2tup(inner), rep_kw2tup(outer)) + +rep_kw2tup(n::Integer) = (n,) +rep_kw2tup(v::AbstractArray{<:Integer}) = (v...,) +rep_kw2tup(t::Tuple) = t + +rep_shapes(A, i, o) = _rshps((), (), size(A), i, o) + +_rshps(shp, shp_i, ::Tuple{}, ::Tuple{}, ::Tuple{}) = (shp, shp_i) +@inline _rshps(shp, shp_i, ::Tuple{}, ::Tuple{}, o) = + _rshps((shp..., o[1]), (shp_i..., 1), (), (), tail(o)) +@inline _rshps(shp, shp_i, ::Tuple{}, i, ::Tuple{}) = (n = i[1]; + _rshps((shp..., n), (shp_i..., n), (), tail(i), ())) +@inline _rshps(shp, shp_i, ::Tuple{}, i, o) = (n = i[1]; + _rshps((shp..., n * o[1]), (shp_i..., n), (), tail(i), tail(o))) +@inline _rshps(shp, shp_i, sz, i, o) = (n = sz[1] * i[1]; + _rshps((shp..., n * o[1]), (shp_i..., n), tail(sz), tail(i), tail(o))) +_rshps(shp, shp_i, sz, ::Tuple{}, ::Tuple{}) = + (n = length(shp); N = n + length(sz); _reperr("inner", n, N)) +_rshps(shp, shp_i, sz, ::Tuple{}, o) = + (n = length(shp); N = n + length(sz); _reperr("inner", n, N)) +_rshps(shp, shp_i, sz, i, ::Tuple{}) = + (n = length(shp); N = n + length(sz); _reperr("outer", n, N)) +_reperr(s, n, N) = throw(ArgumentError("number of " * s * " repetitions " * + "($n) cannot be less than number of dimensions of input ($N)")) + +@noinline function _repeat(A::AbstractArray, inner, outer) + shape, inner_shape = rep_shapes(A, inner, outer) + + R = similar(A, shape) + if any(iszero, shape) + return R + end + + # fill the first inner block + if all(x -> x == 1, inner) + idxs = (axes(A)..., ntuple(n->OneTo(1), ndims(R)-ndims(A))...) # keep dimension consistent + R[idxs...] = A + else + inner_indices = [1:n for n in inner] + for c in CartesianIndices(axes(A)) + for i in 1:ndims(A) + n = inner[i] + inner_indices[i] = (1:n) .+ ((c[i] - 1) * n) + end + fill!(view(R, inner_indices...), A[c]) + end + end + + # fill the outer blocks along each dimension + if all(x -> x == 1, outer) + return R + end + src_indices = [1:n for n in inner_shape] + dest_indices = copy(src_indices) + for i in 1:length(outer) + B = view(R, src_indices...) + for j in 2:outer[i] + dest_indices[i] = dest_indices[i] .+ inner_shape[i] + R[dest_indices...] = B + end + src_indices[i] = dest_indices[i] = 1:shape[i] + end + + return R +end + +""" + eachrow(A::AbstractVecOrMat) + +Create a generator that iterates over the first dimension of vector or matrix `A`, +returning the rows as views. + +See also [`eachcol`](@ref) and [`eachslice`](@ref). + +!!! compat "Julia 1.1" + This function requires at least Julia 1.1. +""" +eachrow(A::AbstractVecOrMat) = (view(A, i, :) for i in axes(A, 1)) + + +""" + eachcol(A::AbstractVecOrMat) + +Create a generator that iterates over the second dimension of matrix `A`, returning the +columns as views. + +See also [`eachrow`](@ref) and [`eachslice`](@ref). + +!!! compat "Julia 1.1" + This function requires at least Julia 1.1. +""" +eachcol(A::AbstractVecOrMat) = (view(A, :, i) for i in axes(A, 2)) + +""" + eachslice(A::AbstractArray; dims) + +Create a generator that iterates over dimensions `dims` of `A`, returning views that select all +the data from the other dimensions in `A`. + +Only a single dimension in `dims` is currently supported. Equivalent to `(view(A,:,:,...,i,:,: +...)) for i in axes(A, dims))`, where `i` is in position `dims`. + +See also [`eachrow`](@ref), [`eachcol`](@ref), and [`selectdim`](@ref). + +!!! compat "Julia 1.1" + This function requires at least Julia 1.1. +""" +@inline function eachslice(A::AbstractArray; dims) + length(dims) == 1 || throw(ArgumentError("only single dimensions are supported")) + dim = first(dims) + dim <= ndims(A) || throw(DimensionMismatch("A doesn't have $dim dimensions")) + idx1, idx2 = ntuple(d->(:), dim-1), ntuple(d->(:), ndims(A)-dim) + return (view(A, idx1..., i, idx2...) for i in axes(A, dim)) +end diff --git a/base/abstractdict.jl b/base/abstractdict.jl new file mode 100644 index 0000000..4e3d568 --- /dev/null +++ b/base/abstractdict.jl @@ -0,0 +1,735 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# generic operations on dictionaries + +""" + KeyError(key) + +An indexing operation into an `AbstractDict` (`Dict`) or `Set` like object tried to access or +delete a non-existent element. +""" +struct KeyError <: Exception + key +end + +const secret_table_token = :__c782dbf1cf4d6a2e5e3865d7e95634f2e09b5902__ + +haskey(d::AbstractDict, k) = in(k, keys(d)) + +function in(p::Pair, a::AbstractDict, valcmp=(==)) + v = get(a,p[1],secret_table_token) + if v !== secret_table_token + return valcmp(v, p[2]) + end + return false +end + +function in(p, a::AbstractDict) + error("""AbstractDict collections only contain Pairs; + Either look for e.g. A=>B instead, or use the `keys` or `values` + function if you are looking for a key or value respectively.""") +end + +function summary(io::IO, t::AbstractDict) + n = length(t) + showarg(io, t, true) + print(io, " with ", n, (n==1 ? " entry" : " entries")) +end + +struct KeySet{K, T <: AbstractDict{K}} <: AbstractSet{K} + dict::T +end + +struct ValueIterator{T<:AbstractDict} + dict::T +end + +function summary(io::IO, iter::T) where {T<:Union{KeySet,ValueIterator}} + print(io, T.name, " for a ") + summary(io, iter.dict) +end + +show(io::IO, iter::Union{KeySet,ValueIterator}) = show_vector(io, iter) + +length(v::Union{KeySet,ValueIterator}) = length(v.dict) +isempty(v::Union{KeySet,ValueIterator}) = isempty(v.dict) +_tt2(::Type{Pair{A,B}}) where {A,B} = B +eltype(::Type{ValueIterator{D}}) where {D} = _tt2(eltype(D)) + +function iterate(v::Union{KeySet,ValueIterator}, state...) + y = iterate(v.dict, state...) + y === nothing && return nothing + return (y[1][isa(v, KeySet) ? 1 : 2], y[2]) +end + +in(k, v::KeySet) = get(v.dict, k, secret_table_token) !== secret_table_token + +""" + keys(iterator) + +For an iterator or collection that has keys and values (e.g. arrays and dictionaries), +return an iterator over the keys. +""" +function keys end + +""" + keys(a::AbstractDict) + +Return an iterator over all keys in a dictionary. +`collect(keys(a))` returns an array of keys. +Since the keys are stored internally in a hash table, +the order in which they are returned may vary. +But `keys(a)` and `values(a)` both iterate `a` and +return the elements in the same order. + +# Examples +```jldoctest +julia> D = Dict('a'=>2, 'b'=>3) +Dict{Char,Int64} with 2 entries: + 'a' => 2 + 'b' => 3 + +julia> collect(keys(D)) +2-element Array{Char,1}: + 'a' + 'b' +``` +""" +keys(a::AbstractDict) = KeySet(a) + +""" + values(a::AbstractDict) + +Return an iterator over all values in a collection. +`collect(values(a))` returns an array of values. +Since the values are stored internally in a hash table, +the order in which they are returned may vary. +But `keys(a)` and `values(a)` both iterate `a` and +return the elements in the same order. + +# Examples +```jldoctest +julia> D = Dict('a'=>2, 'b'=>3) +Dict{Char,Int64} with 2 entries: + 'a' => 2 + 'b' => 3 + +julia> collect(values(D)) +2-element Array{Int64,1}: + 2 + 3 +``` +""" +values(a::AbstractDict) = ValueIterator(a) + +""" + pairs(collection) + +Return an iterator over `key => value` pairs for any +collection that maps a set of keys to a set of values. +This includes arrays, where the keys are the array indices. +""" +pairs(collection) = Generator(=>, keys(collection), values(collection)) + +pairs(a::AbstractDict) = a + +""" + empty(a::AbstractDict, [index_type=keytype(a)], [value_type=valtype(a)]) + +Create an empty `AbstractDict` container which can accept indices of type `index_type` and +values of type `value_type`. The second and third arguments are optional and default to the +input's `keytype` and `valtype`, respectively. (If only one of the two types is specified, +it is assumed to be the `value_type`, and the `index_type` we default to `keytype(a)`). + +Custom `AbstractDict` subtypes may choose which specific dictionary type is best suited to +return for the given index and value types, by specializing on the three-argument signature. +The default is to return an empty `Dict`. +""" +empty(a::AbstractDict) = empty(a, keytype(a), valtype(a)) +empty(a::AbstractDict, ::Type{V}) where {V} = empty(a, keytype(a), V) # Note: this is the form which makes sense for `Vector`. + +copy(a::AbstractDict) = merge!(empty(a), a) +copy!(dst::AbstractDict, src::AbstractDict) = merge!(empty!(dst), src) + +""" + merge!(d::AbstractDict, others::AbstractDict...) + +Update collection with pairs from the other collections. +See also [`merge`](@ref). + +# Examples +```jldoctest +julia> d1 = Dict(1 => 2, 3 => 4); + +julia> d2 = Dict(1 => 4, 4 => 5); + +julia> merge!(d1, d2); + +julia> d1 +Dict{Int64,Int64} with 3 entries: + 4 => 5 + 3 => 4 + 1 => 4 +``` +""" +function merge!(d::AbstractDict, others::AbstractDict...) + for other in others + for (k,v) in other + d[k] = v + end + end + return d +end + +""" + merge!(combine, d::AbstractDict, others::AbstractDict...) + +Update collection with pairs from the other collections. +Values with the same key will be combined using the +combiner function. + +# Examples +```jldoctest +julia> d1 = Dict(1 => 2, 3 => 4); + +julia> d2 = Dict(1 => 4, 4 => 5); + +julia> merge!(+, d1, d2); + +julia> d1 +Dict{Int64,Int64} with 3 entries: + 4 => 5 + 3 => 4 + 1 => 6 + +julia> merge!(-, d1, d1); + +julia> d1 +Dict{Int64,Int64} with 3 entries: + 4 => 0 + 3 => 0 + 1 => 0 +``` +""" +function merge!(combine::Function, d::AbstractDict, others::AbstractDict...) + for other in others + for (k,v) in other + d[k] = haskey(d, k) ? combine(d[k], v) : v + end + end + return d +end + +""" + keytype(type) + +Get the key type of an dictionary type. Behaves similarly to [`eltype`](@ref). + +# Examples +```jldoctest +julia> keytype(Dict(Int32(1) => "foo")) +Int32 +``` +""" +keytype(::Type{<:AbstractDict{K,V}}) where {K,V} = K +keytype(a::AbstractDict) = keytype(typeof(a)) + +""" + valtype(type) + +Get the value type of an dictionary type. Behaves similarly to [`eltype`](@ref). + +# Examples +```jldoctest +julia> valtype(Dict(Int32(1) => "foo")) +String +``` +""" +valtype(::Type{<:AbstractDict{K,V}}) where {K,V} = V +valtype(a::AbstractDict) = valtype(typeof(a)) + +""" + merge(d::AbstractDict, others::AbstractDict...) + +Construct a merged collection from the given collections. If necessary, the +types of the resulting collection will be promoted to accommodate the types of +the merged collections. If the same key is present in another collection, the +value for that key will be the value it has in the last collection listed. + +# Examples +```jldoctest +julia> a = Dict("foo" => 0.0, "bar" => 42.0) +Dict{String,Float64} with 2 entries: + "bar" => 42.0 + "foo" => 0.0 + +julia> b = Dict("baz" => 17, "bar" => 4711) +Dict{String,Int64} with 2 entries: + "bar" => 4711 + "baz" => 17 + +julia> merge(a, b) +Dict{String,Float64} with 3 entries: + "bar" => 4711.0 + "baz" => 17.0 + "foo" => 0.0 + +julia> merge(b, a) +Dict{String,Float64} with 3 entries: + "bar" => 42.0 + "baz" => 17.0 + "foo" => 0.0 +``` +""" +merge(d::AbstractDict, others::AbstractDict...) = + merge!(_typeddict(d, others...), others...) + +""" + merge(combine, d::AbstractDict, others::AbstractDict...) + +Construct a merged collection from the given collections. If necessary, the +types of the resulting collection will be promoted to accommodate the types of +the merged collections. Values with the same key will be combined using the +combiner function. + +# Examples +```jldoctest +julia> a = Dict("foo" => 0.0, "bar" => 42.0) +Dict{String,Float64} with 2 entries: + "bar" => 42.0 + "foo" => 0.0 + +julia> b = Dict("baz" => 17, "bar" => 4711) +Dict{String,Int64} with 2 entries: + "bar" => 4711 + "baz" => 17 + +julia> merge(+, a, b) +Dict{String,Float64} with 3 entries: + "bar" => 4753.0 + "baz" => 17.0 + "foo" => 0.0 +``` +""" +merge(combine::Function, d::AbstractDict, others::AbstractDict...) = + merge!(combine, _typeddict(d, others...), others...) + +promoteK(K) = K +promoteV(V) = V +promoteK(K, d, ds...) = promoteK(promote_type(K, keytype(d)), ds...) +promoteV(V, d, ds...) = promoteV(promote_type(V, valtype(d)), ds...) +function _typeddict(d::AbstractDict, others::AbstractDict...) + K = promoteK(keytype(d), others...) + V = promoteV(valtype(d), others...) + Dict{K,V}(d) +end + +""" + filter!(f, d::AbstractDict) + +Update `d`, removing elements for which `f` is `false`. +The function `f` is passed `key=>value` pairs. + +# Example +```jldoctest +julia> d = Dict(1=>"a", 2=>"b", 3=>"c") +Dict{Int64,String} with 3 entries: + 2 => "b" + 3 => "c" + 1 => "a" + +julia> filter!(p->isodd(p.first), d) +Dict{Int64,String} with 2 entries: + 3 => "c" + 1 => "a" +``` +""" +function filter!(f, d::AbstractDict) + badkeys = Vector{keytype(d)}() + for pair in d + # don't delete!(d, k) here, since dictionary types + # may not support mutation during iteration + f(pair) || push!(badkeys, pair.first) + end + for k in badkeys + delete!(d, k) + end + return d +end + +function filter_in_one_pass!(f, d::AbstractDict) + for pair in d + if !f(pair) + delete!(d, pair.first) + end + end + return d +end + +""" + filter(f, d::AbstractDict) + +Return a copy of `d`, removing elements for which `f` is `false`. +The function `f` is passed `key=>value` pairs. + +# Examples +```jldoctest +julia> d = Dict(1=>"a", 2=>"b") +Dict{Int64,String} with 2 entries: + 2 => "b" + 1 => "a" + +julia> filter(p->isodd(p.first), d) +Dict{Int64,String} with 1 entry: + 1 => "a" +``` +""" +function filter(f, d::AbstractDict) + # don't just do filter!(f, copy(d)): avoid making a whole copy of d + df = empty(d) + try + for pair in d + if f(pair) + df[pair.first] = pair.second + end + end + catch e + if isa(e, MethodError) && e.f === f + depwarn("In `filter(f, dict)`, `f` is now passed a single pair instead of two arguments.", :filter) + for (k, v) in d + if f(k, v) + df[k] = v + end + end + else + rethrow() + end + end + return df +end + +function eltype(::Type{<:AbstractDict{K,V}}) where {K,V} + if @isdefined(K) + if @isdefined(V) + return Pair{K,V} + else + return Pair{K} + end + elseif @isdefined(V) + return Pair{k,V} where k + else + return Pair + end +end + +function isequal(l::AbstractDict, r::AbstractDict) + l === r && return true + if isa(l,IdDict) != isa(r,IdDict) + return false + end + if length(l) != length(r) return false end + for pair in l + if !in(pair, r, isequal) + return false + end + end + true +end + +function ==(l::AbstractDict, r::AbstractDict) + l === r && return true + if isa(l,IdDict) != isa(r,IdDict) + return false + end + length(l) != length(r) && return false + anymissing = false + for pair in l + isin = in(pair, r, ==) + if ismissing(isin) + anymissing = true + elseif !isin + return false + end + end + return anymissing ? missing : true +end + +const hasha_seed = UInt === UInt64 ? 0x6d35bb51952d5539 : 0x952d5539 +function hash(a::AbstractDict, h::UInt) + hv = hasha_seed + for (k,v) in a + hv ⊻= hash(k, hash(v)) + end + hash(hv, h) +end + +function getindex(t::AbstractDict, key) + v = get(t, key, secret_table_token) + if v === secret_table_token + throw(KeyError(key)) + end + return v +end + +# t[k1,k2,ks...] is syntactic sugar for t[(k1,k2,ks...)]. (Note +# that we need to avoid dispatch loops if setindex!(t,v,k) is not defined.) +getindex(t::AbstractDict, k1, k2, ks...) = getindex(t, tuple(k1,k2,ks...)) +setindex!(t::AbstractDict, v, k1, k2, ks...) = setindex!(t, v, tuple(k1,k2,ks...)) + +push!(t::AbstractDict, p::Pair) = setindex!(t, p.second, p.first) +push!(t::AbstractDict, p::Pair, q::Pair) = push!(push!(t, p), q) +push!(t::AbstractDict, p::Pair, q::Pair, r::Pair...) = push!(push!(push!(t, p), q), r...) + +# AbstractDicts are convertible +convert(::Type{T}, x::T) where {T<:AbstractDict} = x + +function convert(::Type{T}, x::AbstractDict) where T<:AbstractDict + h = T(x) + if length(h) != length(x) + error("key collision during dictionary conversion") + end + return h +end + +# hashing objects by identity +_tablesz(x::Integer) = x < 16 ? 16 : one(x)<<((sizeof(x)<<3)-leading_zeros(x-1)) + +""" + IdDict([itr]) + +`IdDict{K,V}()` constructs a hash table using object-id as hash and +`===` as equality with keys of type `K` and values of type `V`. + +See [`Dict`](@ref) for further help. +""" +mutable struct IdDict{K,V} <: AbstractDict{K,V} + ht::Vector{Any} + count::Int + ndel::Int + IdDict{K,V}() where {K, V} = new{K,V}(Vector{Any}(undef, 32), 0, 0) + + function IdDict{K,V}(itr) where {K, V} + d = IdDict{K,V}() + for (k,v) in itr; d[k] = v; end + d + end + + function IdDict{K,V}(pairs::Pair...) where {K, V} + d = IdDict{K,V}() + sizehint!(d, length(pairs)) + for (k,v) in pairs; d[k] = v; end + d + end + + IdDict{K,V}(d::IdDict{K,V}) where {K, V} = new{K,V}(copy(d.ht), d.count, d.ndel) +end + +IdDict() = IdDict{Any,Any}() +IdDict(kv::Tuple{}) = IdDict() + +IdDict(ps::Pair{K,V}...) where {K,V} = IdDict{K,V}(ps) +IdDict(ps::Pair{K}...) where {K} = IdDict{K,Any}(ps) +IdDict(ps::(Pair{K,V} where K)...) where {V} = IdDict{Any,V}(ps) +IdDict(ps::Pair...) = IdDict{Any,Any}(ps) + +TP{K,V} = Union{Type{Tuple{K,V}},Type{Pair{K,V}}} + +dict_with_eltype(DT_apply, kv, ::TP{K,V}) where {K,V} = DT_apply(K, V)(kv) +dict_with_eltype(DT_apply, kv::Generator, ::TP{K,V}) where {K,V} = DT_apply(K, V)(kv) +dict_with_eltype(DT_apply, ::Type{Pair{K,V}}) where {K,V} = DT_apply(K, V)() +dict_with_eltype(DT_apply, ::Type) = DT_apply(Any, Any)() +dict_with_eltype(DT_apply::F, kv, t) where {F} = grow_to!(dict_with_eltype(DT_apply, @default_eltype(typeof(kv))), kv) +function dict_with_eltype(DT_apply::F, kv::Generator, t) where F + T = @default_eltype(kv) + if T <: Union{Pair, Tuple{Any, Any}} && isconcretetype(T) + return dict_with_eltype(DT_apply, kv, T) + end + return grow_to!(dict_with_eltype(DT_apply, T), kv) +end + +function IdDict(kv) + try + dict_with_eltype((K, V) -> IdDict{K, V}, kv, eltype(kv)) + catch + if !applicable(iterate, kv) || !all(x->isa(x,Union{Tuple,Pair}),kv) + throw(ArgumentError( + "IdDict(kv): kv needs to be an iterator of tuples or pairs")) + else + rethrow() + end + end +end + +empty(d::IdDict, ::Type{K}, ::Type{V}) where {K, V} = IdDict{K,V}() + +function rehash!(d::IdDict, newsz = length(d.ht)) + d.ht = ccall(:jl_idtable_rehash, Vector{Any}, (Any, Csize_t), d.ht, newsz) + d +end + +function sizehint!(d::IdDict, newsz) + newsz = _tablesz(newsz*2) # *2 for keys and values in same array + oldsz = length(d.ht) + # grow at least 25% + if newsz < (oldsz*5)>>2 + return d + end + rehash!(d, newsz) +end + +function setindex!(d::IdDict{K,V}, @nospecialize(val), @nospecialize(key)) where {K, V} + !isa(key, K) && throw(ArgumentError("$(limitrepr(key)) is not a valid key for type $K")) + if !(val isa V) # avoid a dynamic call + val = convert(V, val) + end + if d.ndel >= ((3*length(d.ht))>>2) + rehash!(d, max(length(d.ht)>>1, 32)) + d.ndel = 0 + end + inserted = RefValue{Cint}(0) + d.ht = ccall(:jl_eqtable_put, Array{Any,1}, (Any, Any, Any, Ptr{Cint}), d.ht, key, val, inserted) + d.count += inserted[] + return d +end + +function get(d::IdDict{K,V}, @nospecialize(key), @nospecialize(default)) where {K, V} + val = ccall(:jl_eqtable_get, Any, (Any, Any, Any), d.ht, key, default) + val === default ? default : val::V +end +function getindex(d::IdDict{K,V}, @nospecialize(key)) where {K, V} + val = get(d, key, secret_table_token) + val === secret_table_token && throw(KeyError(key)) + return val::V +end + +function pop!(d::IdDict{K,V}, @nospecialize(key), @nospecialize(default)) where {K, V} + found = RefValue{Cint}(0) + val = ccall(:jl_eqtable_pop, Any, (Any, Any, Any, Ptr{Cint}), d.ht, key, default, found) + if found[] === Cint(0) + return default + else + d.count -= 1 + d.ndel += 1 + return val::V + end +end + +function pop!(d::IdDict{K,V}, @nospecialize(key)) where {K, V} + val = pop!(d, key, secret_table_token) + val === secret_table_token && throw(KeyError(key)) + return val::V +end + +function delete!(d::IdDict{K}, @nospecialize(key)) where K + pop!(d, key, secret_table_token) + d +end + +function empty!(d::IdDict) + resize!(d.ht, 32) + ccall(:memset, Ptr{Cvoid}, (Ptr{Cvoid}, Cint, Csize_t), d.ht, 0, sizeof(d.ht)) + d.ndel = 0 + d.count = 0 + return d +end + +_oidd_nextind(a, i) = reinterpret(Int, ccall(:jl_eqtable_nextind, Csize_t, (Any, Csize_t), a, i)) + +function iterate(d::IdDict{K,V}, idx=0) where {K, V} + idx = _oidd_nextind(d.ht, idx) + idx == -1 && return nothing + return (Pair{K, V}(d.ht[idx + 1]::K, d.ht[idx + 2]::V), idx + 2) +end + +length(d::IdDict) = d.count + +copy(d::IdDict) = typeof(d)(d) + +get!(d::IdDict{K,V}, @nospecialize(key), @nospecialize(default)) where {K, V} = (d[key] = get(d, key, default))::V + +function get(default::Callable, d::IdDict{K,V}, @nospecialize(key)) where {K, V} + val = get(d, key, secret_table_token) + if val === secret_table_token + val = default() + end + return val +end + +function get!(default::Callable, d::IdDict{K,V}, @nospecialize(key)) where {K, V} + val = get(d, key, secret_table_token) + if val === secret_table_token + val = default() + setindex!(d, val, key) + end + return val +end + +in(@nospecialize(k), v::KeySet{<:Any,<:IdDict}) = get(v.dict, k, secret_table_token) !== secret_table_token + +# For some AbstractDict types, it is safe to implement filter! +# by deleting keys during iteration. +filter!(f, d::IdDict) = filter_in_one_pass!(f, d) + +# Like Set, but using IdDict +mutable struct IdSet{T} <: AbstractSet{T} + dict::IdDict{T,Nothing} + + IdSet{T}() where {T} = new(IdDict{T,Nothing}()) + IdSet{T}(s::IdSet{T}) where {T} = new(copy(s.dict)) +end + +IdSet{T}(itr) where {T} = union!(IdSet{T}(), itr) +IdSet() = IdSet{Any}() + +copymutable(s::IdSet) = typeof(s)(s) +copy(s::IdSet) = typeof(s)(s) + +isempty(s::IdSet) = isempty(s.dict) +length(s::IdSet) = length(s.dict) +in(@nospecialize(x), s::IdSet) = haskey(s.dict, x) +push!(s::IdSet, @nospecialize(x)) = (s.dict[x] = nothing; s) +pop!(s::IdSet, @nospecialize(x)) = (pop!(s.dict, x); x) +pop!(s::IdSet, @nospecialize(x), @nospecialize(default)) = (x in s ? pop!(s, x) : default) +delete!(s::IdSet, @nospecialize(x)) = (delete!(s.dict, x); s) + +sizehint!(s::IdSet, newsz) = (sizehint!(s.dict, newsz); s) +empty!(s::IdSet) = (empty!(s.dict); s) + +filter!(f, d::IdSet) = unsafe_filter!(f, d) + +function iterate(s::IdSet, state...) + y = iterate(s.dict, state...) + y === nothing && return nothing + ((k, _), i) = y + return (k, i) +end + +""" + map!(f, values(dict::AbstractDict)) + +Modifies `dict` by transforming each value from `val` to `f(val)`. +Note that the type of `dict` cannot be changed: if `f(val)` is not an instance of the key type +of `dict` then it will be converted to the key type if possible and otherwise raise an error. + +# Examples +```jldoctest +julia> d = Dict(:a => 1, :b => 2) +Dict{Symbol,Int64} with 2 entries: + :a => 1 + :b => 2 + +julia> map!(v -> v-1, values(d)) +Dict{Symbol,Int64} with 2 entries: + :a => 0 + :b => 1 + ``` +""" +function map!(f, iter::ValueIterator) + # This is the naive fallback which requires hash evaluations + # Contrary to the example Dict has an implementation which does not require hash evaluations + dict = iter.dict + for (key, val) in pairs(dict) + dict[key] = f(val) + end + return iter +end diff --git a/base/abstractset.jl b/base/abstractset.jl new file mode 100644 index 0000000..6d0a2dd --- /dev/null +++ b/base/abstractset.jl @@ -0,0 +1,377 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +eltype(::Type{<:AbstractSet{T}}) where {T} = @isdefined(T) ? T : Any +sizehint!(s::AbstractSet, n) = nothing + +copy!(dst::AbstractSet, src::AbstractSet) = union!(empty!(dst), src) + +## set operations (union, intersection, symmetric difference) + +""" + union(s, itrs...) + ∪(s, itrs...) + +Construct the union of sets. Maintain order with arrays. + +# Examples +```jldoctest +julia> union([1, 2], [3, 4]) +4-element Array{Int64,1}: + 1 + 2 + 3 + 4 + +julia> union([1, 2], [2, 4]) +3-element Array{Int64,1}: + 1 + 2 + 4 + +julia> union([4, 2], 1:2) +3-element Array{Int64,1}: + 4 + 2 + 1 + +julia> union(Set([1, 2]), 2:3) +Set([2, 3, 1]) +``` +""" +function union end + +_in(itr) = x -> x in itr + +union(s, sets...) = union!(emptymutable(s, promote_eltype(s, sets...)), s, sets...) +union(s::AbstractSet) = copy(s) + +const ∪ = union + +""" + union!(s::Union{AbstractSet,AbstractVector}, itrs...) + +Construct the union of passed in sets and overwrite `s` with the result. +Maintain order with arrays. + +# Examples +```jldoctest +julia> a = Set([1, 3, 4, 5]); + +julia> union!(a, 1:2:8); + +julia> a +Set([7, 4, 3, 5, 1]) +``` +""" +function union!(s::AbstractSet, sets...) + for x in sets + union!(s, x) + end + return s +end + +max_values(::Type) = typemax(Int) +max_values(T::Union{map(X -> Type{X}, BitIntegerSmall_types)...}) = 1 << (8*sizeof(T)) +# saturated addition to prevent overflow with typemax(Int) +max_values(T::Union) = max(max_values(T.a), max_values(T.b), max_values(T.a) + max_values(T.b)) +max_values(::Type{Bool}) = 2 +max_values(::Type{Nothing}) = 1 + +function union!(s::AbstractSet{T}, itr) where T + haslength(itr) && sizehint!(s, length(s) + length(itr)) + for x in itr + push!(s, x) + length(s) == max_values(T) && break + end + return s +end + +""" + intersect(s, itrs...) + ∩(s, itrs...) + +Construct the intersection of sets. +Maintain order with arrays. + +# Examples +```jldoctest +julia> intersect([1, 2, 3], [3, 4, 5]) +1-element Array{Int64,1}: + 3 + +julia> intersect([1, 4, 4, 5, 6], [4, 6, 6, 7, 8]) +2-element Array{Int64,1}: + 4 + 6 + +julia> intersect(Set([1, 2]), BitSet([2, 3])) +Set([2]) +``` +""" +intersect(s::AbstractSet, itr, itrs...) = intersect!(intersect(s, itr), itrs...) +intersect(s) = union(s) +intersect(s::AbstractSet, itr) = mapfilter(_in(s), push!, itr, emptymutable(s)) + +const ∩ = intersect + +""" + intersect!(s::Union{AbstractSet,AbstractVector}, itrs...) + +Intersect all passed in sets and overwrite `s` with the result. +Maintain order with arrays. +""" +function intersect!(s::AbstractSet, itrs...) + for x in itrs + intersect!(s, x) + end + return s +end +intersect!(s::AbstractSet, s2::AbstractSet) = filter!(_in(s2), s) +intersect!(s::AbstractSet, itr) = + intersect!(s, union!(emptymutable(s, eltype(itr)), itr)) + +""" + setdiff(s, itrs...) + +Construct the set of elements in `s` but not in any of the iterables in `itrs`. +Maintain order with arrays. + +# Examples +```jldoctest +julia> setdiff([1,2,3], [3,4,5]) +2-element Array{Int64,1}: + 1 + 2 +``` +""" +setdiff(s::AbstractSet, itrs...) = setdiff!(copymutable(s), itrs...) +setdiff(s) = union(s) + +""" + setdiff!(s, itrs...) + +Remove from set `s` (in-place) each element of each iterable from `itrs`. +Maintain order with arrays. + +# Examples +```jldoctest +julia> a = Set([1, 3, 4, 5]); + +julia> setdiff!(a, 1:2:6); + +julia> a +Set([4]) +``` +""" +function setdiff!(s::AbstractSet, itrs...) + for x in itrs + setdiff!(s, x) + end + return s +end +function setdiff!(s::AbstractSet, itr) + for x in itr + delete!(s, x) + end + return s +end + + +""" + symdiff(s, itrs...) + +Construct the symmetric difference of elements in the passed in sets. +When `s` is not an `AbstractSet`, the order is maintained. +Note that in this case the multiplicity of elements matters. + +# Examples +```jldoctest +julia> symdiff([1,2,3], [3,4,5], [4,5,6]) +3-element Array{Int64,1}: + 1 + 2 + 6 + +julia> symdiff([1,2,1], [2, 1, 2]) +2-element Array{Int64,1}: + 1 + 2 + +julia> symdiff(unique([1,2,1]), unique([2, 1, 2])) +0-element Array{Int64,1} +``` +""" +symdiff(s, sets...) = symdiff!(emptymutable(s, promote_eltype(s, sets...)), s, sets...) +symdiff(s) = symdiff!(copy(s)) + +""" + symdiff!(s::Union{AbstractSet,AbstractVector}, itrs...) + +Construct the symmetric difference of the passed in sets, and overwrite `s` with the result. +When `s` is an array, the order is maintained. +Note that in this case the multiplicity of elements matters. +""" +function symdiff!(s::AbstractSet, itrs...) + for x in itrs + symdiff!(s, x) + end + return s +end + +function symdiff!(s::AbstractSet, itr) + for x in itr + x in s ? delete!(s, x) : push!(s, x) + end + return s +end + +## non-strict subset comparison + +const ⊆ = issubset +function ⊇ end +""" + issubset(a, b) -> Bool + ⊆(a, b) -> Bool + ⊇(b, a) -> Bool + +Determine whether every element of `a` is also in `b`, using [`in`](@ref). + +# Examples +```jldoctest +julia> issubset([1, 2], [1, 2, 3]) +true + +julia> [1, 2, 3] ⊆ [1, 2] +false + +julia> [1, 2, 3] ⊇ [1, 2] +true +``` +""" +issubset, ⊆, ⊇ + +function issubset(l, r) + if haslength(r) + rlen = length(r) + if isa(l, AbstractSet) + # check l for too many unique elements + length(l) > rlen && return false + end + # if r is big enough, convert it to a Set + # threshold empirically determined by repeatedly + # sampling using these two methods (see #26198) + if rlen > 70 && !isa(r, AbstractSet) + return issubset(l, Set(r)) + end + end + for elt in l + elt in r || return false + end + return true +end + +⊇(l, r) = r ⊆ l + +## strict subset comparison + +function ⊊ end +function ⊋ end +""" + ⊊(a, b) -> Bool + ⊋(b, a) -> Bool + +Determines if `a` is a subset of, but not equal to, `b`. + +# Examples +```jldoctest +julia> (1, 2) ⊊ (1, 2, 3) +true + +julia> (1, 2) ⊊ (1, 2) +false +``` +""" +⊊, ⊋ + +⊊(l::AbstractSet, r) = length(l) < length(r) && l ⊆ r +⊊(l, r) = Set(l) ⊊ r +⊋(l, r) = r ⊊ l + +function ⊈ end +function ⊉ end +""" + ⊈(a, b) -> Bool + ⊉(b, a) -> Bool + +Negation of `⊆` and `⊇`, i.e. checks that `a` is not a subset of `b`. + +# Examples +```jldoctest +julia> (1, 2) ⊈ (2, 3) +true + +julia> (1, 2) ⊈ (1, 2, 3) +false +``` +""" +⊈, ⊉ + +⊈(l, r) = !⊆(l, r) +⊉(l, r) = r ⊈ l + +## set equality comparison + +""" + issetequal(a, b) -> Bool + +Determine whether `a` and `b` have the same elements. Equivalent +to `a ⊆ b && b ⊆ a` but more efficient when possible. + +# Examples +```jldoctest +julia> issetequal([1, 2], [1, 2, 3]) +false + +julia> issetequal([1, 2], [2, 1]) +true +``` +""" +issetequal(l::AbstractSet, r::AbstractSet) = l == r +issetequal(l::AbstractSet, r) = issetequal(l, Set(r)) + +function issetequal(l, r::AbstractSet) + if haslength(l) + # check r for too many unique elements + length(l) < length(r) && return false + end + return issetequal(Set(l), r) +end + +function issetequal(l, r) + haslength(l) && return issetequal(l, Set(r)) + haslength(r) && return issetequal(r, Set(l)) + return issetequal(Set(l), Set(r)) +end + +## partial ordering of sets by containment + +==(l::AbstractSet, r::AbstractSet) = length(l) == length(r) && l ⊆ r +# convenience functions for AbstractSet +# (if needed, only their synonyms ⊊ and ⊆ must be specialized) +<( l::AbstractSet, r::AbstractSet) = l ⊊ r +<=(l::AbstractSet, r::AbstractSet) = l ⊆ r + +## filtering sets + +filter(pred, s::AbstractSet) = mapfilter(pred, push!, s, emptymutable(s)) + +# it must be safe to delete the current element while iterating over s: +unsafe_filter!(pred, s::AbstractSet) = mapfilter(!pred, delete!, s, s) + +# TODO: delete mapfilter in favor of comprehensions/foldl/filter when competitive +function mapfilter(pred, f, itr, res) + for x in itr + pred(x) && f(res, x) + end + res +end diff --git a/base/accumulate.jl b/base/accumulate.jl new file mode 100644 index 0000000..c5c4e83 --- /dev/null +++ b/base/accumulate.jl @@ -0,0 +1,391 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# accumulate_pairwise slightly slower then accumulate, but more numerically +# stable in certain situations (e.g. sums). +# it does double the number of operations compared to accumulate, +# though for cheap operations like + this does not have much impact (20%) +function _accumulate_pairwise!(op::Op, c::AbstractVector{T}, v::AbstractVector, s, i1, n)::T where {T,Op} + @inbounds if n < 128 + s_ = v[i1] + c[i1] = op(s, s_) + for i = i1+1:i1+n-1 + s_ = op(s_, v[i]) + c[i] = op(s, s_) + end + else + n2 = n >> 1 + s_ = _accumulate_pairwise!(op, c, v, s, i1, n2) + s_ = op(s_, _accumulate_pairwise!(op, c, v, op(s, s_), i1+n2, n-n2)) + end + return s_ +end + +function accumulate_pairwise!(op::Op, result::AbstractVector, v::AbstractVector) where Op + li = LinearIndices(v) + li != LinearIndices(result) && throw(DimensionMismatch("input and output array sizes and indices must match")) + n = length(li) + n == 0 && return result + i1 = first(li) + @inbounds result[i1] = v1 = reduce_first(op,v[i1]) + n == 1 && return result + _accumulate_pairwise!(op, result, v, v1, i1+1, n-1) + return result +end + +function accumulate_pairwise(op, v::AbstractVector{T}) where T + out = similar(v, promote_op(op, T, T)) + return accumulate_pairwise!(op, out, v) +end + + +""" + cumsum!(B, A; dims::Integer) + +Cumulative sum of `A` along the dimension `dims`, storing the result in `B`. See also [`cumsum`](@ref). +""" +cumsum!(B::AbstractArray{T}, A; dims::Integer) where {T} = + accumulate!(add_sum, B, A, dims=dims) + +function cumsum!(out::AbstractArray, v::AbstractVector; dims::Integer=1) + # we dispatch on the possibility of numerical stability issues + _cumsum!(out, v, dims, ArithmeticStyle(eltype(out))) +end + +function _cumsum!(out::AbstractArray{T}, v, dim, ::ArithmeticRounds) where {T} + dim == 1 ? accumulate_pairwise!(add_sum, out, v) : copyto!(out, v) +end +function _cumsum!(out::AbstractArray, v, dim, ::ArithmeticUnknown) + _cumsum!(out, v, dim, ArithmeticRounds()) +end +function _cumsum!(out::AbstractArray{T}, v, dim, ::ArithmeticStyle) where {T} + dim == 1 ? accumulate!(add_sum, out, v) : copyto!(out, v) +end + +""" + cumsum(A; dims::Integer) + +Cumulative sum along the dimension `dims`. See also [`cumsum!`](@ref) +to use a preallocated output array, both for performance and to control the precision of the +output (e.g. to avoid overflow). + +# Examples +```jldoctest +julia> a = [1 2 3; 4 5 6] +2×3 Array{Int64,2}: + 1 2 3 + 4 5 6 + +julia> cumsum(a, dims=1) +2×3 Array{Int64,2}: + 1 2 3 + 5 7 9 + +julia> cumsum(a, dims=2) +2×3 Array{Int64,2}: + 1 3 6 + 4 9 15 +``` +""" +function cumsum(A::AbstractArray{T}; dims::Integer) where T + out = similar(A, promote_op(add_sum, T, T)) + cumsum!(out, A, dims=dims) +end + +""" + cumsum(x::AbstractVector) + +Cumulative sum a vector. See also [`cumsum!`](@ref) +to use a preallocated output array, both for performance and to control the precision of the +output (e.g. to avoid overflow). + +# Examples +```jldoctest +julia> cumsum([1, 1, 1]) +3-element Array{Int64,1}: + 1 + 2 + 3 + +julia> cumsum([fill(1, 2) for i in 1:3]) +3-element Array{Array{Int64,1},1}: + [1, 1] + [2, 2] + [3, 3] +``` +""" +cumsum(x::AbstractVector) = cumsum(x, dims=1) + + +""" + cumprod!(B, A; dims::Integer) + +Cumulative product of `A` along the dimension `dims`, storing the result in `B`. +See also [`cumprod`](@ref). +""" +cumprod!(B::AbstractArray{T}, A; dims::Integer) where {T} = + accumulate!(mul_prod, B, A, dims=dims) + +""" + cumprod!(y::AbstractVector, x::AbstractVector) + +Cumulative product of a vector `x`, storing the result in `y`. +See also [`cumprod`](@ref). +""" +cumprod!(y::AbstractVector, x::AbstractVector) = cumprod!(y, x, dims=1) + +""" + cumprod(A; dims::Integer) + +Cumulative product along the dimension `dim`. See also +[`cumprod!`](@ref) to use a preallocated output array, both for performance and +to control the precision of the output (e.g. to avoid overflow). + +# Examples +```jldoctest +julia> a = [1 2 3; 4 5 6] +2×3 Array{Int64,2}: + 1 2 3 + 4 5 6 + +julia> cumprod(a, dims=1) +2×3 Array{Int64,2}: + 1 2 3 + 4 10 18 + +julia> cumprod(a, dims=2) +2×3 Array{Int64,2}: + 1 2 6 + 4 20 120 +``` +""" +function cumprod(A::AbstractArray; dims::Integer) + return accumulate(mul_prod, A, dims=dims) +end + +""" + cumprod(x::AbstractVector) + +Cumulative product of a vector. See also +[`cumprod!`](@ref) to use a preallocated output array, both for performance and +to control the precision of the output (e.g. to avoid overflow). + +# Examples +```jldoctest +julia> cumprod(fill(1//2, 3)) +3-element Array{Rational{Int64},1}: + 1//2 + 1//4 + 1//8 + +julia> cumprod([fill(1//3, 2, 2) for i in 1:3]) +3-element Array{Array{Rational{Int64},2},1}: + [1//3 1//3; 1//3 1//3] + [2//9 2//9; 2//9 2//9] + [4//27 4//27; 4//27 4//27] +``` +""" +cumprod(x::AbstractVector) = cumprod(x, dims=1) + + +""" + accumulate(op, A; dims::Integer, [init]) + +Cumulative operation `op` along the dimension `dims` of `A` (providing `dims` is optional +for vectors). An initial value `init` may optionally be provided by a keyword argument. See +also [`accumulate!`](@ref) to use a preallocated output array, both for performance and +to control the precision of the output (e.g. to avoid overflow). For common operations +there are specialized variants of `accumulate`, see: [`cumsum`](@ref), [`cumprod`](@ref) + +# Examples +```jldoctest +julia> accumulate(+, [1,2,3]) +3-element Array{Int64,1}: + 1 + 3 + 6 + +julia> accumulate(*, [1,2,3]) +3-element Array{Int64,1}: + 1 + 2 + 6 + +julia> accumulate(+, [1,2,3]; init=100) +3-element Array{Int64,1}: + 101 + 103 + 106 + +julia> accumulate(min, [1,2,-1]; init=0) +3-element Array{Int64,1}: + 0 + 0 + -1 + +julia> accumulate(+, fill(1, 3, 3), dims=1) +3×3 Array{Int64,2}: + 1 1 1 + 2 2 2 + 3 3 3 + +julia> accumulate(+, fill(1, 3, 3), dims=2) +3×3 Array{Int64,2}: + 1 2 3 + 1 2 3 + 1 2 3 +``` +""" +function accumulate(op, A; dims::Union{Nothing,Integer}=nothing, kw...) + nt = kw.data + if nt isa NamedTuple{()} + out = similar(A, promote_op(op, eltype(A), eltype(A))) + elseif nt isa NamedTuple{(:init,)} + out = similar(A, promote_op(op, typeof(nt.init), eltype(A))) + else + throw(ArgumentError("acccumulate does not support the keyword arguments $(setdiff(keys(nt), (:init,)))")) + end + accumulate!(op, out, A; dims=dims, kw...) +end + +""" + accumulate!(op, B, A; [dims], [init]) + +Cumulative operation `op` on `A` along the dimension `dims`, storing the result in `B`. +Providing `dims` is optional for vectors. If the keyword argument `init` is given, its +value is used to instantiate the accumulation. See also [`accumulate`](@ref). + +# Examples +```jldoctest +julia> x = [1, 0, 2, 0, 3]; + +julia> y = [0, 0, 0, 0, 0]; + +julia> accumulate!(+, y, x); + +julia> y +5-element Array{Int64,1}: + 1 + 1 + 3 + 3 + 6 + +julia> A = [1 2; 3 4]; + +julia> B = [0 0; 0 0]; + +julia> accumulate!(-, B, A, dims=1); + +julia> B +2×2 Array{Int64,2}: + 1 2 + -2 -2 + +julia> accumulate!(-, B, A, dims=2); + +julia> B +2×2 Array{Int64,2}: + 1 -1 + 3 -1 +``` +""" +function accumulate!(op, B, A; dims::Union{Integer, Nothing} = nothing, kw...) + nt = kw.data + if nt isa NamedTuple{()} + _accumulate!(op, B, A, dims, nothing) + elseif nt isa NamedTuple{(:init,)} + _accumulate!(op, B, A, dims, Some(nt.init)) + else + throw(ArgumentError("acccumulate! does not support the keyword arguments $(setdiff(keys(nt), (:init,)))")) + end +end + +function _accumulate!(op, B, A, dims::Nothing, init::Union{Nothing, Some}) + throw(ArgumentError("Keyword argument dims must be provided for multidimensional arrays")) +end + +function _accumulate!(op, B, A::AbstractVector, dims::Nothing, init::Nothing) + isempty(A) && return B + v1 = reduce_first(op, first(A)) + _accumulate1!(op, B, v1, A, 1) +end + +function _accumulate!(op, B, A::AbstractVector, dims::Nothing, init::Some) + isempty(A) && return B + v1 = op(something(init), first(A)) + _accumulate1!(op, B, v1, A, 1) +end + +function _accumulate!(op, B, A, dims::Integer, init::Union{Nothing, Some}) + dims > 0 || throw(ArgumentError("dims must be a positive integer")) + inds_t = axes(A) + axes(B) == inds_t || throw(DimensionMismatch("shape of B must match A")) + dims > ndims(A) && return copyto!(B, A) + isempty(inds_t[dims]) && return B + if dims == 1 + # We can accumulate to a temporary variable, which allows + # register usage and will be slightly faster + ind1 = inds_t[1] + @inbounds for I in CartesianIndices(tail(inds_t)) + if init === nothing + tmp = reduce_first(op, A[first(ind1), I]) + else + tmp = op(something(init), A[first(ind1), I]) + end + B[first(ind1), I] = tmp + for i_1 = first(ind1)+1:last(ind1) + tmp = op(tmp, A[i_1, I]) + B[i_1, I] = tmp + end + end + else + R1 = CartesianIndices(axes(A)[1:dims-1]) # not type-stable + R2 = CartesianIndices(axes(A)[dims+1:end]) + _accumulaten!(op, B, A, R1, inds_t[dims], R2, init) # use function barrier + end + return B +end + +@noinline function _accumulaten!(op, B, A, R1, ind, R2, init::Nothing) + # Copy the initial element in each 1d vector along dimension `dim` + ii = first(ind) + @inbounds for J in R2, I in R1 + B[I, ii, J] = reduce_first(op, A[I, ii, J]) + end + # Accumulate + @inbounds for J in R2, i in first(ind)+1:last(ind), I in R1 + B[I, i, J] = op(B[I, i-1, J], A[I, i, J]) + end + B +end + +@noinline function _accumulaten!(op, B, A, R1, ind, R2, init::Some) + # Copy the initial element in each 1d vector along dimension `dim` + ii = first(ind) + @inbounds for J in R2, I in R1 + B[I, ii, J] = op(something(init), A[I, ii, J]) + end + # Accumulate + @inbounds for J in R2, i in first(ind)+1:last(ind), I in R1 + B[I, i, J] = op(B[I, i-1, J], A[I, i, J]) + end + B +end + +function _accumulate1!(op, B, v1, A::AbstractVector, dim::Integer) + dim > 0 || throw(ArgumentError("dim must be a positive integer")) + inds = LinearIndices(A) + inds == LinearIndices(B) || throw(DimensionMismatch("LinearIndices of A and B don't match")) + dim > 1 && return copyto!(B, A) + (i1, state) = iterate(inds) # We checked earlier that A isn't empty + cur_val = v1 + B[i1] = cur_val + next = iterate(inds, state) + @inbounds while next !== nothing + (i, state) = next + cur_val = op(cur_val, A[i]) + B[i] = cur_val + next = iterate(inds, state) + end + return B +end diff --git a/base/array.jl b/base/array.jl new file mode 100644 index 0000000..300a940 --- /dev/null +++ b/base/array.jl @@ -0,0 +1,2451 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## array.jl: Dense arrays + +""" + DimensionMismatch([msg]) + +The objects called do not have matching dimensionality. Optional argument `msg` is a +descriptive error string. +""" +struct DimensionMismatch <: Exception + msg::AbstractString +end +DimensionMismatch() = DimensionMismatch("") + +## Type aliases for convenience ## +""" + AbstractVector{T} + +Supertype for one-dimensional arrays (or array-like types) with +elements of type `T`. Alias for [`AbstractArray{T,1}`](@ref). +""" +const AbstractVector{T} = AbstractArray{T,1} + +""" + AbstractMatrix{T} + +Supertype for two-dimensional arrays (or array-like types) with +elements of type `T`. Alias for [`AbstractArray{T,2}`](@ref). +""" +const AbstractMatrix{T} = AbstractArray{T,2} + +""" + AbstractVecOrMat{T} + +Union type of [`AbstractVector{T}`](@ref) and [`AbstractMatrix{T}`](@ref). +""" +const AbstractVecOrMat{T} = Union{AbstractVector{T}, AbstractMatrix{T}} +const RangeIndex = Union{Int, AbstractRange{Int}, AbstractUnitRange{Int}} +const DimOrInd = Union{Integer, AbstractUnitRange} +const IntOrInd = Union{Int, AbstractUnitRange} +const DimsOrInds{N} = NTuple{N,DimOrInd} +const NeedsShaping = Union{Tuple{Integer,Vararg{Integer}}, Tuple{OneTo,Vararg{OneTo}}} + +""" + Array{T,N} <: AbstractArray{T,N} + +`N`-dimensional dense array with elements of type `T`. +""" +Array + +""" + Vector{T} <: AbstractVector{T} + +One-dimensional dense array with elements of type `T`, often used to represent +a mathematical vector. Alias for [`Array{T,1}`](@ref). +""" +const Vector{T} = Array{T,1} + +""" + Matrix{T} <: AbstractMatrix{T} + +Two-dimensional dense array with elements of type `T`, often used to represent +a mathematical matrix. Alias for [`Array{T,2}`](@ref). +""" +const Matrix{T} = Array{T,2} +""" + VecOrMat{T} + +Union type of [`Vector{T}`](@ref) and [`Matrix{T}`](@ref). +""" +const VecOrMat{T} = Union{Vector{T}, Matrix{T}} + +""" + DenseArray{T, N} <: AbstractArray{T,N} + +`N`-dimensional dense array with elements of type `T`. +The elements of a dense array are stored contiguously in memory. +""" +DenseArray + +""" + DenseVector{T} + +One-dimensional [`DenseArray`](@ref) with elements of type `T`. Alias for `DenseArray{T,1}`. +""" +const DenseVector{T} = DenseArray{T,1} + +""" + DenseMatrix{T} + +Two-dimensional [`DenseArray`](@ref) with elements of type `T`. Alias for `DenseArray{T,2}`. +""" +const DenseMatrix{T} = DenseArray{T,2} + +""" + DenseVecOrMat{T} + +Union type of [`DenseVector{T}`](@ref) and [`DenseMatrix{T}`](@ref). +""" +const DenseVecOrMat{T} = Union{DenseVector{T}, DenseMatrix{T}} + +## Basic functions ## + +""" + eltype(type) + +Determine the type of the elements generated by iterating a collection of the given `type`. +For dictionary types, this will be a `Pair{KeyType,ValType}`. The definition +`eltype(x) = eltype(typeof(x))` is provided for convenience so that instances can be passed +instead of types. However the form that accepts a type argument should be defined for new +types. + +# Examples +```jldoctest +julia> eltype(fill(1f0, (2,2))) +Float32 + +julia> eltype(fill(0x1, (2,2))) +UInt8 +``` +""" +eltype(::Type) = Any +eltype(::Type{Bottom}) = throw(ArgumentError("Union{} does not have elements")) +eltype(x) = eltype(typeof(x)) + +import Core: arraysize, arrayset, arrayref, const_arrayref + +vect() = Vector{Any}() +vect(X::T...) where {T} = T[ X[i] for i = 1:length(X) ] + +""" + vect(X...) + +Create a [`Vector`](@ref) with element type computed from the `promote_typeof` of the argument, +containing the argument list. + +# Examples +```jldoctest +julia> a = Base.vect(UInt8(1), 2.5, 1//2) +3-element Array{Float64,1}: + 1.0 + 2.5 + 0.5 +``` +""" +function vect(X...) + T = promote_typeof(X...) + #T[ X[i] for i=1:length(X) ] + # TODO: this is currently much faster. should figure out why. not clear. + return copyto!(Vector{T}(undef, length(X)), X) +end + +size(a::Array, d::Integer) = arraysize(a, convert(Int, d)) +size(a::Vector) = (arraysize(a,1),) +size(a::Matrix) = (arraysize(a,1), arraysize(a,2)) +size(a::Array{<:Any,N}) where {N} = (@_inline_meta; ntuple(M -> size(a, M), Val(N))) + +asize_from(a::Array, n) = n > ndims(a) ? () : (arraysize(a,n), asize_from(a, n+1)...) + +allocatedinline(::Type{T}) where {T} = (@_pure_meta; ccall(:jl_array_store_unboxed, Cint, (Any,), T) != Cint(0)) + +""" + Base.isbitsunion(::Type{T}) + +Return whether a type is an "is-bits" Union type, meaning each type included in a Union is [`isbitstype`](@ref). + +# Examples +```jldoctest +julia> Base.isbitsunion(Union{Float64, UInt8}) +true + +julia> Base.isbitsunion(Union{Float64, String}) +false +``` +""" +isbitsunion(u::Union) = (@_pure_meta; ccall(:jl_array_store_unboxed, Cint, (Any,), u) != Cint(0)) +isbitsunion(x) = false + +isptrelement(t::Type) = (@_pure_meta; ccall(:jl_array_store_unboxed, Cint, (Any,), t) == Cint(0)) + +function _unsetindex!(A::Array{T}, i::Int) where {T} + @boundscheck checkbounds(A, i) + if isptrelement(T) + t = @_gc_preserve_begin A + p = Ptr{Ptr{Cvoid}}(pointer(A)) + unsafe_store!(p, C_NULL, i) + @_gc_preserve_end t + end + return A +end + + +""" + Base.bitsunionsize(U::Union) + +For a `Union` of [`isbitstype`](@ref) types, return the size of the largest type; assumes `Base.isbitsunion(U) == true`. + +# Examples +```jldoctest +julia> Base.bitsunionsize(Union{Float64, UInt8}) +0x0000000000000008 + +julia> Base.bitsunionsize(Union{Float64, UInt8, Int128}) +0x0000000000000010 +``` +""" +function bitsunionsize(u::Union) + sz = Ref{Csize_t}(0) + algn = Ref{Csize_t}(0) + isunboxed = ccall(:jl_islayout_inline, Cint, (Any, Ptr{Csize_t}, Ptr{Csize_t}), u, sz, algn) + @assert isunboxed != Cint(0) + return sz[] +end + +length(a::Array) = arraylen(a) +elsize(::Type{<:Array{T}}) where {T} = aligned_sizeof(T) +sizeof(a::Array) = Core.sizeof(a) + +function isassigned(a::Array, i::Int...) + @_inline_meta + ii = (_sub2ind(size(a), i...) % UInt) - 1 + @boundscheck ii < length(a) % UInt || return false + ccall(:jl_array_isassigned, Cint, (Any, UInt), a, ii) == 1 +end + +## copy ## + +""" + unsafe_copyto!(dest::Ptr{T}, src::Ptr{T}, N) + +Copy `N` elements from a source pointer to a destination, with no checking. The size of an +element is determined by the type of the pointers. + +The `unsafe` prefix on this function indicates that no validation is performed on the +pointers `dest` and `src` to ensure that they are valid. Incorrect usage may corrupt or +segfault your program, in the same manner as C. +""" +function unsafe_copyto!(dest::Ptr{T}, src::Ptr{T}, n) where T + # Do not use this to copy data between pointer arrays. + # It can't be made safe no matter how carefully you checked. + ccall(:memmove, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, UInt), + dest, src, n * aligned_sizeof(T)) + return dest +end + +""" + unsafe_copyto!(dest::Array, do, src::Array, so, N) + +Copy `N` elements from a source array to a destination, starting at offset `so` in the +source and `do` in the destination (1-indexed). + +The `unsafe` prefix on this function indicates that no validation is performed to ensure +that N is inbounds on either array. Incorrect usage may corrupt or segfault your program, in +the same manner as C. +""" +function unsafe_copyto!(dest::Array{T}, doffs, src::Array{T}, soffs, n) where T + t1 = @_gc_preserve_begin dest + t2 = @_gc_preserve_begin src + if isbitstype(T) + unsafe_copyto!(pointer(dest, doffs), pointer(src, soffs), n) + elseif isbitsunion(T) + ccall(:memmove, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, UInt), + pointer(dest, doffs), pointer(src, soffs), n * aligned_sizeof(T)) + # copy selector bytes + ccall(:memmove, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, UInt), + ccall(:jl_array_typetagdata, Ptr{UInt8}, (Any,), dest) + doffs - 1, + ccall(:jl_array_typetagdata, Ptr{UInt8}, (Any,), src) + soffs - 1, + n) + else + ccall(:jl_array_ptr_copy, Cvoid, (Any, Ptr{Cvoid}, Any, Ptr{Cvoid}, Int), + dest, pointer(dest, doffs), src, pointer(src, soffs), n) + end + @_gc_preserve_end t2 + @_gc_preserve_end t1 + return dest +end + +""" + copyto!(dest, do, src, so, N) + +Copy `N` elements from collection `src` starting at offset `so`, to array `dest` starting at +offset `do`. Return `dest`. +""" +function copyto!(dest::Array{T}, doffs::Integer, src::Array{T}, soffs::Integer, n::Integer) where T + n == 0 && return dest + n > 0 || _throw_argerror() + if soffs < 1 || doffs < 1 || soffs+n-1 > length(src) || doffs+n-1 > length(dest) + throw(BoundsError()) + end + unsafe_copyto!(dest, doffs, src, soffs, n) + return dest +end + +# Outlining this because otherwise a catastrophic inference slowdown +# occurs, see discussion in #27874. +# It is also mitigated by using a constant string. +function _throw_argerror() + @_noinline_meta + throw(ArgumentError("Number of elements to copy must be nonnegative.")) +end + +copyto!(dest::Array{T}, src::Array{T}) where {T} = copyto!(dest, 1, src, 1, length(src)) + +# N.B: The generic definition in multidimensional.jl covers, this, this is just here +# for bootstrapping purposes. +function fill!(dest::Array{T}, x) where T + @_noinline_meta + xT = convert(T, x) + for i in eachindex(dest) + @inbounds dest[i] = xT + end + return dest +end + +""" + copy(x) + +Create a shallow copy of `x`: the outer structure is copied, but not all internal values. +For example, copying an array produces a new array with identically-same elements as the +original. +""" +copy + +copy(a::T) where {T<:Array} = ccall(:jl_array_copy, Ref{T}, (Any,), a) + +## Constructors ## + +similar(a::Array{T,1}) where {T} = Vector{T}(undef, size(a,1)) +similar(a::Array{T,2}) where {T} = Matrix{T}(undef, size(a,1), size(a,2)) +similar(a::Array{T,1}, S::Type) where {T} = Vector{S}(undef, size(a,1)) +similar(a::Array{T,2}, S::Type) where {T} = Matrix{S}(undef, size(a,1), size(a,2)) +similar(a::Array{T}, m::Int) where {T} = Vector{T}(undef, m) +similar(a::Array, T::Type, dims::Dims{N}) where {N} = Array{T,N}(undef, dims) +similar(a::Array{T}, dims::Dims{N}) where {T,N} = Array{T,N}(undef, dims) + +# T[x...] constructs Array{T,1} +""" + getindex(type[, elements...]) + +Construct a 1-d array of the specified type. This is usually called with the syntax +`Type[]`. Element values can be specified using `Type[a,b,c,...]`. + +# Examples +```jldoctest +julia> Int8[1, 2, 3] +3-element Array{Int8,1}: + 1 + 2 + 3 + +julia> getindex(Int8, 1, 2, 3) +3-element Array{Int8,1}: + 1 + 2 + 3 +``` +""" +function getindex(::Type{T}, vals...) where T + a = Vector{T}(undef, length(vals)) + @inbounds for i = 1:length(vals) + a[i] = vals[i] + end + return a +end + +getindex(::Type{T}) where {T} = (@_inline_meta; Vector{T}()) +getindex(::Type{T}, x) where {T} = (@_inline_meta; a = Vector{T}(undef, 1); @inbounds a[1] = x; a) +getindex(::Type{T}, x, y) where {T} = (@_inline_meta; a = Vector{T}(undef, 2); @inbounds (a[1] = x; a[2] = y); a) +getindex(::Type{T}, x, y, z) where {T} = (@_inline_meta; a = Vector{T}(undef, 3); @inbounds (a[1] = x; a[2] = y; a[3] = z); a) + +function getindex(::Type{Any}, @nospecialize vals...) + a = Vector{Any}(undef, length(vals)) + @inbounds for i = 1:length(vals) + a[i] = vals[i] + end + return a +end +getindex(::Type{Any}) = Vector{Any}() + +function fill!(a::Union{Array{UInt8}, Array{Int8}}, x::Integer) + ccall(:memset, Ptr{Cvoid}, (Ptr{Cvoid}, Cint, Csize_t), a, convert(eltype(a), x), length(a)) + return a +end + +to_dim(d::Integer) = d +to_dim(d::OneTo) = last(d) + +""" + fill(x, dims) + +Create an array filled with the value `x`. For example, `fill(1.0, (5,5))` returns a 5×5 +array of floats, with each element initialized to `1.0`. + +# Examples +```jldoctest +julia> fill(1.0, (5,5)) +5×5 Array{Float64,2}: + 1.0 1.0 1.0 1.0 1.0 + 1.0 1.0 1.0 1.0 1.0 + 1.0 1.0 1.0 1.0 1.0 + 1.0 1.0 1.0 1.0 1.0 + 1.0 1.0 1.0 1.0 1.0 +``` + +If `x` is an object reference, all elements will refer to the same object. `fill(Foo(), +dims)` will return an array filled with the result of evaluating `Foo()` once. +""" +fill(v, dims::DimOrInd...) = fill(v, dims) +fill(v, dims::NTuple{N, Union{Integer, OneTo}}) where {N} = fill(v, map(to_dim, dims)) +fill(v, dims::NTuple{N, Integer}) where {N} = (a=Array{typeof(v),N}(undef, dims); fill!(a, v); a) +fill(v, dims::Tuple{}) = (a=Array{typeof(v),0}(undef, dims); fill!(a, v); a) + +""" + zeros([T=Float64,] dims...) + +Create an `Array`, with element type `T`, of all zeros with size specified by `dims`. +See also [`fill`](@ref), [`ones`](@ref). + +# Examples +```jldoctest +julia> zeros(1) +1-element Array{Float64,1}: + 0.0 + +julia> zeros(Int8, 2, 3) +2×3 Array{Int8,2}: + 0 0 0 + 0 0 0 +``` +""" +function zeros end + +""" + ones([T=Float64,] dims...) + +Create an `Array`, with element type `T`, of all ones with size specified by `dims`. +See also: [`fill`](@ref), [`zeros`](@ref). + +# Examples +```jldoctest +julia> ones(1,2) +1×2 Array{Float64,2}: + 1.0 1.0 + +julia> ones(ComplexF64, 2, 3) +2×3 Array{Complex{Float64},2}: + 1.0+0.0im 1.0+0.0im 1.0+0.0im + 1.0+0.0im 1.0+0.0im 1.0+0.0im +``` +""" +function ones end + +for (fname, felt) in ((:zeros, :zero), (:ones, :one)) + @eval begin + $fname(dims::DimOrInd...) = $fname(dims) + $fname(::Type{T}, dims::DimOrInd...) where {T} = $fname(T, dims) + $fname(dims::Tuple{Vararg{DimOrInd}}) = $fname(Float64, dims) + $fname(::Type{T}, dims::NTuple{N, Union{Integer, OneTo}}) where {T,N} = $fname(T, map(to_dim, dims)) + function $fname(::Type{T}, dims::NTuple{N, Integer}) where {T,N} + a = Array{T,N}(undef, dims) + fill!(a, $felt(T)) + return a + end + function $fname(::Type{T}, dims::Tuple{}) where {T} + a = Array{T}(undef) + fill!(a, $felt(T)) + return a + end + end +end + +function _one(unit::T, x::AbstractMatrix) where T + require_one_based_indexing(x) + m,n = size(x) + m==n || throw(DimensionMismatch("multiplicative identity defined only for square matrices")) + # Matrix{T}(I, m, m) + I = zeros(T, m, m) + for i in 1:m + I[i,i] = unit + end + I +end + +one(x::AbstractMatrix{T}) where {T} = _one(one(T), x) +oneunit(x::AbstractMatrix{T}) where {T} = _one(oneunit(T), x) + +## Conversions ## + +convert(::Type{T}, a::AbstractArray) where {T<:Array} = a isa T ? a : T(a) + +promote_rule(a::Type{Array{T,n}}, b::Type{Array{S,n}}) where {T,n,S} = el_same(promote_type(T,S), a, b) + +## Constructors ## + +if nameof(@__MODULE__) === :Base # avoid method overwrite +# constructors should make copies +Array{T,N}(x::AbstractArray{S,N}) where {T,N,S} = copyto!(Array{T,N}(undef, size(x)), x) +AbstractArray{T,N}(A::AbstractArray{S,N}) where {T,N,S} = copyto!(similar(A,T), A) +end + +## copying iterators to containers + +""" + collect(element_type, collection) + +Return an `Array` with the given element type of all items in a collection or iterable. +The result has the same shape and number of dimensions as `collection`. + +# Examples +```jldoctest +julia> collect(Float64, 1:2:5) +3-element Array{Float64,1}: + 1.0 + 3.0 + 5.0 +``` +""" +collect(::Type{T}, itr) where {T} = _collect(T, itr, IteratorSize(itr)) + +_collect(::Type{T}, itr, isz::HasLength) where {T} = copyto!(Vector{T}(undef, Int(length(itr)::Integer)), itr) +_collect(::Type{T}, itr, isz::HasShape) where {T} = copyto!(similar(Array{T}, axes(itr)), itr) +function _collect(::Type{T}, itr, isz::SizeUnknown) where T + a = Vector{T}() + for x in itr + push!(a,x) + end + return a +end + +# make a collection similar to `c` and appropriate for collecting `itr` +_similar_for(c::AbstractArray, ::Type{T}, itr, ::SizeUnknown) where {T} = similar(c, T, 0) +_similar_for(c::AbstractArray, ::Type{T}, itr, ::HasLength) where {T} = + similar(c, T, Int(length(itr)::Integer)) +_similar_for(c::AbstractArray, ::Type{T}, itr, ::HasShape) where {T} = + similar(c, T, axes(itr)) +_similar_for(c, ::Type{T}, itr, isz) where {T} = similar(c, T) + +""" + collect(collection) + +Return an `Array` of all items in a collection or iterator. For dictionaries, returns +`Pair{KeyType, ValType}`. If the argument is array-like or is an iterator with the +[`HasShape`](@ref IteratorSize) trait, the result will have the same shape +and number of dimensions as the argument. + +# Examples +```jldoctest +julia> collect(1:2:13) +7-element Array{Int64,1}: + 1 + 3 + 5 + 7 + 9 + 11 + 13 +``` +""" +collect(itr) = _collect(1:1 #= Array =#, itr, IteratorEltype(itr), IteratorSize(itr)) + +collect(A::AbstractArray) = _collect_indices(axes(A), A) + +collect_similar(cont, itr) = _collect(cont, itr, IteratorEltype(itr), IteratorSize(itr)) + +_collect(cont, itr, ::HasEltype, isz::Union{HasLength,HasShape}) = + copyto!(_similar_for(cont, eltype(itr), itr, isz), itr) + +function _collect(cont, itr, ::HasEltype, isz::SizeUnknown) + a = _similar_for(cont, eltype(itr), itr, isz) + for x in itr + push!(a,x) + end + return a +end + +_collect_indices(::Tuple{}, A) = copyto!(Array{eltype(A),0}(undef), A) +_collect_indices(indsA::Tuple{Vararg{OneTo}}, A) = + copyto!(Array{eltype(A)}(undef, length.(indsA)), A) +function _collect_indices(indsA, A) + B = Array{eltype(A)}(undef, length.(indsA)) + copyto!(B, CartesianIndices(axes(B)), A, CartesianIndices(indsA)) +end + +# define this as a macro so that the call to Core.Compiler +# gets inlined into the caller before recursion detection +# gets a chance to see it, so that recursive calls to the caller +# don't trigger the inference limiter +if isdefined(Core, :Compiler) + macro default_eltype(itr) + I = esc(itr) + return quote + if $I isa Generator && ($I).f isa Type + ($I).f + else + Core.Compiler.return_type(first, Tuple{typeof($I)}) + end + end + end +else + macro default_eltype(itr) + I = esc(itr) + return quote + if $I isa Generator && ($I).f isa Type + ($I).f + else + Any + end + end + end +end + +_array_for(::Type{T}, itr, ::HasLength) where {T} = Vector{T}(undef, Int(length(itr)::Integer)) +_array_for(::Type{T}, itr, ::HasShape{N}) where {T,N} = similar(Array{T,N}, axes(itr)) + +function collect(itr::Generator) + isz = IteratorSize(itr.iter) + et = @default_eltype(itr) + if isa(isz, SizeUnknown) + return grow_to!(Vector{et}(), itr) + else + y = iterate(itr) + if y === nothing + return _array_for(et, itr.iter, isz) + end + v1, st = y + collect_to_with_first!(_array_for(typeof(v1), itr.iter, isz), v1, itr, st) + end +end + +_collect(c, itr, ::EltypeUnknown, isz::SizeUnknown) = + grow_to!(_similar_for(c, @default_eltype(itr), itr, isz), itr) + +function _collect(c, itr, ::EltypeUnknown, isz::Union{HasLength,HasShape}) + y = iterate(itr) + if y === nothing + return _similar_for(c, @default_eltype(itr), itr, isz) + end + v1, st = y + collect_to_with_first!(_similar_for(c, typeof(v1), itr, isz), v1, itr, st) +end + +function collect_to_with_first!(dest::AbstractArray, v1, itr, st) + i1 = first(LinearIndices(dest)) + dest[i1] = v1 + return collect_to!(dest, itr, i1+1, st) +end + +function collect_to_with_first!(dest, v1, itr, st) + push!(dest, v1) + return grow_to!(dest, itr, st) +end + +function setindex_widen_up_to(dest::AbstractArray{T}, el, i) where T + @_inline_meta + new = similar(dest, promote_typejoin(T, typeof(el))) + copyto!(new, firstindex(new), dest, firstindex(dest), i-1) + @inbounds new[i] = el + return new +end + +function collect_to!(dest::AbstractArray{T}, itr, offs, st) where T + # collect to dest array, checking the type of each result. if a result does not + # match, widen the result type and re-dispatch. + i = offs + while true + y = iterate(itr, st) + y === nothing && break + el, st = y + if el isa T || typeof(el) === T + @inbounds dest[i] = el::T + i += 1 + else + new = setindex_widen_up_to(dest, el, i) + return collect_to!(new, itr, i+1, st) + end + end + return dest +end + +function grow_to!(dest, itr) + y = iterate(itr) + y === nothing && return dest + dest2 = empty(dest, typeof(y[1])) + push!(dest2, y[1]) + grow_to!(dest2, itr, y[2]) +end + +function push_widen(dest, el) + @_inline_meta + new = sizehint!(empty(dest, promote_typejoin(eltype(dest), typeof(el))), length(dest)) + if new isa AbstractSet + # TODO: merge back these two branches when copy! is re-enabled for sets/vectors + union!(new, dest) + else + append!(new, dest) + end + push!(new, el) + return new +end + +function grow_to!(dest, itr, st) + T = eltype(dest) + y = iterate(itr, st) + while y !== nothing + el, st = y + if el isa T || typeof(el) === T + push!(dest, el::T) + else + new = push_widen(dest, el) + return grow_to!(new, itr, st) + end + y = iterate(itr, st) + end + return dest +end + +## Iteration ## + +iterate(A::Array, i=1) = (@_inline_meta; (i % UInt) - 1 < length(A) ? (@inbounds A[i], i + 1) : nothing) + +## Indexing: getindex ## + +""" + getindex(collection, key...) + +Retrieve the value(s) stored at the given key or index within a collection. The syntax +`a[i,j,...]` is converted by the compiler to `getindex(a, i, j, ...)`. + +# Examples +```jldoctest +julia> A = Dict("a" => 1, "b" => 2) +Dict{String,Int64} with 2 entries: + "b" => 2 + "a" => 1 + +julia> getindex(A, "a") +1 +``` +""" +function getindex end + +# This is more complicated than it needs to be in order to get Win64 through bootstrap +@eval getindex(A::Array, i1::Int) = arrayref($(Expr(:boundscheck)), A, i1) +@eval getindex(A::Array, i1::Int, i2::Int, I::Int...) = (@_inline_meta; arrayref($(Expr(:boundscheck)), A, i1, i2, I...)) + +# Faster contiguous indexing using copyto! for UnitRange and Colon +function getindex(A::Array, I::UnitRange{Int}) + @_inline_meta + @boundscheck checkbounds(A, I) + lI = length(I) + X = similar(A, lI) + if lI > 0 + unsafe_copyto!(X, 1, A, first(I), lI) + end + return X +end +function getindex(A::Array, c::Colon) + lI = length(A) + X = similar(A, lI) + if lI > 0 + unsafe_copyto!(X, 1, A, 1, lI) + end + return X +end + +# This is redundant with the abstract fallbacks, but needed for bootstrap +function getindex(A::Array{S}, I::AbstractRange{Int}) where S + return S[ A[i] for i in I ] +end + +## Indexing: setindex! ## + +""" + setindex!(collection, value, key...) + +Store the given value at the given key or index within a collection. The syntax `a[i,j,...] = +x` is converted by the compiler to `(setindex!(a, x, i, j, ...); x)`. +""" +function setindex! end + +@eval setindex!(A::Array{T}, x, i1::Int) where {T} = arrayset($(Expr(:boundscheck)), A, convert(T,x)::T, i1) +@eval setindex!(A::Array{T}, x, i1::Int, i2::Int, I::Int...) where {T} = + (@_inline_meta; arrayset($(Expr(:boundscheck)), A, convert(T,x)::T, i1, i2, I...)) + +# This is redundant with the abstract fallbacks but needed and helpful for bootstrap +function setindex!(A::Array, X::AbstractArray, I::AbstractVector{Int}) + @_propagate_inbounds_meta + @boundscheck setindex_shape_check(X, length(I)) + require_one_based_indexing(X) + X′ = unalias(A, X) + I′ = unalias(A, I) + count = 1 + for i in I′ + @inbounds x = X′[count] + A[i] = x + count += 1 + end + return A +end + +# Faster contiguous setindex! with copyto! +function setindex!(A::Array{T}, X::Array{T}, I::UnitRange{Int}) where T + @_inline_meta + @boundscheck checkbounds(A, I) + lI = length(I) + @boundscheck setindex_shape_check(X, lI) + if lI > 0 + unsafe_copyto!(A, first(I), X, 1, lI) + end + return A +end +function setindex!(A::Array{T}, X::Array{T}, c::Colon) where T + @_inline_meta + lI = length(A) + @boundscheck setindex_shape_check(X, lI) + if lI > 0 + unsafe_copyto!(A, 1, X, 1, lI) + end + return A +end + +# efficiently grow an array + +_growbeg!(a::Vector, delta::Integer) = + ccall(:jl_array_grow_beg, Cvoid, (Any, UInt), a, delta) +_growend!(a::Vector, delta::Integer) = + ccall(:jl_array_grow_end, Cvoid, (Any, UInt), a, delta) +_growat!(a::Vector, i::Integer, delta::Integer) = + ccall(:jl_array_grow_at, Cvoid, (Any, Int, UInt), a, i - 1, delta) + +# efficiently delete part of an array + +_deletebeg!(a::Vector, delta::Integer) = + ccall(:jl_array_del_beg, Cvoid, (Any, UInt), a, delta) +_deleteend!(a::Vector, delta::Integer) = + ccall(:jl_array_del_end, Cvoid, (Any, UInt), a, delta) +_deleteat!(a::Vector, i::Integer, delta::Integer) = + ccall(:jl_array_del_at, Cvoid, (Any, Int, UInt), a, i - 1, delta) + +## Dequeue functionality ## + +""" + push!(collection, items...) -> collection + +Insert one or more `items` at the end of `collection`. + +# Examples +```jldoctest +julia> push!([1, 2, 3], 4, 5, 6) +6-element Array{Int64,1}: + 1 + 2 + 3 + 4 + 5 + 6 +``` + +Use [`append!`](@ref) to add all the elements of another collection to +`collection`. The result of the preceding example is equivalent to `append!([1, 2, 3], [4, +5, 6])`. +""" +function push! end + +function push!(a::Array{T,1}, item) where T + # convert first so we don't grow the array if the assignment won't work + itemT = convert(T, item) + _growend!(a, 1) + a[end] = itemT + return a +end + +function push!(a::Array{Any,1}, @nospecialize item) + _growend!(a, 1) + arrayset(true, a, item, length(a)) + return a +end + +""" + append!(collection, collection2) -> collection. + +Add the elements of `collection2` to the end of `collection`. + +# Examples +```jldoctest +julia> append!([1],[2,3]) +3-element Array{Int64,1}: + 1 + 2 + 3 + +julia> append!([1, 2, 3], [4, 5, 6]) +6-element Array{Int64,1}: + 1 + 2 + 3 + 4 + 5 + 6 +``` + +Use [`push!`](@ref) to add individual items to `collection` which are not already +themselves in another collection. The result of the preceding example is equivalent to +`push!([1, 2, 3], 4, 5, 6)`. +""" +function append!(a::Vector, items::AbstractVector) + itemindices = eachindex(items) + n = length(itemindices) + _growend!(a, n) + copyto!(a, length(a)-n+1, items, first(itemindices), n) + return a +end + +append!(a::AbstractVector, iter) = _append!(a, IteratorSize(iter), iter) +push!(a::AbstractVector, iter...) = append!(a, iter) + +function _append!(a, ::Union{HasLength,HasShape}, iter) + n = length(a) + i = lastindex(a) + resize!(a, n+length(iter)) + @inbounds for (i, item) in zip(i+1:lastindex(a), iter) + a[i] = item + end + a +end + +function _append!(a, ::IteratorSize, iter) + for item in iter + push!(a, item) + end + a +end + +""" + prepend!(a::Vector, items) -> collection + +Insert the elements of `items` to the beginning of `a`. + +# Examples +```jldoctest +julia> prepend!([3],[1,2]) +3-element Array{Int64,1}: + 1 + 2 + 3 +``` +""" +function prepend! end + +function prepend!(a::Vector, items::AbstractVector) + itemindices = eachindex(items) + n = length(itemindices) + _growbeg!(a, n) + if a === items + copyto!(a, 1, items, n+1, n) + else + copyto!(a, 1, items, first(itemindices), n) + end + return a +end + +prepend!(a::Vector, iter) = _prepend!(a, IteratorSize(iter), iter) +pushfirst!(a::Vector, iter...) = prepend!(a, iter) + +function _prepend!(a, ::Union{HasLength,HasShape}, iter) + require_one_based_indexing(a) + n = length(iter) + _growbeg!(a, n) + i = 0 + for item in iter + @inbounds a[i += 1] = item + end + a +end +function _prepend!(a, ::IteratorSize, iter) + n = 0 + for item in iter + n += 1 + pushfirst!(a, item) + end + reverse!(a, 1, n) + a +end + +""" + resize!(a::Vector, n::Integer) -> Vector + +Resize `a` to contain `n` elements. If `n` is smaller than the current collection +length, the first `n` elements will be retained. If `n` is larger, the new elements are not +guaranteed to be initialized. + +# Examples +```jldoctest +julia> resize!([6, 5, 4, 3, 2, 1], 3) +3-element Array{Int64,1}: + 6 + 5 + 4 + +julia> a = resize!([6, 5, 4, 3, 2, 1], 8); + +julia> length(a) +8 + +julia> a[1:6] +6-element Array{Int64,1}: + 6 + 5 + 4 + 3 + 2 + 1 +``` +""" +function resize!(a::Vector, nl::Integer) + l = length(a) + if nl > l + _growend!(a, nl-l) + elseif nl != l + if nl < 0 + throw(ArgumentError("new length must be ≥ 0")) + end + _deleteend!(a, l-nl) + end + return a +end + +""" + sizehint!(s, n) + +Suggest that collection `s` reserve capacity for at least `n` elements. This can improve performance. +""" +function sizehint! end + +function sizehint!(a::Vector, sz::Integer) + ccall(:jl_array_sizehint, Cvoid, (Any, UInt), a, sz) + a +end + +""" + pop!(collection) -> item + +Remove an item in `collection` and return it. If `collection` is an +ordered container, the last item is returned. + +# Examples +```jldoctest +julia> A=[1, 2, 3] +3-element Array{Int64,1}: + 1 + 2 + 3 + +julia> pop!(A) +3 + +julia> A +2-element Array{Int64,1}: + 1 + 2 + +julia> S = Set([1, 2]) +Set([2, 1]) + +julia> pop!(S) +2 + +julia> S +Set([1]) + +julia> pop!(Dict(1=>2)) +1 => 2 +``` +""" +function pop!(a::Vector) + if isempty(a) + throw(ArgumentError("array must be non-empty")) + end + item = a[end] + _deleteend!(a, 1) + return item +end + +""" + pushfirst!(collection, items...) -> collection + +Insert one or more `items` at the beginning of `collection`. + +# Examples +```jldoctest +julia> pushfirst!([1, 2, 3, 4], 5, 6) +6-element Array{Int64,1}: + 5 + 6 + 1 + 2 + 3 + 4 +``` +""" +function pushfirst!(a::Array{T,1}, item) where T + item = convert(T, item) + _growbeg!(a, 1) + a[1] = item + return a +end + +""" + popfirst!(collection) -> item + +Remove the first `item` from `collection`. + +# Examples +```jldoctest +julia> A = [1, 2, 3, 4, 5, 6] +6-element Array{Int64,1}: + 1 + 2 + 3 + 4 + 5 + 6 + +julia> popfirst!(A) +1 + +julia> A +5-element Array{Int64,1}: + 2 + 3 + 4 + 5 + 6 +``` +""" +function popfirst!(a::Vector) + if isempty(a) + throw(ArgumentError("array must be non-empty")) + end + item = a[1] + _deletebeg!(a, 1) + return item +end + +""" + insert!(a::Vector, index::Integer, item) + +Insert an `item` into `a` at the given `index`. `index` is the index of `item` in +the resulting `a`. + +# Examples +```jldoctest +julia> insert!([6, 5, 4, 2, 1], 4, 3) +6-element Array{Int64,1}: + 6 + 5 + 4 + 3 + 2 + 1 +``` +""" +function insert!(a::Array{T,1}, i::Integer, item) where T + # Throw convert error before changing the shape of the array + _item = convert(T, item) + _growat!(a, i, 1) + # _growat! already did bound check + @inbounds a[i] = _item + return a +end + +""" + deleteat!(a::Vector, i::Integer) + +Remove the item at the given `i` and return the modified `a`. Subsequent items +are shifted to fill the resulting gap. + +# Examples +```jldoctest +julia> deleteat!([6, 5, 4, 3, 2, 1], 2) +5-element Array{Int64,1}: + 6 + 4 + 3 + 2 + 1 +``` +""" +deleteat!(a::Vector, i::Integer) = (_deleteat!(a, i, 1); a) + +function deleteat!(a::Vector, r::UnitRange{<:Integer}) + n = length(a) + isempty(r) || _deleteat!(a, first(r), length(r)) + return a +end + +""" + deleteat!(a::Vector, inds) + +Remove the items at the indices given by `inds`, and return the modified `a`. +Subsequent items are shifted to fill the resulting gap. + +`inds` can be either an iterator or a collection of sorted and unique integer indices, +or a boolean vector of the same length as `a` with `true` indicating entries to delete. + +# Examples +```jldoctest +julia> deleteat!([6, 5, 4, 3, 2, 1], 1:2:5) +3-element Array{Int64,1}: + 5 + 3 + 1 + +julia> deleteat!([6, 5, 4, 3, 2, 1], [true, false, true, false, true, false]) +3-element Array{Int64,1}: + 5 + 3 + 1 + +julia> deleteat!([6, 5, 4, 3, 2, 1], (2, 2)) +ERROR: ArgumentError: indices must be unique and sorted +Stacktrace: +[...] +``` +""" +deleteat!(a::Vector, inds) = _deleteat!(a, inds) +deleteat!(a::Vector, inds::AbstractVector) = _deleteat!(a, to_indices(a, (inds,))[1]) + +function _deleteat!(a::Vector, inds) + n = length(a) + y = iterate(inds) + y === nothing && return a + n == 0 && throw(BoundsError(a, inds)) + (p, s) = y + q = p+1 + while true + y = iterate(inds, s) + y === nothing && break + (i,s) = y + if !(q <= i <= n) + if i < q + throw(ArgumentError("indices must be unique and sorted")) + else + throw(BoundsError()) + end + end + while q < i + @inbounds a[p] = a[q] + p += 1; q += 1 + end + q = i+1 + end + while q <= n + @inbounds a[p] = a[q] + p += 1; q += 1 + end + _deleteend!(a, n-p+1) + return a +end + +# Simpler and more efficient version for logical indexing +function deleteat!(a::Vector, inds::AbstractVector{Bool}) + n = length(a) + length(inds) == n || throw(BoundsError(a, inds)) + p = 1 + for (q, i) in enumerate(inds) + @inbounds a[p] = a[q] + p += !i + end + _deleteend!(a, n-p+1) + return a +end + +const _default_splice = [] + +""" + splice!(a::Vector, index::Integer, [replacement]) -> item + +Remove the item at the given index, and return the removed item. +Subsequent items are shifted left to fill the resulting gap. +If specified, replacement values from an ordered +collection will be spliced in place of the removed item. + +# Examples +```jldoctest +julia> A = [6, 5, 4, 3, 2, 1]; splice!(A, 5) +2 + +julia> A +5-element Array{Int64,1}: + 6 + 5 + 4 + 3 + 1 + +julia> splice!(A, 5, -1) +1 + +julia> A +5-element Array{Int64,1}: + 6 + 5 + 4 + 3 + -1 + +julia> splice!(A, 1, [-1, -2, -3]) +6 + +julia> A +7-element Array{Int64,1}: + -1 + -2 + -3 + 5 + 4 + 3 + -1 +``` + +To insert `replacement` before an index `n` without removing any items, use +`splice!(collection, n:n-1, replacement)`. +""" +function splice!(a::Vector, i::Integer, ins=_default_splice) + v = a[i] + m = length(ins) + if m == 0 + _deleteat!(a, i, 1) + elseif m == 1 + a[i] = ins[1] + else + _growat!(a, i, m-1) + k = 1 + for x in ins + a[i+k-1] = x + k += 1 + end + end + return v +end + +""" + splice!(a::Vector, range, [replacement]) -> items + +Remove items in the specified index range, and return a collection containing +the removed items. +Subsequent items are shifted left to fill the resulting gap. +If specified, replacement values from an ordered collection will be spliced in +place of the removed items. + +To insert `replacement` before an index `n` without removing any items, use +`splice!(collection, n:n-1, replacement)`. + +# Examples +```jldoctest +julia> A = [-1, -2, -3, 5, 4, 3, -1]; splice!(A, 4:3, 2) +0-element Array{Int64,1} + +julia> A +8-element Array{Int64,1}: + -1 + -2 + -3 + 2 + 5 + 4 + 3 + -1 +``` +""" +function splice!(a::Vector, r::UnitRange{<:Integer}, ins=_default_splice) + v = a[r] + m = length(ins) + if m == 0 + deleteat!(a, r) + return v + end + + n = length(a) + f = first(r) + l = last(r) + d = length(r) + + if m < d + delta = d - m + _deleteat!(a, (f - 1 < n - l) ? f : (l - delta + 1), delta) + elseif m > d + _growat!(a, (f - 1 < n - l) ? f : (l + 1), m - d) + end + + k = 1 + for x in ins + a[f+k-1] = x + k += 1 + end + return v +end + +function empty!(a::Vector) + _deleteend!(a, length(a)) + return a +end + +_memcmp(a, b, len) = ccall(:memcmp, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), a, b, len % Csize_t) % Int + +# use memcmp for cmp on byte arrays +function cmp(a::Array{UInt8,1}, b::Array{UInt8,1}) + c = _memcmp(a, b, min(length(a),length(b))) + return c < 0 ? -1 : c > 0 ? +1 : cmp(length(a),length(b)) +end + +const BitIntegerArray{N} = Union{map(T->Array{T,N}, BitInteger_types)...} where N +# use memcmp for == on bit integer types +==(a::Arr, b::Arr) where {Arr <: BitIntegerArray} = + size(a) == size(b) && 0 == _memcmp(a, b, sizeof(eltype(Arr)) * length(a)) + +# this is ~20% faster than the generic implementation above for very small arrays +function ==(a::Arr, b::Arr) where Arr <: BitIntegerArray{1} + len = length(a) + len == length(b) && 0 == _memcmp(a, b, sizeof(eltype(Arr)) * len) +end + +""" + reverse(v [, start=1 [, stop=length(v) ]] ) + +Return a copy of `v` reversed from start to stop. See also [`Iterators.reverse`](@ref) +for reverse-order iteration without making a copy. + +# Examples +```jldoctest +julia> A = Vector(1:5) +5-element Array{Int64,1}: + 1 + 2 + 3 + 4 + 5 + +julia> reverse(A) +5-element Array{Int64,1}: + 5 + 4 + 3 + 2 + 1 + +julia> reverse(A, 1, 4) +5-element Array{Int64,1}: + 4 + 3 + 2 + 1 + 5 + +julia> reverse(A, 3, 5) +5-element Array{Int64,1}: + 1 + 2 + 5 + 4 + 3 +``` +""" +function reverse(A::AbstractVector, s=first(LinearIndices(A)), n=last(LinearIndices(A))) + B = similar(A) + for i = first(LinearIndices(A)):s-1 + B[i] = A[i] + end + for i = s:n + B[i] = A[n+s-i] + end + for i = n+1:last(LinearIndices(A)) + B[i] = A[i] + end + return B +end + +# to resolve ambiguity with reverse(A; dims) +reverse(A::Vector) = invoke(reverse, Tuple{AbstractVector}, A) + +function reverseind(a::AbstractVector, i::Integer) + li = LinearIndices(a) + first(li) + last(li) - i +end + +""" + reverse!(v [, start=1 [, stop=length(v) ]]) -> v + +In-place version of [`reverse`](@ref). + +# Examples +```jldoctest +julia> A = Vector(1:5) +5-element Array{Int64,1}: + 1 + 2 + 3 + 4 + 5 + +julia> reverse!(A); + +julia> A +5-element Array{Int64,1}: + 5 + 4 + 3 + 2 + 1 +``` +""" +function reverse!(v::AbstractVector, s=first(LinearIndices(v)), n=last(LinearIndices(v))) + liv = LinearIndices(v) + if n <= s # empty case; ok + elseif !(first(liv) ≤ s ≤ last(liv)) + throw(BoundsError(v, s)) + elseif !(first(liv) ≤ n ≤ last(liv)) + throw(BoundsError(v, n)) + end + r = n + @inbounds for i in s:div(s+n-1, 2) + v[i], v[r] = v[r], v[i] + r -= 1 + end + return v +end + +# concatenations of homogeneous combinations of vectors, horizontal and vertical + +vcat() = Vector{Any}() +hcat() = Vector{Any}() + +function hcat(V::Vector{T}...) where T + height = length(V[1]) + for j = 2:length(V) + if length(V[j]) != height + throw(DimensionMismatch("vectors must have same lengths")) + end + end + return [ V[j][i]::T for i=1:length(V[1]), j=1:length(V) ] +end + +function vcat(arrays::Vector{T}...) where T + n = 0 + for a in arrays + n += length(a) + end + arr = Vector{T}(undef, n) + ptr = pointer(arr) + if isbitsunion(T) + selptr = ccall(:jl_array_typetagdata, Ptr{UInt8}, (Any,), arr) + end + elsz = aligned_sizeof(T) + t = @_gc_preserve_begin arr + for a in arrays + na = length(a) + nba = na * elsz + if isbitstype(T) + ccall(:memcpy, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, UInt), + ptr, a, nba) + elseif isbitsunion(T) + ccall(:memcpy, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, UInt), + ptr, a, nba) + # copy selector bytes + ccall(:memcpy, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, UInt), + selptr, ccall(:jl_array_typetagdata, Ptr{UInt8}, (Any,), a), na) + selptr += na + else + ccall(:jl_array_ptr_copy, Cvoid, (Any, Ptr{Cvoid}, Any, Ptr{Cvoid}, Int), + arr, ptr, a, pointer(a), na) + end + ptr += nba + end + @_gc_preserve_end t + return arr +end + +_cat(n::Integer, x::Integer...) = reshape([x...], (ntuple(x->1, n-1)..., length(x))) + +## find ## + +""" + findnext(A, i) + +Find the next index after or including `i` of a `true` element of `A`, +or `nothing` if not found. + +Indices are of the same type as those returned by [`keys(A)`](@ref) +and [`pairs(A)`](@ref). + +# Examples +```jldoctest +julia> A = [false, false, true, false] +4-element Array{Bool,1}: + 0 + 0 + 1 + 0 + +julia> findnext(A, 1) +3 + +julia> findnext(A, 4) # returns nothing, but not printed in the REPL + +julia> A = [false false; true false] +2×2 Array{Bool,2}: + 0 0 + 1 0 + +julia> findnext(A, CartesianIndex(1, 1)) +CartesianIndex(2, 1) +``` +""" +function findnext(A, start) + l = last(keys(A)) + i = start + i > l && return nothing + while true + A[i] && return i + i == l && break + # nextind(A, l) can throw/overflow + i = nextind(A, i) + end + return nothing +end + +""" + findfirst(A) + +Return the index or key of the first `true` value in `A`. +Return `nothing` if no such value is found. +To search for other kinds of values, pass a predicate as the first argument. + +Indices or keys are of the same type as those returned by [`keys(A)`](@ref) +and [`pairs(A)`](@ref). + +# Examples +```jldoctest +julia> A = [false, false, true, false] +4-element Array{Bool,1}: + 0 + 0 + 1 + 0 + +julia> findfirst(A) +3 + +julia> findfirst(falses(3)) # returns nothing, but not printed in the REPL + +julia> A = [false false; true false] +2×2 Array{Bool,2}: + 0 0 + 1 0 + +julia> findfirst(A) +CartesianIndex(2, 1) +``` +""" +function findfirst(A) + for (i, a) in pairs(A) + if a + return i + end + end + return nothing +end + +# Needed for bootstrap, and allows defining only an optimized findnext method +findfirst(A::Union{AbstractArray, AbstractString}) = findnext(A, first(keys(A))) + +""" + findnext(predicate::Function, A, i) + +Find the next index after or including `i` of an element of `A` +for which `predicate` returns `true`, or `nothing` if not found. + +Indices are of the same type as those returned by [`keys(A)`](@ref) +and [`pairs(A)`](@ref). + +# Examples +```jldoctest +julia> A = [1, 4, 2, 2]; + +julia> findnext(isodd, A, 1) +1 + +julia> findnext(isodd, A, 2) # returns nothing, but not printed in the REPL + +julia> A = [1 4; 2 2]; + +julia> findnext(isodd, A, CartesianIndex(1, 1)) +CartesianIndex(1, 1) +``` +""" +function findnext(testf::Function, A, start) + l = last(keys(A)) + i = start + i > l && return nothing + while true + testf(A[i]) && return i + i == l && break + # nextind(A, l) can throw/overflow + i = nextind(A, i) + end + return nothing +end + +""" + findfirst(predicate::Function, A) + +Return the index or key of the first element of `A` for which `predicate` returns `true`. +Return `nothing` if there is no such element. + +Indices or keys are of the same type as those returned by [`keys(A)`](@ref) +and [`pairs(A)`](@ref). + +# Examples +```jldoctest +julia> A = [1, 4, 2, 2] +4-element Array{Int64,1}: + 1 + 4 + 2 + 2 + +julia> findfirst(iseven, A) +2 + +julia> findfirst(x -> x>10, A) # returns nothing, but not printed in the REPL + +julia> findfirst(isequal(4), A) +2 + +julia> A = [1 4; 2 2] +2×2 Array{Int64,2}: + 1 4 + 2 2 + +julia> findfirst(iseven, A) +CartesianIndex(2, 1) +``` +""" +function findfirst(testf::Function, A) + for (i, a) in pairs(A) + testf(a) && return i + end + return nothing +end + +# Needed for bootstrap, and allows defining only an optimized findnext method +findfirst(testf::Function, A::Union{AbstractArray, AbstractString}) = + findnext(testf, A, first(keys(A))) + +function findfirst(p::Union{Fix2{typeof(isequal),T},Fix2{typeof(==),T}}, r::StepRange{T,S}) where {T,S} + isempty(r) && return nothing + minimum(r) <= p.x <= maximum(r) || return nothing + d = convert(S, p.x - first(r)) + iszero(d % step(r)) || return nothing + return d ÷ step(r) + 1 +end + +""" + findprev(A, i) + +Find the previous index before or including `i` of a `true` element of `A`, +or `nothing` if not found. + +Indices are of the same type as those returned by [`keys(A)`](@ref) +and [`pairs(A)`](@ref). + +# Examples +```jldoctest +julia> A = [false, false, true, true] +4-element Array{Bool,1}: + 0 + 0 + 1 + 1 + +julia> findprev(A, 3) +3 + +julia> findprev(A, 1) # returns nothing, but not printed in the REPL + +julia> A = [false false; true true] +2×2 Array{Bool,2}: + 0 0 + 1 1 + +julia> findprev(A, CartesianIndex(2, 1)) +CartesianIndex(2, 1) +``` +""" +function findprev(A, start) + i = start + f = first(keys(A)) + i < f && return nothing + while true + A[i] && return i + i == f && break + # prevind(A, f) can throw/underflow + i = prevind(A, i) + end + return nothing +end + +""" + findlast(A) + +Return the index or key of the last `true` value in `A`. +Return `nothing` if there is no `true` value in `A`. + +Indices or keys are of the same type as those returned by [`keys(A)`](@ref) +and [`pairs(A)`](@ref). + +# Examples +```jldoctest +julia> A = [true, false, true, false] +4-element Array{Bool,1}: + 1 + 0 + 1 + 0 + +julia> findlast(A) +3 + +julia> A = falses(2,2); + +julia> findlast(A) # returns nothing, but not printed in the REPL + +julia> A = [true false; true false] +2×2 Array{Bool,2}: + 1 0 + 1 0 + +julia> findlast(A) +CartesianIndex(2, 1) +``` +""" +function findlast(A) + for (i, a) in Iterators.reverse(pairs(A)) + if a + return i + end + end + return nothing +end + +# Needed for bootstrap, and allows defining only an optimized findprev method +findlast(A::Union{AbstractArray, AbstractString}) = findprev(A, last(keys(A))) + +""" + findprev(predicate::Function, A, i) + +Find the previous index before or including `i` of an element of `A` +for which `predicate` returns `true`, or `nothing` if not found. + +Indices are of the same type as those returned by [`keys(A)`](@ref) +and [`pairs(A)`](@ref). + +# Examples +```jldoctest +julia> A = [4, 6, 1, 2] +4-element Array{Int64,1}: + 4 + 6 + 1 + 2 + +julia> findprev(isodd, A, 1) # returns nothing, but not printed in the REPL + +julia> findprev(isodd, A, 3) +3 + +julia> A = [4 6; 1 2] +2×2 Array{Int64,2}: + 4 6 + 1 2 + +julia> findprev(isodd, A, CartesianIndex(1, 2)) +CartesianIndex(2, 1) +``` +""" +function findprev(testf::Function, A, start) + i = start + f = first(keys(A)) + i < f && return nothing + while true + testf(A[i]) && return i + i == f && break + # prevind(A, f) can throw/underflow + i = prevind(A, i) + end + return nothing +end + +""" + findlast(predicate::Function, A) + +Return the index or key of the last element of `A` for which `predicate` returns `true`. +Return `nothing` if there is no such element. + +Indices or keys are of the same type as those returned by [`keys(A)`](@ref) +and [`pairs(A)`](@ref). + +# Examples +```jldoctest +julia> A = [1, 2, 3, 4] +4-element Array{Int64,1}: + 1 + 2 + 3 + 4 + +julia> findlast(isodd, A) +3 + +julia> findlast(x -> x > 5, A) # returns nothing, but not printed in the REPL + +julia> A = [1 2; 3 4] +2×2 Array{Int64,2}: + 1 2 + 3 4 + +julia> findlast(isodd, A) +CartesianIndex(2, 1) +``` +""" +function findlast(testf::Function, A) + for (i, a) in Iterators.reverse(pairs(A)) + testf(a) && return i + end + return nothing +end + +# Needed for bootstrap, and allows defining only an optimized findprev method +findlast(testf::Function, A::Union{AbstractArray, AbstractString}) = + findprev(testf, A, last(keys(A))) + +""" + findall(f::Function, A) + +Return a vector `I` of the indices or keys of `A` where `f(A[I])` returns `true`. +If there are no such elements of `A`, return an empty array. + +Indices or keys are of the same type as those returned by [`keys(A)`](@ref) +and [`pairs(A)`](@ref). + +# Examples +```jldoctest +julia> x = [1, 3, 4] +3-element Array{Int64,1}: + 1 + 3 + 4 + +julia> findall(isodd, x) +2-element Array{Int64,1}: + 1 + 2 + +julia> A = [1 2 0; 3 4 0] +2×3 Array{Int64,2}: + 1 2 0 + 3 4 0 +julia> findall(isodd, A) +2-element Array{CartesianIndex{2},1}: + CartesianIndex(1, 1) + CartesianIndex(2, 1) + +julia> findall(!iszero, A) +4-element Array{CartesianIndex{2},1}: + CartesianIndex(1, 1) + CartesianIndex(2, 1) + CartesianIndex(1, 2) + CartesianIndex(2, 2) + +julia> d = Dict(:A => 10, :B => -1, :C => 0) +Dict{Symbol,Int64} with 3 entries: + :A => 10 + :B => -1 + :C => 0 + +julia> findall(x -> x >= 0, d) +2-element Array{Symbol,1}: + :A + :C + +``` +""" +findall(testf::Function, A) = collect(first(p) for p in pairs(A) if testf(last(p))) + +""" + findall(A) + +Return a vector `I` of the `true` indices or keys of `A`. +If there are no such elements of `A`, return an empty array. +To search for other kinds of values, pass a predicate as the first argument. + +Indices or keys are of the same type as those returned by [`keys(A)`](@ref) +and [`pairs(A)`](@ref). + +# Examples +```jldoctest +julia> A = [true, false, false, true] +4-element Array{Bool,1}: + 1 + 0 + 0 + 1 + +julia> findall(A) +2-element Array{Int64,1}: + 1 + 4 + +julia> A = [true false; false true] +2×2 Array{Bool,2}: + 1 0 + 0 1 + +julia> findall(A) +2-element Array{CartesianIndex{2},1}: + CartesianIndex(1, 1) + CartesianIndex(2, 2) + +julia> findall(falses(3)) +0-element Array{Int64,1} +``` +""" +function findall(A) + collect(first(p) for p in pairs(A) if last(p)) +end +# Allocating result upfront is faster (possible only when collection can be iterated twice) +function findall(A::AbstractArray{Bool}) + n = count(A) + I = Vector{eltype(keys(A))}(undef, n) + cnt = 1 + for (i,a) in pairs(A) + if a + I[cnt] = i + cnt += 1 + end + end + I +end + +findall(x::Bool) = x ? [1] : Vector{Int}() +findall(testf::Function, x::Number) = testf(x) ? [1] : Vector{Int}() +findall(p::Fix2{typeof(in)}, x::Number) = x in p.x ? [1] : Vector{Int}() + +""" + findmax(itr) -> (x, index) + +Return the maximum element of the collection `itr` and its index. If there are multiple +maximal elements, then the first one will be returned. +If any data element is `NaN`, this element is returned. +The result is in line with `max`. + +The collection must not be empty. + +# Examples +```jldoctest +julia> findmax([8,0.1,-9,pi]) +(8.0, 1) + +julia> findmax([1,7,7,6]) +(7, 2) + +julia> findmax([1,7,7,NaN]) +(NaN, 4) +``` +""" +findmax(a) = _findmax(a, :) + +function _findmax(a, ::Colon) + p = pairs(a) + y = iterate(p) + if y === nothing + throw(ArgumentError("collection must be non-empty")) + end + (mi, m), s = y + i = mi + while true + y = iterate(p, s) + y === nothing && break + m != m && break + (i, ai), s = y + if ai != ai || isless(m, ai) + m = ai + mi = i + end + end + return (m, mi) +end + +""" + findmin(itr) -> (x, index) + +Return the minimum element of the collection `itr` and its index. If there are multiple +minimal elements, then the first one will be returned. +If any data element is `NaN`, this element is returned. +The result is in line with `min`. + +The collection must not be empty. + +# Examples +```jldoctest +julia> findmin([8,0.1,-9,pi]) +(-9.0, 3) + +julia> findmin([7,1,1,6]) +(1, 2) + +julia> findmin([7,1,1,NaN]) +(NaN, 4) +``` +""" +findmin(a) = _findmin(a, :) + +function _findmin(a, ::Colon) + p = pairs(a) + y = iterate(p) + if y === nothing + throw(ArgumentError("collection must be non-empty")) + end + (mi, m), s = y + i = mi + while true + y = iterate(p, s) + y === nothing && break + m != m && break + (i, ai), s = y + if ai != ai || isless(ai, m) + m = ai + mi = i + end + end + return (m, mi) +end + +""" + argmax(itr) -> Integer + +Return the index of the maximum element in a collection. If there are multiple maximal +elements, then the first one will be returned. + +The collection must not be empty. + +# Examples +```jldoctest +julia> argmax([8,0.1,-9,pi]) +1 + +julia> argmax([1,7,7,6]) +2 + +julia> argmax([1,7,7,NaN]) +4 +``` +""" +argmax(a) = findmax(a)[2] + +""" + argmin(itr) -> Integer + +Return the index of the minimum element in a collection. If there are multiple minimal +elements, then the first one will be returned. + +The collection must not be empty. + +# Examples +```jldoctest +julia> argmin([8,0.1,-9,pi]) +3 + +julia> argmin([7,1,1,6]) +2 + +julia> argmin([7,1,1,NaN]) +4 +``` +""" +argmin(a) = findmin(a)[2] + +# similar to Matlab's ismember +""" + indexin(a, b) + +Return an array containing the first index in `b` for +each value in `a` that is a member of `b`. The output +array contains `nothing` wherever `a` is not a member of `b`. + +# Examples +```jldoctest +julia> a = ['a', 'b', 'c', 'b', 'd', 'a']; + +julia> b = ['a', 'b', 'c']; + +julia> indexin(a, b) +6-element Array{Union{Nothing, Int64},1}: + 1 + 2 + 3 + 2 + nothing + 1 + +julia> indexin(b, a) +3-element Array{Union{Nothing, Int64},1}: + 1 + 2 + 3 +``` +""" +function indexin(a, b::AbstractArray) + inds = keys(b) + bdict = Dict{eltype(b),eltype(inds)}() + for (val, ind) in zip(b, inds) + get!(bdict, val, ind) + end + return Union{eltype(inds), Nothing}[ + get(bdict, i, nothing) for i in a + ] +end + +function _findin(a::Union{AbstractArray, Tuple}, b) + ind = Vector{eltype(keys(a))}() + bset = Set(b) + @inbounds for (i,ai) in pairs(a) + ai in bset && push!(ind, i) + end + ind +end + +# If two collections are already sorted, _findin can be computed with +# a single traversal of the two collections. This is much faster than +# using a hash table (although it has the same complexity). +function _sortedfindin(v::Union{AbstractArray, Tuple}, w) + viter, witer = keys(v), eachindex(w) + out = eltype(viter)[] + vy, wy = iterate(viter), iterate(witer) + if vy === nothing || wy === nothing + return out + end + viteri, i = vy + witerj, j = wy + @inbounds begin + vi, wj = v[viteri], w[witerj] + while true + if isless(vi, wj) + vy = iterate(viter, i) + if vy === nothing + break + end + viteri, i = vy + vi = v[viteri] + elseif isless(wj, vi) + wy = iterate(witer, j) + if wy === nothing + break + end + witerj, j = wy + wj = w[witerj] + else + push!(out, viteri) + vy = iterate(viter, i) + if vy === nothing + break + end + # We only increment the v iterator because v can have + # repeated matches to a single value in w + viteri, i = vy + vi = v[viteri] + end + end + end + return out +end + +function findall(pred::Fix2{typeof(in),<:Union{Array{<:Real},Real}}, x::Array{<:Real}) + if issorted(x, Sort.Forward) && issorted(pred.x, Sort.Forward) + return _sortedfindin(x, pred.x) + else + return _findin(x, pred.x) + end +end +# issorted fails for some element types so the method above has to be restricted +# to element with isless/< defined. +findall(pred::Fix2{typeof(in)}, x::Union{AbstractArray, Tuple}) = _findin(x, pred.x) + +# Copying subregions +function indcopy(sz::Dims, I::Vector) + n = length(I) + s = sz[n] + for i = n+1:length(sz) + s *= sz[i] + end + dst = eltype(I)[_findin(I[i], i < n ? (1:sz[i]) : (1:s)) for i = 1:n] + src = eltype(I)[I[i][_findin(I[i], i < n ? (1:sz[i]) : (1:s))] for i = 1:n] + dst, src +end + +function indcopy(sz::Dims, I::Tuple{Vararg{RangeIndex}}) + n = length(I) + s = sz[n] + for i = n+1:length(sz) + s *= sz[i] + end + dst::typeof(I) = ntuple(i-> _findin(I[i], i < n ? (1:sz[i]) : (1:s)), n)::typeof(I) + src::typeof(I) = ntuple(i-> I[i][_findin(I[i], i < n ? (1:sz[i]) : (1:s))], n)::typeof(I) + dst, src +end + +## Filter ## + +""" + filter(f, a::AbstractArray) + +Return a copy of `a`, removing elements for which `f` is `false`. +The function `f` is passed one argument. + +# Examples +```jldoctest +julia> a = 1:10 +1:10 + +julia> filter(isodd, a) +5-element Array{Int64,1}: + 1 + 3 + 5 + 7 + 9 +``` +""" +function filter(f, a::Array{T, N}) where {T, N} + j = 1 + b = Vector{T}(undef, length(a)) + for ai in a + @inbounds b[j] = ai + j = ifelse(f(ai), j+1, j) + end + resize!(b, j-1) + sizehint!(b, length(b)) + b +end + +function filter(f, a::AbstractArray) + (IndexStyle(a) != IndexLinear()) && return a[map(f, a)::AbstractArray{Bool}] + + j = 1 + idxs = Vector{Int}(undef, length(a)) + for idx in eachindex(a) + @inbounds idxs[j] = idx + ai = @inbounds a[idx] + j = ifelse(f(ai), j+1, j) + end + resize!(idxs, j-1) + res = a[idxs] + empty!(idxs) + sizehint!(idxs, 0) + return res +end + +""" + filter!(f, a::AbstractVector) + +Update `a`, removing elements for which `f` is `false`. +The function `f` is passed one argument. + +# Examples +```jldoctest +julia> filter!(isodd, Vector(1:10)) +5-element Array{Int64,1}: + 1 + 3 + 5 + 7 + 9 +``` +""" +function filter!(f, a::AbstractVector) + j = firstindex(a) + for ai in a + @inbounds a[j] = ai + j = ifelse(f(ai), nextind(a, j), j) + end + j > lastindex(a) && return a + if a isa Vector + resize!(a, j-1) + sizehint!(a, j-1) + else + deleteat!(a, j:lastindex(a)) + end + return a +end + +# set-like operators for vectors +# These are moderately efficient, preserve order, and remove dupes. + +_unique_filter!(pred, update!, state) = function (x) + if pred(x, state) + update!(state, x) + true + else + false + end +end + +_grow_filter!(seen) = _unique_filter!(∉, push!, seen) +_shrink_filter!(keep) = _unique_filter!(∈, pop!, keep) + +function _grow!(pred!, v::AbstractVector, itrs) + filter!(pred!, v) # uniquify v + for itr in itrs + mapfilter(pred!, push!, itr, v) + end + return v +end + +union!(v::AbstractVector{T}, itrs...) where {T} = + _grow!(_grow_filter!(sizehint!(Set{T}(), length(v))), v, itrs) + +symdiff!(v::AbstractVector{T}, itrs...) where {T} = + _grow!(_shrink_filter!(symdiff!(Set{T}(), v, itrs...)), v, itrs) + +function _shrink!(shrinker!, v::AbstractVector, itrs) + seen = Set{eltype(v)}() + filter!(_grow_filter!(seen), v) + shrinker!(seen, itrs...) + filter!(_in(seen), v) +end + +intersect!(v::AbstractVector, itrs...) = _shrink!(intersect!, v, itrs) +setdiff!( v::AbstractVector, itrs...) = _shrink!(setdiff!, v, itrs) + +vectorfilter(f, v::AbstractVector) = filter(f, v) # TODO: do we want this special case? +vectorfilter(f, v) = [x for x in v if f(x)] + +function _shrink(shrinker!, itr, itrs) + keep = shrinker!(Set(itr), itrs...) + vectorfilter(_shrink_filter!(keep), itr) +end + +intersect(itr, itrs...) = _shrink(intersect!, itr, itrs) +setdiff( itr, itrs...) = _shrink(setdiff!, itr, itrs) diff --git a/base/arraymath.jl b/base/arraymath.jl new file mode 100644 index 0000000..f9259a2 --- /dev/null +++ b/base/arraymath.jl @@ -0,0 +1,303 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## Unary operators ## + +""" + conj!(A) + +Transform an array to its complex conjugate in-place. + +See also [`conj`](@ref). + +# Examples +```jldoctest +julia> A = [1+im 2-im; 2+2im 3+im] +2×2 Array{Complex{Int64},2}: + 1+1im 2-1im + 2+2im 3+1im + +julia> conj!(A); + +julia> A +2×2 Array{Complex{Int64},2}: + 1-1im 2+1im + 2-2im 3-1im +``` +""" +conj!(A::AbstractArray{<:Number}) = (@inbounds broadcast!(conj, A, A); A) + +for f in (:-, :conj, :real, :imag) + @eval ($f)(A::AbstractArray) = broadcast_preserving_zero_d($f, A) +end + + +## Binary arithmetic operators ## + +for f in (:+, :-) + @eval function ($f)(A::AbstractArray, B::AbstractArray) + promote_shape(A, B) # check size compatibility + broadcast_preserving_zero_d($f, A, B) + end +end + +function +(A::Array, Bs::Array...) + for B in Bs + promote_shape(A, B) # check size compatibility + end + broadcast_preserving_zero_d(+, A, Bs...) +end + +for f in (:/, :\, :*) + if f !== :/ + @eval ($f)(A::Number, B::AbstractArray) = broadcast_preserving_zero_d($f, A, B) + end + if f !== :\ + @eval ($f)(A::AbstractArray, B::Number) = broadcast_preserving_zero_d($f, A, B) + end +end + +## data movement ## + +function reverse(A::Array{T}; dims::Integer) where T + nd = ndims(A); d = dims + 1 ≤ d ≤ nd || throw(ArgumentError("dimension $d is not 1 ≤ $d ≤ $nd")) + sd = size(A, d) + if sd == 1 || isempty(A) + return copy(A) + end + + B = similar(A) + + nnd = 0 + for i = 1:nd + nnd += Int(size(A,i)==1 || i==d) + end + if nnd==nd + # reverse along the only non-singleton dimension + for i = 1:sd + B[i] = A[sd+1-i] + end + return B + end + + d_in = size(A) + leading = d_in[1:(d-1)] + M = prod(leading) + N = length(A) + stride = M * sd + + if M==1 + for j = 0:stride:(N-stride) + for i = 1:sd + ri = sd+1-i + B[j + ri] = A[j + i] + end + end + else + if isbitstype(T) && M>200 + for i = 1:sd + ri = sd+1-i + for j=0:stride:(N-stride) + offs = j + 1 + (i-1)*M + boffs = j + 1 + (ri-1)*M + copyto!(B, boffs, A, offs, M) + end + end + else + for i = 1:sd + ri = sd+1-i + for j=0:stride:(N-stride) + offs = j + 1 + (i-1)*M + boffs = j + 1 + (ri-1)*M + for k=0:(M-1) + B[boffs + k] = A[offs + k] + end + end + end + end + end + return B +end + +""" + rotl90(A) + +Rotate matrix `A` left 90 degrees. + +# Examples +```jldoctest +julia> a = [1 2; 3 4] +2×2 Array{Int64,2}: + 1 2 + 3 4 + +julia> rotl90(a) +2×2 Array{Int64,2}: + 2 4 + 1 3 +``` +""" +function rotl90(A::AbstractMatrix) + ind1, ind2 = axes(A) + B = similar(A, (ind2,ind1)) + n = first(ind2)+last(ind2) + for i=axes(A,1), j=ind2 + B[n-j,i] = A[i,j] + end + return B +end + +""" + rotr90(A) + +Rotate matrix `A` right 90 degrees. + +# Examples +```jldoctest +julia> a = [1 2; 3 4] +2×2 Array{Int64,2}: + 1 2 + 3 4 + +julia> rotr90(a) +2×2 Array{Int64,2}: + 3 1 + 4 2 +``` +""" +function rotr90(A::AbstractMatrix) + ind1, ind2 = axes(A) + B = similar(A, (ind2,ind1)) + m = first(ind1)+last(ind1) + for i=ind1, j=axes(A,2) + B[j,m-i] = A[i,j] + end + return B +end +""" + rot180(A) + +Rotate matrix `A` 180 degrees. + +# Examples +```jldoctest +julia> a = [1 2; 3 4] +2×2 Array{Int64,2}: + 1 2 + 3 4 + +julia> rot180(a) +2×2 Array{Int64,2}: + 4 3 + 2 1 +``` +""" +function rot180(A::AbstractMatrix) + B = similar(A) + ind1, ind2 = axes(A,1), axes(A,2) + m, n = first(ind1)+last(ind1), first(ind2)+last(ind2) + for j=ind2, i=ind1 + B[m-i,n-j] = A[i,j] + end + return B +end +""" + rotl90(A, k) + +Left-rotate matrix `A` 90 degrees counterclockwise an integer `k` number of times. +If `k` is a multiple of four (including zero), this is equivalent to a `copy`. + +# Examples +```jldoctest +julia> a = [1 2; 3 4] +2×2 Array{Int64,2}: + 1 2 + 3 4 + +julia> rotl90(a,1) +2×2 Array{Int64,2}: + 2 4 + 1 3 + +julia> rotl90(a,2) +2×2 Array{Int64,2}: + 4 3 + 2 1 + +julia> rotl90(a,3) +2×2 Array{Int64,2}: + 3 1 + 4 2 + +julia> rotl90(a,4) +2×2 Array{Int64,2}: + 1 2 + 3 4 +``` +""" +function rotl90(A::AbstractMatrix, k::Integer) + k = mod(k, 4) + k == 1 ? rotl90(A) : + k == 2 ? rot180(A) : + k == 3 ? rotr90(A) : copy(A) +end +""" + rotr90(A, k) + +Right-rotate matrix `A` 90 degrees clockwise an integer `k` number of times. +If `k` is a multiple of four (including zero), this is equivalent to a `copy`. + +# Examples +```jldoctest +julia> a = [1 2; 3 4] +2×2 Array{Int64,2}: + 1 2 + 3 4 + +julia> rotr90(a,1) +2×2 Array{Int64,2}: + 3 1 + 4 2 + +julia> rotr90(a,2) +2×2 Array{Int64,2}: + 4 3 + 2 1 + +julia> rotr90(a,3) +2×2 Array{Int64,2}: + 2 4 + 1 3 + +julia> rotr90(a,4) +2×2 Array{Int64,2}: + 1 2 + 3 4 +``` +""" +rotr90(A::AbstractMatrix, k::Integer) = rotl90(A,-k) +""" + rot180(A, k) + +Rotate matrix `A` 180 degrees an integer `k` number of times. +If `k` is even, this is equivalent to a `copy`. + +# Examples +```jldoctest +julia> a = [1 2; 3 4] +2×2 Array{Int64,2}: + 1 2 + 3 4 + +julia> rot180(a,1) +2×2 Array{Int64,2}: + 4 3 + 2 1 + +julia> rot180(a,2) +2×2 Array{Int64,2}: + 1 2 + 3 4 +``` +""" +rot180(A::AbstractMatrix, k::Integer) = mod(k, 2) == 1 ? rot180(A) : copy(A) diff --git a/base/arrayshow.jl b/base/arrayshow.jl new file mode 100644 index 0000000..5141122 --- /dev/null +++ b/base/arrayshow.jl @@ -0,0 +1,488 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# methods related to array printing + +# Printing a value requires to take into account the :typeinfo property +# from the IO context; this property encodes (as a type) the type information +# that is supposed to have already been displayed concerning this value, +# so that redundancy can be avoided. For example, when printing an array of +# `Float16` values, the header "Float16" will be printed, and the values +# can simply be printed with the decimal representations: +# show(Float16(1)) -> "Float16(1.0)" +# show([Float16(1)]) -> "Float16[1.0]" (instead of "Float16[Float16(1.0)]") +# Similarly: +# show([[Float16(1)]]) -> "Array{Float16}[[1.0]]" (instead of "Array{Float16}[Float16[1.0]]") +# +# The array printing methods here can be grouped into two categories (and are annotated as such): +# 1) "typeinfo aware" : these are "API boundaries" functions, which will read the typeinfo +# property from the context, and pass down to their value an updated property +# according to its eltype; at each layer of nesting, only one "typeinfo aware" +# function must be called; +# 2) "typeinfo agnostic": these are helper functions used by the first category; hence +# they don't manipulate the typeinfo property, and let the printing routines +# for their elements read directly the property set by their callers +# +# Non-annotated functions are even lower level (e.g. print_matrix_row), so they fall +# by default into category 2. +# +# The basic organization of this file is +# 1) printing with `display` +# 2) printing with `show` +# 3) Logic for displaying type information + + +## printing with `display` + +""" +Unexported convenience function used in body of `replace_in_print_matrix` +methods. By default returns a string of the same width as original with a +centered cdot, used in printing of structural zeros of structured matrices. +Accept keyword args `c` for alternate single character marker. +""" +function replace_with_centered_mark(s::AbstractString;c::AbstractChar = '⋅') + N = length(s) + return join(setindex!([" " for i=1:N],string(c),ceil(Int,N/2))) +end + +const undef_ref_alignment = (3,3) + +""" +`alignment(X, rows, cols, cols_if_complete, cols_otherwise, sep)` returns the +alignment for specified parts of array `X`, returning the (left,right) info. +It will look in X's `rows`, `cols` (both lists of indices) +and figure out what's needed to be fully aligned, for example looking all +the way down a column and finding out the maximum size of each element. +Parameter `sep::Integer` is number of spaces to put between elements. +`cols_if_complete` and `cols_otherwise` indicate screen width to use. +Alignment is reported as a vector of (left,right) tuples, one for each +column going across the screen. +""" +function alignment(io::IO, X::AbstractVecOrMat, + rows::AbstractVector, cols::AbstractVector, + cols_if_complete::Integer, cols_otherwise::Integer, sep::Integer) + a = Tuple{Int, Int}[] + for j in cols # need to go down each column one at a time + l = r = 0 + for i in rows # plumb down and see what largest element sizes are + if isassigned(X,i,j) + aij = alignment(io, X[i,j]) + else + aij = undef_ref_alignment + end + l = max(l, aij[1]) # left characters + r = max(r, aij[2]) # right characters + end + push!(a, (l, r)) # one tuple per column of X, pruned to screen width + if length(a) > 1 && sum(map(sum,a)) + sep*length(a) >= cols_if_complete + pop!(a) # remove this latest tuple if we're already beyond screen width + break + end + end + if 1 < length(a) < length(axes(X,2)) + while sum(map(sum,a)) + sep*length(a) >= cols_otherwise + pop!(a) + end + end + return a +end + +""" +`print_matrix_row(io, X, A, i, cols, sep)` produces the aligned output for +a single matrix row X[i, cols] where the desired list of columns is given. +The corresponding alignment A is used, and the separation between elements +is specified as string sep. +`print_matrix_row` will also respect compact output for elements. +""" +function print_matrix_row(io::IO, + X::AbstractVecOrMat, A::Vector, + i::Integer, cols::AbstractVector, sep::AbstractString) + for (k, j) = enumerate(cols) + k > length(A) && break + if isassigned(X,Int(i),Int(j)) # isassigned accepts only `Int` indices + x = X[i,j] + a = alignment(io, x) + sx = sprint(show, x, context=io, sizehint=0) + else + a = undef_ref_alignment + sx = undef_ref_str + end + l = repeat(" ", A[k][1]-a[1]) # pad on left and right as needed + r = repeat(" ", A[k][2]-a[2]) + prettysx = replace_in_print_matrix(X,i,j,sx) + print(io, l, prettysx, r) + if k < length(A); print(io, sep); end + end +end + + +""" +`print_matrix_vdots` is used to show a series of vertical ellipsis instead +of a bunch of rows for long matrices. Not only is the string vdots shown +but it also repeated every M elements if desired. +""" +function print_matrix_vdots(io::IO, vdots::AbstractString, + A::Vector, sep::AbstractString, M::Integer, m::Integer) + for k = 1:length(A) + w = A[k][1] + A[k][2] + if k % M == m + l = repeat(" ", max(0, A[k][1]-length(vdots))) + r = repeat(" ", max(0, w-length(vdots)-length(l))) + print(io, l, vdots, r) + else + print(io, repeat(" ", w)) + end + if k < length(A); print(io, sep); end + end +end + +# typeinfo agnostic +""" + print_matrix(io::IO, mat, pre, sep, post, hdots, vdots, ddots, hmod, vmod) + +Prints a matrix with limited output size. If `io` sets `:limit` to true, +then only the corners of the matrix are printed, separated with vertical, +horizontal, and diagonal ellipses as appropriate. +Optional arguments are string pre (printed before the matrix, e.g. an opening bracket) +which will cause a corresponding same-size indent on following rows, and +string post (printed at the end of the last row of the matrix). +Also options to use different ellipsis characters hdots, vdots, ddots. +These are repeated every hmod or vmod elements. +""" +function print_matrix(io::IO, X::AbstractVecOrMat, + pre::AbstractString = " ", # pre-matrix string + sep::AbstractString = " ", # separator between elements + post::AbstractString = "", # post-matrix string + hdots::AbstractString = " \u2026 ", + vdots::AbstractString = "\u22ee", + ddots::AbstractString = " \u22f1 ", + hmod::Integer = 5, vmod::Integer = 5) + if !get(io, :limit, false) + screenheight = screenwidth = typemax(Int) + else + sz = displaysize(io) + screenheight, screenwidth = sz[1] - 4, sz[2] + end + screenwidth -= length(pre) + length(post) + presp = repeat(" ", length(pre)) # indent each row to match pre string + postsp = "" + @assert textwidth(hdots) == textwidth(ddots) + sepsize = length(sep) + rowsA, colsA = UnitRange(axes(X,1)), UnitRange(axes(X,2)) + m, n = length(rowsA), length(colsA) + # To figure out alignments, only need to look at as many rows as could + # fit down screen. If screen has at least as many rows as A, look at A. + # If not, then we only need to look at the first and last chunks of A, + # each half a screen height in size. + halfheight = div(screenheight,2) + if m > screenheight + rowsA = [rowsA[(0:halfheight-1) .+ firstindex(rowsA)]; rowsA[(end-div(screenheight-1,2)+1):end]] + end + # Similarly for columns, only necessary to get alignments for as many + # columns as could conceivably fit across the screen + maxpossiblecols = div(screenwidth, 1+sepsize) + if n > maxpossiblecols + colsA = [colsA[(0:maxpossiblecols-1) .+ firstindex(colsA)]; colsA[(end-maxpossiblecols+1):end]] + end + A = alignment(io, X, rowsA, colsA, screenwidth, screenwidth, sepsize) + # Nine-slicing is accomplished using print_matrix_row repeatedly + if m <= screenheight # rows fit vertically on screen + if n <= length(A) # rows and cols fit so just print whole matrix in one piece + for i in rowsA + print(io, i == first(rowsA) ? pre : presp) + print_matrix_row(io, X,A,i,colsA,sep) + print(io, i == last(rowsA) ? post : postsp) + if i != last(rowsA); println(io); end + end + else # rows fit down screen but cols don't, so need horizontal ellipsis + c = div(screenwidth-length(hdots)+1,2)+1 # what goes to right of ellipsis + Ralign = reverse(alignment(io, X, rowsA, reverse(colsA), c, c, sepsize)) # alignments for right + c = screenwidth - sum(map(sum,Ralign)) - (length(Ralign)-1)*sepsize - length(hdots) + Lalign = alignment(io, X, rowsA, colsA, c, c, sepsize) # alignments for left of ellipsis + for i in rowsA + print(io, i == first(rowsA) ? pre : presp) + print_matrix_row(io, X,Lalign,i,colsA[1:length(Lalign)],sep) + print(io, (i - first(rowsA)) % hmod == 0 ? hdots : repeat(" ", length(hdots))) + print_matrix_row(io, X, Ralign, i, (n - length(Ralign)) .+ colsA, sep) + print(io, i == last(rowsA) ? post : postsp) + if i != last(rowsA); println(io); end + end + end + else # rows don't fit so will need vertical ellipsis + if n <= length(A) # rows don't fit, cols do, so only vertical ellipsis + for i in rowsA + print(io, i == first(rowsA) ? pre : presp) + print_matrix_row(io, X,A,i,colsA,sep) + print(io, i == last(rowsA) ? post : postsp) + if i != rowsA[end] || i == rowsA[halfheight]; println(io); end + if i == rowsA[halfheight] + print(io, i == first(rowsA) ? pre : presp) + print_matrix_vdots(io, vdots,A,sep,vmod,1) + print(io, i == last(rowsA) ? post : postsp * '\n') + end + end + else # neither rows nor cols fit, so use all 3 kinds of dots + c = div(screenwidth-length(hdots)+1,2)+1 + Ralign = reverse(alignment(io, X, rowsA, reverse(colsA), c, c, sepsize)) + c = screenwidth - sum(map(sum,Ralign)) - (length(Ralign)-1)*sepsize - length(hdots) + Lalign = alignment(io, X, rowsA, colsA, c, c, sepsize) + r = mod((length(Ralign)-n+1),vmod) # where to put dots on right half + for i in rowsA + print(io, i == first(rowsA) ? pre : presp) + print_matrix_row(io, X,Lalign,i,colsA[1:length(Lalign)],sep) + print(io, (i - first(rowsA)) % hmod == 0 ? hdots : repeat(" ", length(hdots))) + print_matrix_row(io, X,Ralign,i,(n-length(Ralign)).+colsA,sep) + print(io, i == last(rowsA) ? post : postsp) + if i != rowsA[end] || i == rowsA[halfheight]; println(io); end + if i == rowsA[halfheight] + print(io, i == first(rowsA) ? pre : presp) + print_matrix_vdots(io, vdots,Lalign,sep,vmod,1) + print(io, ddots) + print_matrix_vdots(io, vdots,Ralign,sep,vmod,r) + print(io, i == last(rowsA) ? post : postsp * '\n') + end + end + end + if isempty(rowsA) + print(io, pre) + print(io, vdots) + length(colsA) > 1 && print(io, " ", ddots) + print(io, post) + end + end +end + +# typeinfo agnostic +# n-dimensional arrays +function show_nd(io::IO, a::AbstractArray, print_matrix::Function, label_slices::Bool) + limit::Bool = get(io, :limit, false) + if isempty(a) + return + end + tailinds = tail(tail(axes(a))) + nd = ndims(a)-2 + for I in CartesianIndices(tailinds) + idxs = I.I + if limit + for i = 1:nd + ii = idxs[i] + ind = tailinds[i] + if length(ind) > 10 + if ii == ind[firstindex(ind)+3] && all(d->idxs[d]==first(tailinds[d]),1:i-1) + for j=i+1:nd + szj = length(axes(a, j+2)) + indj = tailinds[j] + if szj>10 && first(indj)+2 < idxs[j] <= last(indj)-3 + @goto skip + end + end + #println(io, idxs) + print(io, "...\n\n") + @goto skip + end + if ind[firstindex(ind)+2] < ii <= ind[end-3] + @goto skip + end + end + end + end + if label_slices + print(io, "[:, :, ") + for i = 1:(nd-1); print(io, "$(idxs[i]), "); end + println(io, idxs[end], "] =") + end + slice = view(a, axes(a,1), axes(a,2), idxs...) + print_matrix(io, slice) + print(io, idxs == map(last,tailinds) ? "" : "\n\n") + @label skip + end +end + +# print_array: main helper functions for show(io, text/plain, array) +# typeinfo agnostic + +# 0-dimensional arrays +print_array(io::IO, X::AbstractArray{T,0} where T) = + isassigned(X) ? show(io, X[]) : + print(io, undef_ref_str) + +print_array(io::IO, X::AbstractVecOrMat) = print_matrix(io, X) + +print_array(io::IO, X::AbstractArray) = show_nd(io, X, print_matrix, true) + +# typeinfo aware +# implements: show(io::IO, ::MIME"text/plain", X::AbstractArray) +function show(io::IO, ::MIME"text/plain", X::AbstractArray) + # 0) show summary before setting :compact + summary(io, X) + isempty(X) && return + print(io, ":") + + # 1) compute new IOContext + if !haskey(io, :compact) && length(axes(X, 2)) > 1 + io = IOContext(io, :compact => true) + end + if get(io, :limit, false) && eltype(X) === Method + # override usual show method for Vector{Method}: don't abbreviate long lists + io = IOContext(io, :limit => false) + end + + if get(io, :limit, false) && displaysize(io)[1]-4 <= 0 + return print(io, " …") + else + println(io) + end + + # 2) update typeinfo + # + # it must come after printing the summary, which can exploit :typeinfo itself + # (e.g. views) + # we assume this function is always called from top-level, i.e. that it's not nested + # within another "show" method; hence we always print the summary, without + # checking for current :typeinfo (this could be changed in the future) + io = IOContext(io, :typeinfo => eltype(X)) + + # 2) show actual content + print_array(io, X) +end + +## printing with `show` + +### non-Vector arrays + +# _show_nonempty & _show_empty: main helper functions for show(io, X) +# typeinfo agnostic + +""" +`_show_nonempty(io, X::AbstractMatrix, prefix)` prints matrix X with opening and closing square brackets, +preceded by `prefix`, supposed to encode the type of the elements. +""" +function _show_nonempty(io::IO, X::AbstractMatrix, prefix::String) + @assert !isempty(X) + limit = get(io, :limit, false)::Bool + indr, indc = axes(X,1), axes(X,2) + nr, nc = length(indr), length(indc) + rdots, cdots = false, false + rr1, rr2 = UnitRange{Int}(indr), 1:0 + cr1, cr2 = UnitRange{Int}(indc), 1:0 + if limit + if nr > 4 + rr1, rr2 = rr1[1:2], rr1[nr-1:nr] + rdots = true + end + if nc > 4 + cr1, cr2 = cr1[1:2], cr1[nc-1:nc] + cdots = true + end + end + print(io, prefix, "[") + for rr in (rr1, rr2) + for i in rr + for cr in (cr1, cr2) + for j in cr + j > first(cr) && print(io, " ") + if !isassigned(X,i,j) + print(io, undef_ref_str) + else + el = X[i,j] + show(io, el) + end + end + if last(cr) == last(indc) + i < last(indr) && print(io, "; ") + elseif cdots + print(io, " \u2026 ") + end + end + end + last(rr) != nr && rdots && print(io, "\u2026 ; ") + end + print(io, "]") +end + + +_show_nonempty(io::IO, X::AbstractArray, prefix::String) = + show_nd(io, X, (io, slice) -> _show_nonempty(io, slice, prefix), false) + +# a specific call path is used to show vectors (show_vector) +_show_nonempty(::IO, ::AbstractVector, ::String) = + error("_show_nonempty(::IO, ::AbstractVector, ::String) is not implemented") + +_show_nonempty(io::IO, X::AbstractArray{T,0} where T, prefix::String) = print_array(io, X) + +# NOTE: it's not clear how this method could use the :typeinfo attribute +_show_empty(io::IO, X::Array{T}) where {T} = print(io, "Array{", T, "}(undef,", join(size(X),','), ')') +_show_empty(io, X) = nothing # by default, we don't know this constructor + +# typeinfo aware (necessarily) +function show(io::IO, X::AbstractArray) + ndims(X) == 1 && return show_vector(io, X) + prefix = typeinfo_prefix(io, X) + io = IOContext(io, :typeinfo => eltype(X)) + isempty(X) ? + _show_empty(io, X) : + _show_nonempty(io, X, prefix) +end + +### Vector arrays + +# typeinfo aware +# NOTE: v is not constrained to be a vector, as this function can work with iterables +# in general (it's used e.g. by show(::IO, ::Set)) +function show_vector(io::IO, v, opn='[', cls=']') + print(io, typeinfo_prefix(io, v)) + # directly or indirectly, the context now knows about eltype(v) + io = IOContext(io, :typeinfo => eltype(v)) + limited = get(io, :limit, false) + if limited && length(v) > 20 + axs1 = axes1(v) + f, l = first(axs1), last(axs1) + show_delim_array(io, v, opn, ",", "", false, f, f+9) + print(io, " … ") + show_delim_array(io, v, "", ",", cls, false, l-9, l) + else + show_delim_array(io, v, opn, ",", cls, false) + end +end + + +## Logic for displaying type information + +# given type `typeinfo` extracted from context, assuming a collection +# is being displayed, deduce the elements type; in spirit this is +# similar to `eltype` (except that we don't want a default fall-back +# returning Any, as this would cause incorrect printing in e.g. `Vector[Any[1]]`, +# because eltype(Vector) == Any so `Any` wouldn't be printed in `Any[1]`) +typeinfo_eltype(typeinfo) = nothing # element type not precisely known +typeinfo_eltype(typeinfo::Type{<:AbstractArray{T}}) where {T} = eltype(typeinfo) +typeinfo_eltype(typeinfo::Type{<:AbstractDict{K,V}}) where {K,V} = eltype(typeinfo) +typeinfo_eltype(typeinfo::Type{<:AbstractSet{T}}) where {T} = eltype(typeinfo) + + +# X not constrained, can be any iterable (cf. show_vector) +function typeinfo_prefix(io::IO, X) + typeinfo = get(io, :typeinfo, Any)::Type + if !(X isa typeinfo) + typeinfo = Any + end + + # what the context already knows about the eltype of X: + eltype_ctx = typeinfo_eltype(typeinfo) + eltype_X = eltype(X) + + if X isa AbstractDict + if eltype_X == eltype_ctx || !isempty(X) && isconcretetype(keytype(X)) && isconcretetype(valtype(X)) + string(typeof(X).name) + else + string(typeof(X)) + end + else + # Types hard-coded here are those which are created by default for a given syntax + if eltype_X == eltype_ctx || !isempty(X) && eltype_X in (Float64, Int, Char, String) + "" + elseif print_without_params(eltype_X) + string(unwrap_unionall(eltype_X).name) # Print "Array" rather than "Array{T,N}" + else + string(eltype_X) + end + end +end diff --git a/base/asyncevent.jl b/base/asyncevent.jl new file mode 100644 index 0000000..625eff3 --- /dev/null +++ b/base/asyncevent.jl @@ -0,0 +1,292 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## async event notifications + +""" + AsyncCondition() + +Create a async condition that wakes up tasks waiting for it +(by calling [`wait`](@ref) on the object) +when notified from C by a call to `uv_async_send`. +Waiting tasks are woken with an error when the object is closed (by [`close`](@ref). +Use [`isopen`](@ref) to check whether it is still active. +""" +mutable struct AsyncCondition + handle::Ptr{Cvoid} + cond::ThreadSynchronizer + isopen::Bool + set::Bool + + function AsyncCondition() + this = new(Libc.malloc(_sizeof_uv_async), ThreadSynchronizer(), true, false) + iolock_begin() + associate_julia_struct(this.handle, this) + err = ccall(:uv_async_init, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}), + eventloop(), this, uv_jl_asynccb::Ptr{Cvoid}) + if err != 0 + #TODO: this codepath is currently not tested + Libc.free(this.handle) + this.handle = C_NULL + throw(_UVError("uv_async_init", err)) + end + finalizer(uvfinalize, this) + iolock_end() + return this + end +end + +""" + AsyncCondition(callback::Function) + +Create a async condition that calls the given `callback` function. The `callback` is passed one argument, +the async condition object itself. +""" +function AsyncCondition(cb::Function) + async = AsyncCondition() + @async while _trywait(async) + cb(async) + isopen(async) || return + end + return async +end + +## timer-based notifications + +""" + Timer(delay; interval = 0) + +Create a timer that wakes up tasks waiting for it (by calling [`wait`](@ref) on the timer object). + +Waiting tasks are woken after an initial delay of `delay` seconds, and then repeating with the given +`interval` in seconds. If `interval` is equal to `0`, the timer is only triggered once. When +the timer is closed (by [`close`](@ref) waiting tasks are woken with an error. Use [`isopen`](@ref) +to check whether a timer is still active. +""" +mutable struct Timer + handle::Ptr{Cvoid} + cond::ThreadSynchronizer + isopen::Bool + set::Bool + + function Timer(timeout::Real; interval::Real = 0.0) + timeout ≥ 0 || throw(ArgumentError("timer cannot have negative timeout of $timeout seconds")) + interval ≥ 0 || throw(ArgumentError("timer cannot have negative repeat interval of $interval seconds")) + timeout = UInt64(round(timeout * 1000)) + 1 + interval = UInt64(round(interval * 1000)) + loop = eventloop() + + this = new(Libc.malloc(_sizeof_uv_timer), ThreadSynchronizer(), true, false) + associate_julia_struct(this.handle, this) + iolock_begin() + err = ccall(:uv_timer_init, Cint, (Ptr{Cvoid}, Ptr{Cvoid}), loop, this) + @assert err == 0 + finalizer(uvfinalize, this) + ccall(:uv_update_time, Cvoid, (Ptr{Cvoid},), loop) + err = ccall(:uv_timer_start, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, UInt64, UInt64), + this, uv_jl_timercb::Ptr{Cvoid}, timeout, interval) + @assert err == 0 + iolock_end() + return this + end +end + +unsafe_convert(::Type{Ptr{Cvoid}}, t::Timer) = t.handle +unsafe_convert(::Type{Ptr{Cvoid}}, async::AsyncCondition) = async.handle + +function _trywait(t::Union{Timer, AsyncCondition}) + set = t.set + if !set + t.handle == C_NULL && return false + iolock_begin() + set = t.set + if !set + preserve_handle(t) + lock(t.cond) + try + set = t.set + if !set + if t.handle != C_NULL + iolock_end() + set = wait(t.cond) + unlock(t.cond) + iolock_begin() + lock(t.cond) + end + end + finally + unlock(t.cond) + unpreserve_handle(t) + end + end + iolock_end() + end + t.set = false + return set +end + +function wait(t::Union{Timer, AsyncCondition}) + _trywait(t) || throw(EOFError()) + nothing +end + + +isopen(t::Union{Timer, AsyncCondition}) = t.isopen + +function close(t::Union{Timer, AsyncCondition}) + iolock_begin() + if t.handle != C_NULL && isopen(t) + t.isopen = false + ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), t) + end + iolock_end() + nothing +end + +function uvfinalize(t::Union{Timer, AsyncCondition}) + iolock_begin() + lock(t.cond) + try + if t.handle != C_NULL + disassociate_julia_struct(t.handle) # not going to call the usual close hooks + if t.isopen + t.isopen = false + ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), t) + end + t.handle = C_NULL + notify(t.cond, false) + end + finally + unlock(t.cond) + end + iolock_end() + nothing +end + +function _uv_hook_close(t::Union{Timer, AsyncCondition}) + lock(t.cond) + try + t.isopen = false + t.handle = C_NULL + notify(t.cond, t.set) + finally + unlock(t.cond) + end + nothing +end + +function uv_asynccb(handle::Ptr{Cvoid}) + async = @handle_as handle AsyncCondition + lock(async.cond) + try + async.set = true + notify(async.cond, true) + finally + unlock(async.cond) + end + nothing +end + +function uv_timercb(handle::Ptr{Cvoid}) + t = @handle_as handle Timer + lock(t.cond) + try + t.set = true + if ccall(:uv_timer_get_repeat, UInt64, (Ptr{Cvoid},), t) == 0 + # timer is stopped now + close(t) + end + notify(t.cond, true) + finally + unlock(t.cond) + end + nothing +end + +""" + sleep(seconds) + +Block the current task for a specified number of seconds. The minimum sleep time is 1 +millisecond or input of `0.001`. +""" +function sleep(sec::Real) + sec ≥ 0 || throw(ArgumentError("cannot sleep for $sec seconds")) + wait(Timer(sec)) + nothing +end + +# timer with repeated callback +""" + Timer(callback::Function, delay; interval = 0) + +Create a timer that wakes up tasks waiting for it (by calling [`wait`](@ref) on the timer object) and +calls the function `callback`. + +Waiting tasks are woken and the function `callback` is called after an initial delay of `delay` seconds, +and then repeating with the given `interval` in seconds. If `interval` is equal to `0`, the timer +is only triggered once. The function `callback` is called with a single argument, the timer itself. +When the timer is closed (by [`close`](@ref) waiting tasks are woken with an error. Use [`isopen`](@ref) +to check whether a timer is still active. + +# Examples + +Here the first number is printed after a delay of two seconds, then the following numbers are printed quickly. + +```julia-repl +julia> begin + i = 0 + cb(timer) = (global i += 1; println(i)) + t = Timer(cb, 2, interval=0.2) + wait(t) + sleep(0.5) + close(t) + end +1 +2 +3 +``` +""" +function Timer(cb::Function, timeout::Real; interval::Real=0.0) + timer = Timer(timeout, interval=interval) + @async while _trywait(timer) + cb(timer) + isopen(timer) || return + end + return timer +end + +""" + timedwait(testcb::Function, secs::Float64; pollint::Float64=0.1) + +Waits until `testcb` returns `true` or for `secs` seconds, whichever is earlier. +`testcb` is polled every `pollint` seconds. + +Returns :ok, :timed_out, or :error +""" +function timedwait(testcb::Function, secs::Float64; pollint::Float64=0.1) + pollint > 0 || throw(ArgumentError("cannot set pollint to $pollint seconds")) + start = time() + done = Channel(1) + function timercb(aw) + try + if testcb() + put!(done, :ok) + elseif (time() - start) > secs + put!(done, :timed_out) + end + catch e + put!(done, :error) + finally + isready(done) && close(aw) + end + nothing + end + + if !testcb() + t = Timer(timercb, pollint, interval = pollint) + ret = fetch(done)::Symbol + close(t) + else + ret = :ok + end + return ret +end diff --git a/base/asyncmap.jl b/base/asyncmap.jl new file mode 100644 index 0000000..b3e9476 --- /dev/null +++ b/base/asyncmap.jl @@ -0,0 +1,421 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +using Base.Iterators: Enumerate + +""" + asyncmap(f, c...; ntasks=0, batch_size=nothing) + +Uses multiple concurrent tasks to map `f` over a collection (or multiple +equal length collections). For multiple collection arguments, `f` is +applied elementwise. + +`ntasks` specifies the number of tasks to run concurrently. +Depending on the length of the collections, if `ntasks` is unspecified, +up to 100 tasks will be used for concurrent mapping. + +`ntasks` can also be specified as a zero-arg function. In this case, the +number of tasks to run in parallel is checked before processing every element and a new +task started if the value of `ntasks_func` is less than the current number +of tasks. + +If `batch_size` is specified, the collection is processed in batch mode. `f` must +then be a function that must accept a `Vector` of argument tuples and must +return a vector of results. The input vector will have a length of `batch_size` or less. + +The following examples highlight execution in different tasks by returning +the `objectid` of the tasks in which the mapping function is executed. + +First, with `ntasks` undefined, each element is processed in a different task. +``` +julia> tskoid() = objectid(current_task()); + +julia> asyncmap(x->tskoid(), 1:5) +5-element Array{UInt64,1}: + 0x6e15e66c75c75853 + 0x440f8819a1baa682 + 0x9fb3eeadd0c83985 + 0xebd3e35fe90d4050 + 0x29efc93edce2b961 + +julia> length(unique(asyncmap(x->tskoid(), 1:5))) +5 +``` + +With `ntasks=2` all elements are processed in 2 tasks. +``` +julia> asyncmap(x->tskoid(), 1:5; ntasks=2) +5-element Array{UInt64,1}: + 0x027ab1680df7ae94 + 0xa23d2f80cd7cf157 + 0x027ab1680df7ae94 + 0xa23d2f80cd7cf157 + 0x027ab1680df7ae94 + +julia> length(unique(asyncmap(x->tskoid(), 1:5; ntasks=2))) +2 +``` + +With `batch_size` defined, the mapping function needs to be changed to accept an array +of argument tuples and return an array of results. `map` is used in the modified mapping +function to achieve this. +``` +julia> batch_func(input) = map(x->string("args_tuple: ", x, ", element_val: ", x[1], ", task: ", tskoid()), input) +batch_func (generic function with 1 method) + +julia> asyncmap(batch_func, 1:5; ntasks=2, batch_size=2) +5-element Array{String,1}: + "args_tuple: (1,), element_val: 1, task: 9118321258196414413" + "args_tuple: (2,), element_val: 2, task: 4904288162898683522" + "args_tuple: (3,), element_val: 3, task: 9118321258196414413" + "args_tuple: (4,), element_val: 4, task: 4904288162898683522" + "args_tuple: (5,), element_val: 5, task: 9118321258196414413" +``` + +!!! note + Currently, all tasks in Julia are executed in a single OS thread co-operatively. Consequently, + `asyncmap` is beneficial only when the mapping function involves any I/O - disk, network, remote + worker invocation, etc. + +""" +function asyncmap(f, c...; ntasks=0, batch_size=nothing) + return async_usemap(f, c...; ntasks=ntasks, batch_size=batch_size) +end + +function async_usemap(f, c...; ntasks=0, batch_size=nothing) + ntasks = verify_ntasks(c[1], ntasks) + batch_size = verify_batch_size(batch_size) + + if batch_size !== nothing + exec_func = batch -> begin + # extract the Refs from the input tuple + batch_refs = map(x->x[1], batch) + + # and the args tuple.... + batched_args = map(x->x[2], batch) + + results = f(batched_args) + foreach(x -> (batch_refs[x[1]].x = x[2]), enumerate(results)) + end + else + exec_func = (r,args) -> (r.x = f(args...)) + end + chnl, worker_tasks = setup_chnl_and_tasks(exec_func, ntasks, batch_size) + return wrap_n_exec_twice(chnl, worker_tasks, ntasks, exec_func, c...) +end + +batch_size_err_str(batch_size) = string("batch_size must be specified as a positive integer. batch_size=", batch_size) +function verify_batch_size(batch_size) + if batch_size === nothing + return batch_size + elseif isa(batch_size, Number) + batch_size = Int(batch_size) + batch_size < 1 && throw(ArgumentError(batch_size_err_str(batch_size))) + return batch_size + else + throw(ArgumentError(batch_size_err_str(batch_size))) + end +end + + +function verify_ntasks(iterable, ntasks) + if !((isa(ntasks, Number) && (ntasks >= 0)) || isa(ntasks, Function)) + err = string("ntasks must be specified as a positive integer or a 0-arg function. ntasks=", ntasks) + throw(ArgumentError(err)) + end + + if ntasks == 0 + chklen = IteratorSize(iterable) + if (chklen isa HasLength) || (chklen isa HasShape) + ntasks = max(1,min(100, length(iterable))) + else + ntasks = 100 + end + end + return ntasks +end + +function wrap_n_exec_twice(chnl, worker_tasks, ntasks, exec_func, c...) + # The driver task, creates a Ref object and writes it and the args tuple to + # the communication channel for processing by a free worker task. + push_arg_to_channel = (x...) -> (r=Ref{Any}(nothing); put!(chnl,(r,x));r) + + if isa(ntasks, Function) + map_f = (x...) -> begin + # check number of tasks every time, and start one if required. + # number_tasks > optimal_number is fine, the other way around is inefficient. + if length(worker_tasks) < ntasks() + start_worker_task!(worker_tasks, exec_func, chnl) + end + push_arg_to_channel(x...) + end + else + map_f = push_arg_to_channel + end + maptwice(map_f, chnl, worker_tasks, c...) +end + +function maptwice(wrapped_f, chnl, worker_tasks, c...) + # first run, returns a collection of Refs + asyncrun_excp = nothing + local asyncrun + try + asyncrun = map(wrapped_f, c...) + catch ex + if isa(ex,InvalidStateException) + # channel could be closed due to exceptions in the async tasks, + # we propagate those errors, if any, over the `put!` failing + # in asyncrun due to a closed channel. + asyncrun_excp = ex + else + rethrow() + end + end + + # close channel and wait for all worker tasks to finish + close(chnl) + + # check and throw any exceptions from the worker tasks + foreach(x->(v=fetch(x); isa(v, Exception) && throw(v)), worker_tasks) + + # check if there was a genuine problem with asyncrun + (asyncrun_excp !== nothing) && throw(asyncrun_excp) + + if isa(asyncrun, Ref) + # scalar case + return asyncrun.x + else + # second run, extract values from the Refs and return + return map(ref->ref.x, asyncrun) + end +end + +function setup_chnl_and_tasks(exec_func, ntasks, batch_size=nothing) + if isa(ntasks, Function) + nt = ntasks() + # start at least one worker task. + if nt == 0 + nt = 1 + end + else + nt = ntasks + end + + # Use an unbuffered channel for communicating with the worker tasks. In the event + # of an error in any of the worker tasks, the channel is closed. This + # results in the `put!` in the driver task failing immediately. + chnl = Channel(0) + worker_tasks = [] + foreach(_ -> start_worker_task!(worker_tasks, exec_func, chnl, batch_size), 1:nt) + yield() + return (chnl, worker_tasks) +end + +function start_worker_task!(worker_tasks, exec_func, chnl, batch_size=nothing) + t = @async begin + retval = nothing + + try + if isa(batch_size, Number) + while isopen(chnl) + # The mapping function expects an array of input args, as it processes + # elements in a batch. + batch_collection=Any[] + n = 0 + for exec_data in chnl + push!(batch_collection, exec_data) + n += 1 + (n == batch_size) && break + end + if n > 0 + exec_func(batch_collection) + end + end + else + for exec_data in chnl + exec_func(exec_data...) + end + end + catch e + close(chnl) + retval = e + end + retval + end + push!(worker_tasks, t) +end + +# Special handling for some types. +function asyncmap(f, s::AbstractString; kwargs...) + s2 = Vector{Char}(undef, length(s)) + asyncmap!(f, s2, s; kwargs...) + return String(s2) +end + +# map on a single BitArray returns a BitArray if the mapping function is boolean. +function asyncmap(f, b::BitArray; kwargs...) + b2 = async_usemap(f, b; kwargs...) + if eltype(b2) == Bool + return BitArray(b2) + end + return b2 +end + +mutable struct AsyncCollector + f + results + enumerator::Enumerate + ntasks + batch_size + nt_check::Bool # check number of tasks on every iteration + + AsyncCollector(f, r, en::Enumerate, ntasks, batch_size) = new(f, r, en, ntasks, batch_size, isa(ntasks, Function)) +end + +""" + AsyncCollector(f, results, c...; ntasks=0, batch_size=nothing) -> iterator + +Return an iterator which applies `f` to each element of `c` asynchronously +and collects output into `results`. + +Keyword args `ntasks` and `batch_size` have the same behavior as in +[`asyncmap`](@ref). If `batch_size` is specified, `f` must +be a function which operates on an array of argument tuples. + +!!! note + `iterate(::AsyncCollector, state) -> (nothing, state)`. A successful return + from `iterate` indicates that the next element from the input collection is + being processed asynchronously. It blocks until a free worker task becomes + available. + +!!! note + `for _ in AsyncCollector(f, results, c...; ntasks=1) end` is equivalent to + `map!(f, results, c...)`. +""" +function AsyncCollector(f, results, c...; ntasks=0, batch_size=nothing) + AsyncCollector(f, results, enumerate(zip(c...)), ntasks, batch_size) +end + +mutable struct AsyncCollectorState + chnl::Channel + worker_tasks::Array{Task,1} + enum_state # enumerator state + AsyncCollectorState(chnl::Channel, worker_tasks::Vector) = + new(chnl, convert(Vector{Task}, worker_tasks)) +end + +function iterate(itr::AsyncCollector) + itr.ntasks = verify_ntasks(itr.enumerator, itr.ntasks) + itr.batch_size = verify_batch_size(itr.batch_size) + if itr.batch_size !== nothing + exec_func = batch -> begin + # extract indices from the input tuple + batch_idxs = map(x->x[1], batch) + + # and the args tuple.... + batched_args = map(x->x[2], batch) + + results = f(batched_args) + foreach(x -> (itr.results[batch_idxs[x[1]]] = x[2]), enumerate(results)) + end + else + exec_func = (i,args) -> (itr.results[i]=itr.f(args...)) + end + chnl, worker_tasks = setup_chnl_and_tasks((i,args) -> (itr.results[i]=itr.f(args...)), itr.ntasks, itr.batch_size) + return iterate(itr, AsyncCollectorState(chnl, worker_tasks)) +end + +function wait_done(itr::AsyncCollector, state::AsyncCollectorState) + close(state.chnl) + + # wait for all tasks to finish + foreach(x->(v=fetch(x); isa(v, Exception) && throw(v)), state.worker_tasks) + empty!(state.worker_tasks) +end + +function iterate(itr::AsyncCollector, state::AsyncCollectorState) + if itr.nt_check && (length(state.worker_tasks) < itr.ntasks()) + start_worker_task!(state.worker_tasks, itr.f, state.chnl) + end + + # Get index and mapped function arguments from enumeration iterator. + y = isdefined(state, :enum_state) ? + iterate(itr.enumerator, state.enum_state) : + iterate(itr.enumerator) + if y === nothing + wait_done(itr, state) + return nothing + end + (i, args), state.enum_state = y + put!(state.chnl, (i, args)) + + return (nothing, state) +end + +""" + AsyncGenerator(f, c...; ntasks=0, batch_size=nothing) -> iterator + +Apply `f` to each element of `c` using at most `ntasks` asynchronous tasks. + +Keyword args `ntasks` and `batch_size` have the same behavior as in +[`asyncmap`](@ref). If `batch_size` is specified, `f` must +be a function which operates on an array of argument tuples. + +!!! note + `collect(AsyncGenerator(f, c...; ntasks=1))` is equivalent to + `map(f, c...)`. +""" +mutable struct AsyncGenerator + collector::AsyncCollector +end + +function AsyncGenerator(f, c...; ntasks=0) + AsyncGenerator(AsyncCollector(f, Dict{Int,Any}(), c...; ntasks=ntasks)) +end + +mutable struct AsyncGeneratorState + i::Int + collector_done::Bool + collector_state::AsyncCollectorState + AsyncGeneratorState(i::Int) = new(i, false) +end + +function iterate(itr::AsyncGenerator, state::AsyncGeneratorState=AsyncGeneratorState(0)) + state.i += 1 + + results_dict = itr.collector.results + while !state.collector_done && !haskey(results_dict, state.i) + y = isdefined(state, :collector_state) ? + iterate(itr.collector, state.collector_state) : + iterate(itr.collector) + if y === nothing + # `check_done` waits for async tasks to finish. if we do not have the index + # we are looking for, it is an error. + state.collector_done = true + break; + end + _, state.collector_state = y + end + state.collector_done && isempty(results_dict) && return nothing + r = results_dict[state.i] + delete!(results_dict, state.i) + + return (r, state) +end + +# pass-through iterator traits to the iterable +# on which the mapping function is being applied +IteratorSize(::Type{AsyncGenerator}) = SizeUnknown() +IteratorEltype(::Type{AsyncGenerator}) = EltypeUnknown() +size(itr::AsyncGenerator) = size(itr.collector.enumerator) +length(itr::AsyncGenerator) = length(itr.collector.enumerator) + +""" + asyncmap!(f, results, c...; ntasks=0, batch_size=nothing) + +Like [`asyncmap`](@ref), but stores output in `results` rather than +returning a collection. +""" +function asyncmap!(f, r, c1, c...; ntasks=0, batch_size=nothing) + foreach(identity, AsyncCollector(f, r, c1, c...; ntasks=ntasks, batch_size=batch_size)) + r +end diff --git a/base/atomics.jl b/base/atomics.jl new file mode 100644 index 0000000..b5446be --- /dev/null +++ b/base/atomics.jl @@ -0,0 +1,450 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +using Core.Intrinsics: llvmcall + +import .Base: setindex!, getindex, unsafe_convert +import .Base.Sys: ARCH, WORD_SIZE + +export + Atomic, + atomic_cas!, + atomic_xchg!, + atomic_add!, atomic_sub!, + atomic_and!, atomic_nand!, atomic_or!, atomic_xor!, + atomic_max!, atomic_min!, + atomic_fence + +# 128-bit atomics do not exist on AArch32. +if startswith(string(ARCH), "arm") + const inttypes = (Int8, Int16, Int32, Int64, + UInt8, UInt16, UInt32, UInt64) +else + const inttypes = (Int8, Int16, Int32, Int64, Int128, + UInt8, UInt16, UInt32, UInt64, UInt128) +end +const floattypes = (Float16, Float32, Float64) +const arithmetictypes = (inttypes..., floattypes...) +# TODO: Support Ptr +const atomictypes = (arithmetictypes..., Bool) +const IntTypes = Union{inttypes...} +const FloatTypes = Union{floattypes...} +const ArithmeticTypes = Union{arithmetictypes...} +const AtomicTypes = Union{atomictypes...} + +""" + Threads.Atomic{T} + +Holds a reference to an object of type `T`, ensuring that it is only +accessed atomically, i.e. in a thread-safe manner. + +Only certain "simple" types can be used atomically, namely the +primitive boolean, integer, and float-point types. These are `Bool`, +`Int8`...`Int128`, `UInt8`...`UInt128`, and `Float16`...`Float64`. + +New atomic objects can be created from a non-atomic values; if none is +specified, the atomic object is initialized with zero. + +Atomic objects can be accessed using the `[]` notation: + +# Examples +```jldoctest +julia> x = Threads.Atomic{Int}(3) +Base.Threads.Atomic{Int64}(3) + +julia> x[] = 1 +1 + +julia> x[] +1 +``` + +Atomic operations use an `atomic_` prefix, such as [`atomic_add!`](@ref), +[`atomic_xchg!`](@ref), etc. +""" +mutable struct Atomic{T<:AtomicTypes} + value::T + Atomic{T}() where {T<:AtomicTypes} = new(zero(T)) + Atomic{T}(value) where {T<:AtomicTypes} = new(value) +end + +Atomic() = Atomic{Int}() + +""" + Threads.atomic_cas!(x::Atomic{T}, cmp::T, newval::T) where T + +Atomically compare-and-set `x` + +Atomically compares the value in `x` with `cmp`. If equal, write +`newval` to `x`. Otherwise, leaves `x` unmodified. Returns the old +value in `x`. By comparing the returned value to `cmp` (via `===`) one +knows whether `x` was modified and now holds the new value `newval`. + +For further details, see LLVM's `cmpxchg` instruction. + +This function can be used to implement transactional semantics. Before +the transaction, one records the value in `x`. After the transaction, +the new value is stored only if `x` has not been modified in the mean +time. + +# Examples +```jldoctest +julia> x = Threads.Atomic{Int}(3) +Base.Threads.Atomic{Int64}(3) + +julia> Threads.atomic_cas!(x, 4, 2); + +julia> x +Base.Threads.Atomic{Int64}(3) + +julia> Threads.atomic_cas!(x, 3, 2); + +julia> x +Base.Threads.Atomic{Int64}(2) +``` +""" +function atomic_cas! end + +""" + Threads.atomic_xchg!(x::Atomic{T}, newval::T) where T + +Atomically exchange the value in `x` + +Atomically exchanges the value in `x` with `newval`. Returns the **old** +value. + +For further details, see LLVM's `atomicrmw xchg` instruction. + +# Examples +```jldoctest +julia> x = Threads.Atomic{Int}(3) +Base.Threads.Atomic{Int64}(3) + +julia> Threads.atomic_xchg!(x, 2) +3 + +julia> x[] +2 +``` +""" +function atomic_xchg! end + +""" + Threads.atomic_add!(x::Atomic{T}, val::T) where T <: ArithmeticTypes + +Atomically add `val` to `x` + +Performs `x[] += val` atomically. Returns the **old** value. Not defined for +`Atomic{Bool}`. + +For further details, see LLVM's `atomicrmw add` instruction. + +# Examples +```jldoctest +julia> x = Threads.Atomic{Int}(3) +Base.Threads.Atomic{Int64}(3) + +julia> Threads.atomic_add!(x, 2) +3 + +julia> x[] +5 +``` +""" +function atomic_add! end + +""" + Threads.atomic_sub!(x::Atomic{T}, val::T) where T <: ArithmeticTypes + +Atomically subtract `val` from `x` + +Performs `x[] -= val` atomically. Returns the **old** value. Not defined for +`Atomic{Bool}`. + +For further details, see LLVM's `atomicrmw sub` instruction. + +# Examples +```jldoctest +julia> x = Threads.Atomic{Int}(3) +Base.Threads.Atomic{Int64}(3) + +julia> Threads.atomic_sub!(x, 2) +3 + +julia> x[] +1 +``` +""" +function atomic_sub! end + +""" + Threads.atomic_and!(x::Atomic{T}, val::T) where T + +Atomically bitwise-and `x` with `val` + +Performs `x[] &= val` atomically. Returns the **old** value. + +For further details, see LLVM's `atomicrmw and` instruction. + +# Examples +```jldoctest +julia> x = Threads.Atomic{Int}(3) +Base.Threads.Atomic{Int64}(3) + +julia> Threads.atomic_and!(x, 2) +3 + +julia> x[] +2 +``` +""" +function atomic_and! end + +""" + Threads.atomic_nand!(x::Atomic{T}, val::T) where T + +Atomically bitwise-nand (not-and) `x` with `val` + +Performs `x[] = ~(x[] & val)` atomically. Returns the **old** value. + +For further details, see LLVM's `atomicrmw nand` instruction. + +# Examples +```jldoctest +julia> x = Threads.Atomic{Int}(3) +Base.Threads.Atomic{Int64}(3) + +julia> Threads.atomic_nand!(x, 2) +3 + +julia> x[] +-3 +``` +""" +function atomic_nand! end + +""" + Threads.atomic_or!(x::Atomic{T}, val::T) where T + +Atomically bitwise-or `x` with `val` + +Performs `x[] |= val` atomically. Returns the **old** value. + +For further details, see LLVM's `atomicrmw or` instruction. + +# Examples +```jldoctest +julia> x = Threads.Atomic{Int}(5) +Base.Threads.Atomic{Int64}(5) + +julia> Threads.atomic_or!(x, 7) +5 + +julia> x[] +7 +``` +""" +function atomic_or! end + +""" + Threads.atomic_xor!(x::Atomic{T}, val::T) where T + +Atomically bitwise-xor (exclusive-or) `x` with `val` + +Performs `x[] \$= val` atomically. Returns the **old** value. + +For further details, see LLVM's `atomicrmw xor` instruction. + +# Examples +```jldoctest +julia> x = Threads.Atomic{Int}(5) +Base.Threads.Atomic{Int64}(5) + +julia> Threads.atomic_xor!(x, 7) +5 + +julia> x[] +2 +``` +""" +function atomic_xor! end + +""" + Threads.atomic_max!(x::Atomic{T}, val::T) where T + +Atomically store the maximum of `x` and `val` in `x` + +Performs `x[] = max(x[], val)` atomically. Returns the **old** value. + +For further details, see LLVM's `atomicrmw max` instruction. + +# Examples +```jldoctest +julia> x = Threads.Atomic{Int}(5) +Base.Threads.Atomic{Int64}(5) + +julia> Threads.atomic_max!(x, 7) +5 + +julia> x[] +7 +``` +""" +function atomic_max! end + +""" + Threads.atomic_min!(x::Atomic{T}, val::T) where T + +Atomically store the minimum of `x` and `val` in `x` + +Performs `x[] = min(x[], val)` atomically. Returns the **old** value. + +For further details, see LLVM's `atomicrmw min` instruction. + +# Examples +```jldoctest +julia> x = Threads.Atomic{Int}(7) +Base.Threads.Atomic{Int64}(7) + +julia> Threads.atomic_min!(x, 5) +7 + +julia> x[] +5 +``` +""" +function atomic_min! end + +unsafe_convert(::Type{Ptr{T}}, x::Atomic{T}) where {T} = convert(Ptr{T}, pointer_from_objref(x)) +setindex!(x::Atomic{T}, v) where {T} = setindex!(x, convert(T, v)) + +const llvmtypes = IdDict{Any,String}( + Bool => "i8", # julia represents bools with 8-bits for now. # TODO: is this okay? + Int8 => "i8", UInt8 => "i8", + Int16 => "i16", UInt16 => "i16", + Int32 => "i32", UInt32 => "i32", + Int64 => "i64", UInt64 => "i64", + Int128 => "i128", UInt128 => "i128", + Float16 => "i16", # half + Float32 => "float", + Float64 => "double", +) +inttype(::Type{T}) where {T<:Integer} = T +inttype(::Type{Float16}) = Int16 +inttype(::Type{Float32}) = Int32 +inttype(::Type{Float64}) = Int64 + + +import ..Base.gc_alignment + +# All atomic operations have acquire and/or release semantics, depending on +# whether the load or store values. Most of the time, this is what one wants +# anyway, and it's only moderately expensive on most hardware. +for typ in atomictypes + lt = llvmtypes[typ] + ilt = llvmtypes[inttype(typ)] + rt = "$lt, $lt*" + irt = "$ilt, $ilt*" + @eval getindex(x::Atomic{$typ}) = + llvmcall($""" + %ptr = inttoptr i$WORD_SIZE %0 to $lt* + %rv = load atomic $rt %ptr acquire, align $(gc_alignment(typ)) + ret $lt %rv + """, $typ, Tuple{Ptr{$typ}}, unsafe_convert(Ptr{$typ}, x)) + @eval setindex!(x::Atomic{$typ}, v::$typ) = + llvmcall($""" + %ptr = inttoptr i$WORD_SIZE %0 to $lt* + store atomic $lt %1, $lt* %ptr release, align $(gc_alignment(typ)) + ret void + """, Cvoid, Tuple{Ptr{$typ}, $typ}, unsafe_convert(Ptr{$typ}, x), v) + + # Note: atomic_cas! succeeded (i.e. it stored "new") if and only if the result is "cmp" + if typ <: Integer + @eval atomic_cas!(x::Atomic{$typ}, cmp::$typ, new::$typ) = + llvmcall($""" + %ptr = inttoptr i$WORD_SIZE %0 to $lt* + %rs = cmpxchg $lt* %ptr, $lt %1, $lt %2 acq_rel acquire + %rv = extractvalue { $lt, i1 } %rs, 0 + ret $lt %rv + """, $typ, Tuple{Ptr{$typ},$typ,$typ}, + unsafe_convert(Ptr{$typ}, x), cmp, new) + else + @eval atomic_cas!(x::Atomic{$typ}, cmp::$typ, new::$typ) = + llvmcall($""" + %iptr = inttoptr i$WORD_SIZE %0 to $ilt* + %icmp = bitcast $lt %1 to $ilt + %inew = bitcast $lt %2 to $ilt + %irs = cmpxchg $ilt* %iptr, $ilt %icmp, $ilt %inew acq_rel acquire + %irv = extractvalue { $ilt, i1 } %irs, 0 + %rv = bitcast $ilt %irv to $lt + ret $lt %rv + """, $typ, Tuple{Ptr{$typ},$typ,$typ}, + unsafe_convert(Ptr{$typ}, x), cmp, new) + end + + arithmetic_ops = [:add, :sub] + for rmwop in [arithmetic_ops..., :xchg, :and, :nand, :or, :xor, :max, :min] + rmw = string(rmwop) + fn = Symbol("atomic_", rmw, "!") + if (rmw == "max" || rmw == "min") && typ <: Unsigned + # LLVM distinguishes signedness in the operation, not the integer type. + rmw = "u" * rmw + end + if rmwop in arithmetic_ops && !(typ <: ArithmeticTypes) continue end + if typ <: Integer + @eval $fn(x::Atomic{$typ}, v::$typ) = + llvmcall($""" + %ptr = inttoptr i$WORD_SIZE %0 to $lt* + %rv = atomicrmw $rmw $lt* %ptr, $lt %1 acq_rel + ret $lt %rv + """, $typ, Tuple{Ptr{$typ}, $typ}, unsafe_convert(Ptr{$typ}, x), v) + else + rmwop === :xchg || continue + @eval $fn(x::Atomic{$typ}, v::$typ) = + llvmcall($""" + %iptr = inttoptr i$WORD_SIZE %0 to $ilt* + %ival = bitcast $lt %1 to $ilt + %irv = atomicrmw $rmw $ilt* %iptr, $ilt %ival acq_rel + %rv = bitcast $ilt %irv to $lt + ret $lt %rv + """, $typ, Tuple{Ptr{$typ}, $typ}, unsafe_convert(Ptr{$typ}, x), v) + end + end +end + +# Provide atomic floating-point operations via atomic_cas! +const opnames = Dict{Symbol, Symbol}(:+ => :add, :- => :sub) +for op in [:+, :-, :max, :min] + opname = get(opnames, op, op) + @eval function $(Symbol("atomic_", opname, "!"))(var::Atomic{T}, val::T) where T<:FloatTypes + IT = inttype(T) + old = var[] + while true + new = $op(old, val) + cmp = old + old = atomic_cas!(var, cmp, new) + reinterpret(IT, old) == reinterpret(IT, cmp) && return new + # Temporary solution before we have gc transition support in codegen. + ccall(:jl_gc_safepoint, Cvoid, ()) + end + end +end + +""" + Threads.atomic_fence() + +Insert a sequential-consistency memory fence + +Inserts a memory fence with sequentially-consistent ordering +semantics. There are algorithms where this is needed, i.e. where an +acquire/release ordering is insufficient. + +This is likely a very expensive operation. Given that all other atomic +operations in Julia already have acquire/release semantics, explicit +fences should not be necessary in most cases. + +For further details, see LLVM's `fence` instruction. +""" +atomic_fence() = llvmcall(""" + fence seq_cst + ret void + """, Cvoid, Tuple{}) diff --git a/base/baseext.jl b/base/baseext.jl new file mode 100644 index 0000000..75ef96c --- /dev/null +++ b/base/baseext.jl @@ -0,0 +1,35 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# extensions to Core types to add features in Base + +# hook up VecElement constructor to Base.convert +VecElement{T}(arg) where {T} = VecElement{T}(convert(T, arg)) +convert(::Type{T}, arg::T) where {T<:VecElement} = arg +convert(::Type{T}, arg) where {T<:VecElement} = T(arg) + +# ## dims-type-converting Array constructors for convenience +# type and dimensionality specified, accepting dims as series of Integers +Vector{T}(::UndefInitializer, m::Integer) where {T} = Vector{T}(undef, Int(m)) +Matrix{T}(::UndefInitializer, m::Integer, n::Integer) where {T} = Matrix{T}(undef, Int(m), Int(n)) +Array{T,N}(::UndefInitializer, d::Vararg{Integer,N}) where {T,N} = Array{T,N}(undef, convert(Tuple{Vararg{Int}}, d)) +# type but not dimensionality specified, accepting dims as series of Integers +Array{T}(::UndefInitializer, m::Integer) where {T} = Array{T,1}(undef, Int(m)) +Array{T}(::UndefInitializer, m::Integer, n::Integer) where {T} = Array{T,2}(undef, Int(m), Int(n)) +Array{T}(::UndefInitializer, m::Integer, n::Integer, o::Integer) where {T} = Array{T,3}(undef, Int(m), Int(n), Int(o)) +Array{T}(::UndefInitializer, d::Integer...) where {T} = Array{T}(undef, convert(Tuple{Vararg{Int}}, d)) +# dimensionality but not type specified, accepting dims as series of Integers +Vector(::UndefInitializer, m::Integer) = Vector{Any}(undef, Int(m)) +Matrix(::UndefInitializer, m::Integer, n::Integer) = Matrix{Any}(undef, Int(m), Int(n)) +# Dimensions as a single tuple +Array{T}(::UndefInitializer, d::NTuple{N,Integer}) where {T,N} = Array{T,N}(undef, convert(Tuple{Vararg{Int}}, d)) +Array{T,N}(::UndefInitializer, d::NTuple{N,Integer}) where {T,N} = Array{T,N}(undef, convert(Tuple{Vararg{Int}}, d)) +# empty vector constructor +Vector() = Vector{Any}(undef, 0) + +# Array constructors for nothing and missing +# type and dimensionality specified +Array{T,N}(::Nothing, d...) where {T,N} = fill!(Array{T,N}(undef, d...), nothing) +Array{T,N}(::Missing, d...) where {T,N} = fill!(Array{T,N}(undef, d...), missing) +# type but not dimensionality specified +Array{T}(::Nothing, d...) where {T} = fill!(Array{T}(undef, d...), nothing) +Array{T}(::Missing, d...) where {T} = fill!(Array{T}(undef, d...), missing) diff --git a/base/bitarray.jl b/base/bitarray.jl new file mode 100644 index 0000000..a7549e8 --- /dev/null +++ b/base/bitarray.jl @@ -0,0 +1,1816 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## BitArray + +# notes: bits are stored in contiguous chunks +# unused bits must always be set to 0 +""" + BitArray{N} <: AbstractArray{Bool, N} + +Space-efficient `N`-dimensional boolean array, using just one bit for each boolean value. + +`BitArray`s pack up to 64 values into every 8 bytes, resulting in an 8x space efficiency +over `Array{Bool, N}` and allowing some operations to work on 64 values at once. + +By default, Julia returns `BitArrays` from [broadcasting](@ref Broadcasting) operations +that generate boolean elements (including dotted-comparisons like `.==`) as well as from +the functions [`trues`](@ref) and [`falses`](@ref). +""" +mutable struct BitArray{N} <: AbstractArray{Bool, N} + chunks::Vector{UInt64} + len::Int + dims::NTuple{N,Int} + function BitArray{N}(::UndefInitializer, dims::Vararg{Int,N}) where N + n = 1 + i = 1 + for d in dims + d >= 0 || throw(ArgumentError("dimension size must be ≥ 0, got $d for dimension $i")) + n *= d + i += 1 + end + nc = num_bit_chunks(n) + chunks = Vector{UInt64}(undef, nc) + nc > 0 && (chunks[end] = UInt64(0)) + b = new(chunks, n) + N != 1 && (b.dims = dims) + return b + end +end + +# note: the docs for the two signatures are unified, but only +# the first one is recognized by the help system; it would be nice +# to fix this. +""" + BitArray(undef, dims::Integer...) + BitArray{N}(undef, dims::NTuple{N,Int}) + +Construct an undef [`BitArray`](@ref) with the given dimensions. +Behaves identically to the [`Array`](@ref) constructor. See [`undef`](@ref). + +# Examples +```julia-repl +julia> BitArray(undef, 2, 2) +2×2 BitArray{2}: + false false + false true + +julia> BitArray(undef, (3, 1)) +3×1 BitArray{2}: + false + true + false +``` +""" +BitArray(::UndefInitializer, dims::Integer...) = BitArray(undef, map(Int,dims)) +BitArray{N}(::UndefInitializer, dims::Integer...) where {N} = BitArray{N}(undef, map(Int,dims)) +BitArray(::UndefInitializer, dims::NTuple{N,Integer}) where {N} = BitArray{N}(undef, map(Int, dims)...) +BitArray{N}(::UndefInitializer, dims::NTuple{N,Integer}) where {N} = BitArray{N}(undef, map(Int, dims)...) + +const BitVector = BitArray{1} +const BitMatrix = BitArray{2} + +BitVector() = BitArray{1}(undef, 0) + +## utility functions ## + +length(B::BitArray) = B.len +size(B::BitVector) = (B.len,) +size(B::BitArray) = B.dims + +@inline function size(B::BitVector, d::Integer) + d < 1 && throw_boundserror(size(B), d) + ifelse(d == 1, B.len, 1) +end + +isassigned(B::BitArray, i::Int) = 1 <= i <= length(B) + +IndexStyle(::Type{<:BitArray}) = IndexLinear() + +## aux functions ## + +const _msk64 = ~UInt64(0) +@inline _div64(l) = l >> 6 +@inline _mod64(l) = l & 63 +@inline _blsr(x)= x & (x-1) #zeros the last set bit. Has native instruction on many archs. needed in multidimensional.jl +@inline _msk_end(l::Integer) = _msk64 >>> _mod64(-l) +@inline _msk_end(B::BitArray) = _msk_end(length(B)) +num_bit_chunks(n::Int) = _div64(n+63) + +@inline get_chunks_id(i::Integer) = _div64(Int(i)-1)+1, _mod64(Int(i)-1) + +function glue_src_bitchunks(src::Vector{UInt64}, k::Int, ks1::Int, msk_s0::UInt64, ls0::Int) + @inbounds begin + chunk = ((src[k] & msk_s0) >>> ls0) + if ks1 > k && ls0 > 0 + chunk_n = (src[k + 1] & ~msk_s0) + chunk |= (chunk_n << (64 - ls0)) + end + end + return chunk +end + +function copy_chunks!(dest::Vector{UInt64}, pos_d::Integer, src::Vector{UInt64}, pos_s::Integer, numbits::Integer) + numbits == 0 && return + if dest === src && pos_d > pos_s + return copy_chunks_rtol!(dest, pos_d, pos_s, numbits) + end + + kd0, ld0 = get_chunks_id(pos_d) + kd1, ld1 = get_chunks_id(pos_d + numbits - 1) + ks0, ls0 = get_chunks_id(pos_s) + ks1, ls1 = get_chunks_id(pos_s + numbits - 1) + + delta_kd = kd1 - kd0 + delta_ks = ks1 - ks0 + + u = _msk64 + if delta_kd == 0 + msk_d0 = ~(u << ld0) | (u << (ld1+1)) + else + msk_d0 = ~(u << ld0) + msk_d1 = (u << (ld1+1)) + end + if delta_ks == 0 + msk_s0 = (u << ls0) & ~(u << (ls1+1)) + else + msk_s0 = (u << ls0) + end + + chunk_s0 = glue_src_bitchunks(src, ks0, ks1, msk_s0, ls0) + + dest[kd0] = (dest[kd0] & msk_d0) | ((chunk_s0 << ld0) & ~msk_d0) + + delta_kd == 0 && return + + for i = 1 : kd1 - kd0 - 1 + chunk_s1 = glue_src_bitchunks(src, ks0 + i, ks1, msk_s0, ls0) + + chunk_s = (chunk_s0 >>> (64 - ld0)) | (chunk_s1 << ld0) + + dest[kd0 + i] = chunk_s + + chunk_s0 = chunk_s1 + end + + if ks1 >= ks0 + delta_kd + chunk_s1 = glue_src_bitchunks(src, ks0 + delta_kd, ks1, msk_s0, ls0) + else + chunk_s1 = UInt64(0) + end + + chunk_s = (chunk_s0 >>> (64 - ld0)) | (chunk_s1 << ld0) + + dest[kd1] = (dest[kd1] & msk_d1) | (chunk_s & ~msk_d1) + + return +end + +function copy_chunks_rtol!(chunks::Vector{UInt64}, pos_d::Integer, pos_s::Integer, numbits::Integer) + pos_d == pos_s && return + pos_d < pos_s && return copy_chunks!(chunks, pos_d, chunks, pos_s, numbits) + + left = numbits + s = min(left, 64) + b = left - s + ps = pos_s + b + pd = pos_d + b + u = _msk64 + while left > 0 + kd0, ld0 = get_chunks_id(pd) + kd1, ld1 = get_chunks_id(pd + s - 1) + ks0, ls0 = get_chunks_id(ps) + ks1, ls1 = get_chunks_id(ps + s - 1) + + delta_kd = kd1 - kd0 + delta_ks = ks1 - ks0 + + if delta_kd == 0 + msk_d0 = ~(u << ld0) | (u << (ld1+1)) + else + msk_d0 = ~(u << ld0) + msk_d1 = (u << (ld1+1)) + end + if delta_ks == 0 + msk_s0 = (u << ls0) & ~(u << (ls1+1)) + else + msk_s0 = (u << ls0) + end + + chunk_s0 = glue_src_bitchunks(chunks, ks0, ks1, msk_s0, ls0) & ~(u << s) + chunks[kd0] = (chunks[kd0] & msk_d0) | ((chunk_s0 << ld0) & ~msk_d0) + + if delta_kd != 0 + chunk_s = (chunk_s0 >>> (64 - ld0)) + + chunks[kd1] = (chunks[kd1] & msk_d1) | (chunk_s & ~msk_d1) + end + + left -= s + s = min(left, 64) + b = left - s + ps = pos_s + b + pd = pos_d + b + end +end + +function fill_chunks!(Bc::Array{UInt64}, x::Bool, pos::Integer, numbits::Integer) + numbits <= 0 && return + k0, l0 = get_chunks_id(pos) + k1, l1 = get_chunks_id(pos+numbits-1) + + u = _msk64 + if k1 == k0 + msk0 = (u << l0) & ~(u << (l1+1)) + else + msk0 = (u << l0) + msk1 = ~(u << (l1+1)) + end + @inbounds if x + Bc[k0] |= msk0 + for k = k0+1:k1-1 + Bc[k] = u + end + k1 > k0 && (Bc[k1] |= msk1) + else + Bc[k0] &= ~msk0 + for k = k0+1:k1-1 + Bc[k] = 0 + end + k1 > k0 && (Bc[k1] &= ~msk1) + end +end + +copy_to_bitarray_chunks!(dest::Vector{UInt64}, pos_d::Int, src::BitArray, pos_s::Int, numbits::Int) = + copy_chunks!(dest, pos_d, src.chunks, pos_s, numbits) + +# pack 8 Bools encoded as one contiguous UIn64 into a single byte, e.g.: +# 0000001:0000001:00000000:00000000:00000001:00000000:00000000:00000001 → 11001001 → 0xc9 +function pack8bools(z::UInt64) + z |= z >>> 7 + z |= z >>> 14 + z |= z >>> 28 + z &= 0xFF + return z +end + +function copy_to_bitarray_chunks!(Bc::Vector{UInt64}, pos_d::Int, C::Array{Bool}, pos_s::Int, numbits::Int) + kd0, ld0 = get_chunks_id(pos_d) + kd1, ld1 = get_chunks_id(pos_d + numbits - 1) + + delta_kd = kd1 - kd0 + + u = _msk64 + if delta_kd == 0 + msk_d0 = msk_d1 = ~(u << ld0) | (u << (ld1+1)) + lt0 = ld1 + else + msk_d0 = ~(u << ld0) + msk_d1 = (u << (ld1+1)) + lt0 = 63 + end + + bind = kd0 + ind = pos_s + @inbounds if ld0 > 0 + c = UInt64(0) + for j = ld0:lt0 + c |= (UInt64(C[ind]) << j) + ind += 1 + end + Bc[kd0] = (Bc[kd0] & msk_d0) | (c & ~msk_d0) + bind += 1 + end + + nc = _div64(numbits - ind + pos_s) + nc8 = (nc >>> 3) << 3 + if nc8 > 0 + ind8 = 1 + P8 = Ptr{UInt64}(pointer(C, ind)) # unaligned i64 pointer + @inbounds for i = 1:nc8 + c = UInt64(0) + for j = 0:7 + # unaligned load + c |= (pack8bools(unsafe_load(P8, ind8)) << (j<<3)) + ind8 += 1 + end + Bc[bind] = c + bind += 1 + end + ind += (ind8-1) << 3 + end + @inbounds for i = (nc8+1):nc + c = UInt64(0) + for j = 0:63 + c |= (UInt64(C[ind]) << j) + ind += 1 + end + Bc[bind] = c + bind += 1 + end + @inbounds if bind ≤ kd1 + @assert bind == kd1 + c = UInt64(0) + for j = 0:ld1 + c |= (UInt64(C[ind]) << j) + ind += 1 + end + Bc[kd1] = (Bc[kd1] & msk_d1) | (c & ~msk_d1) + end +end + +## More definitions in multidimensional.jl + +# auxiliary definitions used when filling a BitArray via a Vector{Bool} cache +# (e.g. when constructing from an iterable, or in broadcast!) + +const bitcache_chunks = 64 # this can be changed +const bitcache_size = 64 * bitcache_chunks # do not change this + +dumpbitcache(Bc::Vector{UInt64}, bind::Int, C::Vector{Bool}) = + copy_to_bitarray_chunks!(Bc, ((bind - 1) << 6) + 1, C, 1, min(bitcache_size, (length(Bc)-bind+1) << 6)) + + +## custom iterator ## +function iterate(B::BitArray, i::Int=0) + i >= length(B) && return nothing + (B.chunks[_div64(i)+1] & (UInt64(1)<<_mod64(i)) != 0, i+1) +end + +## similar, fill!, copy! etc ## + +similar(B::BitArray) = BitArray(undef, size(B)) +similar(B::BitArray, dims::Int...) = BitArray(undef, dims) +similar(B::BitArray, dims::Dims) = BitArray(undef, dims...) + +similar(B::BitArray, T::Type{Bool}, dims::Dims) = BitArray(undef, dims) +# changing type to a non-Bool returns an Array +# (this triggers conversions like float(bitvector) etc.) +similar(B::BitArray, T::Type, dims::Dims) = Array{T}(undef, dims) + +function fill!(B::BitArray, x) + y = convert(Bool, x) + isempty(B) && return B + Bc = B.chunks + if !y + fill!(Bc, 0) + else + fill!(Bc, _msk64) + Bc[end] &= _msk_end(B) + end + return B +end + +""" + falses(dims) + +Create a `BitArray` with all values set to `false`. + +# Examples +```jldoctest +julia> falses(2,3) +2×3 BitArray{2}: + 0 0 0 + 0 0 0 +``` +""" +falses(dims::DimOrInd...) = falses(dims) +falses(dims::NTuple{N, Union{Integer, OneTo}}) where {N} = falses(map(to_dim, dims)) +falses(dims::NTuple{N, Integer}) where {N} = fill!(BitArray(undef, dims), false) +falses(dims::Tuple{}) = fill!(BitArray(undef, dims), false) + +""" + trues(dims) + +Create a `BitArray` with all values set to `true`. + +# Examples +```jldoctest +julia> trues(2,3) +2×3 BitArray{2}: + 1 1 1 + 1 1 1 +``` +""" +trues(dims::DimOrInd...) = trues(dims) +trues(dims::NTuple{N, Union{Integer, OneTo}}) where {N} = trues(map(to_dim, dims)) +trues(dims::NTuple{N, Integer}) where {N} = fill!(BitArray(undef, dims), true) +trues(dims::Tuple{}) = fill!(BitArray(undef, dims), true) + +function one(x::BitMatrix) + m, n = size(x) + m == n || throw(DimensionMismatch("multiplicative identity defined only for square matrices")) + a = falses(n, n) + for i = 1:n + a[i,i] = true + end + return a +end + +function copyto!(dest::BitArray, src::BitArray) + length(src) > length(dest) && throw(BoundsError(dest, length(dest)+1)) + destc = dest.chunks; srcc = src.chunks + nc = min(length(destc), length(srcc)) + nc == 0 && return dest + @inbounds begin + for i = 1 : nc - 1 + destc[i] = srcc[i] + end + if length(src) == length(dest) + destc[nc] = srcc[nc] + else + msk_s = _msk_end(src) + msk_d = ~msk_s + destc[nc] = (msk_d & destc[nc]) | (msk_s & srcc[nc]) + end + end + return dest +end + +function unsafe_copyto!(dest::BitArray, doffs::Integer, src::Union{BitArray,Array}, soffs::Integer, n::Integer) + copy_to_bitarray_chunks!(dest.chunks, doffs, src, soffs, n) + return dest +end + +function copyto!(dest::BitArray, doffs::Integer, src::Array, soffs::Integer, n::Integer) + n == 0 && return dest + soffs < 1 && throw(BoundsError(src, soffs)) + doffs < 1 && throw(BoundsError(dest, doffs)) + soffs+n-1 > length(src) && throw(BoundsError(src, length(src)+1)) + doffs+n-1 > length(dest) && throw(BoundsError(dest, length(dest)+1)) + return unsafe_copyto!(dest, doffs, src, soffs, n) +end + +function copyto!(dest::BitArray, src::Array) + length(src) > length(dest) && throw(BoundsError(dest, length(dest)+1)) + length(src) == 0 && return dest + return unsafe_copyto!(dest, 1, src, 1, length(src)) +end + +function reshape(B::BitArray{N}, dims::NTuple{N,Int}) where N + return dims == size(B) ? B : _bitreshape(B, dims) +end +reshape(B::BitArray, dims::Tuple{Vararg{Int}}) = _bitreshape(B, dims) +function _bitreshape(B::BitArray, dims::NTuple{N,Int}) where N + prod(dims) == length(B) || + throw(DimensionMismatch("new dimensions $(dims) must be consistent with array size $(length(B))")) + Br = BitArray{N}(undef, ntuple(i->0,Val(N))...) + Br.chunks = B.chunks + Br.len = prod(dims) + N != 1 && (Br.dims = dims) + return Br +end + +## Constructors ## + +function Array{T,N}(B::BitArray{N}) where {T,N} + A = Array{T,N}(undef, size(B)) + Bc = B.chunks + @inbounds for i = 1:length(A) + A[i] = unsafe_bitgetindex(Bc, i) + end + return A +end + +BitArray(A::AbstractArray{<:Any,N}) where {N} = BitArray{N}(A) +function BitArray{N}(A::AbstractArray{T,N}) where N where T + B = BitArray(undef, size(A)) + Bc = B.chunks + l = length(B) + l == 0 && return B + ind = 1 + @inbounds begin + for i = 1:length(Bc)-1 + c = UInt64(0) + for j = 0:63 + c |= (UInt64(convert(Bool, A[ind])) << j) + ind += 1 + end + Bc[i] = c + end + c = UInt64(0) + for j = 0:_mod64(l-1) + c |= (UInt64(convert(Bool, A[ind])) << j) + ind += 1 + end + Bc[end] = c + end + return B +end + +function BitArray{N}(A::Array{Bool,N}) where N + B = BitArray(undef, size(A)) + Bc = B.chunks + l = length(B) + l == 0 && return B + copy_to_bitarray_chunks!(Bc, 1, A, 1, l) + return B +end + +reinterpret(::Type{Bool}, B::BitArray, dims::NTuple{N,Int}) where {N} = reinterpret(B, dims) +reinterpret(B::BitArray, dims::NTuple{N,Int}) where {N} = reshape(B, dims) + +if nameof(@__MODULE__) === :Base # avoid method overwrite +(::Type{T})(x::T) where {T<:BitArray} = copy(x) +BitArray(x::BitArray) = copy(x) +end + +""" + BitArray(itr) + +Construct a [`BitArray`](@ref) generated by the given iterable object. +The shape is inferred from the `itr` object. + +# Examples +```jldoctest +julia> BitArray([1 0; 0 1]) +2×2 BitArray{2}: + 1 0 + 0 1 + +julia> BitArray(x+y == 3 for x = 1:2, y = 1:3) +2×3 BitArray{2}: + 0 1 0 + 1 0 0 + +julia> BitArray(x+y == 3 for x = 1:2 for y = 1:3) +6-element BitArray{1}: + 0 + 1 + 0 + 1 + 0 + 0 +``` +""" +BitArray(itr) = gen_bitarray(IteratorSize(itr), itr) + +convert(T::Type{<:BitArray}, a::AbstractArray) = a isa T ? a : T(a) + +# generic constructor from an iterable without compile-time info +# (we pass start(itr) explicitly to avoid a type-instability with filters) +gen_bitarray(isz::IteratorSize, itr) = gen_bitarray_from_itr(itr) + +# generic iterable with known shape +function gen_bitarray(::HasShape, itr) + B = BitArray(undef, size(itr)) + for (I,x) in zip(CartesianIndices(axes(itr)), itr) + B[I] = x + end + return B +end + +# generator with known shape or length +function gen_bitarray(::HasShape, itr::Generator) + B = BitArray(undef, size(itr)) + return fill_bitarray_from_itr!(B, itr) +end +function gen_bitarray(::HasLength, itr) + b = BitVector(undef, length(itr)) + return fill_bitarray_from_itr!(b, itr) +end + +gen_bitarray(::IsInfinite, itr) = throw(ArgumentError("infinite-size iterable used in BitArray constructor")) + +# The aux functions gen_bitarray_from_itr and fill_bitarray_from_itr! both +# use a Vector{Bool} cache for performance reasons + +function gen_bitarray_from_itr(itr) + B = empty!(BitVector(undef, bitcache_size)) + C = Vector{Bool}(undef, bitcache_size) + Bc = B.chunks + ind = 1 + cind = 1 + y = iterate(itr) + while y !== nothing + x, st = y + @inbounds C[ind] = x + ind += 1 + if ind > bitcache_size + resize!(B, length(B) + bitcache_size) + dumpbitcache(Bc, cind, C) + cind += bitcache_chunks + ind = 1 + end + y = iterate(itr, st) + end + if ind > 1 + @inbounds C[ind:bitcache_size] .= false + resize!(B, length(B) + ind - 1) + dumpbitcache(Bc, cind, C) + end + return B +end + +function fill_bitarray_from_itr!(B::BitArray, itr) + n = length(B) + C = Vector{Bool}(undef, bitcache_size) + Bc = B.chunks + ind = 1 + cind = 1 + y = iterate(itr) + while y !== nothing + x, st = y + @inbounds C[ind] = x + ind += 1 + if ind > bitcache_size + dumpbitcache(Bc, cind, C) + cind += bitcache_chunks + ind = 1 + end + y = iterate(itr, st) + end + if ind > 1 + @inbounds C[ind:bitcache_size] .= false + dumpbitcache(Bc, cind, C) + end + return B +end + + +## Indexing: getindex ## + +@inline function unsafe_bitgetindex(Bc::Vector{UInt64}, i::Int) + i1, i2 = get_chunks_id(i) + u = UInt64(1) << i2 + @inbounds r = (Bc[i1] & u) != 0 + return r +end + +@inline function getindex(B::BitArray, i::Int) + @boundscheck checkbounds(B, i) + unsafe_bitgetindex(B.chunks, i) +end + +## Indexing: setindex! ## + +@inline function unsafe_bitsetindex!(Bc::Array{UInt64}, x::Bool, i::Int) + i1, i2 = get_chunks_id(i) + _unsafe_bitsetindex!(Bc, x, i1, i2) +end + +@inline function _unsafe_bitsetindex!(Bc::Array{UInt64}, x::Bool, i1::Int, i2::Int) + u = UInt64(1) << i2 + @inbounds begin + c = Bc[i1] + Bc[i1] = ifelse(x, c | u, c & ~u) + end +end + +@inline function setindex!(B::BitArray, x, i::Int) + @boundscheck checkbounds(B, i) + unsafe_bitsetindex!(B.chunks, convert(Bool, x), i) + return B +end + +indexoffset(i) = first(i)-1 +indexoffset(::Colon) = 0 + +@propagate_inbounds function setindex!(B::BitArray, X::AbstractArray, J0::Union{Colon,UnitRange{Int}}) + _setindex!(IndexStyle(B), B, X, to_indices(B, (J0,))[1]) +end + +# Assigning an array of bools is more complicated, but we can still do some +# work on chunks by combining X and I 64 bits at a time to improve perf by ~40% +@inline function setindex!(B::BitArray, X::AbstractArray, I::BitArray) + @boundscheck checkbounds(B, I) + _unsafe_setindex!(B, X, I) +end +function _unsafe_setindex!(B::BitArray, X::AbstractArray, I::BitArray) + Bc = B.chunks + Ic = I.chunks + length(Bc) == length(Ic) || throw_boundserror(B, I) + lc = length(Bc) + lx = length(X) + last_chunk_len = _mod64(length(B)-1)+1 + + c = 1 + for i = 1:lc + @inbounds Imsk = Ic[i] + @inbounds C = Bc[i] + u = UInt64(1) + for j = 1:(i < lc ? 64 : last_chunk_len) + if Imsk & u != 0 + lx < c && throw_setindex_mismatch(X, c) + @inbounds x = convert(Bool, X[c]) + C = ifelse(x, C | u, C & ~u) + c += 1 + end + u <<= 1 + end + @inbounds Bc[i] = C + end + if length(X) != c-1 + throw_setindex_mismatch(X, c-1) + end + return B +end + +## Dequeue functionality ## + +function push!(B::BitVector, item) + # convert first so we don't grow the bitarray if the assignment won't work + item = convert(Bool, item) + + Bc = B.chunks + + l = _mod64(length(B)) + if l == 0 + _growend!(Bc, 1) + Bc[end] = UInt64(0) + end + B.len += 1 + if item + B[end] = true + end + return B +end + +function append!(B::BitVector, items::BitVector) + n0 = length(B) + n1 = length(items) + n1 == 0 && return B + Bc = B.chunks + k0 = length(Bc) + k1 = num_bit_chunks(n0 + n1) + if k1 > k0 + _growend!(Bc, k1 - k0) + Bc[end] = UInt64(0) + end + B.len += n1 + copy_chunks!(Bc, n0+1, items.chunks, 1, n1) + return B +end + +append!(B::BitVector, items) = append!(B, BitArray(items)) +append!(A::Vector{Bool}, items::BitVector) = append!(A, Array(items)) + +function prepend!(B::BitVector, items::BitVector) + n0 = length(B) + n1 = length(items) + n1 == 0 && return B + Bc = B.chunks + k0 = length(Bc) + k1 = num_bit_chunks(n0 + n1) + if k1 > k0 + _growend!(Bc, k1 - k0) + Bc[end] = UInt64(0) + end + B.len += n1 + copy_chunks!(Bc, 1 + n1, Bc, 1, n0) + copy_chunks!(Bc, 1, items.chunks, 1, n1) + return B +end + +prepend!(B::BitVector, items) = prepend!(B, BitArray(items)) +prepend!(A::Vector{Bool}, items::BitVector) = prepend!(A, Array(items)) + +function sizehint!(B::BitVector, sz::Integer) + ccall(:jl_array_sizehint, Cvoid, (Any, UInt), B.chunks, num_bit_chunks(sz)) + return B +end + +function resize!(B::BitVector, n::Integer) + n0 = length(B) + n == n0 && return B + n >= 0 || throw(BoundsError(B, n)) + if n < n0 + deleteat!(B, n+1:n0) + return B + end + Bc = B.chunks + k0 = length(Bc) + k1 = num_bit_chunks(Int(n)) + if k1 > k0 + _growend!(Bc, k1 - k0) + Bc[end] = UInt64(0) + end + B.len = n + return B +end + +function pop!(B::BitVector) + isempty(B) && throw(ArgumentError("argument must not be empty")) + item = B[end] + B[end] = false + + l = _mod64(length(B)) + l == 1 && _deleteend!(B.chunks, 1) + B.len -= 1 + + return item +end + +function pushfirst!(B::BitVector, item) + item = convert(Bool, item) + + Bc = B.chunks + + l = _mod64(length(B)) + if l == 0 + _growend!(Bc, 1) + Bc[end] = UInt64(0) + end + B.len += 1 + if B.len == 1 + Bc[1] = item + return B + end + for i = length(Bc) : -1 : 2 + Bc[i] = (Bc[i] << 1) | (Bc[i-1] >>> 63) + end + Bc[1] = UInt64(item) | (Bc[1] << 1) + return B +end + +function popfirst!(B::BitVector) + isempty(B) && throw(ArgumentError("argument must not be empty")) + @inbounds begin + item = B[1] + + Bc = B.chunks + + for i = 1 : length(Bc) - 1 + Bc[i] = (Bc[i] >>> 1) | (Bc[i+1] << 63) + end + + l = _mod64(length(B)) + if l == 1 + _deleteend!(Bc, 1) + else + Bc[end] >>>= 1 + end + B.len -= 1 + end + + return item +end + +function insert!(B::BitVector, i::Integer, item) + n = length(B) + 1 <= i <= n+1 || throw(BoundsError(B, i)) + item = convert(Bool, item) + + Bc = B.chunks + + k, j = get_chunks_id(i) + + l = _mod64(length(B)) + if l == 0 + _growend!(Bc, 1) + Bc[end] = UInt64(0) + end + B.len += 1 + + for t = length(Bc) : -1 : k + 1 + Bc[t] = (Bc[t] << 1) | (Bc[t - 1] >>> 63) + end + + msk_aft = (_msk64 << j) + msk_bef = ~msk_aft + Bc[k] = (msk_bef & Bc[k]) | ((msk_aft & Bc[k]) << 1) + B[i] = item + B +end + +function _deleteat!(B::BitVector, i::Integer) + k, j = get_chunks_id(i) + + msk_bef = _msk64 >>> (63 - j) + msk_aft = ~msk_bef + msk_bef >>>= 1 + + Bc = B.chunks + + @inbounds begin + Bc[k] = (msk_bef & Bc[k]) | ((msk_aft & Bc[k]) >> 1) + if length(Bc) > k + Bc[k] |= (Bc[k + 1] << 63) + end + + for t = k + 1 : length(Bc) - 1 + Bc[t] = (Bc[t] >>> 1) | (Bc[t + 1] << 63) + end + + l = _mod64(length(B)) + + if l == 1 + _deleteend!(Bc, 1) + elseif length(Bc) > k + Bc[end] >>>= 1 + end + end + + B.len -= 1 + + return B +end + +function deleteat!(B::BitVector, i::Integer) + n = length(B) + 1 <= i <= n || throw(BoundsError(B, i)) + + return _deleteat!(B, i) +end + +function deleteat!(B::BitVector, r::UnitRange{Int}) + n = length(B) + i_f = first(r) + i_l = last(r) + 1 <= i_f || throw(BoundsError(B, i_f)) + i_l <= n || throw(BoundsError(B, n+1)) + + Bc = B.chunks + new_l = length(B) - length(r) + delta_k = num_bit_chunks(new_l) - length(Bc) + + copy_chunks!(Bc, i_f, Bc, i_l+1, n-i_l) + + delta_k < 0 && _deleteend!(Bc, -delta_k) + + B.len = new_l + + if new_l > 0 + Bc[end] &= _msk_end(new_l) + end + + return B +end + +function deleteat!(B::BitVector, inds) + n = new_l = length(B) + y = iterate(inds) + y === nothing && return B + n == 0 && throw(BoundsError(B, inds)) + + Bc = B.chunks + + (p, s) = y + q = p+1 + new_l -= 1 + y = iterate(inds, s) + while y !== nothing + (i, s) = y + if !(q <= i <= n) + i < q && throw(ArgumentError("indices must be unique and sorted")) + throw(BoundsError(B, i)) + end + new_l -= 1 + if i > q + copy_chunks!(Bc, p, Bc, q, i-q) + p += i-q + end + q = i+1 + y = iterate(inds, s) + end + + q <= n && copy_chunks!(Bc, p, Bc, q, n-q+1) + + delta_k = num_bit_chunks(new_l) - length(Bc) + delta_k < 0 && _deleteend!(Bc, -delta_k) + + B.len = new_l + + if new_l > 0 + Bc[end] &= _msk_end(new_l) + end + + return B +end + +function splice!(B::BitVector, i::Integer) + n = length(B) + 1 <= i <= n || throw(BoundsError(B, i)) + + v = B[i] # TODO: change to a copy if/when subscripting becomes an ArrayView + _deleteat!(B, i) + return v +end + +const _default_bit_splice = BitVector() + +function splice!(B::BitVector, r::Union{UnitRange{Int}, Integer}, ins::AbstractArray = _default_bit_splice) + n = length(B) + i_f = first(r) + i_l = last(r) + + 1 <= i_f <= n+1 || throw(BoundsError(B, i_f)) + i_l <= n || throw(BoundsError(B, n+1)) + + Bins = convert(BitArray, ins) + + if (i_f > n) + append!(B, Bins) + return BitVector() + end + + v = B[r] # TODO: change to a copy if/when subscripting becomes an ArrayView + + Bc = B.chunks + + lins = length(Bins) + ldel = length(r) + + new_l = length(B) + lins - ldel + delta_k = num_bit_chunks(new_l) - length(Bc) + + delta_k > 0 && _growend!(Bc, delta_k) + + copy_chunks!(Bc, i_f+lins, Bc, i_l+1, n-i_l) + copy_chunks!(Bc, i_f, Bins.chunks, 1, lins) + + delta_k < 0 && _deleteend!(Bc, -delta_k) + + B.len = new_l + + if new_l > 0 + Bc[end] &= _msk_end(new_l) + end + + return v +end + +function splice!(B::BitVector, r::Union{UnitRange{Int}, Integer}, ins) + Bins = BitVector(undef, length(ins)) + i = 1 + for x in ins + Bins[i] = Bool(x) + i += 1 + end + return splice!(B, r, Bins) +end + + +function empty!(B::BitVector) + _deleteend!(B.chunks, length(B.chunks)) + B.len = 0 + return B +end + +## Unary operators ## + +function (-)(B::BitArray) + A = zeros(Int, size(B)) + l = length(B) + l == 0 && return A + Bc = B.chunks + ind = 1 + for i = 1:length(Bc)-1 + u = UInt64(1) + c = Bc[i] + for j = 1:64 + if c & u != 0 + A[ind] = -1 + end + ind += 1 + u <<= 1 + end + end + u = UInt64(1) + c = Bc[end] + for j = 0:_mod64(l-1) + if c & u != 0 + A[ind] = -1 + end + ind += 1 + u <<= 1 + end + return A +end + +## Binary arithmetic operators ## + +for f in (:+, :-) + @eval function ($f)(A::BitArray, B::BitArray) + r = Array{Int}(undef, promote_shape(size(A), size(B))) + ay, by = iterate(A), iterate(B) + ri = 1 + # promote_shape guarantees that A and B have the + # same iteration space + while ay !== nothing + @inbounds r[ri] = ($f)(ay[1], by[1]) + ri += 1 + ay, by = iterate(A, ay[2]), iterate(B, by[2]) + end + return r + end +end + +for f in (:/, :\) + @eval begin + ($f)(A::Union{BitMatrix,BitVector}, B::Union{BitMatrix,BitVector}) = ($f)(Array(A), Array(B)) + end +end +(/)(B::BitArray, x::Number) = (/)(Array(B), x) +(/)(x::Number, B::BitArray) = (/)(x, Array(B)) + +## promotion to complex ## + +# TODO? + +## comparison operators ## + +function (==)(A::BitArray, B::BitArray) + size(A) != size(B) && return false + return A.chunks == B.chunks +end + + +## Data movement ## + +# TODO some of this could be optimized + +function reverse(A::BitArray; dims::Integer) + nd = ndims(A); d = dims + 1 ≤ d ≤ nd || throw(ArgumentError("dimension $d is not 1 ≤ $d ≤ $nd")) + sd = size(A, d) + sd == 1 && return copy(A) + + B = similar(A) + + nnd = 0 + for i = 1:nd + nnd += Int(size(A,i)==1 || i==d) + end + if nnd == nd + # reverse along the only non-singleton dimension + for i = 1:sd + B[i] = A[sd+1-i] + end + return B + end + + d_in = size(A) + leading = d_in[1:(d-1)] + M = prod(leading) + N = length(A) + stride = M * sd + + if M == 1 + for j = 0:stride:(N-stride) + for i = 1:sd + ri = sd+1-i + B[j + ri] = A[j + i] + end + end + else + for i = 1:sd + ri = sd+1-i + for j=0:stride:(N-stride) + offs = j + 1 + (i-1)*M + boffs = j + 1 + (ri-1)*M + copy_chunks!(B.chunks, boffs, A.chunks, offs, M) + end + end + end + return B +end + +function reverse_bits(src::UInt64) + z = src + z = ((z >>> 1) & 0x5555555555555555) | ((z << 1) & 0xaaaaaaaaaaaaaaaa) + z = ((z >>> 2) & 0x3333333333333333) | ((z << 2) & 0xcccccccccccccccc) + z = ((z >>> 4) & 0x0f0f0f0f0f0f0f0f) | ((z << 4) & 0xf0f0f0f0f0f0f0f0) + z = ((z >>> 8) & 0x00ff00ff00ff00ff) | ((z << 8) & 0xff00ff00ff00ff00) + z = ((z >>> 16) & 0x0000ffff0000ffff) | ((z << 16) & 0xffff0000ffff0000) + return ((z >>> 32) & 0x00000000ffffffff) | ((z << 32) & 0xffffffff00000000) +end + +function reverse!(B::BitVector) + # Basic idea: each chunk is divided into two blocks of size k = n % 64, and + # h = 64 - k. Walk from either end (with indices i and j) reversing chunks + # and separately ORing their two blocks into place. + # + # chunk 3 chunk 2 chunk 1 + # ┌───────────────┬───────┐┌───────────────┬───────┐┌───────────────┬───────┐ + # │000000000000000│ E ││ D │ C ││ B │ A │ + # └───────────────┴───────┘└───────────────┴───────┘└───────────────┴───────┘ + # k h k h k + # yielding; + # ┌───────────────┬───────┐┌───────────────┬───────┐┌───────────────┬───────┐ + # │000000000000000│ A' ││ B' │ C' ││ D' │ E' │ + # └───────────────┴───────┘└───────────────┴───────┘└───────────────┴───────┘ + + n = length(B) + n == 0 && return B + + k = _mod64(n+63) + 1 + h = 64 - k + + i, j = 0, length(B.chunks) + u = UInt64(0) + v = reverse_bits(B.chunks[j]) + B.chunks[j] = 0 + @inbounds while true + i += 1 + if i == j + break + end + u = reverse_bits(B.chunks[i]) + B.chunks[i] = 0 + B.chunks[j] |= u >>> h + B.chunks[i] |= v >>> h + + j -= 1 + if i == j + break + end + v = reverse_bits(B.chunks[j]) + B.chunks[j] = 0 + B.chunks[i] |= v << k + B.chunks[j] |= u << k + end + + if isodd(length(B.chunks)) + B.chunks[i] |= v >>> h + else + B.chunks[i] |= u << k + end + + return B +end + +reverse(v::BitVector) = reverse!(copy(v)) + + +function (<<)(B::BitVector, i::UInt) + n = length(B) + i == 0 && return copy(B) + A = falses(n) + i < n && copy_chunks!(A.chunks, 1, B.chunks, i+1, n-i) + return A +end + +function (>>>)(B::BitVector, i::UInt) + n = length(B) + i == 0 && return copy(B) + A = falses(n) + i < n && copy_chunks!(A.chunks, i+1, B.chunks, 1, n-i) + return A +end + +""" + >>(B::BitVector, n) -> BitVector + +Right bit shift operator, `B >> n`. For `n >= 0`, the result is `B` +with elements shifted `n` positions forward, filling with `false` +values. If `n < 0`, elements are shifted backwards. Equivalent to +`B << -n`. + +# Examples +```jldoctest +julia> B = BitVector([true, false, true, false, false]) +5-element BitArray{1}: + 1 + 0 + 1 + 0 + 0 + +julia> B >> 1 +5-element BitArray{1}: + 0 + 1 + 0 + 1 + 0 + +julia> B >> -1 +5-element BitArray{1}: + 0 + 1 + 0 + 0 + 0 +``` +""" +(>>)(B::BitVector, i::Union{Int, UInt}) = B >>> i + +# signed integer version of shift operators with handling of negative values +""" + <<(B::BitVector, n) -> BitVector + +Left bit shift operator, `B << n`. For `n >= 0`, the result is `B` +with elements shifted `n` positions backwards, filling with `false` +values. If `n < 0`, elements are shifted forwards. Equivalent to +`B >> -n`. + +# Examples +```jldoctest +julia> B = BitVector([true, false, true, false, false]) +5-element BitArray{1}: + 1 + 0 + 1 + 0 + 0 + +julia> B << 1 +5-element BitArray{1}: + 0 + 1 + 0 + 0 + 0 + +julia> B << -1 +5-element BitArray{1}: + 0 + 1 + 0 + 1 + 0 +``` +""" +(<<)(B::BitVector, i::Int) = (i >=0 ? B << unsigned(i) : B >> unsigned(-i)) + +""" + >>>(B::BitVector, n) -> BitVector + +Unsigned right bitshift operator, `B >>> n`. Equivalent to `B >> n`. See [`>>`](@ref) for +details and examples. +""" +(>>>)(B::BitVector, i::Int) = (i >=0 ? B >> unsigned(i) : B << unsigned(-i)) + +function circshift!(dest::BitVector, src::BitVector, i::Integer) + length(dest) == length(src) || throw(ArgumentError("destination and source should be of same size")) + n = length(dest) + i %= n + i == 0 && return (src === dest ? src : copyto!(dest, src)) + Bc = (src === dest ? copy(src.chunks) : src.chunks) + if i > 0 # right + copy_chunks!(dest.chunks, i+1, Bc, 1, n-i) + copy_chunks!(dest.chunks, 1, Bc, n-i+1, i) + else # left + i = -i + copy_chunks!(dest.chunks, 1, Bc, i+1, n-i) + copy_chunks!(dest.chunks, n-i+1, Bc, 1, i) + end + return dest +end + +circshift!(B::BitVector, i::Integer) = circshift!(B, B, i) + +## count & find ## + +function bitcount(Bc::Vector{UInt64}) + n = 0 + @inbounds for i = 1:length(Bc) + n += count_ones(Bc[i]) + end + return n +end + +count(B::BitArray) = bitcount(B.chunks) + +function unsafe_bitfindnext(Bc::Vector{UInt64}, start::Integer) + chunk_start = _div64(start-1)+1 + within_chunk_start = _mod64(start-1) + mask = _msk64 << within_chunk_start + + @inbounds begin + if Bc[chunk_start] & mask != 0 + return (chunk_start-1) << 6 + trailing_zeros(Bc[chunk_start] & mask) + 1 + end + + for i = chunk_start+1:length(Bc) + if Bc[i] != 0 + return (i-1) << 6 + trailing_zeros(Bc[i]) + 1 + end + end + end + return nothing +end + +# returns the index of the next true element, or nothing if all false +function findnext(B::BitArray, start::Integer) + start > 0 || throw(BoundsError(B, start)) + start > length(B) && return nothing + unsafe_bitfindnext(B.chunks, start) +end + +#findfirst(B::BitArray) = findnext(B, 1) ## defined in array.jl + +# aux function: same as findnext(~B, start), but performed without temporaries +function findnextnot(B::BitArray, start::Integer) + start > 0 || throw(BoundsError(B, start)) + start > length(B) && return nothing + + Bc = B.chunks + l = length(Bc) + l == 0 && return nothing + + chunk_start = _div64(start-1)+1 + within_chunk_start = _mod64(start-1) + mask = ~(_msk64 << within_chunk_start) + + @inbounds if chunk_start < l + if Bc[chunk_start] | mask != _msk64 + return (chunk_start-1) << 6 + trailing_ones(Bc[chunk_start] | mask) + 1 + end + for i = chunk_start+1:l-1 + if Bc[i] != _msk64 + return (i-1) << 6 + trailing_ones(Bc[i]) + 1 + end + end + if Bc[l] != _msk_end(B) + return (l-1) << 6 + trailing_ones(Bc[l]) + 1 + end + elseif Bc[l] | mask != _msk_end(B) + return (l-1) << 6 + trailing_ones(Bc[l] | mask) + 1 + end + return nothing +end +findfirstnot(B::BitArray) = findnextnot(B,1) + +# returns the index of the first matching element +function findnext(pred::Fix2{<:Union{typeof(isequal),typeof(==)},Bool}, + B::BitArray, start::Integer) + v = pred.x + v == false && return findnextnot(B, start) + v == true && return findnext(B, start) + return nothing +end +#findfirst(B::BitArray, v) = findnext(B, 1, v) ## defined in array.jl + +# returns the index of the first element for which the function returns true +function findnext(testf::Function, B::BitArray, start::Integer) + f0::Bool = testf(false) + f1::Bool = testf(true) + !f0 && f1 && return findnext(B, start) + f0 && !f1 && return findnextnot(B, start) + + start > 0 || throw(BoundsError(B, start)) + start > length(B) && return nothing + f0 && f1 && return Int(start) + return nothing # last case: !f0 && !f1 +end +#findfirst(testf::Function, B::BitArray) = findnext(testf, B, 1) ## defined in array.jl + +function unsafe_bitfindprev(Bc::Vector{UInt64}, start::Integer) + chunk_start = _div64(start-1)+1 + mask = _msk_end(start) + + @inbounds begin + if Bc[chunk_start] & mask != 0 + return (chunk_start-1) << 6 + (64 - leading_zeros(Bc[chunk_start] & mask)) + end + + for i = (chunk_start-1):-1:1 + if Bc[i] != 0 + return (i-1) << 6 + (64 - leading_zeros(Bc[i])) + end + end + end + return nothing +end + +# returns the index of the previous true element, or nothing if all false +function findprev(B::BitArray, start::Integer) + start > 0 || return nothing + start > length(B) && throw(BoundsError(B, start)) + unsafe_bitfindprev(B.chunks, start) +end + +function findprevnot(B::BitArray, start::Integer) + start > 0 || return nothing + start > length(B) && throw(BoundsError(B, start)) + + Bc = B.chunks + + chunk_start = _div64(start-1)+1 + mask = ~_msk_end(start) + + @inbounds begin + if Bc[chunk_start] | mask != _msk64 + return (chunk_start-1) << 6 + (64 - leading_ones(Bc[chunk_start] | mask)) + end + + for i = chunk_start-1:-1:1 + if Bc[i] != _msk64 + return (i-1) << 6 + (64 - leading_ones(Bc[i])) + end + end + end + return nothing +end +findlastnot(B::BitArray) = findprevnot(B, length(B)) + +# returns the index of the previous matching element +function findprev(pred::Fix2{<:Union{typeof(isequal),typeof(==)},Bool}, + B::BitArray, start::Integer) + v = pred.x + v == false && return findprevnot(B, start) + v == true && return findprev(B, start) + return nothing +end +#findlast(B::BitArray, v) = findprev(B, 1, v) ## defined in array.jl + +# returns the index of the previous element for which the function returns true +function findprev(testf::Function, B::BitArray, start::Integer) + f0::Bool = testf(false) + f1::Bool = testf(true) + !f0 && f1 && return findprev(B, start) + f0 && !f1 && return findprevnot(B, start) + + start > 0 || return nothing + start > length(B) && throw(BoundsError(B, start)) + f0 && f1 && return Int(start) + return nothing # last case: !f0 && !f1 +end +#findlast(testf::Function, B::BitArray) = findprev(testf, B, 1) ## defined in array.jl + +function findmax(a::BitArray) + isempty(a) && throw(ArgumentError("BitArray must be non-empty")) + m, mi = false, 1 + ti = 1 + ac = a.chunks + for i = 1:length(ac) + @inbounds k = trailing_zeros(ac[i]) + ti += k + k == 64 || return (true, @inbounds keys(a)[ti]) + end + return m, @inbounds keys(a)[mi] +end + +function findmin(a::BitArray) + isempty(a) && throw(ArgumentError("BitArray must be non-empty")) + m, mi = true, 1 + ti = 1 + ac = a.chunks + for i = 1:length(ac)-1 + @inbounds k = trailing_ones(ac[i]) + ti += k + k == 64 || return (false, @inbounds keys(a)[ti]) + end + l = Base._mod64(length(a)-1) + 1 + @inbounds k = trailing_ones(ac[end] & Base._msk_end(l)) + ti += k + k == l || return (false, @inbounds keys(a)[ti]) + return (m, @inbounds keys(a)[mi]) +end + +# findall helper functions +# Generic case (>2 dimensions) +function allindices!(I, B::BitArray) + ind = first(keys(B)) + for k = 1:length(B) + I[k] = ind + ind = nextind(B, ind) + end +end + +# Optimized case for vector +function allindices!(I, B::BitVector) + I[:] .= 1:length(B) +end + +# Optimized case for matrix +function allindices!(I, B::BitMatrix) + k = 1 + for c = 1:size(B,2), r = 1:size(B,1) + I[k] = CartesianIndex(r, c) + k += 1 + end +end + +@inline _overflowind(i1, irest::Tuple{}, size) = (i1, irest) +@inline function _overflowind(i1, irest, size) + i2 = irest[1] + while i1 > size[1] + i1 -= size[1] + i2 += 1 + end + i2, irest = _overflowind(i2, tail(irest), tail(size)) + return (i1, (i2, irest...)) +end + +@inline _toind(i1, irest::Tuple{}) = i1 +@inline _toind(i1, irest) = CartesianIndex(i1, irest...) + +function findall(B::BitArray) + nnzB = count(B) + I = Vector{eltype(keys(B))}(undef, nnzB) + nnzB == 0 && return I + nnzB == length(B) && (allindices!(I, B); return I) + Bc = B.chunks + Bs = size(B) + Bi = i1 = i = 1 + irest = ntuple(one, ndims(B) - 1) + c = Bc[1] + @inbounds while true + while c == 0 + Bi == length(Bc) && return I + i1 += 64 + Bi += 1 + c = Bc[Bi] + end + + tz = trailing_zeros(c) + c = _blsr(c) + + i1, irest = _overflowind(i1 + tz, irest, Bs) + I[i] = _toind(i1, irest) + i += 1 + i1 -= tz + end +end + +# For performance +findall(::typeof(!iszero), B::BitArray) = findall(B) + +## Reductions ## + +_sum(A::BitArray, dims) = reduce(+, A, dims=dims) +_sum(B::BitArray, ::Colon) = count(B) + +function all(B::BitArray) + isempty(B) && return true + Bc = B.chunks + @inbounds begin + for i = 1:length(Bc)-1 + Bc[i] == _msk64 || return false + end + Bc[end] == _msk_end(B) || return false + end + return true +end + +function any(B::BitArray) + isempty(B) && return false + Bc = B.chunks + @inbounds begin + for i = 1:length(Bc) + Bc[i] == 0 || return true + end + end + return false +end + +minimum(B::BitArray) = isempty(B) ? throw(ArgumentError("argument must be non-empty")) : all(B) +maximum(B::BitArray) = isempty(B) ? throw(ArgumentError("argument must be non-empty")) : any(B) + +## map over bitarrays ## + +# Specializing map is even more important for bitarrays than it is for generic +# arrays since there can be a 64x speedup by working at the level of Int64 +# instead of looping bit-by-bit. + +map(::Union{typeof(~), typeof(!)}, A::BitArray) = bit_map!(~, similar(A), A) +map(::typeof(zero), A::BitArray) = fill!(similar(A), false) +map(::typeof(one), A::BitArray) = fill!(similar(A), true) +map(::typeof(identity), A::BitArray) = copy(A) + +map!(::Union{typeof(~), typeof(!)}, dest::BitArray, A::BitArray) = bit_map!(~, dest, A) +map!(::typeof(zero), dest::BitArray, A::BitArray) = fill!(dest, false) +map!(::typeof(one), dest::BitArray, A::BitArray) = fill!(dest, true) +map!(::typeof(identity), dest::BitArray, A::BitArray) = copyto!(dest, A) + +for (T, f) in ((:(Union{typeof(&), typeof(*), typeof(min)}), :(&)), + (:(Union{typeof(|), typeof(max)}), :(|)), + (:(Union{typeof(xor), typeof(!=)}), :xor), + (:(Union{typeof(>=), typeof(^)}), :((p, q) -> p | ~q)), + (:(typeof(<=)), :((p, q) -> ~p | q)), + (:(typeof(==)), :((p, q) -> ~xor(p, q))), + (:(typeof(<)), :((p, q) -> ~p & q)), + (:(typeof(>)), :((p, q) -> p & ~q))) + @eval map(::$T, A::BitArray, B::BitArray) = bit_map!($f, similar(A), A, B) + @eval map!(::$T, dest::BitArray, A::BitArray, B::BitArray) = bit_map!($f, dest, A, B) +end + +# If we were able to specialize the function to a known bitwise operation, +# map across the chunks. Otherwise, fall-back to the AbstractArray method that +# iterates bit-by-bit. +function bit_map!(f::F, dest::BitArray, A::BitArray) where F + size(A) == size(dest) || throw(DimensionMismatch("sizes of dest and A must match")) + isempty(A) && return dest + destc = dest.chunks + Ac = A.chunks + for i = 1:(length(Ac)-1) + destc[i] = f(Ac[i]) + end + destc[end] = f(Ac[end]) & _msk_end(A) + dest +end +function bit_map!(f::F, dest::BitArray, A::BitArray, B::BitArray) where F + size(A) == size(B) == size(dest) || throw(DimensionMismatch("sizes of dest, A, and B must all match")) + isempty(A) && return dest + destc = dest.chunks + Ac = A.chunks + Bc = B.chunks + for i = 1:(length(Ac)-1) + destc[i] = f(Ac[i], Bc[i]) + end + destc[end] = f(Ac[end], Bc[end]) & _msk_end(A) + dest +end + +## Filter ## + +function filter(f, Bs::BitArray) + boolmap::Array{Bool} = map(f, Bs) + Bs[boolmap] +end + + +## Concatenation ## + +function hcat(B::BitVector...) + height = length(B[1]) + for j = 2:length(B) + length(B[j]) == height || + throw(DimensionMismatch("dimensions must match")) + end + M = BitMatrix(undef, height, length(B)) + for j = 1:length(B) + copy_chunks!(M.chunks, (height*(j-1))+1, B[j].chunks, 1, height) + end + return M +end + +function vcat(V::BitVector...) + n = 0 + for Vk in V + n += length(Vk) + end + B = BitVector(undef, n) + j = 1 + for Vk in V + copy_chunks!(B.chunks, j, Vk.chunks, 1, length(Vk)) + j += length(Vk) + end + return B +end + +function hcat(A::Union{BitMatrix,BitVector}...) + nargs = length(A) + nrows = size(A[1], 1) + ncols = 0 + dense = true + for j = 1:nargs + Aj = A[j] + nd = ndims(Aj) + ncols += (nd==2 ? size(Aj,2) : 1) + size(Aj, 1) == nrows || + throw(DimensionMismatch("row lengths must match")) + end + + B = BitMatrix(undef, nrows, ncols) + + pos = 1 + for k = 1:nargs + Ak = A[k] + n = length(Ak) + copy_chunks!(B.chunks, pos, Ak.chunks, 1, n) + pos += n + end + return B +end + +function vcat(A::BitMatrix...) + nargs = length(A) + nrows = sum(a->size(a, 1), A)::Int + ncols = size(A[1], 2) + for j = 2:nargs + size(A[j], 2) == ncols || + throw(DimensionMismatch("column lengths must match")) + end + B = BitMatrix(undef, nrows, ncols) + Bc = B.chunks + nrowsA = [size(a, 1) for a in A] + Ac = [a.chunks for a in A] + pos_d = 1 + pos_s = fill(1, nargs) + for j = 1:ncols, k = 1:nargs + copy_chunks!(Bc, pos_d, Ac[k], pos_s[k], nrowsA[k]) + pos_s[k] += nrowsA[k] + pos_d += nrowsA[k] + end + return B +end + +# general case, specialized for BitArrays and Integers +function _cat(dims::Integer, X::Union{BitArray, Bool}...) + catdims = dims2cat(dims) + shape = cat_shape(catdims, (), map(cat_size, X)...) + A = falses(shape) + return __cat(A, shape, catdims, X...) +end + +# hvcat -> use fallbacks in abstractarray.jl + + +# BitArray I/O + +write(s::IO, B::BitArray) = write(s, B.chunks) +function read!(s::IO, B::BitArray) + n = length(B) + Bc = B.chunks + nc = length(read!(s, Bc)) + if length(Bc) > 0 && Bc[end] & _msk_end(n) ≠ Bc[end] + Bc[end] &= _msk_end(n) # ensure that the BitArray is not broken + throw(DimensionMismatch("read mismatch, found non-zero bits after BitArray length")) + end + return B +end + +sizeof(B::BitArray) = sizeof(B.chunks) diff --git a/base/bitset.jl b/base/bitset.jl new file mode 100644 index 0000000..1700383 --- /dev/null +++ b/base/bitset.jl @@ -0,0 +1,419 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +const Bits = Vector{UInt64} +const CHK0 = zero(UInt64) +const NO_OFFSET = Int === Int64 ? -one(Int) << 60 : -one(Int) << 29 +# + NO_OFFSET must be small enough to stay < 0 when added with any offset. +# An offset is in the range -2^57:2^57 (64-bits architectures) +# or -2^26:2^26 (32-bits architectures) +# + when the offset is NO_OFFSET, the bits field *must* be empty +# + NO_OFFSET could be made to be > 0, but a negative one allows +# a small optimization in the in(x, ::BitSet) method + +mutable struct BitSet <: AbstractSet{Int} + bits::Vector{UInt64} + # 1st stored Int equals 64*offset + offset::Int + + BitSet() = new(sizehint!(zeros(UInt64, 0), 4), NO_OFFSET) +end + +""" + BitSet([itr]) + +Construct a sorted set of `Int`s generated by the given iterable object, or an +empty set. Implemented as a bit string, and therefore designed for dense integer sets. +If the set will be sparse (for example, holding a few +very large integers), use [`Set`](@ref) instead. +""" +BitSet(itr) = union!(BitSet(), itr) + +# Special implementation for BitSet, which lacks a fast `length` method. +function union!(s::BitSet, itr) + for x in itr + push!(s, x) + end + return s +end + +@inline intoffset(s::BitSet) = s.offset << 6 + +eltype(::Type{BitSet}) = Int + +empty(s::BitSet, ::Type{Int}=Int) = BitSet() +emptymutable(s::BitSet, ::Type{Int}=Int) = BitSet() + +copy(s1::BitSet) = copy!(BitSet(), s1) +copymutable(s::BitSet) = copy(s) + +function copy!(dest::BitSet, src::BitSet) + resize!(dest.bits, length(src.bits)) + copyto!(dest.bits, src.bits) + dest.offset = src.offset + dest +end + +sizehint!(s::BitSet, n::Integer) = (sizehint!(s.bits, (n+63) >> 6); s) + +function _bits_getindex(b::Bits, n::Int, offset::Int) + ci = _div64(n) - offset + 1 + 1 <= ci <= length(b) || return false + @inbounds r = (b[ci] & (one(UInt64) << _mod64(n))) != 0 + r +end + +function _bits_findnext(b::Bits, start::Int) + # start is 0-based + # @assert start >= 0 + _div64(start) + 1 > length(b) && return -1 + ind = unsafe_bitfindnext(b, start+1) + ind === nothing ? -1 : ind - 1 +end + +function _bits_findprev(b::Bits, start::Int) + # start is 0-based + # @assert start <= 64 * length(b) - 1 + start >= 0 || return -1 + ind = unsafe_bitfindprev(b, start+1) + ind === nothing ? -1 : ind - 1 +end + +# An internal function for setting the inclusion bit for a given integer +@inline function _setint!(s::BitSet, idx::Int, b::Bool) + cidx = _div64(idx) + len = length(s.bits) + diff = cidx - s.offset + if diff >= len + b || return s # setting a bit to zero outside the set's bits is a no-op + + # we put the following test within one of the two branches, + # with the NO_OFFSET trick, to avoid having to perform it at + # each and every call to _setint! + if s.offset == NO_OFFSET # initialize the offset + # we assume isempty(s.bits) + s.offset = cidx + diff = 0 + end + _growend0!(s.bits, diff - len + 1) + elseif diff < 0 + b || return s + _growbeg0!(s.bits, -diff) + s.offset += diff + diff = 0 + end + _unsafe_bitsetindex!(s.bits, b, diff+1, _mod64(idx)) + s +end + + +# An internal function to resize a Bits object and ensure the newly allocated +# elements are zeroed (will become unnecessary if this behavior changes) +@inline function _growend0!(b::Bits, nchunks::Int) + len = length(b) + _growend!(b, nchunks) + for i in len+1:length(b) + @inbounds b[i] = CHK0 # resize! gives dirty memory + end +end + +@inline function _growbeg0!(b::Bits, nchunks::Int) + _growbeg!(b, nchunks) + for i in 1:nchunks + @inbounds b[i] = CHK0 + end +end + +function union!(s::BitSet, r::AbstractUnitRange{<:Integer}) + isempty(r) && return s + a, b = _check_bitset_bounds(first(r)), _check_bitset_bounds(last(r)) + cidxa = _div64(a) + cidxb = _div64(b) + if s.offset == NO_OFFSET + s.offset = cidxa + end + len = length(s.bits) + diffa = cidxa - s.offset + diffb = cidxb - s.offset + + # grow s.bits as necessary + if diffb >= len + _growend!(s.bits, diffb - len + 1) + # we set only some values to CHK0, those which will not be + # fully overwritten (i.e. only or'ed with `|`) + s.bits[end] = CHK0 # end == diffb + 1 + if diffa >= len + s.bits[diffa + 1] = CHK0 + end + end + if diffa < 0 + _growbeg!(s.bits, -diffa) + s.bits[1] = CHK0 + if diffb < 0 + s.bits[diffb - diffa + 1] = CHK0 + end + s.offset = cidxa # s.offset += diffa + diffb -= diffa + diffa = 0 + end + + # update s.bits + i = _mod64(a) + j = _mod64(b) + @inbounds if diffa == diffb + s.bits[diffa + 1] |= (((~CHK0) >> i) << (i+63-j)) >> (63-j) + else + s.bits[diffa + 1] |= ((~CHK0) >> i) << i + s.bits[diffb + 1] |= (~CHK0 << (63-j)) >> (63-j) + for n = diffa+1:diffb-1 + s.bits[n+1] = ~CHK0 + end + end + s +end + +function _matched_map!(f, s1::BitSet, s2::BitSet) + left_false_is_false = f(false, false) == f(false, true) == false + right_false_is_false = f(false, false) == f(true, false) == false + + # we must first handle the NO_OFFSET case; we could test for + # isempty(s1) but it can be costly, so the user has to call + # empty!(s1) herself before-hand to re-initialize to NO_OFFSET + if s1.offset == NO_OFFSET + return left_false_is_false ? s1 : copy!(s1, s2) + elseif s2.offset == NO_OFFSET + return right_false_is_false ? empty!(s1) : s1 + end + s1.offset = _matched_map!(f, s1.bits, s1.offset, s2.bits, s2.offset, + left_false_is_false, right_false_is_false) + s1 +end + +# An internal function that takes a pure function `f` and maps across two BitArrays +# allowing the lengths and offsets to be different and altering b1 with the result +# WARNING: the assumptions written in the else clauses must hold +function _matched_map!(f, a1::Bits, b1::Int, a2::Bits, b2::Int, + left_false_is_false::Bool, right_false_is_false::Bool) + l1, l2 = length(a1), length(a2) + bdiff = b2 - b1 + e1, e2 = l1+b1, l2+b2 + ediff = e2 - e1 + + # map! over the common indices + @inbounds for i = max(1, 1+bdiff):min(l1, l2+bdiff) + a1[i] = f(a1[i], a2[i-bdiff]) + end + + if ediff > 0 + if left_false_is_false + # We don't need to worry about the trailing bits — they're all false + else # @assert f(false, x) == x + _growend!(a1, ediff) + # if a1 and a2 are not overlapping, we infer implied "false" values from a2 + for outer l1 = l1+1:bdiff + @inbounds a1[l1] = CHK0 + end + # update ediff in case l1 was updated + ediff = e2 - l1 - b1 + # copy actual chunks from a2 + unsafe_copyto!(a1, l1+1, a2, l2+1-ediff, ediff) + l1 = length(a1) + end + elseif ediff < 0 + if right_false_is_false + # We don't need to worry about the trailing bits — they're all false + _deleteend!(a1, min(l1, -ediff)) + # no need to update l1, as if bdiff > 0 (case below), then bdiff will + # be smaller anyway than an updated l1 + else # @assert f(x, false) == x + # We don't need to worry about the trailing bits — they already have the + # correct value + end + end + + if bdiff < 0 + if left_false_is_false + # We don't need to worry about the leading bits — they're all false + else # @assert f(false, x) == x + _growbeg!(a1, -bdiff) + # if a1 and a2 are not overlapping, we infer implied "false" values from a2 + for i = l2+1:-bdiff + @inbounds a1[i] = CHK0 + end + b1 += bdiff # updated return value + + # copy actual chunks from a2 + unsafe_copyto!(a1, 1, a2, 1, min(-bdiff, l2)) + end + elseif bdiff > 0 + if right_false_is_false + # We don't need to worry about the trailing bits — they're all false + _deletebeg!(a1, min(l1, bdiff)) + b1 += bdiff + else # @assert f(x, false) == x + # We don't need to worry about the trailing bits — they already have the + # correct value + end + end + b1 # the new offset +end + + +@noinline _throw_bitset_bounds_err() = + throw(ArgumentError("elements of BitSet must be between typemin(Int) and typemax(Int)")) + +@inline _is_convertible_Int(n) = typemin(Int) <= n <= typemax(Int) + +@inline _check_bitset_bounds(n) = + _is_convertible_Int(n) ? Int(n) : _throw_bitset_bounds_err() + +@inline _check_bitset_bounds(n::Int) = n + +@noinline _throw_keyerror(n) = throw(KeyError(n)) + +@inline push!(s::BitSet, n::Integer) = _setint!(s, _check_bitset_bounds(n), true) + +push!(s::BitSet, ns::Integer...) = (for n in ns; push!(s, n); end; s) + +@inline pop!(s::BitSet) = pop!(s, last(s)) + +@inline function pop!(s::BitSet, n::Integer) + if n in s + delete!(s, n) + n + else + _throw_keyerror(n) + end +end + +@inline function pop!(s::BitSet, n::Integer, default) + if n in s + delete!(s, n) + n + else + default + end +end + +@inline delete!(s::BitSet, n::Int) = _setint!(s, n, false) +@inline delete!(s::BitSet, n::Integer) = _is_convertible_Int(n) ? delete!(s, Int(n)) : s + +popfirst!(s::BitSet) = pop!(s, first(s)) + +function empty!(s::BitSet) + empty!(s.bits) + s.offset = NO_OFFSET + s +end + +isempty(s::BitSet) = _check0(s.bits, 1, length(s.bits)) + +# Mathematical set functions: union!, intersect!, setdiff!, symdiff! + +union(s::BitSet, sets...) = union!(copy(s), sets...) +union!(s1::BitSet, s2::BitSet) = _matched_map!(|, s1, s2) + +intersect(s1::BitSet, s2::BitSet) = + length(s1.bits) < length(s2.bits) ? intersect!(copy(s1), s2) : intersect!(copy(s2), s1) + +intersect!(s1::BitSet, s2::BitSet) = _matched_map!(&, s1, s2) + +setdiff!(s1::BitSet, s2::BitSet) = _matched_map!((p, q) -> p & ~q, s1, s2) + +function symdiff!(s::BitSet, ns) + for x in ns + int_symdiff!(s, x) + end + return s +end + +function int_symdiff!(s::BitSet, n::Integer) + n0 = _check_bitset_bounds(n) + val = !(n0 in s) + _setint!(s, n0, val) + s +end + +symdiff!(s1::BitSet, s2::BitSet) = _matched_map!(xor, s1, s2) + +filter!(f, s::BitSet) = unsafe_filter!(f, s) + +@inline in(n::Int, s::BitSet) = _bits_getindex(s.bits, n, s.offset) +@inline in(n::Integer, s::BitSet) = _is_convertible_Int(n) ? in(Int(n), s) : false + +function iterate(s::BitSet, idx=0) + idx = _bits_findnext(s.bits, idx) + idx == -1 && return nothing + (idx + intoffset(s), idx+1) +end + +@noinline _throw_bitset_notempty_error() = + throw(ArgumentError("collection must be non-empty")) + +function first(s::BitSet) + idx = _bits_findnext(s.bits, 0) + idx == -1 ? _throw_bitset_notempty_error() : idx + intoffset(s) +end + +function last(s::BitSet) + idx = _bits_findprev(s.bits, (length(s.bits) << 6) - 1) + idx == -1 ? _throw_bitset_notempty_error() : idx + intoffset(s) +end + +length(s::BitSet) = bitcount(s.bits) # = mapreduce(count_ones, +, s.bits; init=0) + +function show(io::IO, s::BitSet) + print(io, "BitSet([") + first = true + for n in s + !first && print(io, ", ") + print(io, n) + first = false + end + print(io, "])") +end + +function _check0(a::Vector{UInt64}, b::Int, e::Int) + @inbounds for i in b:e + a[i] == CHK0 || return false + end + true +end + +function ==(s1::BitSet, s2::BitSet) + # Swap so s1 has always the smallest offset + if s1.offset > s2.offset + s1, s2 = s2, s1 + end + a1 = s1.bits + a2 = s2.bits + b1, b2 = s1.offset, s2.offset + l1, l2 = length(a1), length(a2) + e1 = l1+b1 + overlap0 = max(0, e1 - b2) + included = overlap0 >= l2 # whether a2's indices are included in a1's + overlap = included ? l2 : overlap0 + + # Ensure non-overlap chunks are zero (unlikely) + _check0(a1, 1, l1-overlap0) || return false + if included + _check0(a1, b2-b1+l2+1, l1) || return false + else + _check0(a2, 1+overlap, l2) || return false + end + + # compare overlap values + if overlap > 0 + _memcmp(pointer(a1, b2-b1+1), pointer(a2), overlap<<3) == 0 || return false + end + + return true +end + +issubset(a::BitSet, b::BitSet) = a == intersect(a,b) +⊊(a::BitSet, b::BitSet) = a <= b && a != b + + +minimum(s::BitSet) = first(s) +maximum(s::BitSet) = last(s) +extrema(s::BitSet) = (first(s), last(s)) +issorted(s::BitSet) = true diff --git a/base/bool.jl b/base/bool.jl new file mode 100644 index 0000000..988bf87 --- /dev/null +++ b/base/bool.jl @@ -0,0 +1,118 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# promote Bool to any other numeric type +promote_rule(::Type{Bool}, ::Type{T}) where {T<:Number} = T + +typemin(::Type{Bool}) = false +typemax(::Type{Bool}) = true + +## boolean operations ## + +""" + !(x) + +Boolean not. Implements [three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic), +returning [`missing`](@ref) if `x` is `missing`. + +# Examples +```jldoctest +julia> !true +false + +julia> !false +true + +julia> !missing +missing + +julia> .![true false true] +1×3 BitArray{2}: + 0 1 0 +``` +""" +function !(x::Bool) + ## We need a better heuristic to detect this automatically + @_pure_meta + return not_int(x) +end + +(~)(x::Bool) = !x +(&)(x::Bool, y::Bool) = and_int(x, y) +(|)(x::Bool, y::Bool) = or_int(x, y) + +""" + xor(x, y) + ⊻(x, y) + +Bitwise exclusive or of `x` and `y`. Implements +[three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic), +returning [`missing`](@ref) if one of the arguments is `missing`. + +The infix operation `a ⊻ b` is a synonym for `xor(a,b)`, and +`⊻` can be typed by tab-completing `\\xor` or `\\veebar` in the Julia REPL. + +# Examples +```jldoctest +julia> xor(true, false) +true + +julia> xor(true, true) +false + +julia> xor(true, missing) +missing + +julia> false ⊻ false +false + +julia> [true; true; false] .⊻ [true; false; false] +3-element BitArray{1}: + 0 + 1 + 0 +``` +""" +xor(x::Bool, y::Bool) = (x != y) + +>>(x::Bool, c::UInt) = Int(x) >> c +<<(x::Bool, c::UInt) = Int(x) << c +>>>(x::Bool, c::UInt) = Int(x) >>> c + +signbit(x::Bool) = false +sign(x::Bool) = x +abs(x::Bool) = x +abs2(x::Bool) = x +iszero(x::Bool) = !x +isone(x::Bool) = x + +<(x::Bool, y::Bool) = y&!x +<=(x::Bool, y::Bool) = y|!x + +## do arithmetic as Int ## + ++(x::Bool) = Int(x) +-(x::Bool) = -Int(x) + ++(x::Bool, y::Bool) = Int(x) + Int(y) +-(x::Bool, y::Bool) = Int(x) - Int(y) +*(x::Bool, y::Bool) = x & y +^(x::Bool, y::Bool) = x | !y +^(x::Integer, y::Bool) = ifelse(y, x, one(x)) + +# preserve -0.0 in `false + -0.0` +function +(x::Bool, y::T)::promote_type(Bool,T) where T<:AbstractFloat + return ifelse(x, oneunit(y) + y, y) +end ++(y::AbstractFloat, x::Bool) = x + y + +# make `false` a "strong zero": false*NaN == 0.0 +function *(x::Bool, y::T)::promote_type(Bool,T) where T<:AbstractFloat + return ifelse(x, y, copysign(zero(y), y)) +end +*(y::AbstractFloat, x::Bool) = x * y + +div(x::Bool, y::Bool) = y ? x : throw(DivideError()) +fld(x::Bool, y::Bool) = div(x,y) +cld(x::Bool, y::Bool) = div(x,y) +rem(x::Bool, y::Bool) = y ? false : throw(DivideError()) +mod(x::Bool, y::Bool) = rem(x,y) diff --git a/base/boot.jl b/base/boot.jl new file mode 100644 index 0000000..a9f3356 --- /dev/null +++ b/base/boot.jl @@ -0,0 +1,746 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# commented-out definitions are implemented in C + +#abstract type Any <: Any end +#abstract type Type{T} end + +#abstract type Vararg{T} end + +#mutable struct Symbol +# #opaque +#end + +#mutable struct TypeName +# name::Symbol +#end + +#mutable struct DataType <: Type +# name::TypeName +# super::Type +# parameters::Tuple +# names::Tuple +# types::Tuple +# ctor +# instance +# size::Int32 +# abstract::Bool +# mutable::Bool +# pointerfree::Bool +#end + +#struct Union <: Type +# a +# b +#end + +#mutable struct TypeVar +# name::Symbol +# lb::Type +# ub::Type +#end + +#struct UnionAll +# var::TypeVar +# body +#end + +#struct Nothing +#end +#const nothing = Nothing() + +#abstract type AbstractArray{T,N} end +#abstract type DenseArray{T,N} <: AbstractArray{T,N} end + +#mutable struct Array{T,N} <: DenseArray{T,N} +#end + +#mutable struct Module +# name::Symbol +#end + +#mutable struct Method +#end + +#mutable struct MethodInstance +#end + +#mutable struct CodeInstance +#end + +#mutable struct CodeInfo +#end + +#mutable struct TypeMapLevel +#end + +#mutable struct TypeMapEntry +#end + +#abstract type Ref{T} end +#primitive type Ptr{T} <: Ref{T} {32|64} end + +# types for the front end + +#mutable struct Expr +# head::Symbol +# args::Array{Any,1} +#end + +#struct LineNumberNode +# line::Int +# file::Any # nominally Union{Symbol,Nothing} +#end + +#struct LineInfoNode +# method::Any +# file::Symbol +# line::Int +# inlined_at::Int +#end + +#struct GotoNode +# label::Int +#end + +#struct PiNode +# val +# typ +#end + +#struct PhiNode +# edges::Vector{Any} +# values::Vector{Any} +#end + +#struct PhiCNode +# values::Vector{Any} +#end + +#struct UpsilonNode +# val +#end + +#struct QuoteNode +# value +#end + +#struct GlobalRef +# mod::Module +# name::Symbol +#end + +#mutable struct Task +# parent::Task +# storage::Any +# state::Symbol +# donenotify::Any +# result::Any +# exception::Any +# backtrace::Any +# logstate::Any +# code::Any +#end + +export + # key types + Any, DataType, Vararg, NTuple, + Tuple, Type, UnionAll, TypeVar, Union, Nothing, Cvoid, + AbstractArray, DenseArray, NamedTuple, + # special objects + Function, Method, + Module, Symbol, Task, Array, UndefInitializer, undef, WeakRef, VecElement, + # numeric types + Number, Real, Integer, Bool, Ref, Ptr, + AbstractFloat, Float16, Float32, Float64, + Signed, Int, Int8, Int16, Int32, Int64, Int128, + Unsigned, UInt, UInt8, UInt16, UInt32, UInt64, UInt128, + # string types + AbstractChar, Char, AbstractString, String, IO, + # errors + ErrorException, BoundsError, DivideError, DomainError, Exception, + InterruptException, InexactError, OutOfMemoryError, ReadOnlyMemoryError, + OverflowError, StackOverflowError, SegmentationFault, UndefRefError, UndefVarError, + TypeError, ArgumentError, MethodError, AssertionError, LoadError, InitError, + UndefKeywordError, + # AST representation + Expr, QuoteNode, LineNumberNode, GlobalRef, + # object model functions + fieldtype, getfield, setfield!, nfields, throw, tuple, ===, isdefined, eval, ifelse, + # sizeof # not exported, to avoid conflicting with Base.sizeof + # type reflection + <:, typeof, isa, typeassert, + # method reflection + applicable, invoke, + # constants + nothing, Main + +const getproperty = getfield +const setproperty! = setfield! + +abstract type Number end +abstract type Real <: Number end +abstract type AbstractFloat <: Real end +abstract type Integer <: Real end +abstract type Signed <: Integer end +abstract type Unsigned <: Integer end + +primitive type Float16 <: AbstractFloat 16 end +primitive type Float32 <: AbstractFloat 32 end +primitive type Float64 <: AbstractFloat 64 end + +#primitive type Bool <: Integer 8 end +abstract type AbstractChar end +primitive type Char <: AbstractChar 32 end + +primitive type Int8 <: Signed 8 end +#primitive type UInt8 <: Unsigned 8 end +primitive type Int16 <: Signed 16 end +primitive type UInt16 <: Unsigned 16 end +#primitive type Int32 <: Signed 32 end +#primitive type UInt32 <: Unsigned 32 end +#primitive type Int64 <: Signed 64 end +#primitive type UInt64 <: Unsigned 64 end +primitive type Int128 <: Signed 128 end +primitive type UInt128 <: Unsigned 128 end + +if Int === Int64 + const UInt = UInt64 +else + const UInt = UInt32 +end + +function Typeof end +ccall(:jl_toplevel_eval_in, Any, (Any, Any), + Core, quote + (f::typeof(Typeof))(x) = ($(_expr(:meta,:nospecialize,:x)); isa(x,Type) ? Type{x} : typeof(x)) + end) + +macro nospecialize(x) + _expr(:meta, :nospecialize, x) +end + +Expr(@nospecialize args...) = _expr(args...) + +abstract type Exception end +struct ErrorException <: Exception + msg::AbstractString +end + +macro _inline_meta() + Expr(:meta, :inline) +end + +macro _noinline_meta() + Expr(:meta, :noinline) +end + +struct BoundsError <: Exception + a::Any + i::Any + BoundsError() = new() + BoundsError(@nospecialize(a)) = (@_noinline_meta; new(a)) + BoundsError(@nospecialize(a), i) = (@_noinline_meta; new(a,i)) +end +struct DivideError <: Exception end +struct OutOfMemoryError <: Exception end +struct ReadOnlyMemoryError <: Exception end +struct SegmentationFault <: Exception end +struct StackOverflowError <: Exception end +struct UndefRefError <: Exception end +struct UndefVarError <: Exception + var::Symbol +end +struct InterruptException <: Exception end +struct DomainError <: Exception + val + msg::AbstractString + DomainError(@nospecialize(val)) = (@_noinline_meta; new(val, "")) + DomainError(@nospecialize(val), @nospecialize(msg)) = (@_noinline_meta; new(val, msg)) +end +struct TypeError <: Exception + # `func` is the name of the builtin function that encountered a type error, + # the name of the type that hit an error in its definition or application, or + # some other brief description of where the error happened. + # `context` optionally adds extra detail, e.g. the name of the type parameter + # that got a bad value. + func::Symbol + context::Union{AbstractString,Symbol} + expected::Type + got + TypeError(func, context, @nospecialize(expected::Type), @nospecialize(got)) = + new(func, context, expected, got) +end +TypeError(where, @nospecialize(expected::Type), @nospecialize(got)) = + TypeError(Symbol(where), "", expected, got) +struct InexactError <: Exception + func::Symbol + T # Type + val + InexactError(f::Symbol, @nospecialize(T), @nospecialize(val)) = (@_noinline_meta; new(f, T, val)) +end +struct OverflowError <: Exception + msg::AbstractString +end + +struct ArgumentError <: Exception + msg::AbstractString +end +struct UndefKeywordError <: Exception + var::Symbol +end + +struct MethodError <: Exception + f + args + world::UInt + MethodError(@nospecialize(f), @nospecialize(args), world::UInt) = new(f, args, world) +end +const typemax_UInt = ccall(:jl_typemax_uint, Any, (Any,), UInt) +MethodError(@nospecialize(f), @nospecialize(args)) = MethodError(f, args, typemax_UInt) + +struct AssertionError <: Exception + msg::AbstractString +end +AssertionError() = AssertionError("") + +abstract type WrappedException <: Exception end + +struct LoadError <: WrappedException + file::AbstractString + line::Int + error +end + +struct InitError <: WrappedException + mod::Symbol + error +end + +String(s::String) = s # no constructor yet + +const Cvoid = Nothing +Nothing() = nothing + +# This should always be inlined +getptls() = ccall(:jl_get_ptls_states, Ptr{Cvoid}, ()) + +include(m::Module, fname::String) = ccall(:jl_load_, Any, (Any, Any), m, fname) + +eval(m::Module, @nospecialize(e)) = ccall(:jl_toplevel_eval_in, Any, (Any, Any), m, e) + +kwfunc(@nospecialize(f)) = ccall(:jl_get_keyword_sorter, Any, (Any,), f) + +kwftype(@nospecialize(t)) = typeof(ccall(:jl_get_kwsorter, Any, (Any,), t)) + +mutable struct Box + contents::Any + Box(@nospecialize(x)) = new(x) + Box() = new() +end + +# constructors for built-in types + +mutable struct WeakRef + value + WeakRef() = WeakRef(nothing) + WeakRef(@nospecialize(v)) = ccall(:jl_gc_new_weakref_th, Ref{WeakRef}, + (Ptr{Cvoid}, Any), getptls(), v) +end + +TypeVar(n::Symbol) = _typevar(n, Union{}, Any) +TypeVar(n::Symbol, @nospecialize(ub)) = _typevar(n, Union{}, ub) +TypeVar(n::Symbol, @nospecialize(lb), @nospecialize(ub)) = _typevar(n, lb, ub) + +UnionAll(v::TypeVar, @nospecialize(t)) = ccall(:jl_type_unionall, Any, (Any, Any), v, t) + +(::Type{Tuple{}})() = () # Tuple{}() + +struct VecElement{T} + value::T + VecElement{T}(value::T) where {T} = new(value) # disable converting constructor in Core +end +VecElement(arg::T) where {T} = VecElement{T}(arg) + +_new(typ::Symbol, argty::Symbol) = eval(Core, :($typ(@nospecialize n::$argty) = $(Expr(:new, typ, :n)))) +_new(:GotoNode, :Int) +_new(:NewvarNode, :SlotNumber) +_new(:QuoteNode, :Any) +_new(:SSAValue, :Int) +eval(Core, :(LineNumberNode(l::Int) = $(Expr(:new, :LineNumberNode, :l, nothing)))) +eval(Core, :(LineNumberNode(l::Int, @nospecialize(f)) = $(Expr(:new, :LineNumberNode, :l, :f)))) +eval(Core, :(GlobalRef(m::Module, s::Symbol) = $(Expr(:new, :GlobalRef, :m, :s)))) +eval(Core, :(SlotNumber(n::Int) = $(Expr(:new, :SlotNumber, :n)))) +eval(Core, :(TypedSlot(n::Int, @nospecialize(t)) = $(Expr(:new, :TypedSlot, :n, :t)))) +eval(Core, :(PhiNode(edges::Array{Any, 1}, values::Array{Any, 1}) = $(Expr(:new, :PhiNode, :edges, :values)))) +eval(Core, :(PiNode(val, typ) = $(Expr(:new, :PiNode, :val, :typ)))) +eval(Core, :(PhiCNode(values::Array{Any, 1}) = $(Expr(:new, :PhiCNode, :values)))) +eval(Core, :(UpsilonNode(val) = $(Expr(:new, :UpsilonNode, :val)))) +eval(Core, :(UpsilonNode() = $(Expr(:new, :UpsilonNode)))) +eval(Core, :(LineInfoNode(@nospecialize(method), file::Symbol, line::Int, inlined_at::Int) = + $(Expr(:new, :LineInfoNode, :method, :file, :line, :inlined_at)))) + +Module(name::Symbol=:anonymous, std_imports::Bool=true) = ccall(:jl_f_new_module, Ref{Module}, (Any, Bool), name, std_imports) + +function _Task(@nospecialize(f), reserved_stack::Int, completion_future) + return ccall(:jl_new_task, Ref{Task}, (Any, Any, Int), f, completion_future, reserved_stack) +end + +# simple convert for use by constructors of types in Core +# note that there is no actual conversion defined here, +# so the methods and ccall's in Core aren't permitted to use convert +convert(::Type{Any}, @nospecialize(x)) = x +convert(::Type{T}, x::T) where {T} = x +cconvert(::Type{T}, x) where {T} = convert(T, x) +unsafe_convert(::Type{T}, x::T) where {T} = x + +const NTuple{N,T} = Tuple{Vararg{T,N}} + + +## primitive Array constructors +struct UndefInitializer end +const undef = UndefInitializer() +# type and dimensionality specified, accepting dims as series of Ints +Array{T,1}(::UndefInitializer, m::Int) where {T} = + ccall(:jl_alloc_array_1d, Array{T,1}, (Any, Int), Array{T,1}, m) +Array{T,2}(::UndefInitializer, m::Int, n::Int) where {T} = + ccall(:jl_alloc_array_2d, Array{T,2}, (Any, Int, Int), Array{T,2}, m, n) +Array{T,3}(::UndefInitializer, m::Int, n::Int, o::Int) where {T} = + ccall(:jl_alloc_array_3d, Array{T,3}, (Any, Int, Int, Int), Array{T,3}, m, n, o) +Array{T,N}(::UndefInitializer, d::Vararg{Int,N}) where {T,N} = + ccall(:jl_new_array, Array{T,N}, (Any, Any), Array{T,N}, d) +# type and dimensionality specified, accepting dims as tuples of Ints +Array{T,1}(::UndefInitializer, d::NTuple{1,Int}) where {T} = Array{T,1}(undef, getfield(d,1)) +Array{T,2}(::UndefInitializer, d::NTuple{2,Int}) where {T} = Array{T,2}(undef, getfield(d,1), getfield(d,2)) +Array{T,3}(::UndefInitializer, d::NTuple{3,Int}) where {T} = Array{T,3}(undef, getfield(d,1), getfield(d,2), getfield(d,3)) +Array{T,N}(::UndefInitializer, d::NTuple{N,Int}) where {T,N} = ccall(:jl_new_array, Array{T,N}, (Any, Any), Array{T,N}, d) +# type but not dimensionality specified +Array{T}(::UndefInitializer, m::Int) where {T} = Array{T,1}(undef, m) +Array{T}(::UndefInitializer, m::Int, n::Int) where {T} = Array{T,2}(undef, m, n) +Array{T}(::UndefInitializer, m::Int, n::Int, o::Int) where {T} = Array{T,3}(undef, m, n, o) +Array{T}(::UndefInitializer, d::NTuple{N,Int}) where {T,N} = Array{T,N}(undef, d) +# empty vector constructor +Array{T,1}() where {T} = Array{T,1}(undef, 0) + + +(::Type{Array{T,N} where T})(x::AbstractArray{S,N}) where {S,N} = Array{S,N}(x) + +Array(A::AbstractArray{T,N}) where {T,N} = Array{T,N}(A) +Array{T}(A::AbstractArray{S,N}) where {T,N,S} = Array{T,N}(A) + +AbstractArray{T}(A::AbstractArray{S,N}) where {T,S,N} = AbstractArray{T,N}(A) + +# primitive Symbol constructors +function Symbol(s::String) + return ccall(:jl_symbol_n, Ref{Symbol}, (Ptr{UInt8}, Int), + ccall(:jl_string_ptr, Ptr{UInt8}, (Any,), s), + sizeof(s)) +end +function Symbol(a::Array{UInt8,1}) + return ccall(:jl_symbol_n, Ref{Symbol}, (Ptr{UInt8}, Int), + ccall(:jl_array_ptr, Ptr{UInt8}, (Any,), a), + Intrinsics.arraylen(a)) +end +Symbol(s::Symbol) = s + +# module providing the IR object model +module IR +export CodeInfo, MethodInstance, CodeInstance, GotoNode, + NewvarNode, SSAValue, Slot, SlotNumber, TypedSlot, + PiNode, PhiNode, PhiCNode, UpsilonNode, LineInfoNode + +import Core: CodeInfo, MethodInstance, CodeInstance, GotoNode, + NewvarNode, SSAValue, Slot, SlotNumber, TypedSlot, + PiNode, PhiNode, PhiCNode, UpsilonNode, LineInfoNode + +end + +# docsystem basics +const unescape = Symbol("hygienic-scope") +macro doc(x...) + docex = atdoc(__source__, __module__, x...) + isa(docex, Expr) && docex.head === :escape && return docex + return Expr(:escape, Expr(unescape, docex, typeof(atdoc).name.module)) +end +macro __doc__(x) + return Expr(:escape, Expr(:block, Expr(:meta, :doc), x)) +end +atdoc = (source, mod, str, expr) -> Expr(:escape, expr) +atdoc!(λ) = global atdoc = λ + +# macros for big integer syntax +macro int128_str end +macro uint128_str end +macro big_str end + +# macro for command syntax +macro cmd end + + +# simple stand-alone print definitions for debugging +abstract type IO end +struct CoreSTDOUT <: IO end +struct CoreSTDERR <: IO end +const stdout = CoreSTDOUT() +const stderr = CoreSTDERR() +io_pointer(::CoreSTDOUT) = Intrinsics.pointerref(Intrinsics.cglobal(:jl_uv_stdout, Ptr{Cvoid}), 1, 1) +io_pointer(::CoreSTDERR) = Intrinsics.pointerref(Intrinsics.cglobal(:jl_uv_stderr, Ptr{Cvoid}), 1, 1) + +unsafe_write(io::IO, x::Ptr{UInt8}, nb::UInt) = + (ccall(:jl_uv_puts, Cvoid, (Ptr{Cvoid}, Ptr{UInt8}, UInt), io_pointer(io), x, nb); nb) +unsafe_write(io::IO, x::Ptr{UInt8}, nb::Int) = + (ccall(:jl_uv_puts, Cvoid, (Ptr{Cvoid}, Ptr{UInt8}, Int), io_pointer(io), x, nb); nb) +write(io::IO, x::UInt8) = + (ccall(:jl_uv_putb, Cvoid, (Ptr{Cvoid}, UInt8), io_pointer(io), x); 1) +function write(io::IO, x::String) + nb = sizeof(x) + unsafe_write(io, ccall(:jl_string_ptr, Ptr{UInt8}, (Any,), x), nb) + return nb +end + +show(io::IO, @nospecialize x) = ccall(:jl_static_show, Cvoid, (Ptr{Cvoid}, Any), io_pointer(io), x) +print(io::IO, x::AbstractChar) = ccall(:jl_uv_putc, Cvoid, (Ptr{Cvoid}, Char), io_pointer(io), x) +print(io::IO, x::String) = (write(io, x); nothing) +print(io::IO, @nospecialize x) = show(io, x) +print(io::IO, @nospecialize(x), @nospecialize a...) = (print(io, x); print(io, a...)) +println(io::IO) = (write(io, 0x0a); nothing) # 0x0a = '\n' +println(io::IO, @nospecialize x...) = (print(io, x...); println(io)) + +show(@nospecialize a) = show(stdout, a) +print(@nospecialize a...) = print(stdout, a...) +println(@nospecialize a...) = println(stdout, a...) + +struct GeneratedFunctionStub + gen + argnames::Array{Any,1} + spnames::Union{Nothing, Array{Any,1}} + line::Int + file::Symbol + expand_early::Bool +end + +# invoke and wrap the results of @generated +function (g::GeneratedFunctionStub)(@nospecialize args...) + body = g.gen(args...) + if body isa CodeInfo + return body + end + lam = Expr(:lambda, g.argnames, + Expr(Symbol("scope-block"), + Expr(:block, + LineNumberNode(g.line, g.file), + Expr(:meta, :push_loc, g.file, Symbol("@generated body")), + Expr(:return, body), + Expr(:meta, :pop_loc)))) + if g.spnames === nothing + return lam + else + return Expr(Symbol("with-static-parameters"), lam, g.spnames...) + end +end + +NamedTuple() = NamedTuple{(),Tuple{}}(()) + +""" + NamedTuple{names}(args::Tuple) + +Construct a named tuple with the given `names` (a tuple of Symbols) from a tuple of values. +""" +NamedTuple{names}(args::Tuple) where {names} = NamedTuple{names,typeof(args)}(args) + +using .Intrinsics: sle_int, add_int + +eval(Core, :(NamedTuple{names,T}(args::T) where {names, T <: Tuple} = + $(Expr(:splatnew, :(NamedTuple{names,T}), :args)))) + +# constructors for built-in types + +import .Intrinsics: eq_int, trunc_int, lshr_int, sub_int, shl_int, bitcast, sext_int, zext_int, and_int + +throw_inexacterror(f::Symbol, ::Type{T}, val) where {T} = (@_noinline_meta; throw(InexactError(f, T, val))) + +function is_top_bit_set(x) + @_inline_meta + eq_int(trunc_int(UInt8, lshr_int(x, sub_int(shl_int(sizeof(x), 3), 1))), trunc_int(UInt8, 1)) +end + +function is_top_bit_set(x::Union{Int8,UInt8}) + @_inline_meta + eq_int(lshr_int(x, 7), trunc_int(typeof(x), 1)) +end + +function check_top_bit(x) + @_inline_meta + is_top_bit_set(x) && throw_inexacterror(:check_top_bit, typeof(x), x) + x +end + +function checked_trunc_sint(::Type{To}, x::From) where {To,From} + @_inline_meta + y = trunc_int(To, x) + back = sext_int(From, y) + eq_int(x, back) || throw_inexacterror(:trunc, To, x) + y +end + +function checked_trunc_uint(::Type{To}, x::From) where {To,From} + @_inline_meta + y = trunc_int(To, x) + back = zext_int(From, y) + eq_int(x, back) || throw_inexacterror(:trunc, To, x) + y +end + +toInt8(x::Int8) = x +toInt8(x::Int16) = checked_trunc_sint(Int8, x) +toInt8(x::Int32) = checked_trunc_sint(Int8, x) +toInt8(x::Int64) = checked_trunc_sint(Int8, x) +toInt8(x::Int128) = checked_trunc_sint(Int8, x) +toInt8(x::UInt8) = bitcast(Int8, check_top_bit(x)) +toInt8(x::UInt16) = checked_trunc_sint(Int8, check_top_bit(x)) +toInt8(x::UInt32) = checked_trunc_sint(Int8, check_top_bit(x)) +toInt8(x::UInt64) = checked_trunc_sint(Int8, check_top_bit(x)) +toInt8(x::UInt128) = checked_trunc_sint(Int8, check_top_bit(x)) +toInt8(x::Bool) = and_int(bitcast(Int8, x), Int8(1)) +toInt16(x::Int8) = sext_int(Int16, x) +toInt16(x::Int16) = x +toInt16(x::Int32) = checked_trunc_sint(Int16, x) +toInt16(x::Int64) = checked_trunc_sint(Int16, x) +toInt16(x::Int128) = checked_trunc_sint(Int16, x) +toInt16(x::UInt8) = zext_int(Int16, x) +toInt16(x::UInt16) = bitcast(Int16, check_top_bit(x)) +toInt16(x::UInt32) = checked_trunc_sint(Int16, check_top_bit(x)) +toInt16(x::UInt64) = checked_trunc_sint(Int16, check_top_bit(x)) +toInt16(x::UInt128) = checked_trunc_sint(Int16, check_top_bit(x)) +toInt16(x::Bool) = and_int(zext_int(Int16, x), Int16(1)) +toInt32(x::Int8) = sext_int(Int32, x) +toInt32(x::Int16) = sext_int(Int32, x) +toInt32(x::Int32) = x +toInt32(x::Int64) = checked_trunc_sint(Int32, x) +toInt32(x::Int128) = checked_trunc_sint(Int32, x) +toInt32(x::UInt8) = zext_int(Int32, x) +toInt32(x::UInt16) = zext_int(Int32, x) +toInt32(x::UInt32) = bitcast(Int32, check_top_bit(x)) +toInt32(x::UInt64) = checked_trunc_sint(Int32, check_top_bit(x)) +toInt32(x::UInt128) = checked_trunc_sint(Int32, check_top_bit(x)) +toInt32(x::Bool) = and_int(zext_int(Int32, x), Int32(1)) +toInt64(x::Int8) = sext_int(Int64, x) +toInt64(x::Int16) = sext_int(Int64, x) +toInt64(x::Int32) = sext_int(Int64, x) +toInt64(x::Int64) = x +toInt64(x::Int128) = checked_trunc_sint(Int64, x) +toInt64(x::UInt8) = zext_int(Int64, x) +toInt64(x::UInt16) = zext_int(Int64, x) +toInt64(x::UInt32) = zext_int(Int64, x) +toInt64(x::UInt64) = bitcast(Int64, check_top_bit(x)) +toInt64(x::UInt128) = checked_trunc_sint(Int64, check_top_bit(x)) +toInt64(x::Bool) = and_int(zext_int(Int64, x), Int64(1)) +toInt128(x::Int8) = sext_int(Int128, x) +toInt128(x::Int16) = sext_int(Int128, x) +toInt128(x::Int32) = sext_int(Int128, x) +toInt128(x::Int64) = sext_int(Int128, x) +toInt128(x::Int128) = x +toInt128(x::UInt8) = zext_int(Int128, x) +toInt128(x::UInt16) = zext_int(Int128, x) +toInt128(x::UInt32) = zext_int(Int128, x) +toInt128(x::UInt64) = zext_int(Int128, x) +toInt128(x::UInt128) = bitcast(Int128, check_top_bit(x)) +toInt128(x::Bool) = and_int(zext_int(Int128, x), Int128(1)) +toUInt8(x::Int8) = bitcast(UInt8, check_top_bit(x)) +toUInt8(x::Int16) = checked_trunc_uint(UInt8, x) +toUInt8(x::Int32) = checked_trunc_uint(UInt8, x) +toUInt8(x::Int64) = checked_trunc_uint(UInt8, x) +toUInt8(x::Int128) = checked_trunc_uint(UInt8, x) +toUInt8(x::UInt8) = x +toUInt8(x::UInt16) = checked_trunc_uint(UInt8, x) +toUInt8(x::UInt32) = checked_trunc_uint(UInt8, x) +toUInt8(x::UInt64) = checked_trunc_uint(UInt8, x) +toUInt8(x::UInt128) = checked_trunc_uint(UInt8, x) +toUInt8(x::Bool) = and_int(bitcast(UInt8, x), UInt8(1)) +toUInt16(x::Int8) = sext_int(UInt16, check_top_bit(x)) +toUInt16(x::Int16) = bitcast(UInt16, check_top_bit(x)) +toUInt16(x::Int32) = checked_trunc_uint(UInt16, x) +toUInt16(x::Int64) = checked_trunc_uint(UInt16, x) +toUInt16(x::Int128) = checked_trunc_uint(UInt16, x) +toUInt16(x::UInt8) = zext_int(UInt16, x) +toUInt16(x::UInt16) = x +toUInt16(x::UInt32) = checked_trunc_uint(UInt16, x) +toUInt16(x::UInt64) = checked_trunc_uint(UInt16, x) +toUInt16(x::UInt128) = checked_trunc_uint(UInt16, x) +toUInt16(x::Bool) = and_int(zext_int(UInt16, x), UInt16(1)) +toUInt32(x::Int8) = sext_int(UInt32, check_top_bit(x)) +toUInt32(x::Int16) = sext_int(UInt32, check_top_bit(x)) +toUInt32(x::Int32) = bitcast(UInt32, check_top_bit(x)) +toUInt32(x::Int64) = checked_trunc_uint(UInt32, x) +toUInt32(x::Int128) = checked_trunc_uint(UInt32, x) +toUInt32(x::UInt8) = zext_int(UInt32, x) +toUInt32(x::UInt16) = zext_int(UInt32, x) +toUInt32(x::UInt32) = x +toUInt32(x::UInt64) = checked_trunc_uint(UInt32, x) +toUInt32(x::UInt128) = checked_trunc_uint(UInt32, x) +toUInt32(x::Bool) = and_int(zext_int(UInt32, x), UInt32(1)) +toUInt64(x::Int8) = sext_int(UInt64, check_top_bit(x)) +toUInt64(x::Int16) = sext_int(UInt64, check_top_bit(x)) +toUInt64(x::Int32) = sext_int(UInt64, check_top_bit(x)) +toUInt64(x::Int64) = bitcast(UInt64, check_top_bit(x)) +toUInt64(x::Int128) = checked_trunc_uint(UInt64, x) +toUInt64(x::UInt8) = zext_int(UInt64, x) +toUInt64(x::UInt16) = zext_int(UInt64, x) +toUInt64(x::UInt32) = zext_int(UInt64, x) +toUInt64(x::UInt64) = x +toUInt64(x::UInt128) = checked_trunc_uint(UInt64, x) +toUInt64(x::Bool) = and_int(zext_int(UInt64, x), UInt64(1)) +toUInt128(x::Int8) = sext_int(UInt128, check_top_bit(x)) +toUInt128(x::Int16) = sext_int(UInt128, check_top_bit(x)) +toUInt128(x::Int32) = sext_int(UInt128, check_top_bit(x)) +toUInt128(x::Int64) = sext_int(UInt128, check_top_bit(x)) +toUInt128(x::Int128) = bitcast(UInt128, check_top_bit(x)) +toUInt128(x::UInt8) = zext_int(UInt128, x) +toUInt128(x::UInt16) = zext_int(UInt128, x) +toUInt128(x::UInt32) = zext_int(UInt128, x) +toUInt128(x::UInt64) = zext_int(UInt128, x) +toUInt128(x::UInt128) = x +toUInt128(x::Bool) = and_int(zext_int(UInt128, x), UInt128(1)) + +# TODO: this is here to work around the 4 method limit in inference (#23210). +const BuiltinInts = Union{Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64, UInt8, Bool} +Int8(x::BuiltinInts) = toInt8(x)::Int8 +Int16(x::BuiltinInts) = toInt16(x)::Int16 +Int32(x::BuiltinInts) = toInt32(x)::Int32 +Int64(x::BuiltinInts) = toInt64(x)::Int64 +Int128(x::BuiltinInts) = toInt128(x)::Int128 +UInt8(x::BuiltinInts) = toUInt8(x)::UInt8 +UInt16(x::BuiltinInts) = toUInt16(x)::UInt16 +UInt32(x::BuiltinInts) = toUInt32(x)::UInt32 +UInt64(x::BuiltinInts) = toUInt64(x)::UInt64 +UInt128(x::BuiltinInts) = toUInt128(x)::UInt128 + +(::Type{T})(x::T) where {T<:Number} = x + +Int(x::Ptr) = bitcast(Int, x) +UInt(x::Ptr) = bitcast(UInt, x) +if Int === Int32 +Int64(x::Ptr) = Int64(UInt32(x)) +UInt64(x::Ptr) = UInt64(UInt32(x)) +end +Ptr{T}(x::Union{Int,UInt,Ptr}) where {T} = bitcast(Ptr{T}, x) +Ptr{T}() where {T} = Ptr{T}(0) + +Signed(x::UInt8) = Int8(x) +Unsigned(x::Int8) = UInt8(x) +Signed(x::UInt16) = Int16(x) +Unsigned(x::Int16) = UInt16(x) +Signed(x::UInt32) = Int32(x) +Unsigned(x::Int32) = UInt32(x) +Signed(x::UInt64) = Int64(x) +Unsigned(x::Int64) = UInt64(x) +Signed(x::UInt128) = Int128(x) +Unsigned(x::Int128) = UInt128(x) + +Signed(x::Union{Float32, Float64, Bool}) = Int(x) +Unsigned(x::Union{Float32, Float64, Bool}) = UInt(x) + +Integer(x::Integer) = x +Integer(x::Union{Float32, Float64}) = Int(x) + +ccall(:jl_set_istopmod, Cvoid, (Any, Bool), Core, true) diff --git a/base/broadcast.jl b/base/broadcast.jl new file mode 100644 index 0000000..b635d2d --- /dev/null +++ b/base/broadcast.jl @@ -0,0 +1,1241 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" + Base.Broadcast + +Module containing the broadcasting implementation. +""" +module Broadcast + +using .Base.Cartesian +using .Base: Indices, OneTo, tail, to_shape, isoperator, promote_typejoin, + _msk_end, unsafe_bitgetindex, bitcache_chunks, bitcache_size, dumpbitcache, unalias +import .Base: copy, copyto!, axes +export broadcast, broadcast!, BroadcastStyle, broadcast_axes, broadcastable, dotview, @__dot__, broadcast_preserving_zero_d + +## Computing the result's axes: deprecated name +const broadcast_axes = axes + +### Objects with customized broadcasting behavior should declare a BroadcastStyle + +""" +`BroadcastStyle` is an abstract type and trait-function used to determine behavior of +objects under broadcasting. `BroadcastStyle(typeof(x))` returns the style associated +with `x`. To customize the broadcasting behavior of a type, one can declare a style +by defining a type/method pair + + struct MyContainerStyle <: BroadcastStyle end + Base.BroadcastStyle(::Type{<:MyContainer}) = MyContainerStyle() + +One then writes method(s) (at least [`similar`](@ref)) operating on +`Broadcasted{MyContainerStyle}`. There are also several pre-defined subtypes of `BroadcastStyle` +that you may be able to leverage; see the +[Interfaces chapter](@ref man-interfaces-broadcasting) for more information. +""" +abstract type BroadcastStyle end + +""" +`Broadcast.Style{C}()` defines a [`BroadcastStyle`](@ref) signaling through the type +parameter `C`. You can use this as an alternative to creating custom subtypes of `BroadcastStyle`, +for example + + Base.BroadcastStyle(::Type{<:MyContainer}) = Broadcast.Style{MyContainer}() +""" +struct Style{T} <: BroadcastStyle end + +BroadcastStyle(::Type{<:Tuple}) = Style{Tuple}() + +struct Unknown <: BroadcastStyle end +BroadcastStyle(::Type{Union{}}) = Unknown() # ambiguity resolution + +""" +`Broadcast.AbstractArrayStyle{N} <: BroadcastStyle` is the abstract supertype for any style +associated with an `AbstractArray` type. +The `N` parameter is the dimensionality, which can be handy for AbstractArray types +that only support specific dimensionalities: + + struct SparseMatrixStyle <: Broadcast.AbstractArrayStyle{2} end + Base.BroadcastStyle(::Type{<:SparseMatrixCSC}) = SparseMatrixStyle() + +For `AbstractArray` types that support arbitrary dimensionality, `N` can be set to `Any`: + + struct MyArrayStyle <: Broadcast.AbstractArrayStyle{Any} end + Base.BroadcastStyle(::Type{<:MyArray}) = MyArrayStyle() + +In cases where you want to be able to mix multiple `AbstractArrayStyle`s and keep track +of dimensionality, your style needs to support a [`Val`](@ref) constructor: + + struct MyArrayStyleDim{N} <: Broadcast.AbstractArrayStyle{N} end + (::Type{<:MyArrayStyleDim})(::Val{N}) where N = MyArrayStyleDim{N}() + +Note that if two or more `AbstractArrayStyle` subtypes conflict, broadcasting machinery +will fall back to producing `Array`s. If this is undesirable, you may need to +define binary [`BroadcastStyle`](@ref) rules to control the output type. + +See also [`Broadcast.DefaultArrayStyle`](@ref). +""" +abstract type AbstractArrayStyle{N} <: BroadcastStyle end + +""" +`Broadcast.ArrayStyle{MyArrayType}()` is a [`BroadcastStyle`](@ref) indicating that an object +behaves as an array for broadcasting. It presents a simple way to construct +[`Broadcast.AbstractArrayStyle`](@ref)s for specific `AbstractArray` container types. +Broadcast styles created this way lose track of dimensionality; if keeping track is important +for your type, you should create your own custom [`Broadcast.AbstractArrayStyle`](@ref). +""" +struct ArrayStyle{A<:AbstractArray} <: AbstractArrayStyle{Any} end +ArrayStyle{A}(::Val) where A = ArrayStyle{A}() + +""" +`Broadcast.DefaultArrayStyle{N}()` is a [`BroadcastStyle`](@ref) indicating that an object +behaves as an `N`-dimensional array for broadcasting. Specifically, `DefaultArrayStyle` is +used for any +`AbstractArray` type that hasn't defined a specialized style, and in the absence of +overrides from other `broadcast` arguments the resulting output type is `Array`. +When there are multiple inputs to `broadcast`, `DefaultArrayStyle` "loses" to any other [`Broadcast.ArrayStyle`](@ref). +""" +struct DefaultArrayStyle{N} <: AbstractArrayStyle{N} end +DefaultArrayStyle(::Val{N}) where N = DefaultArrayStyle{N}() +DefaultArrayStyle{M}(::Val{N}) where {N,M} = DefaultArrayStyle{N}() +const DefaultVectorStyle = DefaultArrayStyle{1} +const DefaultMatrixStyle = DefaultArrayStyle{2} +BroadcastStyle(::Type{<:AbstractArray{T,N}}) where {T,N} = DefaultArrayStyle{N}() +BroadcastStyle(::Type{T}) where {T} = DefaultArrayStyle{ndims(T)}() + +# `ArrayConflict` is an internal type signaling that two or more different `AbstractArrayStyle` +# objects were supplied as arguments, and that no rule was defined for resolving the +# conflict. The resulting output is `Array`. While this is the same output type +# produced by `DefaultArrayStyle`, `ArrayConflict` "poisons" the BroadcastStyle so that +# 3 or more arguments still return an `ArrayConflict`. +struct ArrayConflict <: AbstractArrayStyle{Any} end +ArrayConflict(::Val) = ArrayConflict() + +### Binary BroadcastStyle rules +""" + BroadcastStyle(::Style1, ::Style2) = Style3() + +Indicate how to resolve different `BroadcastStyle`s. For example, + + BroadcastStyle(::Primary, ::Secondary) = Primary() + +would indicate that style `Primary` has precedence over `Secondary`. +You do not have to (and generally should not) define both argument orders. +The result does not have to be one of the input arguments, it could be a third type. + +Please see the [Interfaces chapter](@ref man-interfaces-broadcasting) of the manual for +more information. +""" +BroadcastStyle(::S, ::S) where S<:BroadcastStyle = S() # homogeneous types preserved +# Fall back to Unknown. This is necessary to implement argument-swapping +BroadcastStyle(::BroadcastStyle, ::BroadcastStyle) = Unknown() +# Unknown loses to everything +BroadcastStyle(::Unknown, ::Unknown) = Unknown() +BroadcastStyle(::S, ::Unknown) where S<:BroadcastStyle = S() +# Precedence rules +BroadcastStyle(a::AbstractArrayStyle{0}, b::Style{Tuple}) = b +BroadcastStyle(a::AbstractArrayStyle, ::Style{Tuple}) = a +BroadcastStyle(::A, ::A) where A<:ArrayStyle = A() +BroadcastStyle(::ArrayStyle, ::ArrayStyle) = Unknown() +BroadcastStyle(::A, ::A) where A<:AbstractArrayStyle = A() +Base.@pure function BroadcastStyle(a::A, b::B) where {A<:AbstractArrayStyle{M},B<:AbstractArrayStyle{N}} where {M,N} + if Base.typename(A) === Base.typename(B) + return A(Val(max(M, N))) + end + return Unknown() +end +# Any specific array type beats DefaultArrayStyle +BroadcastStyle(a::AbstractArrayStyle{Any}, ::DefaultArrayStyle) = a +BroadcastStyle(a::AbstractArrayStyle{N}, ::DefaultArrayStyle{N}) where N = a +BroadcastStyle(a::AbstractArrayStyle{M}, ::DefaultArrayStyle{N}) where {M,N} = + typeof(a)(Val(max(M, N))) + +### Lazy-wrapper for broadcasting + +# `Broadcasted` wrap the arguments to `broadcast(f, args...)`. A statement like +# y = x .* (x .+ 1) +# will result in code that is essentially +# y = copy(Broadcasted(*, x, Broadcasted(+, x, 1))) +# `broadcast!` results in `copyto!(dest, Broadcasted(...))`. + +# The use of `Nothing` in place of a `BroadcastStyle` has a different +# application, in the fallback method +# copyto!(dest, bc::Broadcasted) = copyto!(dest, convert(Broadcasted{Nothing}, bc)) +# This allows methods +# copyto!(dest::DestType, bc::Broadcasted{Nothing}) +# that specialize on `DestType` to be easily disambiguated from +# methods that instead specialize on `BroadcastStyle`, +# copyto!(dest::AbstractArray, bc::Broadcasted{MyStyle}) + +struct Broadcasted{Style<:Union{Nothing,BroadcastStyle}, Axes, F, Args<:Tuple} + f::F + args::Args + axes::Axes # the axes of the resulting object (may be bigger than implied by `args` if this is nested inside a larger `Broadcasted`) +end + +Broadcasted(f::F, args::Args, axes=nothing) where {F, Args<:Tuple} = + Broadcasted{typeof(combine_styles(args...))}(f, args, axes) +function Broadcasted{Style}(f::F, args::Args, axes=nothing) where {Style, F, Args<:Tuple} + # using Core.Typeof rather than F preserves inferrability when f is a type + Broadcasted{Style, typeof(axes), Core.Typeof(f), Args}(f, args, axes) +end + +Base.convert(::Type{Broadcasted{NewStyle}}, bc::Broadcasted{Style,Axes,F,Args}) where {NewStyle,Style,Axes,F,Args} = + Broadcasted{NewStyle,Axes,F,Args}(bc.f, bc.args, bc.axes) + +function Base.show(io::IO, bc::Broadcasted{Style}) where {Style} + print(io, Broadcasted) + # Only show the style parameter if we have a set of axes — representing an instantiated + # "outermost" Broadcasted. The styles of nested Broadcasteds represent an intermediate + # computation that is not relevant for dispatch, confusing, and just extra line noise. + bc.axes isa Tuple && print(io, '{', Style, '}') + print(io, '(', bc.f, ", ", bc.args, ')') + nothing +end + +## Allocating the output container +Base.similar(bc::Broadcasted{DefaultArrayStyle{N}}, ::Type{ElType}) where {N,ElType} = + similar(Array{ElType}, axes(bc)) +Base.similar(bc::Broadcasted{DefaultArrayStyle{N}}, ::Type{Bool}) where N = + similar(BitArray, axes(bc)) +# In cases of conflict we fall back on Array +Base.similar(bc::Broadcasted{ArrayConflict}, ::Type{ElType}) where ElType = + similar(Array{ElType}, axes(bc)) +Base.similar(bc::Broadcasted{ArrayConflict}, ::Type{Bool}) = + similar(BitArray, axes(bc)) + +@inline Base.axes(bc::Broadcasted) = _axes(bc, bc.axes) +_axes(::Broadcasted, axes::Tuple) = axes +@inline _axes(bc::Broadcasted, ::Nothing) = combine_axes(bc.args...) +_axes(bc::Broadcasted{<:AbstractArrayStyle{0}}, ::Nothing) = () + +BroadcastStyle(::Type{<:Broadcasted{Style}}) where {Style} = Style() +BroadcastStyle(::Type{<:Broadcasted{S}}) where {S<:Union{Nothing,Unknown}} = + throw(ArgumentError("Broadcasted{Unknown} wrappers do not have a style assigned")) + +argtype(::Type{Broadcasted{Style,Axes,F,Args}}) where {Style,Axes,F,Args} = Args +argtype(bc::Broadcasted) = argtype(typeof(bc)) + +@inline Base.eachindex(bc::Broadcasted) = _eachindex(axes(bc)) +_eachindex(t::Tuple{Any}) = t[1] +_eachindex(t::Tuple) = CartesianIndices(t) + +Base.ndims(::Broadcasted{<:Any,<:NTuple{N,Any}}) where {N} = N +Base.ndims(::Type{<:Broadcasted{<:Any,<:NTuple{N,Any}}}) where {N} = N + +Base.size(bc::Broadcasted) = map(length, axes(bc)) +Base.length(bc::Broadcasted) = prod(size(bc)) + +function Base.iterate(bc::Broadcasted) + iter = eachindex(bc) + iterate(bc, (iter,)) +end +Base.@propagate_inbounds function Base.iterate(bc::Broadcasted, s) + y = iterate(s...) + y === nothing && return nothing + i, newstate = y + return (bc[i], (s[1], newstate)) +end + +Base.IteratorSize(::Type{<:Broadcasted{<:Any,<:NTuple{N,Base.OneTo}}}) where {N} = Base.HasShape{N}() +Base.IteratorEltype(::Type{<:Broadcasted}) = Base.EltypeUnknown() + +## Instantiation fills in the "missing" fields in Broadcasted. +instantiate(x) = x + +""" + Broadcast.instantiate(bc::Broadcasted) + +Construct and check the axes for the lazy Broadcasted object `bc`. + +Custom [`BroadcastStyle`](@ref)s may override this default in cases where it is fast and easy +to compute and verify the resulting `axes` on-demand, leaving the `axis` field +of the `Broadcasted` object empty (populated with [`nothing`](@ref)). +""" +@inline function instantiate(bc::Broadcasted{Style}) where {Style} + if bc.axes isa Nothing # Not done via dispatch to make it easier to extend instantiate(::Broadcasted{Style}) + axes = combine_axes(bc.args...) + else + axes = bc.axes + check_broadcast_axes(axes, bc.args...) + end + return Broadcasted{Style}(bc.f, bc.args, axes) +end +instantiate(bc::Broadcasted{<:AbstractArrayStyle{0}}) = bc +# Tuples don't need axes, but when they have axes (for .= assignment), we need to check them (#33020) +instantiate(bc::Broadcasted{Style{Tuple}, Nothing}) = bc +function instantiate(bc::Broadcasted{Style{Tuple}}) + check_broadcast_axes(bc.axes, bc.args...) + return bc +end +## Flattening + +""" + bcf = flatten(bc) + +Create a "flat" representation of a lazy-broadcast operation. +From + f.(a, g.(b, c), d) +we produce the equivalent of + h.(a, b, c, d) +where + h(w, x, y, z) = f(w, g(x, y), z) +In terms of its internal representation, + Broadcasted(f, a, Broadcasted(g, b, c), d) +becomes + Broadcasted(h, a, b, c, d) + +This is an optional operation that may make custom implementation of broadcasting easier in +some cases. +""" +function flatten(bc::Broadcasted{Style}) where {Style} + isflat(bc) && return bc + # concatenate the nested arguments into {a, b, c, d} + args = cat_nested(bc) + # build a function `makeargs` that takes a "flat" argument list and + # and creates the appropriate input arguments for `f`, e.g., + # makeargs = (w, x, y, z) -> (w, g(x, y), z) + # + # `makeargs` is built recursively and looks a bit like this: + # makeargs(w, x, y, z) = (w, makeargs1(x, y, z)...) + # = (w, g(x, y), makeargs2(z)...) + # = (w, g(x, y), z) + let makeargs = make_makeargs(()->(), bc.args), f = bc.f + newf = @inline function(args::Vararg{Any,N}) where N + f(makeargs(args...)...) + end + return Broadcasted{Style}(newf, args, bc.axes) + end +end + +const NestedTuple = Tuple{<:Broadcasted,Vararg{Any}} +isflat(bc::Broadcasted) = _isflat(bc.args) +_isflat(args::NestedTuple) = false +_isflat(args::Tuple) = _isflat(tail(args)) +_isflat(args::Tuple{}) = true + +cat_nested(t::Broadcasted, rest...) = (cat_nested(t.args...)..., cat_nested(rest...)...) +cat_nested(t::Any, rest...) = (t, cat_nested(rest...)...) +cat_nested() = () + +""" + make_makeargs(makeargs_tail::Function, t::Tuple) -> Function + +Each element of `t` is one (consecutive) node in a broadcast tree. +Ignoring `makeargs_tail` for the moment, the job of `make_makeargs` is +to return a function that takes in flattened argument list and returns a +tuple (each entry corresponding to an entry in `t`, having evaluated +the corresponding element in the broadcast tree). As an additional +complication, the passed in tuple may be longer than the number of leaves +in the subtree described by `t`. The `makeargs_tail` function should +be called on such additional arguments (but not the arguments consumed +by `t`). +""" +@inline make_makeargs(makeargs_tail, t::Tuple{}) = makeargs_tail +@inline function make_makeargs(makeargs_tail, t::Tuple) + makeargs = make_makeargs(makeargs_tail, tail(t)) + (head, tail...)->(head, makeargs(tail...)...) +end +function make_makeargs(makeargs_tail, t::Tuple{<:Broadcasted, Vararg{Any}}) + bc = t[1] + # c.f. the same expression in the function on leaf nodes above. Here + # we recurse into siblings in the broadcast tree. + let makeargs_tail = make_makeargs(makeargs_tail, tail(t)), + # Here we recurse into children. It would be valid to pass in makeargs_tail + # here, and not use it below. However, in that case, our recursion is no + # longer purely structural because we're building up one argument (the closure) + # while destructuing another. + makeargs_head = make_makeargs((args...)->args, bc.args), + f = bc.f + # Create two functions, one that splits of the first length(bc.args) + # elements from the tuple and one that yields the remaining arguments. + # N.B. We can't call headargs on `args...` directly because + # args is flattened (i.e. our children have not been evaluated + # yet). + headargs, tailargs = make_headargs(bc.args), make_tailargs(bc.args) + return @inline function(args::Vararg{Any,N}) where N + args1 = makeargs_head(args...) + a, b = headargs(args1...), makeargs_tail(tailargs(args1...)...) + (f(a...), b...) + end + end +end + +@inline function make_headargs(t::Tuple) + let headargs = make_headargs(tail(t)) + return @inline function(head, tail::Vararg{Any,N}) where N + (head, headargs(tail...)...) + end + end +end +@inline function make_headargs(::Tuple{}) + return @inline function(tail::Vararg{Any,N}) where N + () + end +end + +@inline function make_tailargs(t::Tuple) + let tailargs = make_tailargs(tail(t)) + return @inline function(head, tail::Vararg{Any,N}) where N + tailargs(tail...) + end + end +end +@inline function make_tailargs(::Tuple{}) + return @inline function(tail::Vararg{Any,N}) where N + tail + end +end + +## Broadcasting utilities ## + +## logic for deciding the BroadcastStyle + +""" + combine_styles(cs...) -> BroadcastStyle + +Decides which `BroadcastStyle` to use for any number of value arguments. +Uses [`BroadcastStyle`](@ref) to get the style for each argument, and uses +[`result_style`](@ref) to combine styles. + +# Examples + +```jldoctest +julia> Broadcast.combine_styles([1], [1 2; 3 4]) +Base.Broadcast.DefaultArrayStyle{2}() +``` +""" +function combine_styles end + +combine_styles() = DefaultArrayStyle{0}() +combine_styles(c) = result_style(BroadcastStyle(typeof(c))) +combine_styles(c1, c2) = result_style(combine_styles(c1), combine_styles(c2)) +@inline combine_styles(c1, c2, cs...) = result_style(combine_styles(c1), combine_styles(c2, cs...)) + +""" + result_style(s1::BroadcastStyle[, s2::BroadcastStyle]) -> BroadcastStyle + +Takes one or two `BroadcastStyle`s and combines them using [`BroadcastStyle`](@ref) to +determine a common `BroadcastStyle`. + +# Examples + +```jldoctest +julia> Broadcast.result_style(Broadcast.DefaultArrayStyle{0}(), Broadcast.DefaultArrayStyle{3}()) +Base.Broadcast.DefaultArrayStyle{3}() + +julia> Broadcast.result_style(Broadcast.Unknown(), Broadcast.DefaultArrayStyle{1}()) +Base.Broadcast.DefaultArrayStyle{1}() +``` +""" +function result_style end + +result_style(s::BroadcastStyle) = s +result_style(s1::S, s2::S) where S<:BroadcastStyle = S() +# Test both orders so users typically only have to declare one order +result_style(s1, s2) = result_join(s1, s2, BroadcastStyle(s1, s2), BroadcastStyle(s2, s1)) + +# result_join is the final arbiter. Because `BroadcastStyle` for undeclared pairs results in Unknown, +# we defer to any case where the result of `BroadcastStyle` is known. +result_join(::Any, ::Any, ::Unknown, ::Unknown) = Unknown() +result_join(::Any, ::Any, ::Unknown, s::BroadcastStyle) = s +result_join(::Any, ::Any, s::BroadcastStyle, ::Unknown) = s +# For AbstractArray types with specialized broadcasting and undefined precedence rules, +# we have to signal conflict. Because ArrayConflict is a subtype of AbstractArray, +# this will "poison" any future operations (if we instead returned `DefaultArrayStyle`, then for +# 3-array broadcasting the returned type would depend on argument order). +result_join(::AbstractArrayStyle, ::AbstractArrayStyle, ::Unknown, ::Unknown) = + ArrayConflict() +# Fallbacks in case users define `rule` for both argument-orders (not recommended) +result_join(::Any, ::Any, ::S, ::S) where S<:BroadcastStyle = S() +@noinline function result_join(::S, ::T, ::U, ::V) where {S,T,U,V} + error(""" +conflicting broadcast rules defined + Broadcast.BroadcastStyle(::$S, ::$T) = $U() + Broadcast.BroadcastStyle(::$T, ::$S) = $V() +One of these should be undefined (and thus return Broadcast.Unknown).""") +end + +# Indices utilities + +""" + combine_axes(As...) -> Tuple + +Determine the result axes for broadcasting across all values in `As`. + +```jldoctest +julia> Broadcast.combine_axes([1], [1 2; 3 4; 5 6]) +(Base.OneTo(3), Base.OneTo(2)) + +julia> Broadcast.combine_axes(1, 1, 1) +() +``` +""" +@inline combine_axes(A, B...) = broadcast_shape(axes(A), combine_axes(B...)) +combine_axes(A) = axes(A) + +# shape (i.e., tuple-of-indices) inputs +broadcast_shape(shape::Tuple) = shape +broadcast_shape(shape::Tuple, shape1::Tuple, shapes::Tuple...) = broadcast_shape(_bcs(shape, shape1), shapes...) +# _bcs consolidates two shapes into a single output shape +_bcs(::Tuple{}, ::Tuple{}) = () +_bcs(::Tuple{}, newshape::Tuple) = (newshape[1], _bcs((), tail(newshape))...) +_bcs(shape::Tuple, ::Tuple{}) = (shape[1], _bcs(tail(shape), ())...) +function _bcs(shape::Tuple, newshape::Tuple) + return (_bcs1(shape[1], newshape[1]), _bcs(tail(shape), tail(newshape))...) +end +# _bcs1 handles the logic for a single dimension +_bcs1(a::Integer, b::Integer) = a == 1 ? b : (b == 1 ? a : (a == b ? a : throw(DimensionMismatch("arrays could not be broadcast to a common size; got a dimension with lengths $a and $b")))) +_bcs1(a::Integer, b) = a == 1 ? b : (first(b) == 1 && last(b) == a ? b : throw(DimensionMismatch("arrays could not be broadcast to a common size; got a dimension with lengths $a and $(length(b))"))) +_bcs1(a, b::Integer) = _bcs1(b, a) +_bcs1(a, b) = _bcsm(b, a) ? axistype(b, a) : (_bcsm(a, b) ? axistype(a, b) : throw(DimensionMismatch("arrays could not be broadcast to a common size; got a dimension with lengths $(length(a)) and $(length(b))"))) +# _bcsm tests whether the second index is consistent with the first +_bcsm(a, b) = a == b || length(b) == 1 +_bcsm(a, b::Number) = b == 1 +_bcsm(a::Number, b::Number) = a == b || b == 1 +# Ensure inferrability when dealing with axes of different AbstractUnitRange types +# (We may not want to define general promotion rules between, say, OneTo and Slice, but if +# we get here we know the axes are at least consistent for the purposes of broadcasting) +axistype(a::T, b::T) where T = a +axistype(a, b) = UnitRange{Int}(a) + +## Check that all arguments are broadcast compatible with shape +# comparing one input against a shape +check_broadcast_shape(shp) = nothing +check_broadcast_shape(shp, ::Tuple{}) = nothing +check_broadcast_shape(::Tuple{}, ::Tuple{}) = nothing +check_broadcast_shape(::Tuple{}, Ashp::Tuple) = throw(DimensionMismatch("cannot broadcast array to have fewer dimensions")) +function check_broadcast_shape(shp, Ashp::Tuple) + _bcsm(shp[1], Ashp[1]) || throw(DimensionMismatch("array could not be broadcast to match destination")) + check_broadcast_shape(tail(shp), tail(Ashp)) +end +@inline check_broadcast_axes(shp, A) = check_broadcast_shape(shp, axes(A)) +# comparing many inputs +@inline function check_broadcast_axes(shp, A, As...) + check_broadcast_axes(shp, A) + check_broadcast_axes(shp, As...) +end + +## Indexing manipulations +""" + newindex(argument, I) + newindex(I, keep, default) + +Recompute index `I` such that it appropriately constrains broadcasted dimensions to the source. + +Two methods are supported, both allowing for `I` to be specified as either a [`CartesianIndex`](@ref) or +an `Int`. + +* `newindex(argument, I)` dynamically constrains `I` based upon the axes of `argument`. +* `newindex(I, keep, default)` constrains `I` using the pre-computed tuples `keeps` and `defaults`. + * `keep` is a tuple of `Bool`s, where `keep[d] == true` means that dimension `d` in `I` should be preserved as is + * `default` is a tuple of Integers, specifying what index to use in dimension `d` when `keep[d] == false`. + Any remaining indices in `I` beyond the length of the `keep` tuple are truncated. The `keep` and `default` + tuples may be created by `newindexer(argument)`. +""" +Base.@propagate_inbounds newindex(arg, I::CartesianIndex) = CartesianIndex(_newindex(axes(arg), I.I)) +Base.@propagate_inbounds newindex(arg, I::Integer) = CartesianIndex(_newindex(axes(arg), (I,))) +Base.@propagate_inbounds _newindex(ax::Tuple, I::Tuple) = (ifelse(Base.unsafe_length(ax[1])==1, ax[1][1], I[1]), _newindex(tail(ax), tail(I))...) +Base.@propagate_inbounds _newindex(ax::Tuple{}, I::Tuple) = () +Base.@propagate_inbounds _newindex(ax::Tuple, I::Tuple{}) = (ax[1][1], _newindex(tail(ax), ())...) +Base.@propagate_inbounds _newindex(ax::Tuple{}, I::Tuple{}) = () + +# If dot-broadcasting were already defined, this would be `ifelse.(keep, I, Idefault)`. +@inline newindex(I::CartesianIndex, keep, Idefault) = CartesianIndex(_newindex(I.I, keep, Idefault)) +@inline newindex(i::Integer, keep::Tuple{Bool}, idefault) = ifelse(keep[1], i, idefault[1]) +@inline newindex(i::Integer, keep::Tuple{}, idefault) = CartesianIndex(()) +@inline _newindex(I, keep, Idefault) = + (ifelse(keep[1], I[1], Idefault[1]), _newindex(tail(I), tail(keep), tail(Idefault))...) +@inline _newindex(I, keep::Tuple{}, Idefault) = () # truncate if keep is shorter than I + +# newindexer(A) generates `keep` and `Idefault` (for use by `newindex` above) +# for a particular array `A`; `shapeindexer` does so for its axes. +@inline newindexer(A) = shapeindexer(axes(A)) +@inline shapeindexer(ax) = _newindexer(ax) +@inline _newindexer(indsA::Tuple{}) = (), () +@inline function _newindexer(indsA::Tuple) + ind1 = indsA[1] + keep, Idefault = _newindexer(tail(indsA)) + (Base.length(ind1)::Integer != 1, keep...), (first(ind1), Idefault...) +end + +@inline function Base.getindex(bc::Broadcasted, I::Union{Integer,CartesianIndex}) + @boundscheck checkbounds(bc, I) + @inbounds _broadcast_getindex(bc, I) +end +Base.@propagate_inbounds Base.getindex(bc::Broadcasted, i1::Integer, i2::Integer, I::Integer...) = bc[CartesianIndex((i1, i2, I...))] +Base.@propagate_inbounds Base.getindex(bc::Broadcasted) = bc[CartesianIndex(())] + +@inline Base.checkbounds(bc::Broadcasted, I::Union{Integer,CartesianIndex}) = + Base.checkbounds_indices(Bool, axes(bc), (I,)) || Base.throw_boundserror(bc, (I,)) + + +""" + _broadcast_getindex(A, I) + +Index into `A` with `I`, collapsing broadcasted indices to their singleton indices as appropriate. +""" +Base.@propagate_inbounds _broadcast_getindex(A::Union{Ref,AbstractArray{<:Any,0},Number}, I) = A[] # Scalar-likes can just ignore all indices +Base.@propagate_inbounds _broadcast_getindex(::Ref{Type{T}}, I) where {T} = T +# Tuples are statically known to be singleton or vector-like +Base.@propagate_inbounds _broadcast_getindex(A::Tuple{Any}, I) = A[1] +Base.@propagate_inbounds _broadcast_getindex(A::Tuple, I) = A[I[1]] +# Everything else falls back to dynamically dropping broadcasted indices based upon its axes +Base.@propagate_inbounds _broadcast_getindex(A, I) = A[newindex(A, I)] + +# In some cases, it's more efficient to sort out which dimensions should be dropped +# ahead of time (often when the size checks aren't able to be lifted out of the loop). +# The Extruded struct computes that information ahead of time and stores it as a pair +# of tuples to optimize indexing later. This is most commonly needed for `Array` and +# other `AbstractArray` subtypes that wrap `Array` and dynamically ask it for its size. +struct Extruded{T, K, D} + x::T + keeps::K # A tuple of booleans, specifying which indices should be passed normally + defaults::D # A tuple of integers, specifying the index to use when keeps[i] is false (as defaults[i]) +end +@inline axes(b::Extruded) = axes(b.x) +Base.@propagate_inbounds _broadcast_getindex(b::Extruded, i) = b.x[newindex(i, b.keeps, b.defaults)] +extrude(x::AbstractArray) = Extruded(x, newindexer(x)...) +extrude(x) = x + +# For Broadcasted +Base.@propagate_inbounds function _broadcast_getindex(bc::Broadcasted{<:Any,<:Any,<:Any,<:Any}, I) + args = _getindex(bc.args, I) + return _broadcast_getindex_evalf(bc.f, args...) +end +# Hack around losing Type{T} information in the final args tuple. Julia actually +# knows (in `code_typed`) the _value_ of these types, statically displaying them, +# but inference is currently skipping inferring the type of the types as they are +# transiently placed in a tuple as the argument list is lispily constructed. These +# additional methods recover type stability when a `Type` appears in one of the +# first two arguments of a function. +Base.@propagate_inbounds function _broadcast_getindex(bc::Broadcasted{<:Any,<:Any,<:Any,<:Tuple{Ref{Type{T}},Vararg{Any}}}, I) where {T} + args = _getindex(tail(bc.args), I) + return _broadcast_getindex_evalf(bc.f, T, args...) +end +Base.@propagate_inbounds function _broadcast_getindex(bc::Broadcasted{<:Any,<:Any,<:Any,<:Tuple{Any,Ref{Type{T}},Vararg{Any}}}, I) where {T} + arg1 = _broadcast_getindex(bc.args[1], I) + args = _getindex(tail(tail(bc.args)), I) + return _broadcast_getindex_evalf(bc.f, arg1, T, args...) +end +Base.@propagate_inbounds function _broadcast_getindex(bc::Broadcasted{<:Any,<:Any,<:Any,<:Tuple{Ref{Type{T}},Ref{Type{S}},Vararg{Any}}}, I) where {T,S} + args = _getindex(tail(tail(bc.args)), I) + return _broadcast_getindex_evalf(bc.f, T, S, args...) +end + +# Utilities for _broadcast_getindex +Base.@propagate_inbounds _getindex(args::Tuple, I) = (_broadcast_getindex(args[1], I), _getindex(tail(args), I)...) +Base.@propagate_inbounds _getindex(args::Tuple{Any}, I) = (_broadcast_getindex(args[1], I),) +Base.@propagate_inbounds _getindex(args::Tuple{}, I) = () + +@inline _broadcast_getindex_evalf(f::Tf, args::Vararg{Any,N}) where {Tf,N} = f(args...) # not propagate_inbounds + +""" + Broadcast.broadcastable(x) + +Return either `x` or an object like `x` such that it supports [`axes`](@ref), indexing, and its type supports [`ndims`](@ref). + +If `x` supports iteration, the returned value should have the same `axes` and indexing +behaviors as [`collect(x)`](@ref). + +If `x` is not an `AbstractArray` but it supports `axes`, indexing, and its type supports +`ndims`, then `broadcastable(::typeof(x))` may be implemented to just return itself. +Further, if `x` defines its own [`BroadcastStyle`](@ref), then it must define its +`broadcastable` method to return itself for the custom style to have any effect. + +# Examples +```jldoctest +julia> Broadcast.broadcastable([1,2,3]) # like `identity` since arrays already support axes and indexing +3-element Array{Int64,1}: + 1 + 2 + 3 + +julia> Broadcast.broadcastable(Int) # Types don't support axes, indexing, or iteration but are commonly used as scalars +Base.RefValue{Type{Int64}}(Int64) + +julia> Broadcast.broadcastable("hello") # Strings break convention of matching iteration and act like a scalar instead +Base.RefValue{String}("hello") +``` +""" +broadcastable(x::Union{Symbol,AbstractString,Function,UndefInitializer,Nothing,RoundingMode,Missing,Val,Ptr,Regex,Pair}) = Ref(x) +broadcastable(::Type{T}) where {T} = Ref{Type{T}}(T) +broadcastable(x::Union{AbstractArray,Number,Ref,Tuple,Broadcasted}) = x +# Default to collecting iterables — which will error for non-iterables +broadcastable(x) = collect(x) +broadcastable(::Union{AbstractDict, NamedTuple}) = throw(ArgumentError("broadcasting over dictionaries and `NamedTuple`s is reserved")) + +## Computation of inferred result type, for empty and concretely inferred cases only +_broadcast_getindex_eltype(bc::Broadcasted) = Base._return_type(bc.f, eltypes(bc.args)) +_broadcast_getindex_eltype(A) = eltype(A) # Tuple, Array, etc. + +eltypes(::Tuple{}) = Tuple{} +eltypes(t::Tuple{Any}) = Tuple{_broadcast_getindex_eltype(t[1])} +eltypes(t::Tuple{Any,Any}) = Tuple{_broadcast_getindex_eltype(t[1]), _broadcast_getindex_eltype(t[2])} +eltypes(t::Tuple) = Tuple{_broadcast_getindex_eltype(t[1]), eltypes(tail(t)).types...} + +# Inferred eltype of result of broadcast(f, args...) +combine_eltypes(f, args::Tuple) = Base._return_type(f, eltypes(args)) + +## Broadcasting core + +""" + broadcast(f, As...) + +Broadcast the function `f` over the arrays, tuples, collections, [`Ref`](@ref)s and/or scalars `As`. + +Broadcasting applies the function `f` over the elements of the container arguments and the +scalars themselves in `As`. Singleton and missing dimensions are expanded to match the +extents of the other arguments by virtually repeating the value. By default, only a limited +number of types are considered scalars, including `Number`s, `String`s, `Symbol`s, `Type`s, +`Function`s and some common singletons like [`missing`](@ref) and [`nothing`](@ref). All other arguments are +iterated over or indexed into elementwise. + +The resulting container type is established by the following rules: + + - If all the arguments are scalars or zero-dimensional arrays, it returns an unwrapped scalar. + - If at least one argument is a tuple and all others are scalars or zero-dimensional arrays, + it returns a tuple. + - All other combinations of arguments default to returning an `Array`, but + custom container types can define their own implementation and promotion-like + rules to customize the result when they appear as arguments. + +A special syntax exists for broadcasting: `f.(args...)` is equivalent to +`broadcast(f, args...)`, and nested `f.(g.(args...))` calls are fused into a +single broadcast loop. + +# Examples +```jldoctest +julia> A = [1, 2, 3, 4, 5] +5-element Array{Int64,1}: + 1 + 2 + 3 + 4 + 5 + +julia> B = [1 2; 3 4; 5 6; 7 8; 9 10] +5×2 Array{Int64,2}: + 1 2 + 3 4 + 5 6 + 7 8 + 9 10 + +julia> broadcast(+, A, B) +5×2 Array{Int64,2}: + 2 3 + 5 6 + 8 9 + 11 12 + 14 15 + +julia> parse.(Int, ["1", "2"]) +2-element Array{Int64,1}: + 1 + 2 + +julia> abs.((1, -2)) +(1, 2) + +julia> broadcast(+, 1.0, (0, -2.0)) +(1.0, -1.0) + +julia> (+).([[0,2], [1,3]], Ref{Vector{Int}}([1,-1])) +2-element Array{Array{Int64,1},1}: + [1, 1] + [2, 2] + +julia> string.(("one","two","three","four"), ": ", 1:4) +4-element Array{String,1}: + "one: 1" + "two: 2" + "three: 3" + "four: 4" + +``` +""" +broadcast(f::Tf, As...) where {Tf} = materialize(broadcasted(f, As...)) + +# special cases defined for performance +@inline broadcast(f, x::Number...) = f(x...) +@inline broadcast(f, t::NTuple{N,Any}, ts::Vararg{NTuple{N,Any}}) where {N} = map(f, t, ts...) + +""" + broadcast!(f, dest, As...) + +Like [`broadcast`](@ref), but store the result of +`broadcast(f, As...)` in the `dest` array. +Note that `dest` is only used to store the result, and does not supply +arguments to `f` unless it is also listed in the `As`, +as in `broadcast!(f, A, A, B)` to perform `A[:] = broadcast(f, A, B)`. + +# Examples +```jldoctest +julia> A = [1.0; 0.0]; B = [0.0; 0.0]; + +julia> broadcast!(+, B, A, (0, -2.0)); + +julia> B +2-element Array{Float64,1}: + 1.0 + -2.0 + +julia> A +2-element Array{Float64,1}: + 1.0 + 0.0 + +julia> broadcast!(+, A, A, (0, -2.0)); + +julia> A +2-element Array{Float64,1}: + 1.0 + -2.0 +``` +""" +broadcast!(f::Tf, dest, As::Vararg{Any,N}) where {Tf,N} = (materialize!(dest, broadcasted(f, As...)); dest) + +""" + broadcast_preserving_zero_d(f, As...) + +Like [`broadcast`](@ref), except in the case of a 0-dimensional result where it returns a 0-dimensional container + +Broadcast automatically unwraps zero-dimensional results to be just the element itself, +but in some cases it is necessary to always return a container — even in the 0-dimensional case. +""" +@inline function broadcast_preserving_zero_d(f, As...) + bc = broadcasted(f, As...) + r = materialize(bc) + return length(axes(bc)) == 0 ? fill!(similar(bc, typeof(r)), r) : r +end +@inline broadcast_preserving_zero_d(f) = fill(f()) +@inline broadcast_preserving_zero_d(f, as::Number...) = fill(f(as...)) + +""" + Broadcast.materialize(bc) + +Take a lazy `Broadcasted` object and compute the result +""" +@inline materialize(bc::Broadcasted) = copy(instantiate(bc)) +materialize(x) = x +@inline function materialize!(dest, bc::Broadcasted{Style}) where {Style} + return copyto!(dest, instantiate(Broadcasted{Style}(bc.f, bc.args, axes(dest)))) +end +@inline function materialize!(dest, x) + return copyto!(dest, instantiate(Broadcasted(identity, (x,), axes(dest)))) +end + +## general `copy` methods +@inline copy(bc::Broadcasted{<:AbstractArrayStyle{0}}) = bc[CartesianIndex()] +copy(bc::Broadcasted{<:Union{Nothing,Unknown}}) = + throw(ArgumentError("broadcasting requires an assigned BroadcastStyle")) + +const NonleafHandlingStyles = Union{DefaultArrayStyle,ArrayConflict} + +@inline function copy(bc::Broadcasted{Style}) where {Style} + ElType = combine_eltypes(bc.f, bc.args) + if Base.isconcretetype(ElType) + # We can trust it and defer to the simpler `copyto!` + return copyto!(similar(bc, ElType), bc) + end + # When ElType is not concrete, use narrowing. Use the first output + # value to determine the starting output eltype; copyto_nonleaf! + # will widen `dest` as needed to accommodate later values. + bc′ = preprocess(nothing, bc) + iter = eachindex(bc′) + y = iterate(iter) + if y === nothing + # if empty, take the ElType at face value + return similar(bc′, ElType) + end + # Initialize using the first value + I, state = y + @inbounds val = bc′[I] + dest = similar(bc′, typeof(val)) + @inbounds dest[I] = val + # Now handle the remaining values + return copyto_nonleaf!(dest, bc′, iter, state, 1) +end + +## general `copyto!` methods +# The most general method falls back to a method that replaces Style->Nothing +# This permits specialization on typeof(dest) without introducing ambiguities +@inline copyto!(dest::AbstractArray, bc::Broadcasted) = copyto!(dest, convert(Broadcasted{Nothing}, bc)) + +# Performance optimization for the common identity scalar case: dest .= val +@inline function copyto!(dest::AbstractArray, bc::Broadcasted{<:AbstractArrayStyle{0}}) + # Typically, we must independently execute bc for every storage location in `dest`, but: + # IF we're in the common no-op identity case with no nested args (like `dest .= val`), + if bc.f === identity && bc.args isa Tuple{Any} && isflat(bc) + # THEN we can just extract the argument and `fill!` the destination with it + return fill!(dest, bc.args[1][]) + else + # Otherwise, fall back to the default implementation like above + return copyto!(dest, convert(Broadcasted{Nothing}, bc)) + end +end + +# For broadcasted assignments like `broadcast!(f, A, ..., A, ...)`, where `A` +# appears on both the LHS and the RHS of the `.=`, then we know we're only +# going to make one pass through the array, and even though `A` is aliasing +# against itself, the mutations won't affect the result as the indices on the +# LHS and RHS will always match. This is not true in general, but with the `.op=` +# syntax it's fairly common for an argument to be `===` a source. +broadcast_unalias(dest, src) = dest === src ? src : unalias(dest, src) +broadcast_unalias(::Nothing, src) = src + +# Preprocessing a `Broadcasted` does two things: +# * unaliases any arguments from `dest` +# * "extrudes" the arguments where it is advantageous to pre-compute the broadcasted indices +@inline preprocess(dest, bc::Broadcasted{Style}) where {Style} = Broadcasted{Style}(bc.f, preprocess_args(dest, bc.args), bc.axes) +preprocess(dest, x) = extrude(broadcast_unalias(dest, x)) + +@inline preprocess_args(dest, args::Tuple) = (preprocess(dest, args[1]), preprocess_args(dest, tail(args))...) +preprocess_args(dest, args::Tuple{Any}) = (preprocess(dest, args[1]),) +preprocess_args(dest, args::Tuple{}) = () + +# Specialize this method if all you want to do is specialize on typeof(dest) +@inline function copyto!(dest::AbstractArray, bc::Broadcasted{Nothing}) + axes(dest) == axes(bc) || throwdm(axes(dest), axes(bc)) + # Performance optimization: broadcast!(identity, dest, A) is equivalent to copyto!(dest, A) if indices match + if bc.f === identity && bc.args isa Tuple{AbstractArray} # only a single input argument to broadcast! + A = bc.args[1] + if axes(dest) == axes(A) + return copyto!(dest, A) + end + end + bc′ = preprocess(dest, bc) + @simd for I in eachindex(bc′) + @inbounds dest[I] = bc′[I] + end + return dest +end + +# Performance optimization: for BitArray outputs, we cache the result +# in a "small" Vector{Bool}, and then copy in chunks into the output +@inline function copyto!(dest::BitArray, bc::Broadcasted{Nothing}) + axes(dest) == axes(bc) || throwdm(axes(dest), axes(bc)) + ischunkedbroadcast(dest, bc) && return chunkedcopyto!(dest, bc) + tmp = Vector{Bool}(undef, bitcache_size) + destc = dest.chunks + ind = cind = 1 + bc′ = preprocess(dest, bc) + @simd for I in eachindex(bc′) + @inbounds tmp[ind] = bc′[I] + ind += 1 + if ind > bitcache_size + dumpbitcache(destc, cind, tmp) + cind += bitcache_chunks + ind = 1 + end + end + if ind > 1 + @inbounds tmp[ind:bitcache_size] .= false + dumpbitcache(destc, cind, tmp) + end + return dest +end + +# For some BitArray operations, we can work at the level of chunks. The trivial +# implementation just walks over the UInt64 chunks in a linear fashion. +# This requires three things: +# 1. The function must be known to work at the level of chunks (or can be converted to do so) +# 2. The only arrays involved must be BitArrays or scalar Bools +# 3. There must not be any broadcasting beyond scalar — all array sizes must match +# We could eventually allow for all broadcasting and other array types, but that +# requires very careful consideration of all the edge effects. +const ChunkableOp = Union{typeof(&), typeof(|), typeof(xor), typeof(~), typeof(identity), + typeof(!), typeof(*), typeof(==)} # these are convertible to chunkable ops by liftfuncs +const BroadcastedChunkableOp{Style<:Union{Nothing,BroadcastStyle}, Axes, F<:ChunkableOp, Args<:Tuple} = Broadcasted{Style,Axes,F,Args} +ischunkedbroadcast(R, bc::BroadcastedChunkableOp) = ischunkedbroadcast(R, bc.args) +ischunkedbroadcast(R, args) = false +ischunkedbroadcast(R, args::Tuple{<:BitArray,Vararg{Any}}) = size(R) == size(args[1]) && ischunkedbroadcast(R, tail(args)) +ischunkedbroadcast(R, args::Tuple{<:Bool,Vararg{Any}}) = ischunkedbroadcast(R, tail(args)) +ischunkedbroadcast(R, args::Tuple{<:BroadcastedChunkableOp,Vararg{Any}}) = ischunkedbroadcast(R, args[1]) && ischunkedbroadcast(R, tail(args)) +ischunkedbroadcast(R, args::Tuple{}) = true + +# Convert compatible functions to chunkable ones. They must also be green-lighted as ChunkableOps +liftfuncs(bc::Broadcasted{Style}) where {Style} = Broadcasted{Style}(bc.f, map(liftfuncs, bc.args), bc.axes) +liftfuncs(bc::Broadcasted{Style,<:Any,typeof(sign)}) where {Style} = Broadcasted{Style}(identity, map(liftfuncs, bc.args), bc.axes) +liftfuncs(bc::Broadcasted{Style,<:Any,typeof(!)}) where {Style} = Broadcasted{Style}(~, map(liftfuncs, bc.args), bc.axes) +liftfuncs(bc::Broadcasted{Style,<:Any,typeof(*)}) where {Style} = Broadcasted{Style}(&, map(liftfuncs, bc.args), bc.axes) +liftfuncs(bc::Broadcasted{Style,<:Any,typeof(==)}) where {Style} = Broadcasted{Style}((~)∘(xor), map(liftfuncs, bc.args), bc.axes) +liftfuncs(x) = x + +liftchunks(::Tuple{}) = () +liftchunks(args::Tuple{<:BitArray,Vararg{Any}}) = (args[1].chunks, liftchunks(tail(args))...) +# Transform scalars to repeated scalars the size of a chunk +liftchunks(args::Tuple{<:Bool,Vararg{Any}}) = (ifelse(args[1], typemax(UInt64), UInt64(0)), liftchunks(tail(args))...) +ithchunk(i) = () +Base.@propagate_inbounds ithchunk(i, c::Vector{UInt64}, args...) = (c[i], ithchunk(i, args...)...) +Base.@propagate_inbounds ithchunk(i, b::UInt64, args...) = (b, ithchunk(i, args...)...) +@inline function chunkedcopyto!(dest::BitArray, bc::Broadcasted) + isempty(dest) && return dest + f = flatten(liftfuncs(bc)) + args = liftchunks(f.args) + dc = dest.chunks + @simd for i in eachindex(dc) + @inbounds dc[i] = f.f(ithchunk(i, args...)...) + end + @inbounds dc[end] &= Base._msk_end(dest) + return dest +end + + +@noinline throwdm(axdest, axsrc) = + throw(DimensionMismatch("destination axes $axdest are not compatible with source axes $axsrc")) + +function restart_copyto_nonleaf!(newdest, dest, bc, val, I, iter, state, count) + # Function barrier that makes the copying to newdest type stable + for II in Iterators.take(iter, count) + newdest[II] = dest[II] + end + newdest[I] = val + return copyto_nonleaf!(newdest, bc, iter, state, count+1) +end + +function copyto_nonleaf!(dest, bc::Broadcasted, iter, state, count) + T = eltype(dest) + while true + y = iterate(iter, state) + y === nothing && break + I, state = y + @inbounds val = bc[I] + if val isa T || typeof(val) === T + @inbounds dest[I] = val + else + # This element type doesn't fit in dest. Allocate a new dest with wider eltype, + # copy over old values, and continue + newdest = Base.similar(dest, promote_typejoin(T, typeof(val))) + return restart_copyto_nonleaf!(newdest, dest, bc, val, I, iter, state, count) + end + count += 1 + end + return dest +end + +## Tuple methods + +@inline function copy(bc::Broadcasted{Style{Tuple}}) + dim = axes(bc) + length(dim) == 1 || throw(DimensionMismatch("tuple only supports one dimension")) + N = length(dim[1]) + return ntuple(k -> @inbounds(_broadcast_getindex(bc, k)), Val(N)) +end + +## scalar-range broadcast operations ## +# DefaultArrayStyle and \ are not available at the time of range.jl +broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::OrdinalRange) = r +broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::StepRangeLen) = r +broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::LinRange) = r + +broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::OrdinalRange) = range(-first(r), step=-step(r), length=length(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::StepRangeLen) = StepRangeLen(-r.ref, -r.step, length(r), r.offset) +broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::LinRange) = LinRange(-r.start, -r.stop, length(r)) + +broadcasted(::DefaultArrayStyle{1}, ::typeof(+), x::Real, r::AbstractUnitRange) = range(x + first(r), length=length(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::AbstractUnitRange, x::Real) = range(first(r) + x, length=length(r)) +# For #18336 we need to prevent promotion of the step type: +broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::AbstractRange, x::Number) = range(first(r) + x, step=step(r), length=length(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(+), x::Number, r::AbstractRange) = range(x + first(r), step=step(r), length=length(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::StepRangeLen{T}, x::Number) where T = + StepRangeLen{typeof(T(r.ref)+x)}(r.ref + x, r.step, length(r), r.offset) +broadcasted(::DefaultArrayStyle{1}, ::typeof(+), x::Number, r::StepRangeLen{T}) where T = + StepRangeLen{typeof(x+T(r.ref))}(x + r.ref, r.step, length(r), r.offset) +broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r::LinRange, x::Number) = LinRange(r.start + x, r.stop + x, length(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(+), x::Number, r::LinRange) = LinRange(x + r.start, x + r.stop, length(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(+), r1::AbstractRange, r2::AbstractRange) = r1 + r2 + +broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::AbstractUnitRange, x::Number) = range(first(r)-x, length=length(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::AbstractRange, x::Number) = range(first(r)-x, step=step(r), length=length(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(-), x::Number, r::AbstractRange) = range(x-first(r), step=-step(r), length=length(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::StepRangeLen{T}, x::Number) where T = + StepRangeLen{typeof(T(r.ref)-x)}(r.ref - x, r.step, length(r), r.offset) +broadcasted(::DefaultArrayStyle{1}, ::typeof(-), x::Number, r::StepRangeLen{T}) where T = + StepRangeLen{typeof(x-T(r.ref))}(x - r.ref, -r.step, length(r), r.offset) +broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r::LinRange, x::Number) = LinRange(r.start - x, r.stop - x, length(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(-), x::Number, r::LinRange) = LinRange(x - r.start, x - r.stop, length(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(-), r1::AbstractRange, r2::AbstractRange) = r1 - r2 + +broadcasted(::DefaultArrayStyle{1}, ::typeof(*), x::Number, r::AbstractRange) = range(x*first(r), step=x*step(r), length=length(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(*), x::Number, r::StepRangeLen{T}) where {T} = + StepRangeLen{typeof(x*T(r.ref))}(x*r.ref, x*r.step, length(r), r.offset) +broadcasted(::DefaultArrayStyle{1}, ::typeof(*), x::Number, r::LinRange) = LinRange(x * r.start, x * r.stop, r.len) +# separate in case of noncommutative multiplication +broadcasted(::DefaultArrayStyle{1}, ::typeof(*), r::AbstractRange, x::Number) = range(first(r)*x, step=step(r)*x, length=length(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(*), r::StepRangeLen{T}, x::Number) where {T} = + StepRangeLen{typeof(T(r.ref)*x)}(r.ref*x, r.step*x, length(r), r.offset) +broadcasted(::DefaultArrayStyle{1}, ::typeof(*), r::LinRange, x::Number) = LinRange(r.start * x, r.stop * x, r.len) + +broadcasted(::DefaultArrayStyle{1}, ::typeof(/), r::AbstractRange, x::Number) = range(first(r)/x, step=step(r)/x, length=length(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(/), r::StepRangeLen{T}, x::Number) where {T} = + StepRangeLen{typeof(T(r.ref)/x)}(r.ref/x, r.step/x, length(r), r.offset) +broadcasted(::DefaultArrayStyle{1}, ::typeof(/), r::LinRange, x::Number) = LinRange(r.start / x, r.stop / x, r.len) + +broadcasted(::DefaultArrayStyle{1}, ::typeof(\), x::Number, r::AbstractRange) = range(x\first(r), step=x\step(r), length=length(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(\), x::Number, r::StepRangeLen) = StepRangeLen(x\r.ref, x\r.step, length(r), r.offset) +broadcasted(::DefaultArrayStyle{1}, ::typeof(\), x::Number, r::LinRange) = LinRange(x \ r.start, x \ r.stop, r.len) + +broadcasted(::DefaultArrayStyle{1}, ::typeof(big), r::UnitRange) = big(r.start):big(last(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(big), r::StepRange) = big(r.start):big(r.step):big(last(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(big), r::StepRangeLen) = StepRangeLen(big(r.ref), big(r.step), length(r), r.offset) +broadcasted(::DefaultArrayStyle{1}, ::typeof(big), r::LinRange) = LinRange(big(r.start), big(r.stop), length(r)) + +## CartesianIndices +broadcasted(::typeof(+), I::CartesianIndices{N}, j::CartesianIndex{N}) where N = + CartesianIndices(map((rng, offset)->rng .+ offset, I.indices, Tuple(j))) +broadcasted(::typeof(+), j::CartesianIndex{N}, I::CartesianIndices{N}) where N = + I .+ j +broadcasted(::typeof(-), I::CartesianIndices{N}, j::CartesianIndex{N}) where N = + CartesianIndices(map((rng, offset)->rng .- offset, I.indices, Tuple(j))) +function broadcasted(::typeof(-), j::CartesianIndex{N}, I::CartesianIndices{N}) where N + diffrange(offset, rng) = range(offset-last(rng), length=length(rng)) + Iterators.reverse(CartesianIndices(map(diffrange, Tuple(j), I.indices))) +end + +## In specific instances, we can broadcast masked BitArrays whole chunks at a time +# Very intentionally do not support much functionality here: scalar indexing would be O(n) +struct BitMaskedBitArray{N,M} + parent::BitArray{N} + mask::BitArray{M} + BitMaskedBitArray{N,M}(parent, mask) where {N,M} = new(parent, mask) +end +@inline function BitMaskedBitArray(parent::BitArray{N}, mask::BitArray{M}) where {N,M} + @boundscheck checkbounds(parent, mask) + BitMaskedBitArray{N,M}(parent, mask) +end +Base.@propagate_inbounds dotview(B::BitArray, i::BitArray) = BitMaskedBitArray(B, i) +Base.show(io::IO, B::BitMaskedBitArray) = foreach(arg->show(io, arg), (typeof(B), (B.parent, B.mask))) +# Override materialize! to prevent the BitMaskedBitArray from escaping to an overrideable method +@inline materialize!(B::BitMaskedBitArray, bc::Broadcasted{<:Any,<:Any,typeof(identity),Tuple{Bool}}) = fill!(B, bc.args[1]) +@inline materialize!(B::BitMaskedBitArray, bc::Broadcasted{<:Any}) = materialize!(SubArray(B.parent, to_indices(B.parent, (B.mask,))), bc) +function Base.fill!(B::BitMaskedBitArray, b::Bool) + Bc = B.parent.chunks + Ic = B.mask.chunks + @inbounds if b + for i = 1:length(Bc) + Bc[i] |= Ic[i] + end + else + for i = 1:length(Bc) + Bc[i] &= ~Ic[i] + end + end + return B +end + + + +############################################################ + +# x[...] .= f.(y...) ---> broadcast!(f, dotview(x, ...), y...). +# The dotview function defaults to getindex, but we override it in +# a few cases to get the expected in-place behavior without affecting +# explicit calls to view. (All of this can go away if slices +# are changed to generate views by default.) + +Base.@propagate_inbounds dotview(args...) = Base.maybeview(args...) + +############################################################ +# The parser turns @. into a call to the __dot__ macro, +# which converts all function calls and assignments into +# broadcasting "dot" calls/assignments: + +dottable(x) = false # avoid dotting spliced objects (e.g. view calls inserted by @view) +# don't add dots to dot operators +dottable(x::Symbol) = (!isoperator(x) || first(string(x)) != '.' || x === :..) && x !== :(:) +dottable(x::Expr) = x.head !== :$ +undot(x) = x +function undot(x::Expr) + if x.head === :.= + Expr(:(=), x.args...) + elseif x.head === :block # occurs in for x=..., y=... + Expr(:block, map(undot, x.args)...) + else + x + end +end +__dot__(x) = x +function __dot__(x::Expr) + dotargs = map(__dot__, x.args) + if x.head === :call && dottable(x.args[1]) + Expr(:., dotargs[1], Expr(:tuple, dotargs[2:end]...)) + elseif x.head === :comparison + Expr(:comparison, (iseven(i) && dottable(arg) && arg isa Symbol && isoperator(arg) ? + Symbol('.', arg) : arg for (i, arg) in pairs(dotargs))...) + elseif x.head === :$ + x.args[1] + elseif x.head === :let # don't add dots to `let x=...` assignments + Expr(:let, undot(dotargs[1]), dotargs[2]) + elseif x.head === :for # don't add dots to for x=... assignments + Expr(:for, undot(dotargs[1]), dotargs[2]) + elseif (x.head === :(=) || x.head === :function || x.head === :macro) && + Meta.isexpr(x.args[1], :call) # function or macro definition + Expr(x.head, x.args[1], dotargs[2]) + else + if x.head === :&& || x.head === :|| + error(""" + Using `&&` and `||` is disallowed in `@.` expressions. + Use `&` or `|` for elementwise logical operations. + """) + end + head = string(x.head) + if last(head) == '=' && first(head) != '.' + Expr(Symbol('.',head), dotargs...) + else + Expr(x.head, dotargs...) + end + end +end +""" + @. expr + +Convert every function call or operator in `expr` into a "dot call" +(e.g. convert `f(x)` to `f.(x)`), and convert every assignment in `expr` +to a "dot assignment" (e.g. convert `+=` to `.+=`). + +If you want to *avoid* adding dots for selected function calls in +`expr`, splice those function calls in with `\$`. For example, +`@. sqrt(abs(\$sort(x)))` is equivalent to `sqrt.(abs.(sort(x)))` +(no dot for `sort`). + +(`@.` is equivalent to a call to `@__dot__`.) + +# Examples +```jldoctest +julia> x = 1.0:3.0; y = similar(x); + +julia> @. y = x + 3 * sin(x) +3-element Array{Float64,1}: + 3.5244129544236893 + 4.727892280477045 + 3.4233600241796016 +``` +""" +macro __dot__(x) + esc(__dot__(x)) +end + +@inline broadcasted_kwsyntax(f, args...; kwargs...) = broadcasted((args...)->f(args...; kwargs...), args...) +@inline function broadcasted(f, args...) + args′ = map(broadcastable, args) + broadcasted(combine_styles(args′...), f, args′...) +end +# Due to the current Type{T}/DataType specialization heuristics within Tuples, +# the totally generic varargs broadcasted(f, args...) method above loses Type{T}s in +# mapping broadcastable across the args. These additional methods with explicit +# arguments ensure we preserve Type{T}s in the first or second argument position. +@inline function broadcasted(f, arg1, args...) + arg1′ = broadcastable(arg1) + args′ = map(broadcastable, args) + broadcasted(combine_styles(arg1′, args′...), f, arg1′, args′...) +end +@inline function broadcasted(f, arg1, arg2, args...) + arg1′ = broadcastable(arg1) + arg2′ = broadcastable(arg2) + args′ = map(broadcastable, args) + broadcasted(combine_styles(arg1′, arg2′, args′...), f, arg1′, arg2′, args′...) +end +@inline broadcasted(::S, f, args...) where S<:BroadcastStyle = Broadcasted{S}(f, args) + +end # module diff --git a/base/c.jl b/base/c.jl new file mode 100644 index 0000000..8674a55 --- /dev/null +++ b/base/c.jl @@ -0,0 +1,505 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# definitions related to C interface + +import Core.Intrinsics: cglobal, bitcast + +""" + cglobal((symbol, library) [, type=Cvoid]) + +Obtain a pointer to a global variable in a C-exported shared library, specified exactly as +in [`ccall`](@ref). +Returns a `Ptr{Type}`, defaulting to `Ptr{Cvoid}` if no `Type` argument is +supplied. +The values can be read or written by [`unsafe_load`](@ref) or [`unsafe_store!`](@ref), +respectively. +""" +cglobal + +""" + CFunction struct + +Garbage-collection handle for the return value from `@cfunction` +when the first argument is annotated with '\\\$'. +Like all `cfunction` handles, it should be passed to `ccall` as a `Ptr{Cvoid}`, +and will be converted automatically at the call site to the appropriate type. + +See [`@cfunction`](@ref). +""" +struct CFunction <: Ref{Cvoid} + ptr::Ptr{Cvoid} + f::Any + _1::Ptr{Cvoid} + _2::Ptr{Cvoid} + let constructor = false end +end +unsafe_convert(::Type{Ptr{Cvoid}}, cf::CFunction) = cf.ptr + +""" + @cfunction(callable, ReturnType, (ArgumentTypes...,)) -> Ptr{Cvoid} + @cfunction(\$callable, ReturnType, (ArgumentTypes...,)) -> CFunction + +Generate a C-callable function pointer from the Julia function `callable` +for the given type signature. +To pass the return value to a `ccall`, use the argument type `Ptr{Cvoid}` in the signature. + +Note that the argument type tuple must be a literal tuple, and not a tuple-valued variable or expression +(although it can include a splat expression). And that these arguments will be evaluated in global scope +during compile-time (not deferred until runtime). +Adding a '\\\$' in front of the function argument changes this to instead create a runtime closure +over the local variable `callable`. + +See [manual section on ccall and cfunction usage](@ref Calling-C-and-Fortran-Code). + +# Examples +```julia-repl +julia> function foo(x::Int, y::Int) + return x + y + end + +julia> @cfunction(foo, Int, (Int, Int)) +Ptr{Cvoid} @0x000000001b82fcd0 +``` +""" +macro cfunction(f, at, rt) + if !(isa(rt, Expr) && rt.head === :tuple) + throw(ArgumentError("@cfunction argument types must be a literal tuple")) + end + rt.head = :call + pushfirst!(rt.args, GlobalRef(Core, :svec)) + if isa(f, Expr) && f.head === :$ + fptr = f.args[1] + typ = CFunction + else + fptr = QuoteNode(f) + typ = Ptr{Cvoid} + end + cfun = Expr(:cfunction, typ, fptr, at, rt, QuoteNode(:ccall)) + return esc(cfun) +end + +if ccall(:jl_is_char_signed, Ref{Bool}, ()) + const Cchar = Int8 +else + const Cchar = UInt8 +end +""" + Cchar + +Equivalent to the native `char` c-type. +""" +Cchar + +# The ccall here is equivalent to Sys.iswindows(), but that's not defined yet +@static if ccall(:jl_get_UNAME, Any, ()) === :NT + const Clong = Int32 + const Culong = UInt32 + const Cwchar_t = UInt16 +else + const Clong = Int + const Culong = UInt + const Cwchar_t = Int32 +end + +""" + Clong + +Equivalent to the native `signed long` c-type. +""" +Clong + +""" + Culong + +Equivalent to the native `unsigned long` c-type. +""" +Culong + +""" + Cwchar_t + +Equivalent to the native `wchar_t` c-type ([`Int32`](@ref)). +""" +Cwchar_t + +""" + Cwstring + +A C-style string composed of the native wide character type +[`Cwchar_t`](@ref)s. `Cwstring`s are NUL-terminated. For +C-style strings composed of the native character +type, see [`Cstring`](@ref). For more information +about string interopability with C, see the +[manual](@ref man-bits-types). + +""" +Cwstring + +""" + Cstring + +A C-style string composed of the native character type +[`Cchar`](@ref)s. `Cstring`s are NUL-terminated. For +C-style strings composed of the native wide character +type, see [`Cwstring`](@ref). For more information +about string interopability with C, see the +[manual](@ref man-bits-types). +""" +Cstring + +@static if ccall(:jl_get_UNAME, Any, ()) !== :NT + const sizeof_mode_t = ccall(:jl_sizeof_mode_t, Cint, ()) + if sizeof_mode_t == 2 + const Cmode_t = Int16 + elseif sizeof_mode_t == 4 + const Cmode_t = Int32 + elseif sizeof_mode_t == 8 + const Cmode_t = Int64 + end +end + +# construction from pointers +Cstring(p::Union{Ptr{Int8},Ptr{UInt8},Ptr{Cvoid}}) = bitcast(Cstring, p) +Cwstring(p::Union{Ptr{Cwchar_t},Ptr{Cvoid}}) = bitcast(Cwstring, p) +(::Type{Ptr{T}})(p::Cstring) where {T<:Union{Int8,UInt8,Cvoid}} = bitcast(Ptr{T}, p) +(::Type{Ptr{T}})(p::Cwstring) where {T<:Union{Cwchar_t,Cvoid}} = bitcast(Ptr{Cwchar_t}, p) + +convert(::Type{Cstring}, p::Union{Ptr{Int8},Ptr{UInt8},Ptr{Cvoid}}) = Cstring(p) +convert(::Type{Cwstring}, p::Union{Ptr{Cwchar_t},Ptr{Cvoid}}) = Cwstring(p) +convert(::Type{Ptr{T}}, p::Cstring) where {T<:Union{Int8,UInt8,Cvoid}} = Ptr{T}(p) +convert(::Type{Ptr{T}}, p::Cwstring) where {T<:Union{Cwchar_t,Cvoid}} = Ptr{T}(p) + +""" + pointer(array [, index]) + +Get the native address of an array or string, optionally at a given location `index`. + +This function is "unsafe". Be careful to ensure that a Julia reference to +`array` exists as long as this pointer will be used. The [`GC.@preserve`](@ref) +macro should be used to protect the `array` argument from garbage collection +within a given block of code. + +Calling [`Ref(array[, index])`](@ref Ref) is generally preferable to this function as it guarantees validity. +""" +function pointer end + +pointer(p::Cstring) = convert(Ptr{Cchar}, p) +pointer(p::Cwstring) = convert(Ptr{Cwchar_t}, p) + +# comparisons against pointers (mainly to support `cstr==C_NULL`) +==(x::Union{Cstring,Cwstring}, y::Ptr) = pointer(x) == y +==(x::Ptr, y::Union{Cstring,Cwstring}) = x == pointer(y) + +unsafe_string(s::Cstring) = unsafe_string(convert(Ptr{UInt8}, s)) + +# convert strings to String etc. to pass as pointers +cconvert(::Type{Cstring}, s::String) = s +cconvert(::Type{Cstring}, s::AbstractString) = + cconvert(Cstring, String(s)::String) + +function cconvert(::Type{Cwstring}, s::AbstractString) + v = transcode(Cwchar_t, String(s)) + !isempty(v) && v[end] == 0 || push!(v, 0) + return v +end + +eltype(::Type{Cstring}) = Cchar +eltype(::Type{Cwstring}) = Cwchar_t + +containsnul(p::Ptr, len) = + C_NULL != ccall(:memchr, Ptr{Cchar}, (Ptr{Cchar}, Cint, Csize_t), p, 0, len) +containsnul(s::String) = containsnul(unsafe_convert(Ptr{Cchar}, s), sizeof(s)) +containsnul(s::AbstractString) = '\0' in s + +function unsafe_convert(::Type{Cstring}, s::Union{String,AbstractVector{UInt8}}) + p = unsafe_convert(Ptr{Cchar}, s) + containsnul(p, sizeof(s)) && + throw(ArgumentError("embedded NULs are not allowed in C strings: $(repr(s))")) + return Cstring(p) +end + +function unsafe_convert(::Type{Cwstring}, v::Vector{Cwchar_t}) + for i = 1:length(v)-1 + v[i] == 0 && + throw(ArgumentError("embedded NULs are not allowed in C strings: $(repr(v))")) + end + v[end] == 0 || + throw(ArgumentError("C string data must be NUL terminated: $(repr(v))")) + p = unsafe_convert(Ptr{Cwchar_t}, v) + return Cwstring(p) +end + +# symbols are guaranteed not to contain embedded NUL +cconvert(::Type{Cstring}, s::Symbol) = s +unsafe_convert(::Type{Cstring}, s::Symbol) = Cstring(unsafe_convert(Ptr{Cchar}, s)) + +@static if ccall(:jl_get_UNAME, Any, ()) === :NT +""" + Base.cwstring(s) + +Converts a string `s` to a NUL-terminated `Vector{Cwchar_t}`, suitable for passing to C +functions expecting a `Ptr{Cwchar_t}`. The main advantage of using this over the implicit +conversion provided by [`Cwstring`](@ref) is if the function is called multiple times with the +same argument. + +This is only available on Windows. +""" +function cwstring(s::AbstractString) + bytes = codeunits(String(s)) + 0 in bytes && throw(ArgumentError("embedded NULs are not allowed in C strings: $(repr(s))")) + return push!(transcode(UInt16, bytes), 0) +end +end + +# transcoding between data in UTF-8 and UTF-16 for Windows APIs, +# and also UTF-32 for APIs using Cwchar_t on other platforms. + +""" + transcode(T, src) + +Convert string data between Unicode encodings. `src` is either a +`String` or a `Vector{UIntXX}` of UTF-XX code units, where +`XX` is 8, 16, or 32. `T` indicates the encoding of the return value: +`String` to return a (UTF-8 encoded) `String` or `UIntXX` +to return a `Vector{UIntXX}` of UTF-`XX` data. (The alias [`Cwchar_t`](@ref) +can also be used as the integer type, for converting `wchar_t*` strings +used by external C libraries.) + +The `transcode` function succeeds as long as the input data can be +reasonably represented in the target encoding; it always succeeds for +conversions between UTF-XX encodings, even for invalid Unicode data. + +Only conversion to/from UTF-8 is currently supported. +""" +function transcode end + +transcode(::Type{T}, src::AbstractVector{T}) where {T<:Union{UInt8,UInt16,UInt32,Int32}} = src +transcode(::Type{T}, src::String) where {T<:Union{Int32,UInt32}} = T[T(c) for c in src] +transcode(::Type{T}, src::AbstractVector{UInt8}) where {T<:Union{Int32,UInt32}} = + transcode(T, String(Vector(src))) +transcode(::Type{T}, src::CodeUnits{UInt8,String}) where {T<:Union{Int32,UInt32}} = + transcode(T, String(src)) + +function transcode(::Type{UInt8}, src::Vector{<:Union{Int32,UInt32}}) + buf = IOBuffer() + for c in src + print(buf, Char(c)) + end + take!(buf) +end +transcode(::Type{String}, src::String) = src +transcode(T, src::String) = transcode(T, codeunits(src)) +transcode(::Type{String}, src) = String(transcode(UInt8, src)) + +function transcode(::Type{UInt16}, src::AbstractVector{UInt8}) + require_one_based_indexing(src) + dst = UInt16[] + i, n = 1, length(src) + n > 0 || return dst + sizehint!(dst, 2n) + a = src[1] + while true + if i < n && -64 <= a % Int8 <= -12 # multi-byte character + b = src[i += 1] + if -64 <= (b % Int8) || a == 0xf4 && 0x8f < b + # invalid UTF-8 (non-continuation or too-high code point) + push!(dst, a) + a = b; continue + elseif a < 0xe0 # 2-byte UTF-8 + push!(dst, xor(0x3080, UInt16(a) << 6, b)) + elseif i < n # 3/4-byte character + c = src[i += 1] + if -64 <= (c % Int8) # invalid UTF-8 (non-continuation) + push!(dst, a, b) + a = c; continue + elseif a < 0xf0 # 3-byte UTF-8 + push!(dst, xor(0x2080, UInt16(a) << 12, UInt16(b) << 6, c)) + elseif i < n + d = src[i += 1] + if -64 <= (d % Int8) # invalid UTF-8 (non-continuation) + push!(dst, a, b, c) + a = d; continue + elseif a == 0xf0 && b < 0x90 # overlong encoding + push!(dst, xor(0x2080, UInt16(b) << 12, UInt16(c) << 6, d)) + else # 4-byte UTF-8 + push!(dst, 0xe5b8 + (UInt16(a) << 8) + (UInt16(b) << 2) + (c >> 4), + xor(0xdc80, UInt16(c & 0xf) << 6, d)) + end + else # too short + push!(dst, a, b, c) + break + end + else # too short + push!(dst, a, b) + break + end + else # ASCII or invalid UTF-8 (continuation byte or too-high code point) + push!(dst, a) + end + i < n || break + a = src[i += 1] + end + return dst +end + +function transcode(::Type{UInt8}, src::AbstractVector{UInt16}) + require_one_based_indexing(src) + n = length(src) + n == 0 && return UInt8[] + + # Precompute m = sizeof(dst). This involves annoying duplication + # of the loop over the src array. However, this is not just an + # optimization: it is problematic for security reasons to grow + # dst dynamically, because Base.winprompt uses this function to + # convert passwords to UTF-8 and we don't want to make unintentional + # copies of the password data. + a = src[1] + i, m = 1, 0 + while true + if a < 0x80 + m += 1 + elseif a < 0x800 # 2-byte UTF-8 + m += 2 + elseif a & 0xfc00 == 0xd800 && i < length(src) + b = src[i += 1] + if (b & 0xfc00) == 0xdc00 # 2-unit UTF-16 sequence => 4-byte UTF-8 + m += 4 + else + m += 3 + a = b; continue + end + else + # 1-unit high UTF-16 or unpaired high surrogate + # either way, encode as 3-byte UTF-8 code point + m += 3 + end + i < n || break + a = src[i += 1] + end + + dst = StringVector(m) + a = src[1] + i, j = 1, 0 + while true + if a < 0x80 # ASCII + dst[j += 1] = a % UInt8 + elseif a < 0x800 # 2-byte UTF-8 + dst[j += 1] = 0xc0 | ((a >> 6) % UInt8) + dst[j += 1] = 0x80 | ((a % UInt8) & 0x3f) + elseif a & 0xfc00 == 0xd800 && i < n + b = src[i += 1] + if (b & 0xfc00) == 0xdc00 + # 2-unit UTF-16 sequence => 4-byte UTF-8 + a += 0x2840 + dst[j += 1] = 0xf0 | ((a >> 8) % UInt8) + dst[j += 1] = 0x80 | ((a % UInt8) >> 2) + dst[j += 1] = xor(0xf0, ((a % UInt8) << 4) & 0x3f, (b >> 6) % UInt8) + dst[j += 1] = 0x80 | ((b % UInt8) & 0x3f) + else + dst[j += 1] = 0xe0 | ((a >> 12) % UInt8) + dst[j += 1] = 0x80 | (((a >> 6) % UInt8) & 0x3f) + dst[j += 1] = 0x80 | ((a % UInt8) & 0x3f) + a = b; continue + end + else + # 1-unit high UTF-16 or unpaired high surrogate + # either way, encode as 3-byte UTF-8 code point + dst[j += 1] = 0xe0 | ((a >> 12) % UInt8) + dst[j += 1] = 0x80 | (((a >> 6) % UInt8) & 0x3f) + dst[j += 1] = 0x80 | ((a % UInt8) & 0x3f) + end + i < n || break + a = src[i += 1] + end + return dst +end + +# deferring (or un-deferring) ctrl-c handler for external C code that +# is not interrupt safe (see also issue #2622). The sigatomic_begin/end +# functions should always be called in matched pairs, ideally via: +# disable_sigint() do .. end +# reennable_sigint is provided so that immediate ctrl-c handling is +# re-enabled within a sigatomic region, e.g. inside a Julia callback function +# within a long-running C routine. +sigatomic_begin() = ccall(:jl_sigatomic_begin, Cvoid, ()) +sigatomic_end() = ccall(:jl_sigatomic_end, Cvoid, ()) + +""" + disable_sigint(f::Function) + +Disable Ctrl-C handler during execution of a function on the current task, +for calling external code that may call julia code that is not interrupt safe. +Intended to be called using `do` block syntax as follows: + + disable_sigint() do + # interrupt-unsafe code + ... + end + +This is not needed on worker threads (`Threads.threadid() != 1`) since the +`InterruptException` will only be delivered to the master thread. +External functions that do not call julia code or julia runtime +automatically disable sigint during their execution. +""" +function disable_sigint(f::Function) + sigatomic_begin() + res = f() + # Exception unwind sigatomic automatically + sigatomic_end() + res +end + +""" + reenable_sigint(f::Function) + +Re-enable Ctrl-C handler during execution of a function. +Temporarily reverses the effect of [`disable_sigint`](@ref). +""" +function reenable_sigint(f::Function) + sigatomic_end() + res = f() + # Exception unwind sigatomic automatically + sigatomic_begin() + res +end + +function ccallable(f::Function, rt::Type, argt::Type, name::Union{AbstractString,Symbol}=string(f)) + ccall(:jl_extern_c, Cvoid, (Any, Any, Any, Cstring), f, rt, argt, name) +end + +function expand_ccallable(rt, def) + if isa(def,Expr) && (def.head === :(=) || def.head === :function) + sig = def.args[1] + if sig.head === :(::) + if rt === nothing + rt = sig.args[2] + end + sig = sig.args[1] + end + if rt === nothing + error("@ccallable requires a return type") + end + if sig.head === :call + name = sig.args[1] + at = map(sig.args[2:end]) do a + if isa(a,Expr) && a.head === :(::) + a.args[2] + else + :Any + end + end + return quote + $(esc(def)) + ccallable($(esc(name)), $(esc(rt)), $(Expr(:curly, :Tuple, map(esc, at)...)), $(string(name))) + end + end + end + error("expected method definition in @ccallable") +end + +macro ccallable(def) + expand_ccallable(nothing, def) +end +macro ccallable(rt, def) + expand_ccallable(rt, def) +end diff --git a/base/cartesian.jl b/base/cartesian.jl new file mode 100644 index 0000000..45276e9 --- /dev/null +++ b/base/cartesian.jl @@ -0,0 +1,404 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +module Cartesian + +export @nloops, @nref, @ncall, @nexprs, @nextract, @nall, @nany, @ntuple, @nif + +### Cartesian-specific macros + +""" + @nloops N itersym rangeexpr bodyexpr + @nloops N itersym rangeexpr preexpr bodyexpr + @nloops N itersym rangeexpr preexpr postexpr bodyexpr + +Generate `N` nested loops, using `itersym` as the prefix for the iteration variables. +`rangeexpr` may be an anonymous-function expression, or a simple symbol `var` in which case +the range is `axes(var, d)` for dimension `d`. + +Optionally, you can provide "pre" and "post" expressions. These get executed first and last, +respectively, in the body of each loop. For example: + + @nloops 2 i A d -> j_d = min(i_d, 5) begin + s += @nref 2 A j + end + +would generate: + + for i_2 = axes(A, 2) + j_2 = min(i_2, 5) + for i_1 = axes(A, 1) + j_1 = min(i_1, 5) + s += A[j_1, j_2] + end + end + +If you want just a post-expression, supply [`nothing`](@ref) for the pre-expression. Using +parentheses and semicolons, you can supply multi-statement expressions. +""" +macro nloops(N, itersym, rangeexpr, args...) + _nloops(N, itersym, rangeexpr, args...) +end + +function _nloops(N::Int, itersym::Symbol, arraysym::Symbol, args::Expr...) + @gensym d + _nloops(N, itersym, :($d->Base.axes($arraysym, $d)), args...) +end + +function _nloops(N::Int, itersym::Symbol, rangeexpr::Expr, args::Expr...) + if rangeexpr.head !== :-> + throw(ArgumentError("second argument must be an anonymous function expression to compute the range")) + end + if !(1 <= length(args) <= 3) + throw(ArgumentError("number of arguments must be 1 ≤ length(args) ≤ 3, got $nargs")) + end + body = args[end] + ex = Expr(:escape, body) + for dim = 1:N + itervar = inlineanonymous(itersym, dim) + rng = inlineanonymous(rangeexpr, dim) + preexpr = length(args) > 1 ? inlineanonymous(args[1], dim) : (:(nothing)) + postexpr = length(args) > 2 ? inlineanonymous(args[2], dim) : (:(nothing)) + ex = quote + for $(esc(itervar)) = $(esc(rng)) + $(esc(preexpr)) + $ex + $(esc(postexpr)) + end + end + end + ex +end + +""" + @nref N A indexexpr + +Generate expressions like `A[i_1, i_2, ...]`. `indexexpr` can either be an iteration-symbol +prefix, or an anonymous-function expression. + +# Examples +```jldoctest +julia> @macroexpand Base.Cartesian.@nref 3 A i +:(A[i_1, i_2, i_3]) +``` +""" +macro nref(N::Int, A::Symbol, ex) + vars = Any[ inlineanonymous(ex,i) for i = 1:N ] + Expr(:escape, Expr(:ref, A, vars...)) +end + +""" + @ncall N f sym... + +Generate a function call expression. `sym` represents any number of function arguments, the +last of which may be an anonymous-function expression and is expanded into `N` arguments. + +For example, `@ncall 3 func a` generates + + func(a_1, a_2, a_3) + +while `@ncall 2 func a b i->c[i]` yields + + func(a, b, c[1], c[2]) + +""" +macro ncall(N::Int, f, args...) + pre = args[1:end-1] + ex = args[end] + vars = Any[ inlineanonymous(ex,i) for i = 1:N ] + Expr(:escape, Expr(:call, f, pre..., vars...)) +end + +""" + @nexprs N expr + +Generate `N` expressions. `expr` should be an anonymous-function expression. + +# Examples +```jldoctest +julia> @macroexpand Base.Cartesian.@nexprs 4 i -> y[i] = A[i+j] +quote + y[1] = A[1 + j] + y[2] = A[2 + j] + y[3] = A[3 + j] + y[4] = A[4 + j] +end +``` +""" +macro nexprs(N::Int, ex::Expr) + exs = Any[ inlineanonymous(ex,i) for i = 1:N ] + Expr(:escape, Expr(:block, exs...)) +end + +""" + @nextract N esym isym + +Generate `N` variables `esym_1`, `esym_2`, ..., `esym_N` to extract values from `isym`. +`isym` can be either a `Symbol` or anonymous-function expression. + +`@nextract 2 x y` would generate + + x_1 = y[1] + x_2 = y[2] + +while `@nextract 3 x d->y[2d-1]` yields + + x_1 = y[1] + x_2 = y[3] + x_3 = y[5] + +""" +macro nextract(N::Int, esym::Symbol, isym::Symbol) + aexprs = Any[ Expr(:escape, Expr(:(=), inlineanonymous(esym, i), :(($isym)[$i]))) for i = 1:N ] + Expr(:block, aexprs...) +end + +macro nextract(N::Int, esym::Symbol, ex::Expr) + aexprs = Any[ Expr(:escape, Expr(:(=), inlineanonymous(esym, i), inlineanonymous(ex,i))) for i = 1:N ] + Expr(:block, aexprs...) +end + +""" + @nall N expr + +Check whether all of the expressions generated by the anonymous-function expression `expr` +evaluate to `true`. + +`@nall 3 d->(i_d > 1)` would generate the expression `(i_1 > 1 && i_2 > 1 && i_3 > 1)`. This +can be convenient for bounds-checking. +""" +macro nall(N::Int, criterion::Expr) + if criterion.head !== :-> + throw(ArgumentError("second argument must be an anonymous function expression yielding the criterion")) + end + conds = Any[ Expr(:escape, inlineanonymous(criterion, i)) for i = 1:N ] + Expr(:&&, conds...) +end + +""" + @nany N expr + +Check whether any of the expressions generated by the anonymous-function expression `expr` +evaluate to `true`. + +`@nany 3 d->(i_d > 1)` would generate the expression `(i_1 > 1 || i_2 > 1 || i_3 > 1)`. +""" +macro nany(N::Int, criterion::Expr) + if criterion.head !== :-> + error("Second argument must be an anonymous function expression yielding the criterion") + end + conds = Any[ Expr(:escape, inlineanonymous(criterion, i)) for i = 1:N ] + Expr(:||, conds...) +end + +""" + @ntuple N expr + +Generates an `N`-tuple. `@ntuple 2 i` would generate `(i_1, i_2)`, and `@ntuple 2 k->k+1` +would generate `(2,3)`. +""" +macro ntuple(N::Int, ex) + vars = Any[ inlineanonymous(ex,i) for i = 1:N ] + Expr(:escape, Expr(:tuple, vars...)) +end + +""" + @nif N conditionexpr expr + @nif N conditionexpr expr elseexpr + +Generates a sequence of `if ... elseif ... else ... end` statements. For example: + + @nif 3 d->(i_d >= size(A,d)) d->(error("Dimension ", d, " too big")) d->println("All OK") + +would generate: + + if i_1 > size(A, 1) + error("Dimension ", 1, " too big") + elseif i_2 > size(A, 2) + error("Dimension ", 2, " too big") + else + println("All OK") + end +""" +macro nif(N, condition, operation...) + # Handle the final "else" + ex = esc(inlineanonymous(length(operation) > 1 ? operation[2] : operation[1], N)) + # Make the nested if statements + for i = N-1:-1:1 + ex = Expr(:if, esc(inlineanonymous(condition,i)), esc(inlineanonymous(operation[1],i)), ex) + end + ex +end + +## Utilities + +# Simplify expressions like :(d->3:size(A,d)-3) given an explicit value for d +function inlineanonymous(ex::Expr, val) + if ex.head !== :-> + throw(ArgumentError("not an anonymous function")) + end + if !isa(ex.args[1], Symbol) + throw(ArgumentError("not a single-argument anonymous function")) + end + sym = ex.args[1] + ex = ex.args[2] + exout = lreplace(ex, sym, val) + exout = poplinenum(exout) + exprresolve(exout) +end + +# Given :i and 3, this generates :i_3 +inlineanonymous(base::Symbol, ext) = Symbol(base,'_',ext) + +# Replace a symbol by a value or a "coded" symbol +# E.g., for d = 3, +# lreplace(:d, :d, 3) -> 3 +# lreplace(:i_d, :d, 3) -> :i_3 +# lreplace(:i_{d-1}, :d, 3) -> :i_2 +# This follows LaTeX notation. +struct LReplace{S<:AbstractString} + pat_sym::Symbol + pat_str::S + val::Int +end +LReplace(sym::Symbol, val::Integer) = LReplace(sym, string(sym), val) + +lreplace(ex, sym::Symbol, val) = lreplace!(copy(ex), LReplace(sym, val)) + +function lreplace!(sym::Symbol, r::LReplace) + sym == r.pat_sym && return r.val + Symbol(lreplace!(string(sym), r)) +end + +function lreplace!(str::AbstractString, r::LReplace) + i = firstindex(str) + pat = r.pat_str + j = firstindex(pat) + matching = false + local istart::Int + while i <= ncodeunits(str) + cstr = str[i] + i = nextind(str, i) + if !matching + if cstr != '_' || i > ncodeunits(str) + continue + end + istart = i + cstr = str[i] + i = nextind(str, i) + end + if j <= lastindex(pat) + cr = pat[j] + j = nextind(pat, j) + if cstr == cr + matching = true + else + matching = false + j = firstindex(pat) + i = istart + continue + end + end + if matching && j > lastindex(pat) + if i > lastindex(str) || str[i] == '_' + # We have a match + return string(str[1:prevind(str, istart)], r.val, lreplace!(str[i:end], r)) + end + matching = false + j = firstindex(pat) + i = istart + end + end + str +end + +function lreplace!(ex::Expr, r::LReplace) + # Curly-brace notation, which acts like parentheses + if ex.head === :curly && length(ex.args) == 2 && isa(ex.args[1], Symbol) && endswith(string(ex.args[1]), "_") + excurly = exprresolve(lreplace!(ex.args[2], r)) + if isa(excurly, Number) + return Symbol(ex.args[1],excurly) + else + ex.args[2] = excurly + return ex + end + end + for i in 1:length(ex.args) + ex.args[i] = lreplace!(ex.args[i], r) + end + ex +end + +lreplace!(arg, r::LReplace) = arg + + +poplinenum(arg) = arg +function poplinenum(ex::Expr) + if ex.head === :block + if length(ex.args) == 1 + return ex.args[1] + elseif length(ex.args) == 2 && isa(ex.args[1], LineNumberNode) + return ex.args[2] + elseif (length(ex.args) == 2 && isa(ex.args[1], Expr) && ex.args[1].head === :line) + return ex.args[2] + end + end + ex +end + +## Resolve expressions at parsing time ## + +const exprresolve_arith_dict = Dict{Symbol,Function}(:+ => +, + :- => -, :* => *, :/ => /, :^ => ^, :div => div) +const exprresolve_cond_dict = Dict{Symbol,Function}(:(==) => ==, + :(<) => <, :(>) => >, :(<=) => <=, :(>=) => >=) + +function exprresolve_arith(ex::Expr) + if ex.head === :call && haskey(exprresolve_arith_dict, ex.args[1]) && all([isa(ex.args[i], Number) for i = 2:length(ex.args)]) + return true, exprresolve_arith_dict[ex.args[1]](ex.args[2:end]...) + end + false, 0 +end +exprresolve_arith(arg) = false, 0 + +exprresolve_conditional(b::Bool) = true, b +function exprresolve_conditional(ex::Expr) + if ex.head === :call && ex.args[1] ∈ keys(exprresolve_cond_dict) && isa(ex.args[2], Number) && isa(ex.args[3], Number) + return true, exprresolve_cond_dict[ex.args[1]](ex.args[2], ex.args[3]) + end + false, false +end +exprresolve_conditional(arg) = false, false + +exprresolve(arg) = arg +function exprresolve(ex::Expr) + for i = 1:length(ex.args) + ex.args[i] = exprresolve(ex.args[i]) + end + # Handle simple arithmetic + can_eval, result = exprresolve_arith(ex) + if can_eval + return result + elseif ex.head === :call && (ex.args[1] === :+ || ex.args[1] === :-) && length(ex.args) == 3 && ex.args[3] == 0 + # simplify x+0 and x-0 + return ex.args[2] + end + # Resolve array references + if ex.head === :ref && isa(ex.args[1], Array) + for i = 2:length(ex.args) + if !isa(ex.args[i], Real) + return ex + end + end + return ex.args[1][ex.args[2:end]...] + end + # Resolve conditionals + if ex.head === :if + can_eval, tf = exprresolve_conditional(ex.args[1]) + if can_eval + ex = tf ? ex.args[2] : ex.args[3] + end + end + ex +end + +end diff --git a/base/channels.jl b/base/channels.jl new file mode 100644 index 0000000..fdb5208 --- /dev/null +++ b/base/channels.jl @@ -0,0 +1,470 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" + AbstractChannel{T} + +Representation of a channel passing objects of type `T`. +""" +abstract type AbstractChannel{T} end + +""" + Channel{T=Any}(size::Int=0) + +Constructs a `Channel` with an internal buffer that can hold a maximum of `size` objects +of type `T`. +[`put!`](@ref) calls on a full channel block until an object is removed with [`take!`](@ref). + +`Channel(0)` constructs an unbuffered channel. `put!` blocks until a matching `take!` is called. +And vice-versa. + +Other constructors: + +* `Channel()`: default constructor, equivalent to `Channel{Any}(0)` +* `Channel(Inf)`: equivalent to `Channel{Any}(typemax(Int))` +* `Channel(sz)`: equivalent to `Channel{Any}(sz)` + +!!! compat "Julia 1.3" + The default constructor `Channel()` and default `size=0` were added in Julia 1.3. +""" +mutable struct Channel{T} <: AbstractChannel{T} + cond_take::Threads.Condition # waiting for data to become available + cond_wait::Threads.Condition # waiting for data to become maybe available + cond_put::Threads.Condition # waiting for a writeable slot + state::Symbol + excp::Union{Exception, Nothing} # exception to be thrown when state !== :open + + data::Vector{T} + sz_max::Int # maximum size of channel + + function Channel{T}(sz::Integer = 0) where T + if sz < 0 + throw(ArgumentError("Channel size must be either 0, a positive integer or Inf")) + end + lock = ReentrantLock() + cond_put, cond_take = Threads.Condition(lock), Threads.Condition(lock) + cond_wait = (sz == 0 ? Threads.Condition(lock) : cond_take) # wait is distinct from take iff unbuffered + return new(cond_take, cond_wait, cond_put, :open, nothing, Vector{T}(), sz) + end +end + +function Channel{T}(sz::Float64) where T + sz = (sz == Inf ? typemax(Int) : convert(Int, sz)) + return Channel{T}(sz) +end +Channel(sz=0) = Channel{Any}(sz) + +# special constructors +""" + Channel{T=Any}(func::Function, size=0; taskref=nothing, spawn=false) + +Create a new task from `func`, bind it to a new channel of type +`T` and size `size`, and schedule the task, all in a single call. + +`func` must accept the bound channel as its only argument. + +If you need a reference to the created task, pass a `Ref{Task}` object via +the keyword argument `taskref`. + +If `spawn = true`, the Task created for `func` may be scheduled on another thread +in parallel, equivalent to creating a task via [`Threads.@spawn`](@ref). + +Return a `Channel`. + +# Examples +```jldoctest +julia> chnl = Channel() do ch + foreach(i -> put!(ch, i), 1:4) + end; + +julia> typeof(chnl) +Channel{Any} + +julia> for i in chnl + @show i + end; +i = 1 +i = 2 +i = 3 +i = 4 +``` + +Referencing the created task: + +```jldoctest +julia> taskref = Ref{Task}(); + +julia> chnl = Channel(taskref=taskref) do ch + println(take!(ch)) + end; + +julia> istaskdone(taskref[]) +false + +julia> put!(chnl, "Hello"); +Hello + +julia> istaskdone(taskref[]) +true +``` + +!!! compat "Julia 1.3" + The `spawn=` parameter was added in Julia 1.3. This constructor was added in Julia 1.3. + In earlier versions of Julia, Channel used keyword arguments to set `size` and `T`, but + those constructors are deprecated. + +```jldoctest +julia> chnl = Channel{Char}(1, spawn=true) do ch + for c in "hello world" + put!(ch, c) + end + end +Channel{Char}(sz_max:1,sz_curr:1) + +julia> String(collect(chnl)) +"hello world" +``` +""" +function Channel{T}(func::Function, size=0; taskref=nothing, spawn=false) where T + chnl = Channel{T}(size) + task = Task(() -> func(chnl)) + task.sticky = !spawn + bind(chnl, task) + if spawn + schedule(task) # start it on (potentially) another thread + else + yield(task) # immediately start it, yielding the current thread + end + isa(taskref, Ref{Task}) && (taskref[] = task) + return chnl +end +Channel(func::Function, args...; kwargs...) = Channel{Any}(func, args...; kwargs...) + +# This constructor is deprecated as of Julia v1.3, and should not be used. +# (Note that this constructor also matches `Channel(::Function)` w/out any kwargs, which is +# of course not deprecated.) +# We use `nothing` default values to check which arguments were set in order to throw the +# deprecation warning if users try to use `spawn=` with `ctype=` or `csize=`. +function Channel(func::Function; ctype=nothing, csize=nothing, taskref=nothing, spawn=nothing) + # The spawn= keyword argument was added in Julia v1.3, and cannot be used with the + # deprecated keyword arguments `ctype=` or `csize=`. + if (ctype !== nothing || csize !== nothing) && spawn !== nothing + throw(ArgumentError("Cannot set `spawn=` in the deprecated constructor `Channel(f; ctype=Any, csize=0)`. Please use `Channel{T=Any}(f, size=0; taskref=nothing, spawn=false)` instead!")) + end + # Set the actual default values for the arguments. + ctype === nothing && (ctype = Any) + csize === nothing && (csize = 0) + spawn === nothing && (spawn = false) + return Channel{ctype}(func, csize; taskref=taskref, spawn=spawn) +end + +closed_exception() = InvalidStateException("Channel is closed.", :closed) + +isbuffered(c::Channel) = c.sz_max==0 ? false : true + +function check_channel_state(c::Channel) + if !isopen(c) + excp = c.excp + excp !== nothing && throw(excp) + throw(closed_exception()) + end +end +""" + close(c::Channel[, excp::Exception]) + +Close a channel. An exception (optionally given by `excp`), is thrown by: + +* [`put!`](@ref) on a closed channel. +* [`take!`](@ref) and [`fetch`](@ref) on an empty, closed channel. +""" +function close(c::Channel, excp::Exception=closed_exception()) + lock(c) + try + c.state = :closed + c.excp = excp + notify_error(c.cond_take, excp) + notify_error(c.cond_wait, excp) + notify_error(c.cond_put, excp) + finally + unlock(c) + end + nothing +end +isopen(c::Channel) = (c.state === :open) + +""" + bind(chnl::Channel, task::Task) + +Associate the lifetime of `chnl` with a task. +`Channel` `chnl` is automatically closed when the task terminates. +Any uncaught exception in the task is propagated to all waiters on `chnl`. + +The `chnl` object can be explicitly closed independent of task termination. +Terminating tasks have no effect on already closed `Channel` objects. + +When a channel is bound to multiple tasks, the first task to terminate will +close the channel. When multiple channels are bound to the same task, +termination of the task will close all of the bound channels. + +# Examples +```jldoctest +julia> c = Channel(0); + +julia> task = @async foreach(i->put!(c, i), 1:4); + +julia> bind(c,task); + +julia> for i in c + @show i + end; +i = 1 +i = 2 +i = 3 +i = 4 + +julia> isopen(c) +false +``` + +```jldoctest +julia> c = Channel(0); + +julia> task = @async (put!(c,1);error("foo")); + +julia> bind(c,task); + +julia> take!(c) +1 + +julia> put!(c,1); +ERROR: foo +Stacktrace: +[...] +``` +""" +function bind(c::Channel, task::Task) + # TODO: implement "schedulewait" and deprecate taskdone_hook + #T = Task(() -> close_chnl_on_taskdone(task, c)) + #schedulewait(task, T) + register_taskdone_hook(task, tsk -> close_chnl_on_taskdone(tsk, c)) + return c +end + +""" + channeled_tasks(n::Int, funcs...; ctypes=fill(Any,n), csizes=fill(0,n)) + +A convenience method to create `n` channels and bind them to tasks started +from the provided functions in a single call. Each `func` must accept `n` arguments +which are the created channels. Channel types and sizes may be specified via +keyword arguments `ctypes` and `csizes` respectively. If unspecified, all channels are +of type `Channel{Any}(0)`. + +Returns a tuple, `(Array{Channel}, Array{Task})`, of the created channels and tasks. +""" +function channeled_tasks(n::Int, funcs...; ctypes=fill(Any,n), csizes=fill(0,n)) + @assert length(csizes) == n + @assert length(ctypes) == n + + chnls = map(i -> Channel{ctypes[i]}(csizes[i]), 1:n) + tasks = Task[ Task(() -> f(chnls...)) for f in funcs ] + + # bind all tasks to all channels and schedule them + foreach(t -> foreach(c -> bind(c, t), chnls), tasks) + foreach(schedule, tasks) + yield() # Allow scheduled tasks to run + + return (chnls, tasks) +end + +function close_chnl_on_taskdone(t::Task, c::Channel) + isopen(c) || return + cleanup = () -> try + isopen(c) || return + if istaskfailed(t) + excp = task_result(t) + if excp isa Exception + close(c, excp) + return + end + end + close(c) + return + finally + unlock(c) + end + if trylock(c) + # can't use `lock`, since attempts to task-switch to wait for it + # will just silently fail and leave us with broken state + cleanup() + else + # so schedule this to happen once we are finished destroying our task + # (on a new Task) + @async (lock(c); cleanup()) + end + nothing +end + +struct InvalidStateException <: Exception + msg::AbstractString + state::Symbol +end + +""" + put!(c::Channel, v) + +Append an item `v` to the channel `c`. Blocks if the channel is full. + +For unbuffered channels, blocks until a [`take!`](@ref) is performed by a different +task. + +!!! compat "Julia 1.1" + `v` now gets converted to the channel's type with [`convert`](@ref) as `put!` is called. +""" +function put!(c::Channel{T}, v) where T + check_channel_state(c) + v = convert(T, v) + return isbuffered(c) ? put_buffered(c, v) : put_unbuffered(c, v) +end + +function put_buffered(c::Channel, v) + lock(c) + try + while length(c.data) == c.sz_max + check_channel_state(c) + wait(c.cond_put) + end + push!(c.data, v) + # notify all, since some of the waiters may be on a "fetch" call. + notify(c.cond_take, nothing, true, false) + finally + unlock(c) + end + return v +end + +function put_unbuffered(c::Channel, v) + lock(c) + taker = try + while isempty(c.cond_take.waitq) + check_channel_state(c) + notify(c.cond_wait) + wait(c.cond_put) + end + # unfair scheduled version of: notify(c.cond_take, v, false, false); yield() + popfirst!(c.cond_take.waitq) + finally + unlock(c) + end + schedule(taker, v) + yield() # immediately give taker a chance to run, but don't block the current task + return v +end + +push!(c::Channel, v) = put!(c, v) + +""" + fetch(c::Channel) + +Wait for and get the first available item from the channel. Does not +remove the item. `fetch` is unsupported on an unbuffered (0-size) channel. +""" +fetch(c::Channel) = isbuffered(c) ? fetch_buffered(c) : fetch_unbuffered(c) +function fetch_buffered(c::Channel) + lock(c) + try + while isempty(c.data) + check_channel_state(c) + wait(c.cond_take) + end + return c.data[1] + finally + unlock(c) + end +end +fetch_unbuffered(c::Channel) = throw(ErrorException("`fetch` is not supported on an unbuffered Channel.")) + + +""" + take!(c::Channel) + +Remove and return a value from a [`Channel`](@ref). Blocks until data is available. + +For unbuffered channels, blocks until a [`put!`](@ref) is performed by a different +task. +""" +take!(c::Channel) = isbuffered(c) ? take_buffered(c) : take_unbuffered(c) +function take_buffered(c::Channel) + lock(c) + try + while isempty(c.data) + check_channel_state(c) + wait(c.cond_take) + end + v = popfirst!(c.data) + notify(c.cond_put, nothing, false, false) # notify only one, since only one slot has become available for a put!. + return v + finally + unlock(c) + end +end + +popfirst!(c::Channel) = take!(c) + +# 0-size channel +function take_unbuffered(c::Channel{T}) where T + lock(c) + try + check_channel_state(c) + notify(c.cond_put, nothing, false, false) + return wait(c.cond_take)::T + finally + unlock(c) + end +end + +""" + isready(c::Channel) + +Determine whether a [`Channel`](@ref) has a value stored to it. Returns +immediately, does not block. + +For unbuffered channels returns `true` if there are tasks waiting +on a [`put!`](@ref). +""" +isready(c::Channel) = n_avail(c) > 0 +n_avail(c::Channel) = isbuffered(c) ? length(c.data) : length(c.cond_put.waitq) + +lock(c::Channel) = lock(c.cond_take) +unlock(c::Channel) = unlock(c.cond_take) +trylock(c::Channel) = trylock(c.cond_take) + +function wait(c::Channel) + isready(c) && return + lock(c) + try + while !isready(c) + check_channel_state(c) + wait(c.cond_wait) + end + finally + unlock(c) + end + nothing +end + +eltype(::Type{Channel{T}}) where {T} = T + +show(io::IO, c::Channel) = print(io, "$(typeof(c))(sz_max:$(c.sz_max),sz_curr:$(n_avail(c)))") + +function iterate(c::Channel, state=nothing) + try + return (take!(c), nothing) + catch e + if isa(e, InvalidStateException) && e.state === :closed + return nothing + else + rethrow() + end + end +end + +IteratorSize(::Type{<:Channel}) = SizeUnknown() diff --git a/base/char.jl b/base/char.jl new file mode 100644 index 0000000..6d7fdb0 --- /dev/null +++ b/base/char.jl @@ -0,0 +1,315 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" +The `AbstractChar` type is the supertype of all character implementations +in Julia. A character represents a Unicode code point, and can be converted +to an integer via the [`codepoint`](@ref) function in order to obtain the +numerical value of the code point, or constructed from the same integer. +These numerical values determine how characters are compared with `<` and `==`, +for example. New `T <: AbstractChar` types should define a `codepoint(::T)` +method and a `T(::UInt32)` constructor, at minimum. + +A given `AbstractChar` subtype may be capable of representing only a subset +of Unicode, in which case conversion from an unsupported `UInt32` value +may throw an error. Conversely, the built-in [`Char`](@ref) type represents +a *superset* of Unicode (in order to losslessly encode invalid byte streams), +in which case conversion of a non-Unicode value *to* `UInt32` throws an error. +The [`isvalid`](@ref) function can be used to check which codepoints are +representable in a given `AbstractChar` type. + +Internally, an `AbstractChar` type may use a variety of encodings. Conversion +via `codepoint(char)` will not reveal this encoding because it always returns the +Unicode value of the character. `print(io, c)` of any `c::AbstractChar` +produces an encoding determined by `io` (UTF-8 for all built-in `IO` +types), via conversion to `Char` if necessary. + +`write(io, c)`, in contrast, may emit an encoding depending on +`typeof(c)`, and `read(io, typeof(c))` should read the same encoding as `write`. +New `AbstractChar` types must provide their own implementations of +`write` and `read`. +""" +AbstractChar + +""" + Char(c::Union{Number,AbstractChar}) + +`Char` is a 32-bit [`AbstractChar`](@ref) type that is the default representation +of characters in Julia. `Char` is the type used for character literals like `'x'` +and it is also the element type of [`String`](@ref). + +In order to losslessly represent arbitrary byte streams stored in a `String`, +a `Char` value may store information that cannot be converted to a Unicode +codepoint — converting such a `Char` to `UInt32` will throw an error. +The [`isvalid(c::Char)`](@ref) function can be used to query whether `c` +represents a valid Unicode character. +""" +Char + +(::Type{T})(x::Number) where {T<:AbstractChar} = T(UInt32(x)) +(::Type{AbstractChar})(x::Number) = Char(x) +(::Type{T})(x::AbstractChar) where {T<:Union{Number,AbstractChar}} = T(codepoint(x)) +(::Type{T})(x::T) where {T<:AbstractChar} = x + +""" + ncodeunits(c::Char) -> Int + +Return the number of code units required to encode a character as UTF-8. +This is the number of bytes which will be printed if the character is written +to an output stream, or `ncodeunits(string(c))` but computed efficiently. + +!!! compat "Julia 1.1" + This method requires at least Julia 1.1. In Julia 1.0 consider + using `ncodeunits(string(c))`. +""" +ncodeunits(c::Char) = write(devnull, c) # this is surprisingly efficient + +""" + codepoint(c::AbstractChar) -> Integer + +Return the Unicode codepoint (an unsigned integer) corresponding +to the character `c` (or throw an exception if `c` does not represent +a valid character). For `Char`, this is a `UInt32` value, but +`AbstractChar` types that represent only a subset of Unicode may +return a different-sized integer (e.g. `UInt8`). +""" +function codepoint end + +codepoint(c::Char) = UInt32(c) + +struct InvalidCharError{T<:AbstractChar} <: Exception + char::T +end +struct CodePointError{T<:Integer} <: Exception + code::T +end +@noinline invalid_char(c::AbstractChar) = throw(InvalidCharError(c)) +@noinline code_point_err(u::Integer) = throw(CodePointError(u)) + +function ismalformed(c::Char) + u = reinterpret(UInt32, c) + l1 = leading_ones(u) << 3 + t0 = trailing_zeros(u) & 56 + (l1 == 8) | (l1 + t0 > 32) | + (((u & 0x00c0c0c0) ⊻ 0x00808080) >> t0 != 0) +end + +@inline is_overlong_enc(u::UInt32) = (u >> 24 == 0xc0) | (u >> 24 == 0xc1) | (u >> 21 == 0x0704) | (u >> 20 == 0x0f08) + +function isoverlong(c::Char) + u = reinterpret(UInt32, c) + is_overlong_enc(u) +end + +# fallback: other AbstractChar types, by default, are assumed +# not to support malformed or overlong encodings. + +""" + ismalformed(c::AbstractChar) -> Bool + +Return `true` if `c` represents malformed (non-Unicode) data according to the +encoding used by `c`. Defaults to `false` for non-`Char` types. See also +[`show_invalid`](@ref). +""" +ismalformed(c::AbstractChar) = false + +""" + isoverlong(c::AbstractChar) -> Bool + +Return `true` if `c` represents an overlong UTF-8 sequence. Defaults +to `false` for non-`Char` types. See also [`decode_overlong`](@ref) +and [`show_invalid`](@ref). +""" +isoverlong(c::AbstractChar) = false + +function UInt32(c::Char) + # TODO: use optimized inline LLVM + u = reinterpret(UInt32, c) + u < 0x80000000 && return u >> 24 + l1 = leading_ones(u) + t0 = trailing_zeros(u) & 56 + (l1 == 1) | (8l1 + t0 > 32) | + ((((u & 0x00c0c0c0) ⊻ 0x00808080) >> t0 != 0) | is_overlong_enc(u)) && + invalid_char(c)::Union{} + u &= 0xffffffff >> l1 + u >>= t0 + ((u & 0x0000007f) >> 0) | ((u & 0x00007f00) >> 2) | + ((u & 0x007f0000) >> 4) | ((u & 0x7f000000) >> 6) +end + +""" + decode_overlong(c::AbstractChar) -> Integer + +When [`isoverlong(c)`](@ref) is `true`, `decode_overlong(c)` returns +the Unicode codepoint value of `c`. `AbstractChar` implementations +that support overlong encodings should implement `Base.decode_overlong`. +""" +function decode_overlong end + +function decode_overlong(c::Char) + u = reinterpret(UInt32, c) + l1 = leading_ones(u) + t0 = trailing_zeros(u) & 56 + u &= 0xffffffff >> l1 + u >>= t0 + ((u & 0x0000007f) >> 0) | ((u & 0x00007f00) >> 2) | + ((u & 0x007f0000) >> 4) | ((u & 0x7f000000) >> 6) +end + +function Char(u::UInt32) + u < 0x80 && return reinterpret(Char, u << 24) + u < 0x00200000 || code_point_err(u)::Union{} + c = ((u << 0) & 0x0000003f) | ((u << 2) & 0x00003f00) | + ((u << 4) & 0x003f0000) | ((u << 6) & 0x3f000000) + c = u < 0x00000800 ? (c << 16) | 0xc0800000 : + u < 0x00010000 ? (c << 08) | 0xe0808000 : + (c << 00) | 0xf0808080 + reinterpret(Char, c) +end + +function (T::Union{Type{Int8},Type{UInt8}})(c::Char) + i = reinterpret(Int32, c) + i ≥ 0 ? ((i >>> 24) % T) : T(UInt32(c)) +end + +function Char(b::Union{Int8,UInt8}) + 0 ≤ b ≤ 0x7f ? reinterpret(Char, (b % UInt32) << 24) : Char(UInt32(b)) +end + +convert(::Type{AbstractChar}, x::Number) = Char(x) # default to Char +convert(::Type{T}, x::Number) where {T<:AbstractChar} = T(x) +convert(::Type{T}, x::AbstractChar) where {T<:Number} = T(x) +convert(::Type{T}, c::AbstractChar) where {T<:AbstractChar} = T(c) +convert(::Type{T}, c::T) where {T<:AbstractChar} = c + +rem(x::AbstractChar, ::Type{T}) where {T<:Number} = rem(codepoint(x), T) + +typemax(::Type{Char}) = reinterpret(Char, typemax(UInt32)) +typemin(::Type{Char}) = reinterpret(Char, typemin(UInt32)) + +size(c::AbstractChar) = () +size(c::AbstractChar, d::Integer) = d < 1 ? throw(BoundsError()) : 1 +ndims(c::AbstractChar) = 0 +ndims(::Type{<:AbstractChar}) = 0 +length(c::AbstractChar) = 1 +IteratorSize(::Type{Char}) = HasShape{0}() +firstindex(c::AbstractChar) = 1 +lastindex(c::AbstractChar) = 1 +getindex(c::AbstractChar) = c +getindex(c::AbstractChar, i::Integer) = i == 1 ? c : throw(BoundsError()) +getindex(c::AbstractChar, I::Integer...) = all(x -> x == 1, I) ? c : throw(BoundsError()) +first(c::AbstractChar) = c +last(c::AbstractChar) = c +eltype(::Type{T}) where {T<:AbstractChar} = T + +iterate(c::AbstractChar, done=false) = done ? nothing : (c, true) +isempty(c::AbstractChar) = false +in(x::AbstractChar, y::AbstractChar) = x == y + +==(x::Char, y::Char) = reinterpret(UInt32, x) == reinterpret(UInt32, y) +isless(x::Char, y::Char) = reinterpret(UInt32, x) < reinterpret(UInt32, y) +hash(x::Char, h::UInt) = + hash_uint64(((reinterpret(UInt32, x) + UInt64(0xd4d64234)) << 32) ⊻ UInt64(h)) + +first_utf8_byte(c::Char) = (reinterpret(UInt32, c) >> 24) % UInt8 + +# fallbacks: +isless(x::AbstractChar, y::AbstractChar) = isless(Char(x), Char(y)) +==(x::AbstractChar, y::AbstractChar) = Char(x) == Char(y) +hash(x::AbstractChar, h::UInt) = hash(Char(x), h) +widen(::Type{T}) where {T<:AbstractChar} = T + +-(x::AbstractChar, y::AbstractChar) = Int(x) - Int(y) +-(x::T, y::Integer) where {T<:AbstractChar} = T(Int32(x) - Int32(y)) ++(x::T, y::Integer) where {T<:AbstractChar} = T(Int32(x) + Int32(y)) ++(x::Integer, y::AbstractChar) = y + x + +# `print` should output UTF-8 by default for all AbstractChar types. +# (Packages may implement other IO subtypes to specify different encodings.) +# In contrast, `write(io, c)` outputs a `c` in an encoding determined by typeof(c). +print(io::IO, c::Char) = (write(io, c); nothing) +print(io::IO, c::AbstractChar) = print(io, Char(c)) # fallback: convert to output UTF-8 + +const hex_chars = UInt8['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', + 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', + 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'] + +function show_invalid(io::IO, c::Char) + write(io, 0x27) + u = reinterpret(UInt32, c) + while true + a = hex_chars[((u >> 28) & 0xf) + 1] + b = hex_chars[((u >> 24) & 0xf) + 1] + write(io, 0x5c, UInt8('x'), a, b) + (u <<= 8) == 0 && break + end + write(io, 0x27) +end + +""" + show_invalid(io::IO, c::AbstractChar) + +Called by `show(io, c)` when [`isoverlong(c)`](@ref) or +[`ismalformed(c)`](@ref) return `true`. Subclasses +of `AbstractChar` should define `Base.show_invalid` methods +if they support storing invalid character data. +""" +show_invalid + +# show c to io, assuming UTF-8 encoded output +function show(io::IO, c::AbstractChar) + if c <= '\\' + b = c == '\0' ? 0x30 : + c == '\a' ? 0x61 : + c == '\b' ? 0x62 : + c == '\t' ? 0x74 : + c == '\n' ? 0x6e : + c == '\v' ? 0x76 : + c == '\f' ? 0x66 : + c == '\r' ? 0x72 : + c == '\e' ? 0x65 : + c == '\'' ? 0x27 : + c == '\\' ? 0x5c : 0xff + if b != 0xff + write(io, 0x27, 0x5c, b, 0x27) + return + end + end + if isoverlong(c) || ismalformed(c) + show_invalid(io, c) + elseif isprint(c) + write(io, 0x27) + print(io, c) # use print, not write, to use UTF-8 for any AbstractChar + write(io, 0x27) + else # unprintable, well-formed, non-overlong Unicode + u = codepoint(c) + write(io, 0x27, 0x5c, u <= 0x7f ? 0x78 : u <= 0xffff ? 0x75 : 0x55) + d = max(2, 8 - (leading_zeros(u) >> 2)) + while 0 < d + write(io, hex_chars[((u >> ((d -= 1) << 2)) & 0xf) + 1]) + end + write(io, 0x27) + end + return +end + +function show(io::IO, ::MIME"text/plain", c::T) where {T<:AbstractChar} + show(io, c) + if !ismalformed(c) + print(io, ": ") + if isoverlong(c) + print(io, "[overlong] ") + u = decode_overlong(c) + c = T(u) + else + u = codepoint(c) + end + h = string(u, base = 16, pad = u ≤ 0xffff ? 4 : 6) + print(io, (isascii(c) ? "ASCII/" : ""), "Unicode U+", h) + else + print(io, ": Malformed UTF-8") + end + abr = Unicode.category_abbrev(c) + str = Unicode.category_string(c) + print(io, " (category ", abr, ": ", str, ")") +end diff --git a/base/checked.jl b/base/checked.jl new file mode 100644 index 0000000..8400158 --- /dev/null +++ b/base/checked.jl @@ -0,0 +1,352 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Support for checked integer arithmetic + +module Checked + +export checked_neg, checked_abs, checked_add, checked_sub, checked_mul, + checked_div, checked_rem, checked_fld, checked_mod, checked_cld, + add_with_overflow, sub_with_overflow, mul_with_overflow + +import Core.Intrinsics: + checked_sadd_int, checked_ssub_int, checked_smul_int, checked_sdiv_int, + checked_srem_int, + checked_uadd_int, checked_usub_int, checked_umul_int, checked_udiv_int, + checked_urem_int +import ..no_op_err, ..@_inline_meta, ..@_noinline_meta + +# define promotion behavior for checked operations +checked_add(x::Integer, y::Integer) = checked_add(promote(x,y)...) +checked_sub(x::Integer, y::Integer) = checked_sub(promote(x,y)...) +checked_mul(x::Integer, y::Integer) = checked_mul(promote(x,y)...) +checked_div(x::Integer, y::Integer) = checked_div(promote(x,y)...) +checked_rem(x::Integer, y::Integer) = checked_rem(promote(x,y)...) +checked_fld(x::Integer, y::Integer) = checked_fld(promote(x,y)...) +checked_mod(x::Integer, y::Integer) = checked_mod(promote(x,y)...) +checked_cld(x::Integer, y::Integer) = checked_cld(promote(x,y)...) + +# fallback catchall rules to prevent infinite recursion if promotion succeeds, +# but no method exists to handle those types +checked_abs(x::T) where {T<:Integer} = no_op_err("checked_abs", T) + +const SignedInt = Union{Int8,Int16,Int32,Int64,Int128} +const UnsignedInt = Union{UInt8,UInt16,UInt32,UInt64,UInt128} + +# LLVM has several code generation bugs for checked integer arithmetic (see e.g. +# #4905). We thus distinguish between operations that can be implemented via +# intrinsics, and operations for which we have to provide work-arounds. + +# Note: As far as this code has been tested, most checked_* functions are +# working fine in LLVM. (Note that division is still handled via `base/int.jl`, +# which always checks for overflow, and which provides its own sets of +# work-arounds for LLVM codegen bugs.) However, the comments in `base/int.jl` +# and in issue #4905 are more pessimistic. For the time being, we thus retain +# the ability to handle codegen bugs in LLVM, until the code here has been +# tested on more systems and architectures. It also seems that things depend on +# which compiler that was used to build LLVM (i.e. either gcc or clang). + +# These unions are used for most checked functions: +# BrokenSignedInt +# BrokenUnsignedInt +# These unions are used for checked_{mul,div,rem}: +# BrokenSignedIntMul +# BrokenUnsignedIntMul + +# This code runs early during bootstrap, and we can't use Julia's version +# strings yet +const llvm_version = Int(ccall(:jl_get_LLVM_VERSION, UInt32, ())) + +brokenSignedInt = Union{} +brokenUnsignedInt = Union{} +brokenSignedIntMul = Int128 +brokenUnsignedIntMul = UInt128 +if Core.sizeof(Ptr{Cvoid}) == 4 + brokenSignedIntMul = Union{brokenSignedIntMul, Int64} + brokenUnsignedIntMul = Union{brokenUnsignedIntMul, UInt64} +end +const BrokenSignedInt = brokenSignedInt +const BrokenUnsignedInt = brokenUnsignedInt +const BrokenSignedIntMul = brokenSignedIntMul +const BrokenUnsignedIntMul = brokenUnsignedIntMul +# Use these definitions to test the non-LLVM implementations +# const BrokenSignedInt = SignedInt +# const BrokenUnsignedInt = UnsignedInt +# const BrokenSignedIntMul = SignedInt +# const BrokenUnsignedIntMul = UnsignedInt + +""" + Base.checked_neg(x) + +Calculates `-x`, checking for overflow errors where applicable. For +example, standard two's complement signed integers (e.g. `Int`) cannot +represent `-typemin(Int)`, thus leading to an overflow. + +The overflow protection may impose a perceptible performance penalty. +""" +function checked_neg(x::T) where T<:Integer + checked_sub(T(0), x) +end +throw_overflowerr_negation(x) = (@_noinline_meta; + throw(OverflowError(Base.invokelatest(string, "checked arithmetic: cannot compute -x for x = ", x, "::", typeof(x))))) +if BrokenSignedInt != Union{} +function checked_neg(x::BrokenSignedInt) + r = -x + (x<0) & (r<0) && throw_overflowerr_negation(x) + r +end +end +if BrokenUnsignedInt != Union{} +function checked_neg(x::T) where T<:BrokenUnsignedInt + x != 0 && throw_overflowerr_negation(x) + T(0) +end +end + +""" + Base.checked_abs(x) + +Calculates `abs(x)`, checking for overflow errors where applicable. +For example, standard two's complement signed integers (e.g. `Int`) +cannot represent `abs(typemin(Int))`, thus leading to an overflow. + +The overflow protection may impose a perceptible performance penalty. +""" +function checked_abs end + +function checked_abs(x::SignedInt) + r = ifelse(x<0, -x, x) + r<0 && throw(OverflowError(string("checked arithmetic: cannot compute |x| for x = ", x, "::", typeof(x)))) + r + end +checked_abs(x::UnsignedInt) = x +checked_abs(x::Bool) = x + + + +""" + Base.add_with_overflow(x, y) -> (r, f) + +Calculates `r = x+y`, with the flag `f` indicating whether overflow has occurred. +""" +function add_with_overflow end +add_with_overflow(x::T, y::T) where {T<:SignedInt} = checked_sadd_int(x, y) +add_with_overflow(x::T, y::T) where {T<:UnsignedInt} = checked_uadd_int(x, y) +add_with_overflow(x::Bool, y::Bool) = (x+y, false) + +if BrokenSignedInt != Union{} +function add_with_overflow(x::T, y::T) where T<:BrokenSignedInt + r = x + y + # x and y have the same sign, and the result has a different sign + f = (x<0) == (y<0) != (r<0) + r, f +end +end +if BrokenUnsignedInt != Union{} +function add_with_overflow(x::T, y::T) where T<:BrokenUnsignedInt + # x + y > typemax(T) + # Note: ~y == -y-1 + x + y, x > ~y +end +end + + +throw_overflowerr_binaryop(op, x, y) = (@_noinline_meta; + throw(OverflowError(Base.invokelatest(string, x, " ", op, " ", y, " overflowed for type ", typeof(x))))) + +""" + Base.checked_add(x, y) + +Calculates `x+y`, checking for overflow errors where applicable. + +The overflow protection may impose a perceptible performance penalty. +""" +function checked_add(x::T, y::T) where T<:Integer + @_inline_meta + z, b = add_with_overflow(x, y) + b && throw_overflowerr_binaryop(:+, x, y) + z +end + +# Handle multiple arguments +checked_add(x) = x +checked_add(x::Bool) = +x + +checked_add(x1::T, x2::T, x3::T) where {T} = + checked_add(checked_add(x1, x2), x3) +checked_add(x1::T, x2::T, x3::T, x4::T) where {T} = + checked_add(checked_add(x1, x2), x3, x4) +checked_add(x1::T, x2::T, x3::T, x4::T, x5::T) where {T} = + checked_add(checked_add(x1, x2), x3, x4, x5) +checked_add(x1::T, x2::T, x3::T, x4::T, x5::T, x6::T) where {T} = + checked_add(checked_add(x1, x2), x3, x4, x5, x6) +checked_add(x1::T, x2::T, x3::T, x4::T, x5::T, x6::T, x7::T) where {T} = + checked_add(checked_add(x1, x2), x3, x4, x5, x6, x7) +checked_add(x1::T, x2::T, x3::T, x4::T, x5::T, x6::T, x7::T, x8::T) where {T} = + checked_add(checked_add(x1, x2), x3, x4, x5, x6, x7, x8) + + +""" + Base.sub_with_overflow(x, y) -> (r, f) + +Calculates `r = x-y`, with the flag `f` indicating whether overflow has occurred. +""" +function sub_with_overflow end +sub_with_overflow(x::T, y::T) where {T<:SignedInt} = checked_ssub_int(x, y) +sub_with_overflow(x::T, y::T) where {T<:UnsignedInt} = checked_usub_int(x, y) +sub_with_overflow(x::Bool, y::Bool) = (x-y, false) + +if BrokenSignedInt != Union{} +function sub_with_overflow(x::T, y::T) where T<:BrokenSignedInt + r = x - y + # x and y have different signs, and the result has a different sign than x + f = (x<0) != (y<0) == (r<0) + r, f +end +end +if BrokenUnsignedInt != Union{} +function sub_with_overflow(x::T, y::T) where T<:BrokenUnsignedInt + # x - y < 0 + x - y, x < y +end +end + +""" + Base.checked_sub(x, y) + +Calculates `x-y`, checking for overflow errors where applicable. + +The overflow protection may impose a perceptible performance penalty. +""" +function checked_sub(x::T, y::T) where T<:Integer + @_inline_meta + z, b = sub_with_overflow(x, y) + b && throw_overflowerr_binaryop(:-, x, y) + z +end + + +""" + Base.mul_with_overflow(x, y) -> (r, f) + +Calculates `r = x*y`, with the flag `f` indicating whether overflow has occurred. +""" +function mul_with_overflow end +mul_with_overflow(x::T, y::T) where {T<:SignedInt} = checked_smul_int(x, y) +mul_with_overflow(x::T, y::T) where {T<:UnsignedInt} = checked_umul_int(x, y) +mul_with_overflow(x::Bool, y::Bool) = (x*y, false) + +if BrokenSignedIntMul != Union{} && BrokenSignedIntMul != Int128 +function mul_with_overflow(x::T, y::T) where T<:BrokenSignedIntMul + r = widemul(x, y) + f = r % T != r + r % T, f +end +end +if BrokenUnsignedIntMul != Union{} && BrokenUnsignedIntMul != UInt128 +function mul_with_overflow(x::T, y::T) where T<:BrokenUnsignedIntMul + r = widemul(x, y) + f = r % T != r + r % T, f +end +end +if Int128 <: BrokenSignedIntMul + # Avoid BigInt + function mul_with_overflow(x::T, y::T) where T<:Int128 + f = if y > 0 + # x * y > typemax(T) + # x * y < typemin(T) + x > fld(typemax(T), y) || x < cld(typemin(T), y) + elseif y < 0 + # x * y > typemax(T) + # x * y < typemin(T) + # y == -1 can overflow fld + x < cld(typemax(T), y) || y != -1 && x > fld(typemin(T), y) + else + false + end + x*y, f + end +end +if UInt128 <: BrokenUnsignedIntMul + # Avoid BigInt + function mul_with_overflow(x::T, y::T) where T<:UInt128 + # x * y > typemax(T) + x * y, y > 0 && x > fld(typemax(T), y) + end +end + +""" + Base.checked_mul(x, y) + +Calculates `x*y`, checking for overflow errors where applicable. + +The overflow protection may impose a perceptible performance penalty. +""" +function checked_mul(x::T, y::T) where T<:Integer + @_inline_meta + z, b = mul_with_overflow(x, y) + b && throw_overflowerr_binaryop(:*, x, y) + z +end + +# Handle multiple arguments +checked_mul(x) = x +checked_mul(x1::T, x2::T, x3::T) where {T} = + checked_mul(checked_mul(x1, x2), x3) +checked_mul(x1::T, x2::T, x3::T, x4::T) where {T} = + checked_mul(checked_mul(x1, x2), x3, x4) +checked_mul(x1::T, x2::T, x3::T, x4::T, x5::T) where {T} = + checked_mul(checked_mul(x1, x2), x3, x4, x5) +checked_mul(x1::T, x2::T, x3::T, x4::T, x5::T, x6::T) where {T} = + checked_mul(checked_mul(x1, x2), x3, x4, x5, x6) +checked_mul(x1::T, x2::T, x3::T, x4::T, x5::T, x6::T, x7::T) where {T} = + checked_mul(checked_mul(x1, x2), x3, x4, x5, x6, x7) +checked_mul(x1::T, x2::T, x3::T, x4::T, x5::T, x6::T, x7::T, x8::T) where {T} = + checked_mul(checked_mul(x1, x2), x3, x4, x5, x6, x7, x8) + +""" + Base.checked_div(x, y) + +Calculates `div(x,y)`, checking for overflow errors where applicable. + +The overflow protection may impose a perceptible performance penalty. +""" +checked_div(x::T, y::T) where {T<:Integer} = div(x, y) # Base.div already checks + +""" + Base.checked_rem(x, y) + +Calculates `x%y`, checking for overflow errors where applicable. + +The overflow protection may impose a perceptible performance penalty. +""" +checked_rem(x::T, y::T) where {T<:Integer} = rem(x, y) # Base.rem already checks + +""" + Base.checked_fld(x, y) + +Calculates `fld(x,y)`, checking for overflow errors where applicable. + +The overflow protection may impose a perceptible performance penalty. +""" +checked_fld(x::T, y::T) where {T<:Integer} = fld(x, y) # Base.fld already checks + +""" + Base.checked_mod(x, y) + +Calculates `mod(x,y)`, checking for overflow errors where applicable. + +The overflow protection may impose a perceptible performance penalty. +""" +checked_mod(x::T, y::T) where {T<:Integer} = mod(x, y) # Base.mod already checks + +""" + Base.checked_cld(x, y) + +Calculates `cld(x,y)`, checking for overflow errors where applicable. + +The overflow protection may impose a perceptible performance penalty. +""" +checked_cld(x::T, y::T) where {T<:Integer} = cld(x, y) # Base.cld already checks + +end diff --git a/base/client.jl b/base/client.jl new file mode 100644 index 0000000..b7346b1 --- /dev/null +++ b/base/client.jl @@ -0,0 +1,468 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## client.jl - frontend handling command line options, environment setup, +## and REPL + +have_color = false +default_color_warn = :yellow +default_color_error = :light_red +default_color_info = :cyan +default_color_debug = :blue +default_color_input = :normal +default_color_answer = :normal +color_normal = text_colors[:normal] + +function repl_color(key, default) + env_str = get(ENV, key, "") + c = tryparse(Int, env_str) + c_conv = something(c, Symbol(env_str)) + haskey(text_colors, c_conv) ? c_conv : default +end + +error_color() = repl_color("JULIA_ERROR_COLOR", default_color_error) +warn_color() = repl_color("JULIA_WARN_COLOR" , default_color_warn) +info_color() = repl_color("JULIA_INFO_COLOR" , default_color_info) +debug_color() = repl_color("JULIA_DEBUG_COLOR" , default_color_debug) + +input_color() = text_colors[repl_color("JULIA_INPUT_COLOR", default_color_input)] +answer_color() = text_colors[repl_color("JULIA_ANSWER_COLOR", default_color_answer)] + +stackframe_lineinfo_color() = repl_color("JULIA_STACKFRAME_LINEINFO_COLOR", :bold) +stackframe_function_color() = repl_color("JULIA_STACKFRAME_FUNCTION_COLOR", :bold) + +function repl_cmd(cmd, out) + shell = shell_split(get(ENV, "JULIA_SHELL", get(ENV, "SHELL", "/bin/sh"))) + shell_name = Base.basename(shell[1]) + + # Immediately expand all arguments, so that typing e.g. ~/bin/foo works. + cmd.exec .= expanduser.(cmd.exec) + + if isempty(cmd.exec) + throw(ArgumentError("no cmd to execute")) + elseif cmd.exec[1] == "cd" + new_oldpwd = pwd() + if length(cmd.exec) > 2 + throw(ArgumentError("cd method only takes one argument")) + elseif length(cmd.exec) == 2 + dir = cmd.exec[2] + if dir == "-" + if !haskey(ENV, "OLDPWD") + error("cd: OLDPWD not set") + end + dir = ENV["OLDPWD"] + end + cd(dir) + else + cd() + end + ENV["OLDPWD"] = new_oldpwd + println(out, pwd()) + else + @static if !Sys.iswindows() + if shell_name == "fish" + shell_escape_cmd = "begin; $(shell_escape_posixly(cmd)); and true; end" + else + shell_escape_cmd = "($(shell_escape_posixly(cmd))) && true" + end + cmd = `$shell -c $shell_escape_cmd` + end + run(ignorestatus(cmd)) + end + nothing +end + +function ip_matches_func(ip, func::Symbol) + for fr in StackTraces.lookup(ip) + if fr === StackTraces.UNKNOWN || fr.from_c + return false + end + fr.func === func && return true + end + return false +end + +function scrub_repl_backtrace(bt) + if bt !== nothing + # remove REPL-related frames from interactive printing + eval_ind = findlast(addr->ip_matches_func(addr, :eval), bt) + if eval_ind !== nothing + return bt[1:eval_ind-1] + end + end + return bt +end + +function display_error(io::IO, er, bt) + printstyled(io, "ERROR: "; bold=true, color=Base.error_color()) + showerror(IOContext(io, :limit => true), er, scrub_repl_backtrace(bt)) + println(io) +end +function display_error(io::IO, stack::Vector) + printstyled(io, "ERROR: "; bold=true, color=Base.error_color()) + show_exception_stack(IOContext(io, :limit => true), Any[ (x[1], scrub_repl_backtrace(x[2])) for x in stack ]) +end +display_error(stack::Vector) = display_error(stderr, stack) +display_error(er, bt) = display_error(stderr, er, bt) +display_error(er) = display_error(er, []) + +function eval_user_input(errio, @nospecialize(ast), show_value::Bool) + errcount = 0 + lasterr = nothing + while true + try + if have_color + print(color_normal) + end + if lasterr !== nothing + invokelatest(display_error, errio, lasterr) + errcount = 0 + lasterr = nothing + else + ast = Meta.lower(Main, ast) + value = Core.eval(Main, ast) + ccall(:jl_set_global, Cvoid, (Any, Any, Any), Main, :ans, value) + if !(value === nothing) && show_value + if have_color + print(answer_color()) + end + try + invokelatest(display, value) + catch + @error "Evaluation succeeded, but an error occurred while displaying the value" typeof(value) + rethrow() + end + println() + end + end + break + catch + if errcount > 0 + @error "SYSTEM: display_error(errio, lasterr) caused an error" + end + errcount += 1 + lasterr = catch_stack() + if errcount > 2 + @error "It is likely that something important is broken, and Julia will not be able to continue normally" errcount + break + end + end + end + isa(stdin, TTY) && println() + nothing +end + +function _parse_input_line_core(s::String, filename::String) + ex = ccall(:jl_parse_all, Any, (Ptr{UInt8}, Csize_t, Ptr{UInt8}, Csize_t), + s, sizeof(s), filename, sizeof(filename)) + if ex isa Expr && ex.head === :toplevel + if isempty(ex.args) + return nothing + end + last = ex.args[end] + if last isa Expr && (last.head === :error || last.head === :incomplete) + # if a parse error happens in the middle of a multi-line input + # return only the error, so that none of the input is evaluated. + return last + end + end + return ex +end + +function parse_input_line(s::String; filename::String="none", depwarn=true) + # For now, assume all parser warnings are depwarns + ex = if depwarn + _parse_input_line_core(s, filename) + else + with_logger(NullLogger()) do + _parse_input_line_core(s, filename) + end + end + return ex +end +parse_input_line(s::AbstractString) = parse_input_line(String(s)) + +function parse_input_line(io::IO) + s = "" + while !eof(io) + s *= readline(io, keep=true) + e = parse_input_line(s) + if !(isa(e,Expr) && e.head === :incomplete) + return e + end + end +end + +# detect the reason which caused an :incomplete expression +# from the error message +# NOTE: the error messages are defined in src/julia-parser.scm +incomplete_tag(ex) = :none +function incomplete_tag(ex::Expr) + Meta.isexpr(ex, :incomplete) || return :none + msg = ex.args[1] + occursin("string", msg) && return :string + occursin("comment", msg) && return :comment + occursin("requires end", msg) && return :block + occursin("\"`\"", msg) && return :cmd + occursin("character", msg) && return :char + return :other +end + +# call include() on a file, ignoring if not found +include_ifexists(mod::Module, path::AbstractString) = isfile(path) && include(mod, path) + +function exec_options(opts) + if !isempty(ARGS) + idxs = findall(x -> x == "--", ARGS) + length(idxs) > 0 && deleteat!(ARGS, idxs[1]) + end + quiet = (opts.quiet != 0) + startup = (opts.startupfile != 2) + history_file = (opts.historyfile != 0) + color_set = (opts.color != 0) # --color!=auto + global have_color = (opts.color == 1) # --color=on + global is_interactive = (opts.isinteractive != 0) + + # pre-process command line argument list + arg_is_program = !isempty(ARGS) + repl = !arg_is_program + cmds = unsafe_load_commands(opts.commands) + for (cmd, arg) in cmds + if cmd == 'e' + arg_is_program = false + repl = false + elseif cmd == 'E' + arg_is_program = false + repl = false + elseif cmd == 'L' + # nothing + else + @warn "Unexpected command -$cmd'$arg'" + end + end + + # remove filename from ARGS + global PROGRAM_FILE = arg_is_program ? popfirst!(ARGS) : "" + + # Load Distributed module only if any of the Distributed options have been specified. + distributed_mode = (opts.worker == 1) || (opts.nprocs > 0) || (opts.machine_file != C_NULL) + if distributed_mode + let Distributed = require(PkgId(UUID((0x8ba89e20_285c_5b6f, 0x9357_94700520ee1b)), "Distributed")) + Core.eval(Main, :(const Distributed = $Distributed)) + Core.eval(Main, :(using .Distributed)) + end + + invokelatest(Main.Distributed.process_opts, opts) + end + + # load ~/.julia/config/startup.jl file + startup && load_julia_startup() + + # process cmds list + for (cmd, arg) in cmds + if cmd == 'e' + Core.eval(Main, parse_input_line(arg)) + elseif cmd == 'E' + invokelatest(show, Core.eval(Main, parse_input_line(arg))) + println() + elseif cmd == 'L' + # load file immediately on all processors + if !distributed_mode + include(Main, arg) + else + # TODO: Move this logic to Distributed and use a callback + @sync for p in invokelatest(Main.procs) + @async invokelatest(Main.remotecall_wait, include, p, Main, arg) + end + end + end + end + + # load file + if arg_is_program + # program + if !is_interactive + ccall(:jl_exit_on_sigint, Cvoid, (Cint,), 1) + end + try + include(Main, PROGRAM_FILE) + catch + invokelatest(display_error, catch_stack()) + if !is_interactive + exit(1) + end + end + end + repl |= is_interactive + if repl + interactiveinput = isa(stdin, TTY) + if interactiveinput + global is_interactive = true + banner = (opts.banner != 0) # --banner!=no + else + banner = (opts.banner == 1) # --banner=yes + end + run_main_repl(interactiveinput, quiet, banner, history_file, color_set) + end + nothing +end + +function load_julia_startup() + # If the user built us with a specific Base.SYSCONFDIR, check that location first for a startup.jl file + # If it is not found, then continue on to the relative path based on Sys.BINDIR + BINDIR = Sys.BINDIR::String + SYSCONFDIR = Base.SYSCONFDIR::String + if !isempty(SYSCONFDIR) && isfile(joinpath(BINDIR, SYSCONFDIR, "julia", "startup.jl")) + include(Main, abspath(BINDIR, SYSCONFDIR, "julia", "startup.jl")) + else + include_ifexists(Main, abspath(BINDIR, "..", "etc", "julia", "startup.jl")) + end + !isempty(DEPOT_PATH) && include_ifexists(Main, abspath(DEPOT_PATH[1], "config", "startup.jl")) + return nothing +end + +const repl_hooks = [] + +""" + atreplinit(f) + +Register a one-argument function to be called before the REPL interface is initialized in +interactive sessions; this is useful to customize the interface. The argument of `f` is the +REPL object. This function should be called from within the `.julia/config/startup.jl` +initialization file. +""" +atreplinit(f::Function) = (pushfirst!(repl_hooks, f); nothing) + +function __atreplinit(repl) + for f in repl_hooks + try + f(repl) + catch err + showerror(stderr, err) + println(stderr) + end + end +end +_atreplinit(repl) = invokelatest(__atreplinit, repl) + +# The REPL stdlib hooks into Base using this Ref +const REPL_MODULE_REF = Ref{Module}() + +# run the requested sort of evaluation loop on stdio +function run_main_repl(interactive::Bool, quiet::Bool, banner::Bool, history_file::Bool, color_set::Bool) + global active_repl + # load interactive-only libraries + if !isdefined(Main, :InteractiveUtils) + try + let InteractiveUtils = require(PkgId(UUID(0xb77e0a4c_d291_57a0_90e8_8db25a27a240), "InteractiveUtils")) + Core.eval(Main, :(const InteractiveUtils = $InteractiveUtils)) + Core.eval(Main, :(using .InteractiveUtils)) + end + catch ex + @warn "Failed to import InteractiveUtils into module Main" exception=(ex, catch_backtrace()) + end + end + + if interactive && isassigned(REPL_MODULE_REF) + invokelatest(REPL_MODULE_REF[]) do REPL + term_env = get(ENV, "TERM", @static Sys.iswindows() ? "" : "dumb") + term = REPL.Terminals.TTYTerminal(term_env, stdin, stdout, stderr) + color_set || (global have_color = REPL.Terminals.hascolor(term)) + banner && Base.banner(term) + if term.term_type == "dumb" + active_repl = REPL.BasicREPL(term) + quiet || @warn "Terminal not fully functional" + else + active_repl = REPL.LineEditREPL(term, have_color, true) + active_repl.history_file = history_file + end + # Make sure any displays pushed in .julia/config/startup.jl ends up above the + # REPLDisplay + pushdisplay(REPL.REPLDisplay(active_repl)) + _atreplinit(active_repl) + REPL.run_repl(active_repl, backend->(global active_repl_backend = backend)) + end + else + # otherwise provide a simple fallback + if interactive && !quiet + @warn "REPL provider not available: using basic fallback" + end + banner && Base.banner() + let input = stdin + if isa(input, File) || isa(input, IOStream) + # for files, we can slurp in the whole thing at once + ex = parse_input_line(read(input, String)) + if Meta.isexpr(ex, :toplevel) + # if we get back a list of statements, eval them sequentially + # as if we had parsed them sequentially + for stmt in ex.args + eval_user_input(stderr, stmt, true) + end + body = ex.args + else + eval_user_input(stderr, ex, true) + end + else + while isopen(input) || !eof(input) + if interactive + print("julia> ") + flush(stdout) + end + try + eval_user_input(stderr, parse_input_line(input), true) + catch err + isa(err, InterruptException) ? print("\n\n") : rethrow() + end + end + end + end + end + nothing +end + +# MainInclude exists to hide Main.include and eval from `names(Main)`. +baremodule MainInclude +include(fname::AbstractString) = Main.Base.include(Main, fname) +eval(x) = Core.eval(Main, x) +end + +""" + eval(expr) + +Evaluate an expression in the global scope of the containing module. +Every `Module` (except those defined with `baremodule`) has its own 1-argument +definition of `eval`, which evaluates expressions in that module. +""" +MainInclude.eval + +""" + include(path::AbstractString) + +Evaluate the contents of the input source file in the global scope of the containing module. +Every module (except those defined with `baremodule`) has its own 1-argument +definition of `include`, which evaluates the file in that module. +Returns the result of the last evaluated expression of the input file. During including, +a task-local include path is set to the directory containing the file. Nested calls to +`include` will search relative to that path. This function is typically used to load source +interactively, or to combine files in packages that are broken into multiple source files. + +Use [`Base.include`](@ref) to evaluate a file into another module. +""" +MainInclude.include + +function _start() + empty!(ARGS) + append!(ARGS, Core.ARGS) + if ccall(:jl_generating_output, Cint, ()) != 0 && JLOptions().incremental == 0 + # clear old invalid pointers + PCRE.__init__() + end + try + exec_options(JLOptions()) + catch + invokelatest(display_error, catch_stack()) + exit(1) + end + if is_interactive && have_color + print(color_normal) + end +end diff --git a/base/cmd.jl b/base/cmd.jl new file mode 100644 index 0000000..3da3313 --- /dev/null +++ b/base/cmd.jl @@ -0,0 +1,388 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +abstract type AbstractCmd end + +# libuv process option flags +const UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS = UInt8(1 << 2) +const UV_PROCESS_DETACHED = UInt8(1 << 3) +const UV_PROCESS_WINDOWS_HIDE = UInt8(1 << 4) + +struct Cmd <: AbstractCmd + exec::Vector{String} + ignorestatus::Bool + flags::UInt32 # libuv process flags + env::Union{Array{String},Nothing} + dir::String + Cmd(exec::Vector{String}) = + new(exec, false, 0x00, nothing, "") + Cmd(cmd::Cmd, ignorestatus, flags, env, dir) = + new(cmd.exec, ignorestatus, flags, env, + dir === cmd.dir ? dir : cstr(dir)) + function Cmd(cmd::Cmd; ignorestatus::Bool=cmd.ignorestatus, env=cmd.env, dir::AbstractString=cmd.dir, + detach::Bool = 0 != cmd.flags & UV_PROCESS_DETACHED, + windows_verbatim::Bool = 0 != cmd.flags & UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS, + windows_hide::Bool = 0 != cmd.flags & UV_PROCESS_WINDOWS_HIDE) + flags = detach * UV_PROCESS_DETACHED | + windows_verbatim * UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS | + windows_hide * UV_PROCESS_WINDOWS_HIDE + new(cmd.exec, ignorestatus, flags, byteenv(env), + dir === cmd.dir ? dir : cstr(dir)) + end +end + +has_nondefault_cmd_flags(c::Cmd) = + c.ignorestatus || + c.flags != 0x00 || + c.env !== nothing || + c.dir !== "" + +""" + Cmd(cmd::Cmd; ignorestatus, detach, windows_verbatim, windows_hide, env, dir) + +Construct a new `Cmd` object, representing an external program and arguments, from `cmd`, +while changing the settings of the optional keyword arguments: + +* `ignorestatus::Bool`: If `true` (defaults to `false`), then the `Cmd` will not throw an + error if the return code is nonzero. +* `detach::Bool`: If `true` (defaults to `false`), then the `Cmd` will be run in a new + process group, allowing it to outlive the `julia` process and not have Ctrl-C passed to + it. +* `windows_verbatim::Bool`: If `true` (defaults to `false`), then on Windows the `Cmd` will + send a command-line string to the process with no quoting or escaping of arguments, even + arguments containing spaces. (On Windows, arguments are sent to a program as a single + "command-line" string, and programs are responsible for parsing it into arguments. By + default, empty arguments and arguments with spaces or tabs are quoted with double quotes + `"` in the command line, and `\\` or `"` are preceded by backslashes. + `windows_verbatim=true` is useful for launching programs that parse their command line in + nonstandard ways.) Has no effect on non-Windows systems. +* `windows_hide::Bool`: If `true` (defaults to `false`), then on Windows no new console + window is displayed when the `Cmd` is executed. This has no effect if a console is + already open or on non-Windows systems. +* `env`: Set environment variables to use when running the `Cmd`. `env` is either a + dictionary mapping strings to strings, an array of strings of the form `"var=val"`, an + array or tuple of `"var"=>val` pairs, or `nothing`. In order to modify (rather than + replace) the existing environment, create `env` by `copy(ENV)` and then set + `env["var"]=val` as desired. +* `dir::AbstractString`: Specify a working directory for the command (instead + of the current directory). + +For any keywords that are not specified, the current settings from `cmd` are used. Normally, +to create a `Cmd` object in the first place, one uses backticks, e.g. + + Cmd(`echo "Hello world"`, ignorestatus=true, detach=false) +""" +Cmd + +hash(x::Cmd, h::UInt) = hash(x.exec, hash(x.env, hash(x.ignorestatus, hash(x.dir, hash(x.flags, h))))) +==(x::Cmd, y::Cmd) = x.exec == y.exec && x.env == y.env && x.ignorestatus == y.ignorestatus && + x.dir == y.dir && isequal(x.flags, y.flags) + +struct OrCmds <: AbstractCmd + a::AbstractCmd + b::AbstractCmd + OrCmds(a::AbstractCmd, b::AbstractCmd) = new(a, b) +end + +struct ErrOrCmds <: AbstractCmd + a::AbstractCmd + b::AbstractCmd + ErrOrCmds(a::AbstractCmd, b::AbstractCmd) = new(a, b) +end + +struct AndCmds <: AbstractCmd + a::AbstractCmd + b::AbstractCmd + AndCmds(a::AbstractCmd, b::AbstractCmd) = new(a, b) +end + +hash(x::AndCmds, h::UInt) = hash(x.a, hash(x.b, h)) +==(x::AndCmds, y::AndCmds) = x.a == y.a && x.b == y.b + +shell_escape(cmd::Cmd; special::AbstractString="") = + shell_escape(cmd.exec..., special=special) +shell_escape_posixly(cmd::Cmd) = + shell_escape_posixly(cmd.exec...) + +function show(io::IO, cmd::Cmd) + print_env = cmd.env !== nothing + print_dir = !isempty(cmd.dir) + (print_env || print_dir) && print(io, "setenv(") + print(io, '`') + join(io, map(cmd.exec) do arg + replace(sprint(context=io) do io + with_output_color(:underline, io) do io + print_shell_word(io, arg, shell_special) + end + end, '`' => "\\`") + end, ' ') + print(io, '`') + print_env && (print(io, ","); show(io, cmd.env)) + print_dir && (print(io, "; dir="); show(io, cmd.dir)) + (print_dir || print_env) && print(io, ")") + nothing +end + +function show(io::IO, cmds::Union{OrCmds,ErrOrCmds}) + print(io, "pipeline(") + show(io, cmds.a) + print(io, ", ") + print(io, isa(cmds, ErrOrCmds) ? "stderr=" : "stdout=") + show(io, cmds.b) + print(io, ")") +end + +function show(io::IO, cmds::AndCmds) + show(io, cmds.a) + print(io, " & ") + show(io, cmds.b) +end + +const STDIN_NO = 0 +const STDOUT_NO = 1 +const STDERR_NO = 2 + +struct FileRedirect + filename::String + append::Bool + FileRedirect(filename::AbstractString, append::Bool) = FileRedirect(convert(String, filename), append) + function FileRedirect(filename::String, append::Bool) + if lowercase(filename) == (@static Sys.iswindows() ? "nul" : "/dev/null") + @warn "For portability use devnull instead of a file redirect" maxlog=1 + end + return new(filename, append) + end +end + +# setup_stdio ≈ cconvert +# rawhandle ≈ unsafe_convert +rawhandle(::DevNull) = C_NULL +rawhandle(x::OS_HANDLE) = x +if OS_HANDLE !== RawFD + rawhandle(x::RawFD) = Libc._get_osfhandle(x) +end + +const Redirectable = Union{IO, FileRedirect, RawFD, OS_HANDLE} +const StdIOSet = NTuple{3, Redirectable} + +struct CmdRedirect <: AbstractCmd + cmd::AbstractCmd + handle::Redirectable + stream_no::Int + readable::Bool +end +CmdRedirect(cmd, handle, stream_no) = CmdRedirect(cmd, handle, stream_no, stream_no == STDIN_NO) + +function show(io::IO, cr::CmdRedirect) + print(io, "pipeline(") + show(io, cr.cmd) + print(io, ", ") + if cr.stream_no == STDOUT_NO + print(io, "stdout") + elseif cr.stream_no == STDERR_NO + print(io, "stderr") + elseif cr.stream_no == STDIN_NO + print(io, "stdin") + else + print(io, cr.stream_no) + end + print(io, cr.readable ? "<" : ">") + show(io, cr.handle) + print(io, ")") +end + +""" + ignorestatus(command) + +Mark a command object so that running it will not throw an error if the result code is non-zero. +""" +ignorestatus(cmd::Cmd) = Cmd(cmd, ignorestatus=true) +ignorestatus(cmd::Union{OrCmds,AndCmds}) = + typeof(cmd)(ignorestatus(cmd.a), ignorestatus(cmd.b)) + +""" + detach(command) + +Mark a command object so that it will be run in a new process group, allowing it to outlive the julia process, and not have Ctrl-C interrupts passed to it. +""" +detach(cmd::Cmd) = Cmd(cmd; detach=true) + +# like String(s), but throw an error if s contains NUL, since +# libuv requires NUL-terminated strings +function cstr(s) + if Base.containsnul(s) + throw(ArgumentError("strings containing NUL cannot be passed to spawned processes")) + end + return String(s) +end + +# convert various env representations into an array of "key=val" strings +byteenv(env::AbstractArray{<:AbstractString}) = + String[cstr(x) for x in env] +byteenv(env::AbstractDict) = + String[cstr(string(k)*"="*string(v)) for (k,v) in env] +byteenv(env::Nothing) = nothing +byteenv(env::Union{AbstractVector{Pair{T}}, Tuple{Vararg{Pair{T}}}}) where {T<:AbstractString} = + String[cstr(k*"="*string(v)) for (k,v) in env] + +""" + setenv(command::Cmd, env; dir="") + +Set environment variables to use when running the given `command`. `env` is either a +dictionary mapping strings to strings, an array of strings of the form `"var=val"`, or zero +or more `"var"=>val` pair arguments. In order to modify (rather than replace) the existing +environment, create `env` by `copy(ENV)` and then setting `env["var"]=val` as desired, or +use `withenv`. + +The `dir` keyword argument can be used to specify a working directory for the command. +""" +setenv(cmd::Cmd, env; dir="") = Cmd(cmd; env=byteenv(env), dir=dir) +setenv(cmd::Cmd, env::Pair{<:AbstractString}...; dir="") = + setenv(cmd, env; dir=dir) +setenv(cmd::Cmd; dir="") = Cmd(cmd; dir=dir) + +(&)(left::AbstractCmd, right::AbstractCmd) = AndCmds(left, right) +redir_out(src::AbstractCmd, dest::AbstractCmd) = OrCmds(src, dest) +redir_err(src::AbstractCmd, dest::AbstractCmd) = ErrOrCmds(src, dest) + +# Stream Redirects +redir_out(dest::Redirectable, src::AbstractCmd) = CmdRedirect(src, dest, STDIN_NO) +redir_out(src::AbstractCmd, dest::Redirectable) = CmdRedirect(src, dest, STDOUT_NO) +redir_err(src::AbstractCmd, dest::Redirectable) = CmdRedirect(src, dest, STDERR_NO) + +# File redirects +redir_out(src::AbstractCmd, dest::AbstractString) = CmdRedirect(src, FileRedirect(dest, false), STDOUT_NO) +redir_out(src::AbstractString, dest::AbstractCmd) = CmdRedirect(dest, FileRedirect(src, false), STDIN_NO) +redir_err(src::AbstractCmd, dest::AbstractString) = CmdRedirect(src, FileRedirect(dest, false), STDERR_NO) +redir_out_append(src::AbstractCmd, dest::AbstractString) = CmdRedirect(src, FileRedirect(dest, true), STDOUT_NO) +redir_err_append(src::AbstractCmd, dest::AbstractString) = CmdRedirect(src, FileRedirect(dest, true), STDERR_NO) + +""" + pipeline(command; stdin, stdout, stderr, append=false) + +Redirect I/O to or from the given `command`. Keyword arguments specify which of the +command's streams should be redirected. `append` controls whether file output appends to the +file. This is a more general version of the 2-argument `pipeline` function. +`pipeline(from, to)` is equivalent to `pipeline(from, stdout=to)` when `from` is a command, +and to `pipeline(to, stdin=from)` when `from` is another kind of data source. + +**Examples**: + +```julia +run(pipeline(`dothings`, stdout="out.txt", stderr="errs.txt")) +run(pipeline(`update`, stdout="log.txt", append=true)) +``` +""" +function pipeline(cmd::AbstractCmd; stdin=nothing, stdout=nothing, stderr=nothing, append::Bool=false) + if append && stdout === nothing && stderr === nothing + throw(ArgumentError("append set to true, but no output redirections specified")) + end + if stdin !== nothing + cmd = redir_out(stdin, cmd) + end + if stdout !== nothing + cmd = append ? redir_out_append(cmd, stdout) : redir_out(cmd, stdout) + end + if stderr !== nothing + cmd = append ? redir_err_append(cmd, stderr) : redir_err(cmd, stderr) + end + return cmd +end + +pipeline(cmd::AbstractCmd, dest) = pipeline(cmd, stdout=dest) +pipeline(src::Union{Redirectable,AbstractString}, cmd::AbstractCmd) = pipeline(cmd, stdin=src) + +""" + pipeline(from, to, ...) + +Create a pipeline from a data source to a destination. The source and destination can be +commands, I/O streams, strings, or results of other `pipeline` calls. At least one argument +must be a command. Strings refer to filenames. When called with more than two arguments, +they are chained together from left to right. For example, `pipeline(a,b,c)` is equivalent to +`pipeline(pipeline(a,b),c)`. This provides a more concise way to specify multi-stage +pipelines. + +**Examples**: + +```julia +run(pipeline(`ls`, `grep xyz`)) +run(pipeline(`ls`, "out.txt")) +run(pipeline("out.txt", `grep xyz`)) +``` +""" +pipeline(a, b, c, d...) = pipeline(pipeline(a, b), c, d...) + + +## implementation of `cmd` syntax ## + +cmd_interpolate(xs...) = cstr(string(map(cmd_interpolate1, xs)...)) +cmd_interpolate1(x) = x +cmd_interpolate1(::Nothing) = throw(ArgumentError("`nothing` can not be interpolated into commands (`Cmd`)")) + +arg_gen() = String[] +arg_gen(x::AbstractString) = String[cstr(x)] +function arg_gen(cmd::Cmd) + if has_nondefault_cmd_flags(cmd) + throw(ArgumentError("Non-default environment behavior is only permitted for the first interpolant.")) + end + cmd.exec +end + +function arg_gen(head) + if isiterable(typeof(head)) + vals = String[] + for x in head + push!(vals, cmd_interpolate(x)) + end + return vals + else + return String[cmd_interpolate(head)] + end +end + +function arg_gen(head, tail...) + head = arg_gen(head) + tail = arg_gen(tail...) + vals = String[] + for h = head, t = tail + push!(vals, cmd_interpolate(h,t)) + end + return vals +end + +function cmd_gen(parsed) + args = String[] + if length(parsed) >= 1 && isa(parsed[1], Tuple{Cmd}) + cmd = parsed[1][1] + (ignorestatus, flags, env, dir) = (cmd.ignorestatus, cmd.flags, cmd.env, cmd.dir) + append!(args, cmd.exec) + for arg in tail(parsed) + append!(args, arg_gen(arg...)) + end + return Cmd(Cmd(args), ignorestatus, flags, env, dir) + else + for arg in parsed + append!(args, arg_gen(arg...)) + end + return Cmd(args) + end +end + +""" + @cmd str + +Similar to `cmd`, generate a `Cmd` from the `str` string which represents the shell command(s) to be executed. +The [`Cmd`](@ref) object can be run as a process and can outlive the spawning julia process (see `Cmd` for more). + +# Examples +```jldoctest +julia> cm = @cmd " echo 1 " +`echo 1` + +julia> run(cm) +1 +Process(`echo 1`, ProcessExited(0)) +``` +""" +macro cmd(str) + return :(cmd_gen($(esc(shell_parse(str, special=shell_special)[1])))) +end diff --git a/base/combinatorics.jl b/base/combinatorics.jl new file mode 100644 index 0000000..225fc9d --- /dev/null +++ b/base/combinatorics.jl @@ -0,0 +1,299 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Factorials + +const _fact_table64 = Vector{Int64}(undef, 20) +_fact_table64[1] = 1 +for n in 2:20 + _fact_table64[n] = _fact_table64[n-1] * n +end + +const _fact_table128 = Vector{UInt128}(undef, 34) +_fact_table128[1] = 1 +for n in 2:34 + _fact_table128[n] = _fact_table128[n-1] * n +end + +function factorial_lookup(n::Integer, table, lim) + n < 0 && throw(DomainError(n, "`n` must not be negative.")) + n > lim && throw(OverflowError(string(n, " is too large to look up in the table; consider using `factorial(big(", n, "))` instead"))) + n == 0 && return one(n) + @inbounds f = table[n] + return oftype(n, f) +end + +factorial(n::Int128) = factorial_lookup(n, _fact_table128, 33) +factorial(n::UInt128) = factorial_lookup(n, _fact_table128, 34) +factorial(n::Union{Int64,UInt64}) = factorial_lookup(n, _fact_table64, 20) + +if Int === Int32 + factorial(n::Union{Int8,UInt8,Int16,UInt16}) = factorial(Int32(n)) + factorial(n::Union{Int32,UInt32}) = factorial_lookup(n, _fact_table64, 12) +else + factorial(n::Union{Int8,UInt8,Int16,UInt16,Int32,UInt32}) = factorial(Int64(n)) +end + + +# Basic functions for working with permutations + +""" + isperm(v) -> Bool + +Return `true` if `v` is a valid permutation. + +# Examples +```jldoctest +julia> isperm([1; 2]) +true + +julia> isperm([1; 3]) +false +``` +""" +function isperm(A) + n = length(A) + used = falses(n) + for a in A + (0 < a <= n) && (used[a] ⊻= true) || return false + end + true +end + +isperm(p::Tuple{}) = true +isperm(p::Tuple{Int}) = p[1] == 1 +isperm(p::Tuple{Int,Int}) = ((p[1] == 1) & (p[2] == 2)) | ((p[1] == 2) & (p[2] == 1)) + +# swap columns i and j of a, in-place +function swapcols!(a::AbstractMatrix, i, j) + i == j && return + cols = axes(a,2) + @boundscheck i in cols || throw(BoundsError(a, (:,i))) + @boundscheck j in cols || throw(BoundsError(a, (:,j))) + for k in axes(a,1) + @inbounds a[k,i],a[k,j] = a[k,j],a[k,i] + end +end +# like permute!! applied to each row of a, in-place in a (overwriting p). +function permutecols!!(a::AbstractMatrix, p::AbstractVector{<:Integer}) + require_one_based_indexing(a, p) + count = 0 + start = 0 + while count < length(p) + ptr = start = findnext(!iszero, p, start+1)::Int + next = p[start] + count += 1 + while next != start + swapcols!(a, ptr, next) + p[ptr] = 0 + ptr = next + next = p[next] + count += 1 + end + p[ptr] = 0 + end + a +end + +function permute!!(a, p::AbstractVector{<:Integer}) + require_one_based_indexing(a, p) + count = 0 + start = 0 + while count < length(a) + ptr = start = findnext(!iszero, p, start+1)::Int + temp = a[start] + next = p[start] + count += 1 + while next != start + a[ptr] = a[next] + p[ptr] = 0 + ptr = next + next = p[next] + count += 1 + end + a[ptr] = temp + p[ptr] = 0 + end + a +end + +""" + permute!(v, p) + +Permute vector `v` in-place, according to permutation `p`. No checking is done +to verify that `p` is a permutation. + +To return a new permutation, use `v[p]`. Note that this is generally faster than +`permute!(v,p)` for large vectors. + +See also [`invpermute!`](@ref). + +# Examples +```jldoctest +julia> A = [1, 1, 3, 4]; + +julia> perm = [2, 4, 3, 1]; + +julia> permute!(A, perm); + +julia> A +4-element Array{Int64,1}: + 1 + 4 + 3 + 1 +``` +""" +permute!(a, p::AbstractVector) = permute!!(a, copymutable(p)) + +function invpermute!!(a, p::AbstractVector{<:Integer}) + require_one_based_indexing(a, p) + count = 0 + start = 0 + while count < length(a) + start = findnext(!iszero, p, start+1)::Int + temp = a[start] + next = p[start] + count += 1 + while next != start + temp_next = a[next] + a[next] = temp + temp = temp_next + ptr = p[next] + p[next] = 0 + next = ptr + count += 1 + end + a[next] = temp + p[next] = 0 + end + a +end + +""" + invpermute!(v, p) + +Like [`permute!`](@ref), but the inverse of the given permutation is applied. + +# Examples +```jldoctest +julia> A = [1, 1, 3, 4]; + +julia> perm = [2, 4, 3, 1]; + +julia> invpermute!(A, perm); + +julia> A +4-element Array{Int64,1}: + 4 + 1 + 3 + 1 +``` +""" +invpermute!(a, p::AbstractVector) = invpermute!!(a, copymutable(p)) + +""" + invperm(v) + +Return the inverse permutation of `v`. +If `B = A[v]`, then `A == B[invperm(v)]`. + +# Examples +```jldoctest +julia> v = [2; 4; 3; 1]; + +julia> invperm(v) +4-element Array{Int64,1}: + 4 + 1 + 3 + 2 + +julia> A = ['a','b','c','d']; + +julia> B = A[v] +4-element Array{Char,1}: + 'b' + 'd' + 'c' + 'a' + +julia> B[invperm(v)] +4-element Array{Char,1}: + 'a' + 'b' + 'c' + 'd' +``` +""" +function invperm(a::AbstractVector) + require_one_based_indexing(a) + b = zero(a) # similar vector of zeros + n = length(a) + @inbounds for (i, j) in enumerate(a) + ((1 <= j <= n) && b[j] == 0) || + throw(ArgumentError("argument is not a permutation")) + b[j] = i + end + b +end + +function invperm(p::Union{Tuple{},Tuple{Int},Tuple{Int,Int}}) + isperm(p) || throw(ArgumentError("argument is not a permutation")) + p # in dimensions 0-2, every permutation is its own inverse +end +invperm(a::Tuple) = (invperm([a...])...,) + +#XXX This function should be moved to Combinatorics.jl but is currently used by Base.DSP. +""" + nextprod([k_1, k_2,...], n) + +Next integer greater than or equal to `n` that can be written as ``\\prod k_i^{p_i}`` for integers +``p_1``, ``p_2``, etc. + +# Examples +```jldoctest +julia> nextprod([2, 3], 105) +108 + +julia> 2^2 * 3^3 +108 +``` +""" +function nextprod(a::Vector{Int}, x) + if x > typemax(Int) + throw(ArgumentError("unsafe for x > typemax(Int), got $x")) + end + k = length(a) + v = fill(1, k) # current value of each counter + mx = [nextpow(ai,x) for ai in a] # maximum value of each counter + v[1] = mx[1] # start at first case that is >= x + p::widen(Int) = mx[1] # initial value of product in this case + best = p + icarry = 1 + + while v[end] < mx[end] + if p >= x + best = p < best ? p : best # keep the best found yet + carrytest = true + while carrytest + p = div(p, v[icarry]) + v[icarry] = 1 + icarry += 1 + p *= a[icarry] + v[icarry] *= a[icarry] + carrytest = v[icarry] > mx[icarry] && icarry < k + end + if p < x + icarry = 1 + end + else + while p < x + p *= a[1] + v[1] *= a[1] + end + end + end + # might overflow, but want predictable return type + return mx[end] < best ? Int(mx[end]) : Int(best) +end diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl new file mode 100644 index 0000000..7a8382c --- /dev/null +++ b/base/compiler/abstractinterpretation.jl @@ -0,0 +1,1288 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +############# +# constants # +############# + +const CoreNumType = Union{Int32, Int64, Float32, Float64} + +const _REF_NAME = Ref.body.name + +######### +# logic # +######### + +# see if the inference result might affect the final answer +call_result_unused(frame::InferenceState, pc::LineNum=frame.currpc) = + isexpr(frame.src.code[frame.currpc], :call) && isempty(frame.ssavalue_uses[pc]) + +function abstract_call_gf_by_type(@nospecialize(f), argtypes::Vector{Any}, @nospecialize(atype), sv::InferenceState, + max_methods = sv.params.MAX_METHODS) + atype_params = unwrap_unionall(atype).parameters + ft = unwrap_unionall(atype_params[1]) # TODO: ccall jl_method_table_for here + isa(ft, DataType) || return Any # the function being called is unknown. can't properly handle this backedge right now + ftname = ft.name + isdefined(ftname, :mt) || return Any # not callable. should be Bottom, but can't track this backedge right now + if ftname === _TYPE_NAME + tname = ft.parameters[1] + if isa(tname, TypeVar) + tname = tname.ub + end + tname = unwrap_unionall(tname) + if !isa(tname, DataType) + # can't track the backedge to the ctor right now + # for things like Union + return Any + end + end + min_valid = UInt[typemin(UInt)] + max_valid = UInt[typemax(UInt)] + splitunions = 1 < countunionsplit(atype_params) <= sv.params.MAX_UNION_SPLITTING + if splitunions + splitsigs = switchtupleunion(atype) + applicable = Any[] + for sig_n in splitsigs + xapplicable = _methods_by_ftype(sig_n, max_methods, sv.params.world, min_valid, max_valid) + xapplicable === false && return Any + append!(applicable, xapplicable) + end + else + applicable = _methods_by_ftype(atype, max_methods, sv.params.world, min_valid, max_valid) + if applicable === false + # this means too many methods matched + # (assume this will always be true, so we don't compute / update valid age in this case) + return Any + end + end + update_valid_age!(min_valid[1], max_valid[1], sv) + applicable = applicable::Array{Any,1} + napplicable = length(applicable) + rettype = Bottom + edgecycle = false + edges = Any[] + nonbot = 0 # the index of the only non-Bottom inference result if > 0 + seen = 0 # number of signatures actually inferred + istoplevel = sv.linfo.def isa Module + multiple_matches = napplicable > 1 + for i in 1:napplicable + match = applicable[i]::SimpleVector + method = match[3]::Method + sig = match[1] + if istoplevel && !isdispatchtuple(sig) + # only infer concrete call sites in top-level expressions + rettype = Any + break + end + sigtuple = unwrap_unionall(sig)::DataType + splitunions = false + this_rt = Bottom + # TODO: splitunions = 1 < countunionsplit(sigtuple.parameters) * napplicable <= sv.params.MAX_UNION_SPLITTING + # currently this triggers a bug in inference recursion detection + if splitunions + splitsigs = switchtupleunion(sig) + for sig_n in splitsigs + rt, edgecycle1, edge = abstract_call_method(method, sig_n, svec(), multiple_matches, sv) + if edge !== nothing + push!(edges, edge) + end + edgecycle |= edgecycle1::Bool + this_rt = tmerge(this_rt, rt) + this_rt === Any && break + end + else + this_rt, edgecycle1, edge = abstract_call_method(method, sig, match[2]::SimpleVector, multiple_matches, sv) + edgecycle |= edgecycle1::Bool + if edge !== nothing + push!(edges, edge) + end + end + if this_rt !== Bottom + if nonbot === 0 + nonbot = i + else + nonbot = -1 + end + end + seen += 1 + rettype = tmerge(rettype, this_rt) + rettype === Any && break + end + # try constant propagation if only 1 method is inferred to non-Bottom + # this is in preparation for inlining, or improving the return result + if nonbot > 0 && seen == napplicable && !edgecycle && isa(rettype, Type) && sv.params.ipo_constant_propagation + # if there's a possibility we could constant-propagate a better result + # (hopefully without doing too much work), try to do that now + # TODO: it feels like this could be better integrated into abstract_call_method / typeinf_edge + const_rettype = abstract_call_method_with_const_args(rettype, f, argtypes, applicable[nonbot]::SimpleVector, sv) + if const_rettype ⊑ rettype + # use the better result, if it's a refinement of rettype + rettype = const_rettype + end + end + if call_result_unused(sv) && !(rettype === Bottom) + # We're mainly only here because the optimizer might want this code, + # but we ourselves locally don't typically care about it locally + # (beyond checking if it always throws). + # So avoid adding an edge, since we don't want to bother attempting + # to improve our result even if it does change (to always throw), + # and avoid keeping track of a more complex result type. + rettype = Any + end + if !(rettype === Any) # adding a new method couldn't refine (widen) this type + for edge in edges + add_backedge!(edge::MethodInstance, sv) + end + fullmatch = false + for i in napplicable:-1:1 + match = applicable[i]::SimpleVector + method = match[3]::Method + if atype <: method.sig + fullmatch = true + break + end + end + if !fullmatch + # also need an edge to the method table in case something gets + # added that did not intersect with any existing method + add_mt_backedge!(ftname.mt, atype, sv) + end + end + #print("=> ", rettype, "\n") + return rettype +end + + +function const_prop_profitable(@nospecialize(arg)) + # have new information from argtypes that wasn't available from the signature + if isa(arg, PartialStruct) + for b in arg.fields + isconstType(b) && return true + const_prop_profitable(b) && return true + end + elseif !isa(arg, Const) || (isa(arg.val, Symbol) || isa(arg.val, Type) || (!isa(arg.val, String) && isimmutable(arg.val))) + # don't consider mutable values or Strings useful constants + return true + end + return false +end + +function abstract_call_method_with_const_args(@nospecialize(rettype), @nospecialize(f), argtypes::Vector{Any}, match::SimpleVector, sv::InferenceState) + method = match[3]::Method + nargs::Int = method.nargs + method.isva && (nargs -= 1) + length(argtypes) >= nargs || return Any + haveconst = false + allconst = true + # see if any or all of the arguments are constant and propagating constants may be worthwhile + for a in argtypes + a = widenconditional(a) + if allconst && !isa(a, Const) && !isconstType(a) && !isa(a, PartialStruct) + allconst = false + end + if !haveconst && has_nontrivial_const_info(a) && const_prop_profitable(a) + haveconst = true + end + if haveconst && !allconst + break + end + end + haveconst || improvable_via_constant_propagation(rettype) || return Any + if nargs > 1 + if istopfunction(f, :getindex) || istopfunction(f, :setindex!) + arrty = argtypes[2] + # don't propagate constant index into indexing of non-constant array + if arrty isa Type && arrty <: AbstractArray && !issingletontype(arrty) + return Any + end + elseif istopfunction(f, :iterate) + itrty = argtypes[2] + if itrty isa Type && !issingletontype(itrty) + return Any + end + end + end + if !allconst && (istopfunction(f, :+) || istopfunction(f, :-) || istopfunction(f, :*) || + istopfunction(f, :(==)) || istopfunction(f, :!=) || + istopfunction(f, :<=) || istopfunction(f, :>=) || istopfunction(f, :<) || istopfunction(f, :>) || + istopfunction(f, :<<) || istopfunction(f, :>>)) + return Any + end + force_inference = allconst || sv.params.aggressive_constant_propagation + if istopfunction(f, :getproperty) || istopfunction(f, :setproperty!) + force_inference = true + end + sig = match[1] + sparams = match[2]::SimpleVector + mi = specialize_method(method, sig, sparams, !force_inference) + mi === nothing && return Any + mi = mi::MethodInstance + # decide if it's likely to be worthwhile + if !force_inference + code = inf_for_methodinstance(mi, sv.params.world) + declared_inline = isdefined(method, :source) && ccall(:jl_ast_flag_inlineable, Bool, (Any,), method.source) + cache_inlineable = declared_inline + if isdefined(code, :inferred) && !cache_inlineable + cache_inf = code.inferred + if !(cache_inf === nothing) + cache_src_inferred = ccall(:jl_ast_flag_inferred, Bool, (Any,), cache_inf) + cache_src_inlineable = ccall(:jl_ast_flag_inlineable, Bool, (Any,), cache_inf) + cache_inlineable = cache_src_inferred && cache_src_inlineable + end + end + if !cache_inlineable + return Any + end + end + inf_result = cache_lookup(mi, argtypes, sv.params.cache) + if inf_result === nothing + inf_result = InferenceResult(mi, argtypes) + frame = InferenceState(inf_result, #=cache=#false, sv.params) + frame.limited = true + frame.parent = sv + push!(sv.params.cache, inf_result) + typeinf(frame) || return Any + end + result = inf_result.result + isa(result, InferenceState) && return Any # TODO: unexpected, is this recursive constant inference? + add_backedge!(inf_result.linfo, sv) + return result +end + +function abstract_call_method(method::Method, @nospecialize(sig), sparams::SimpleVector, hardlimit::Bool, sv::InferenceState) + if method.name === :depwarn && isdefined(Main, :Base) && method.module === Main.Base + return Any, false, nothing + end + topmost = nothing + # Limit argument type tuple growth of functions: + # look through the parents list to see if there's a call to the same method + # and from the same method. + # Returns the topmost occurrence of that repeated edge. + cyclei = 0 + infstate = sv + edgecycle = false + # The `method_for_inference_heuristics` will expand the given method's generator if + # necessary in order to retrieve this field from the generated `CodeInfo`, if it exists. + # The other `CodeInfo`s we inspect will already have this field inflated, so we just + # access it directly instead (to avoid regeneration). + method2 = method_for_inference_heuristics(method, sig, sparams) # Union{Method, Nothing} + sv_method2 = sv.src.method_for_inference_limit_heuristics # limit only if user token match + sv_method2 isa Method || (sv_method2 = nothing) # Union{Method, Nothing} + while !(infstate === nothing) + infstate = infstate::InferenceState + if method === infstate.linfo.def + if infstate.linfo.specTypes == sig + # avoid widening when detecting self-recursion + # TODO: merge call cycle and return right away + if call_result_unused(sv) + # since we don't use the result (typically), + # we have a self-cycle in the call-graph, but not in the inference graph (typically): + # break this edge now (before we record it) by returning early + # (non-typically, this means that we lose the ability to detect a guaranteed StackOverflow in some cases) + return Any, true, nothing + end + topmost = nothing + edgecycle = true + break + end + inf_method2 = infstate.src.method_for_inference_limit_heuristics # limit only if user token match + inf_method2 isa Method || (inf_method2 = nothing) # Union{Method, Nothing} + if topmost === nothing && method2 === inf_method2 + if hardlimit + topmost = infstate + edgecycle = true + else + # if this is a soft limit, + # also inspect the parent of this edge, + # to see if they are the same Method as sv + # in which case we'll need to ensure it is convergent + # otherwise, we don't + for parent in infstate.callers_in_cycle + # check in the cycle list first + # all items in here are mutual parents of all others + parent_method2 = parent.src.method_for_inference_limit_heuristics # limit only if user token match + parent_method2 isa Method || (parent_method2 = nothing) # Union{Method, Nothing} + if parent.linfo.def === sv.linfo.def && sv_method2 === parent_method2 + topmost = infstate + edgecycle = true + break + end + end + let parent = infstate.parent + # then check the parent link + if topmost === nothing && parent !== nothing + parent = parent::InferenceState + parent_method2 = parent.src.method_for_inference_limit_heuristics # limit only if user token match + parent_method2 isa Method || (parent_method2 = nothing) # Union{Method, Nothing} + if (parent.cached || parent.limited) && parent.linfo.def === sv.linfo.def && sv_method2 === parent_method2 + topmost = infstate + edgecycle = true + end + end + end + end + end + end + # iterate through the cycle before walking to the parent + if cyclei < length(infstate.callers_in_cycle) + cyclei += 1 + infstate = infstate.callers_in_cycle[cyclei] + else + cyclei = 0 + infstate = infstate.parent + end + end + + if !(topmost === nothing) + topmost = topmost::InferenceState + sigtuple = unwrap_unionall(sig)::DataType + msig = unwrap_unionall(method.sig)::DataType + spec_len = length(msig.parameters) + 1 + ls = length(sigtuple.parameters) + if method === sv.linfo.def + # Under direct self-recursion, permit much greater use of reducers. + # here we assume that complexity(specTypes) :>= complexity(sig) + comparison = sv.linfo.specTypes + l_comparison = length(unwrap_unionall(comparison).parameters) + spec_len = max(spec_len, l_comparison) + else + comparison = method.sig + end + # see if the type is actually too big (relative to the caller), and limit it if required + newsig = limit_type_size(sig, comparison, hardlimit ? comparison : sv.linfo.specTypes, sv.params.TUPLE_COMPLEXITY_LIMIT_DEPTH, spec_len) + + if newsig !== sig + # continue inference, but note that we've limited parameter complexity + # on this call (to ensure convergence), so that we don't cache this result + if call_result_unused(sv) + # if we don't (typically) actually care about this result, + # don't bother trying to examine some complex abstract signature + # since it's very unlikely that we'll try to inline this, + # or want make an invoke edge to its calling convention return type. + # (non-typically, this means that we lose the ability to detect a guaranteed StackOverflow in some cases) + return Any, true, nothing + end + poison_callstack(sv, topmost::InferenceState, true) + sig = newsig + sparams = svec() + end + end + + # if sig changed, may need to recompute the sparams environment + if isa(method.sig, UnionAll) && isempty(sparams) + recomputed = ccall(:jl_type_intersection_with_env, Any, (Any, Any), sig, method.sig)::SimpleVector + #@assert recomputed[1] !== Bottom + # We must not use `sig` here, since that may re-introduce structural complexity that + # our limiting heuristic sought to eliminate. The alternative would be to not increment depth over covariant contexts, + # but we prefer to permit inference of tuple-destructuring, so we don't do that right now + # For example, with a signature such as `Tuple{T, Ref{T}} where {T <: S}` + # we might want to limit this to `Tuple{S, Ref}`, while type-intersection can instead give us back the original type + # (which moves `S` back up to a lower comparison depth) + # Optionally, we could try to drive this to a fixed point, but I think this is getting too complex, + # and this would only cause more questions and more problems + # (the following is only an example, most of the statements are probable in the wrong order): + # newsig = sig + # seen = IdSet() + # while !(newsig in seen) + # push!(seen, newsig) + # lsig = length((unwrap_unionall(sig)::DataType).parameters) + # newsig = limit_type_size(newsig, sig, sv.linfo.specTypes, sv.params.TUPLE_COMPLEXITY_LIMIT_DEPTH, lsig) + # recomputed = ccall(:jl_type_intersection_with_env, Any, (Any, Any), newsig, method.sig)::SimpleVector + # newsig = recomputed[2] + # end + # sig = ? + sparams = recomputed[2]::SimpleVector + end + + rt, edge = typeinf_edge(method, sig, sparams, sv) + if edge === nothing + edgecycle = true + end + return rt, edgecycle, edge +end + +# This is only for use with `Conditional`. +# In general, usage of this is wrong. +function ssa_def_slot(@nospecialize(arg), sv::InferenceState) + init = sv.currpc + while isa(arg, SSAValue) + init = arg.id + arg = sv.src.code[init] + end + arg isa SlotNumber || return nothing + for i = init:(sv.currpc - 1) + # conservatively make sure there isn't potentially another conflicting assignment to + # the same slot between the def and usage + # we can assume the IR is sorted, since the front-end only creates SSA values in order + e = sv.src.code[i] + e isa Expr || continue + if e.head === :(=) && e.args[1] === arg + return nothing + end + end + return arg +end + +# `typ` is the inferred type for expression `arg`. +# if the expression constructs a container (e.g. `svec(x,y,z)`), +# refine its type to an array of element types. +# Union of Tuples of the same length is converted to Tuple of Unions. +# returns an array of types +function precise_container_type(@nospecialize(typ), vtypes::VarTable, sv::InferenceState) + if isa(typ, PartialStruct) && typ.typ.name === Tuple.name + return typ.fields + end + + if isa(typ, Const) + val = typ.val + if isa(val, SimpleVector) || isa(val, Tuple) + return Any[ Const(val[i]) for i in 1:length(val) ] # avoid making a tuple Generator here! + end + end + + tti0 = widenconst(typ) + tti = unwrap_unionall(tti0) + if isa(tti, DataType) && tti.name === NamedTuple_typename + # A NamedTuple iteration is the the same as the iteration of its Tuple parameter: + # compute a new `tti == unwrap_unionall(tti0)` based on that Tuple type + tti = tti.parameters[2] + while isa(tti, TypeVar) + tti = tti.ub + end + tti0 = rewrap_unionall(tti, tti0) + end + if isa(tti, Union) + utis = uniontypes(tti) + if _any(t -> !isa(t, DataType) || !(t <: Tuple) || !isknownlength(t), utis) + return Any[Vararg{Any}] + end + result = Any[rewrap_unionall(p, tti0) for p in utis[1].parameters] + for t in utis[2:end] + if length(t.parameters) != length(result) + return Any[Vararg{Any}] + end + for j in 1:length(t.parameters) + result[j] = tmerge(result[j], rewrap_unionall(t.parameters[j], tti0)) + end + end + return result + elseif tti0 <: Tuple + if isa(tti0, DataType) + if isvatuple(tti0) && length(tti0.parameters) == 1 + return Any[Vararg{unwrapva(tti0.parameters[1])}] + else + return Any[ p for p in tti0.parameters ] + end + elseif !isa(tti, DataType) + return Any[Vararg{Any}] + else + len = length(tti.parameters) + last = tti.parameters[len] + va = isvarargtype(last) + elts = Any[ fieldtype(tti0, i) for i = 1:len ] + if va + elts[len] = Vararg{elts[len]} + end + return elts + end + elseif tti0 === SimpleVector || tti0 === Any + return Any[Vararg{Any}] + elseif tti0 <: Array + return Any[Vararg{eltype(tti0)}] + else + return abstract_iteration(typ, vtypes, sv) + end +end + +# simulate iteration protocol on container type up to fixpoint +function abstract_iteration(@nospecialize(itertype), vtypes::VarTable, sv::InferenceState) + if !isdefined(Main, :Base) || !isdefined(Main.Base, :iterate) || !isconst(Main.Base, :iterate) + return Any[Vararg{Any}] + end + iteratef = getfield(Main.Base, :iterate) + stateordonet = abstract_call(iteratef, nothing, Any[Const(iteratef), itertype], vtypes, sv) + # Return Bottom if this is not an iterator. + # WARNING: Changes to the iteration protocol must be reflected here, + # this is not just an optimization. + stateordonet === Bottom && return Any[Bottom] + valtype = statetype = Bottom + ret = Any[] + stateordonet = widenconst(stateordonet) + while !(Nothing <: stateordonet) && length(ret) < sv.params.MAX_TUPLE_SPLAT + if !isa(stateordonet, DataType) || !(stateordonet <: Tuple) || isvatuple(stateordonet) || length(stateordonet.parameters) != 2 + break + end + if stateordonet.parameters[2] <: statetype + # infinite (or failing) iterator + return Any[Bottom] + end + valtype = stateordonet.parameters[1] + statetype = stateordonet.parameters[2] + push!(ret, valtype) + stateordonet = abstract_call(iteratef, nothing, Any[Const(iteratef), itertype, statetype], vtypes, sv) + stateordonet = widenconst(stateordonet) + end + if stateordonet === Nothing + return ret + end + while valtype !== Any + nounion = typesubtract(stateordonet, Nothing) + if !isa(nounion, DataType) || !(nounion <: Tuple) || isvatuple(nounion) || length(nounion.parameters) != 2 + valtype = Any + break + end + if nounion.parameters[1] <: valtype && nounion.parameters[2] <: statetype + break + end + valtype = tmerge(valtype, nounion.parameters[1]) + statetype = tmerge(statetype, nounion.parameters[2]) + stateordonet = abstract_call(iteratef, nothing, Any[Const(iteratef), itertype, statetype], vtypes, sv) + stateordonet = widenconst(stateordonet) + end + push!(ret, Vararg{valtype}) + return ret +end + +# do apply(af, fargs...), where af is a function value +function abstract_apply(@nospecialize(aft), aargtypes::Vector{Any}, vtypes::VarTable, sv::InferenceState, + max_methods = sv.params.MAX_METHODS) + aftw = widenconst(aft) + if !isa(aft, Const) && (!isType(aftw) || has_free_typevars(aftw)) + if !isconcretetype(aftw) || (aftw <: Builtin) + # non-constant function of unknown type: bail now, + # since it seems unlikely that abstract_call will be able to do any better after splitting + # this also ensures we don't call abstract_call_gf_by_type below on an IntrinsicFunction or Builtin + return Any + end + end + res = Union{} + nargs = length(aargtypes) + splitunions = 1 < countunionsplit(aargtypes) <= sv.params.MAX_APPLY_UNION_ENUM + ctypes = Any[Any[aft]] + for i = 1:nargs + ctypes´ = [] + for ti in (splitunions ? uniontypes(aargtypes[i]) : Any[aargtypes[i]]) + cti = precise_container_type(ti, vtypes, sv) + if _any(t -> t === Bottom, cti) + continue + end + for ct in ctypes + if isvarargtype(ct[end]) + tail = tuple_tail_elem(unwrapva(ct[end]), cti) + push!(ctypes´, push!(ct[1:(end - 1)], tail)) + else + push!(ctypes´, append!(ct[:], cti)) + end + end + end + ctypes = ctypes´ + end + for ct in ctypes + if isa(aft, Const) + rt = abstract_call(aft.val, nothing, ct, vtypes, sv, max_methods) + elseif isconstType(aft) + rt = abstract_call(aft.parameters[1], nothing, ct, vtypes, sv, max_methods) + else + astype = argtypes_to_type(ct) + rt = abstract_call_gf_by_type(nothing, ct, astype, sv, max_methods) + end + res = tmerge(res, rt) + if res === Any + break + end + end + return res +end + +function pure_eval_call(@nospecialize(f), argtypes::Vector{Any}, @nospecialize(atype), sv::InferenceState) + for i = 2:length(argtypes) + a = widenconditional(argtypes[i]) + if !(isa(a, Const) || isconstType(a)) + return false + end + end + + min_valid = UInt[typemin(UInt)] + max_valid = UInt[typemax(UInt)] + meth = _methods_by_ftype(atype, 1, sv.params.world, min_valid, max_valid) + if meth === false || length(meth) != 1 + return false + end + meth = meth[1]::SimpleVector + sig = meth[1]::DataType + sparams = meth[2]::SimpleVector + method = meth[3]::Method + + if isdefined(method, :generator) + method.generator.expand_early || return false + mi = specialize_method(method, sig, sparams, false) + isa(mi, MethodInstance) || return false + staged = get_staged(mi) + (staged isa CodeInfo && (staged::CodeInfo).pure) || return false + elseif !method.pure + return false + end + + args = Any[ (a = widenconditional(argtypes[i]); isa(a, Const) ? a.val : a.parameters[1]) for i in 2:length(argtypes) ] + try + value = Core._apply_pure(f, args) + # TODO: add some sort of edge(s) + return Const(value, true) + catch + return false + end +end + +function abstract_call(@nospecialize(f), fargs::Union{Nothing,Vector{Any}}, argtypes::Vector{Any}, vtypes::VarTable, sv::InferenceState, max_methods = sv.params.MAX_METHODS) + if f === _apply + return abstract_apply(argtypes[2], argtypes[3:end], vtypes, sv, max_methods) + end + + la = length(argtypes) + for i = 2:(la - 1) + if isvarargtype(argtypes[i]) + return Any + end + end + + if isa(f, Builtin) || isa(f, IntrinsicFunction) + if f === ifelse && fargs isa Vector{Any} && length(argtypes) == 4 && argtypes[2] isa Conditional + # try to simulate this as a real conditional (`cnd ? x : y`), so that the penalty for using `ifelse` instead isn't too high + cnd = argtypes[2]::Conditional + tx = argtypes[3] + ty = argtypes[4] + a = ssa_def_slot(fargs[3], sv) + b = ssa_def_slot(fargs[4], sv) + if isa(a, Slot) && slot_id(cnd.var) == slot_id(a) + tx = typeintersect(tx, cnd.vtype) + end + if isa(b, Slot) && slot_id(cnd.var) == slot_id(b) + ty = typeintersect(ty, cnd.elsetype) + end + return tmerge(tx, ty) + end + rt = builtin_tfunction(f, argtypes[2:end], sv) + if f === getfield && isa(fargs, Vector{Any}) && length(argtypes) == 3 && isa(argtypes[3], Const) && isa(argtypes[3].val, Int) && argtypes[2] ⊑ Tuple + cti = precise_container_type(argtypes[2], vtypes, sv) + idx = argtypes[3].val + if 1 <= idx <= length(cti) + rt = unwrapva(cti[idx]) + end + elseif (rt === Bool || (isa(rt, Const) && isa(rt.val, Bool))) && isa(fargs, Vector{Any}) + # perform very limited back-propagation of type information for `is` and `isa` + if f === isa + a = ssa_def_slot(fargs[2], sv) + if isa(a, Slot) + aty = widenconst(argtypes[2]) + if rt === Const(false) + return Conditional(a, Union{}, aty) + elseif rt === Const(true) + return Conditional(a, aty, Union{}) + end + tty_ub, isexact_tty = instanceof_tfunc(argtypes[3]) + if isexact_tty && !isa(tty_ub, TypeVar) + tty_lb = tty_ub # TODO: this would be wrong if !isexact_tty, but instanceof_tfunc doesn't preserve this info + if !has_free_typevars(tty_lb) && !has_free_typevars(tty_ub) + ifty = typeintersect(aty, tty_ub) + elty = typesubtract(aty, tty_lb) + return Conditional(a, ifty, elty) + end + end + end + elseif f === (===) + a = ssa_def_slot(fargs[2], sv) + b = ssa_def_slot(fargs[3], sv) + aty = argtypes[2] + bty = argtypes[3] + # if doing a comparison to a singleton, consider returning a `Conditional` instead + if isa(aty, Const) && isa(b, Slot) + if rt === Const(false) + aty = Union{} + elseif rt === Const(true) + bty = Union{} + elseif bty isa Type && isdefined(typeof(aty.val), :instance) # can only widen a if it is a singleton + bty = typesubtract(bty, typeof(aty.val)) + end + return Conditional(b, aty, bty) + end + if isa(bty, Const) && isa(a, Slot) + if rt === Const(false) + bty = Union{} + elseif rt === Const(true) + aty = Union{} + elseif aty isa Type && isdefined(typeof(bty.val), :instance) # same for b + aty = typesubtract(aty, typeof(bty.val)) + end + return Conditional(a, bty, aty) + end + if isa(b, Slot) + return Conditional(b, bty, bty) + end + if isa(a, Slot) + return Conditional(a, aty, aty) + end + elseif f === Core.Compiler.not_int + aty = argtypes[2] + if isa(aty, Conditional) + ifty = aty.elsetype + elty = aty.vtype + if rt === Const(false) + ifty = Union{} + elseif rt === Const(true) + elty = Union{} + end + return Conditional(aty.var, ifty, elty) + end + end + end + return isa(rt, TypeVar) ? rt.ub : rt + elseif f === Core.kwfunc + if length(argtypes) == 2 + ft = widenconst(argtypes[2]) + if isa(ft, DataType) && isdefined(ft.name, :mt) && isdefined(ft.name.mt, :kwsorter) + return Const(ft.name.mt.kwsorter) + end + end + return Any + elseif f === TypeVar + # Manually look through the definition of TypeVar to + # make sure to be able to get `PartialTypeVar`s out. + (length(argtypes) < 2 || length(argtypes) > 4) && return Union{} + n = argtypes[2] + ub_var = Const(Any) + lb_var = Const(Union{}) + if length(argtypes) == 4 + ub_var = argtypes[4] + lb_var = argtypes[3] + elseif length(argtypes) == 3 + ub_var = argtypes[3] + end + return typevar_tfunc(n, lb_var, ub_var) + elseif f === UnionAll + if length(argtypes) == 3 + canconst = true + if isa(argtypes[3], Const) + body = argtypes[3].val + elseif isType(argtypes[3]) + body = argtypes[3].parameters[1] + canconst = false + else + return Any + end + if !isa(body, Type) && !isa(body, TypeVar) + return Any + end + if has_free_typevars(body) + if isa(argtypes[2], Const) + tv = argtypes[2].val + elseif isa(argtypes[2], PartialTypeVar) + ptv = argtypes[2] + tv = ptv.tv + canconst = false + else + return Any + end + !isa(tv, TypeVar) && return Any + body = UnionAll(tv, body) + end + ret = canconst ? AbstractEvalConstant(body) : Type{body} + return ret + end + return Any + elseif f === Tuple && la == 2 && !isconcretetype(widenconst(argtypes[2])) + return Tuple + elseif is_return_type(f) + rt_rt = return_type_tfunc(argtypes, vtypes, sv) + if rt_rt !== nothing + return rt_rt + end + elseif length(argtypes) == 2 && istopfunction(f, :!) + # handle Conditional propagation through !Bool + aty = argtypes[2] + if isa(aty, Conditional) + abstract_call_gf_by_type(f, Any[Const(f), Bool], Tuple{typeof(f), Bool}, sv) # make sure we've inferred `!(::Bool)` + return Conditional(aty.var, aty.elsetype, aty.vtype) + end + elseif length(argtypes) == 3 && istopfunction(f, :!==) + # mark !== as exactly a negated call to === + rty = abstract_call((===), fargs, argtypes, vtypes, sv) + if isa(rty, Conditional) + return Conditional(rty.var, rty.elsetype, rty.vtype) # swap if-else + elseif isa(rty, Const) + return Const(rty.val === false) + end + return rty + elseif length(argtypes) == 3 && istopfunction(f, :(>:)) + # mark issupertype as a exact alias for issubtype + # swap T1 and T2 arguments and call <: + if length(fargs) == 3 + fargs = Any[<:, fargs[3], fargs[2]] + else + fargs = nothing + end + argtypes = Any[typeof(<:), argtypes[3], argtypes[2]] + rty = abstract_call(<:, fargs, argtypes, vtypes, sv) + return rty + elseif length(argtypes) == 2 && isa(argtypes[2], Const) && isa(argtypes[2].val, SimpleVector) && istopfunction(f, :length) + # mark length(::SimpleVector) as @pure + return Const(length(argtypes[2].val)) + elseif length(argtypes) == 3 && isa(argtypes[2], Const) && isa(argtypes[3], Const) && + isa(argtypes[2].val, SimpleVector) && isa(argtypes[3].val, Int) && istopfunction(f, :getindex) + # mark getindex(::SimpleVector, i::Int) as @pure + svecval = argtypes[2].val::SimpleVector + idx = argtypes[3].val::Int + if 1 <= idx <= length(svecval) && isassigned(svecval, idx) + return Const(getindex(svecval, idx)) + end + elseif length(argtypes) == 2 && istopfunction(f, :typename) + return typename_static(argtypes[2]) + elseif max_methods > 1 && istopfunction(f, :copyto!) + max_methods = 1 + end + + atype = argtypes_to_type(argtypes) + t = pure_eval_call(f, argtypes, atype, sv) + t !== false && return t + + if istopfunction(f, :typejoin) || is_return_type(f) + return Type # don't try to infer these function edges directly -- it won't actually come up with anything useful + end + + return abstract_call_gf_by_type(f, argtypes, atype, sv, max_methods) +end + +# wrapper around `abstract_call` for first computing if `f` is available +function abstract_eval_call(fargs::Union{Nothing,Vector{Any}}, argtypes::Vector{Any}, vtypes::VarTable, sv::InferenceState) + #print("call ", e.args[1], argtypes, "\n\n") + for x in argtypes + x === Bottom && return Bottom + end + ft = argtypes[1] + if isa(ft, Const) + f = ft.val + elseif isconstType(ft) + f = ft.parameters[1] + elseif isa(ft, DataType) && isdefined(ft, :instance) + f = ft.instance + else + for i = 2:(length(argtypes) - 1) + if isvarargtype(argtypes[i]) + return Any + end + end + # non-constant function, but the number of arguments is known + # and the ft is not a Builtin or IntrinsicFunction + if typeintersect(widenconst(ft), Builtin) != Union{} + return Any + end + return abstract_call_gf_by_type(nothing, argtypes, argtypes_to_type(argtypes), sv) + end + return abstract_call(f, fargs, argtypes, vtypes, sv) +end + +function sp_type_rewrap(@nospecialize(T), linfo::MethodInstance, isreturn::Bool) + isref = false + if T === Bottom + return Bottom + elseif isa(T, Type) + if isa(T, DataType) && (T::DataType).name === _REF_NAME + isref = true + T = T.parameters[1] + if isreturn && T === Any + return Bottom # a return type of Ref{Any} is invalid + end + end + else + return Any + end + if isa(linfo.def, Method) + spsig = linfo.def.sig + if isa(spsig, UnionAll) + if !isempty(linfo.sparam_vals) + env = pointer_from_objref(linfo.sparam_vals) + sizeof(Ptr{Cvoid}) + T = ccall(:jl_instantiate_type_in_env, Any, (Any, Any, Ptr{Any}), T, spsig, env) + isref && isreturn && T === Any && return Bottom # catch invalid return Ref{T} where T = Any + for v in linfo.sparam_vals + if isa(v, TypeVar) + T = UnionAll(v, T) + end + end + else + T = rewrap_unionall(T, spsig) + end + end + end + while isa(T, TypeVar) + T = T.ub + end + return T +end + +function abstract_eval_cfunction(e::Expr, vtypes::VarTable, sv::InferenceState) + f = abstract_eval(e.args[2], vtypes, sv) + # rt = sp_type_rewrap(e.args[3], sv.linfo, true) + at = Any[ sp_type_rewrap(argt, sv.linfo, false) for argt in e.args[4]::SimpleVector ] + pushfirst!(at, f) + # this may be the wrong world for the call, + # but some of the result is likely to be valid anyways + # and that may help generate better codegen + abstract_eval_call(nothing, at, vtypes, sv) + nothing +end + +function abstract_eval(@nospecialize(e), vtypes::VarTable, sv::InferenceState) + if isa(e, QuoteNode) + return AbstractEvalConstant((e::QuoteNode).value) + elseif isa(e, SSAValue) + return abstract_eval_ssavalue(e::SSAValue, sv.src) + elseif isa(e, Slot) + return vtypes[slot_id(e)].typ + elseif isa(e, GlobalRef) + return abstract_eval_global(e.mod, e.name) + end + + if !isa(e, Expr) + return AbstractEvalConstant(e) + end + e = e::Expr + if e.head === :call + argtypes = Any[ abstract_eval(a, vtypes, sv) for a in e.args ] + t = abstract_eval_call(e.args, argtypes, vtypes, sv) + elseif e.head === :new + t = instanceof_tfunc(abstract_eval(e.args[1], vtypes, sv))[1] + if isconcretetype(t) && !t.mutable + args = Vector{Any}(undef, length(e.args)-1) + ats = Vector{Any}(undef, length(e.args)-1) + anyconst = false + allconst = true + for i = 2:length(e.args) + at = abstract_eval(e.args[i], vtypes, sv) + if !anyconst + anyconst = has_nontrivial_const_info(at) + end + ats[i-1] = at + if at === Bottom + t = Bottom + allconst = anyconst = false + break + elseif at isa Const + if !(at.val isa fieldtype(t, i - 1)) + t = Bottom + allconst = anyconst = false + break + end + args[i-1] = at.val + else + allconst = false + end + end + # For now, don't allow partially initialized Const/PartialStruct + if t !== Bottom && fieldcount(t) == length(ats) + if allconst + t = Const(ccall(:jl_new_structv, Any, (Any, Ptr{Cvoid}, UInt32), t, args, length(args))) + elseif anyconst + t = PartialStruct(t, ats) + end + end + end + elseif e.head === :splatnew + t = instanceof_tfunc(abstract_eval(e.args[1], vtypes, sv))[1] + elseif e.head === :& + abstract_eval(e.args[1], vtypes, sv) + t = Any + elseif e.head === :foreigncall + abstract_eval(e.args[1], vtypes, sv) + t = sp_type_rewrap(e.args[2], sv.linfo, true) + for i = 3:length(e.args) + if abstract_eval(e.args[i], vtypes, sv) === Bottom + t = Bottom + end + end + elseif e.head === :cfunction + t = e.args[1] + isa(t, Type) || (t = Any) + abstract_eval_cfunction(e, vtypes, sv) + elseif e.head === :static_parameter + n = e.args[1] + t = Any + if 1 <= n <= length(sv.sptypes) + t = sv.sptypes[n] + end + elseif e.head === :method + t = (length(e.args) == 1) ? Any : Nothing + elseif e.head === :copyast + t = abstract_eval(e.args[1], vtypes, sv) + if t isa Const && t.val isa Expr + # `copyast` makes copies of Exprs + t = Expr + end + elseif e.head === :invoke + error("type inference data-flow error: tried to double infer a function") + elseif e.head === :boundscheck + return Bool + elseif e.head === :isdefined + sym = e.args[1] + t = Bool + if isa(sym, Slot) + vtyp = vtypes[slot_id(sym)] + if vtyp.typ === Bottom + t = Const(false) # never assigned previously + elseif !vtyp.undef + t = Const(true) # definitely assigned previously + end + elseif isa(sym, Symbol) + if isdefined(sv.mod, sym.name) + t = Const(true) + end + elseif isa(sym, GlobalRef) + if isdefined(sym.mod, sym.name) + t = Const(true) + end + elseif isa(sym, Expr) && sym.head === :static_parameter + n = sym.args[1] + if 1 <= n <= length(sv.sptypes) + spty = sv.sptypes[n] + if isa(spty, Const) + t = Const(true) + end + end + end + else + t = Any + end + @assert !isa(t, TypeVar) + if isa(t, DataType) && isdefined(t, :instance) + # replace singleton types with their equivalent Const object + t = Const(t.instance) + end + return t +end + +function abstract_eval_global(M::Module, s::Symbol) + if isdefined(M,s) && isconst(M,s) + return AbstractEvalConstant(getfield(M,s)) + end + return Any +end + +function abstract_eval_ssavalue(s::SSAValue, src::CodeInfo) + typ = src.ssavaluetypes[s.id] + if typ === NOT_FOUND + return Bottom + end + return typ +end + +# determine whether `ex` abstractly evals to constant `c` +function abstract_evals_to_constant(@nospecialize(ex), @nospecialize(c), vtypes::VarTable, sv::InferenceState) + av = abstract_eval(ex, vtypes, sv) + return isa(av, Const) && av.val === c +end + +# make as much progress on `frame` as possible (without handling cycles) +function typeinf_local(frame::InferenceState) + @assert !frame.inferred + frame.dont_work_on_me = true # mark that this function is currently on the stack + W = frame.ip + s = frame.stmt_types + n = frame.nstmts + while frame.pc´´ <= n + # make progress on the active ip set + local pc::Int = frame.pc´´ # current program-counter + while true # inner loop optimizes the common case where it can run straight from pc to pc + 1 + #print(pc,": ",s[pc],"\n") + local pc´::Int = pc + 1 # next program-counter (after executing instruction) + if pc == frame.pc´´ + # need to update pc´´ to point at the new lowest instruction in W + min_pc = _bits_findnext(W.bits, pc + 1) + frame.pc´´ = min_pc == -1 ? n + 1 : min_pc + end + delete!(W, pc) + frame.currpc = pc + frame.cur_hand = frame.handler_at[pc] + frame.stmt_edges[pc] === nothing || empty!(frame.stmt_edges[pc]) + stmt = frame.src.code[pc] + changes = s[pc]::VarTable + t = nothing + + hd = isa(stmt, Expr) ? stmt.head : nothing + + if isa(stmt, NewvarNode) + sn = slot_id(stmt.slot) + changes[sn] = VarState(Bottom, true) + elseif isa(stmt, GotoNode) + pc´ = (stmt::GotoNode).label + elseif hd === :gotoifnot + condt = abstract_eval(stmt.args[1], s[pc], frame) + if condt === Bottom + break + end + condval = maybe_extract_const_bool(condt) + l = stmt.args[2]::Int + # constant conditions + if condval === true + elseif condval === false + pc´ = l + else + # general case + frame.handler_at[l] = frame.cur_hand + changes_else = changes + if isa(condt, Conditional) + if condt.elsetype !== Any && condt.elsetype !== changes[slot_id(condt.var)] + changes_else = StateUpdate(condt.var, VarState(condt.elsetype, false), changes_else) + end + if condt.vtype !== Any && condt.vtype !== changes[slot_id(condt.var)] + changes = StateUpdate(condt.var, VarState(condt.vtype, false), changes) + end + end + newstate_else = stupdate!(s[l], changes_else) + if newstate_else !== false + # add else branch to active IP list + if l < frame.pc´´ + frame.pc´´ = l + end + push!(W, l) + s[l] = newstate_else + end + end + elseif hd === :return + pc´ = n + 1 + rt = widenconditional(abstract_eval(stmt.args[1], s[pc], frame)) + if !isa(rt, Const) && !isa(rt, Type) && !isa(rt, PartialStruct) + # only propagate information we know we can store + # and is valid inter-procedurally + rt = widenconst(rt) + elseif isa(rt, Const) && rt.actual + rt = Const(rt.val) + end + if tchanged(rt, frame.bestguess) + # new (wider) return type for frame + frame.bestguess = tmerge(frame.bestguess, rt) + for (caller, caller_pc) in frame.cycle_backedges + # notify backedges of updated type information + typeassert(caller.stmt_types[caller_pc], VarTable) # we must have visited this statement before + if !(caller.src.ssavaluetypes[caller_pc] === Any) + # no reason to revisit if that call-site doesn't affect the final result + if caller_pc < caller.pc´´ + caller.pc´´ = caller_pc + end + push!(caller.ip, caller_pc) + end + end + end + elseif hd === :enter + l = stmt.args[1]::Int + frame.cur_hand = Pair{Any,Any}(l, frame.cur_hand) + # propagate type info to exception handler + old = s[l] + new = s[pc]::Array{Any,1} + newstate_catch = stupdate!(old, new) + if newstate_catch !== false + if l < frame.pc´´ + frame.pc´´ = l + end + push!(W, l) + s[l] = newstate_catch + end + typeassert(s[l], VarTable) + frame.handler_at[l] = frame.cur_hand + elseif hd === :leave + for i = 1:((stmt.args[1])::Int) + frame.cur_hand = (frame.cur_hand::Pair{Any,Any}).second + end + else + if hd === :(=) + t = abstract_eval(stmt.args[2], changes, frame) + t === Bottom && break + frame.src.ssavaluetypes[pc] = t + lhs = stmt.args[1] + if isa(lhs, Slot) + changes = StateUpdate(lhs, VarState(t, false), changes) + end + elseif hd === :method + fname = stmt.args[1] + if isa(fname, Slot) + changes = StateUpdate(fname, VarState(Any, false), changes) + end + elseif hd === :inbounds || hd === :meta || hd === :loopinfo + else + t = abstract_eval(stmt, changes, frame) + t === Bottom && break + if !isempty(frame.ssavalue_uses[pc]) + record_ssa_assign(pc, t, frame) + else + frame.src.ssavaluetypes[pc] = t + end + end + if frame.cur_hand !== nothing && isa(changes, StateUpdate) + # propagate new type info to exception handler + # the handling for Expr(:enter) propagates all changes from before the try/catch + # so this only needs to propagate any changes + l = frame.cur_hand.first::Int + if stupdate1!(s[l]::VarTable, changes::StateUpdate) !== false + if l < frame.pc´´ + frame.pc´´ = l + end + push!(W, l) + end + end + end + + if t === nothing + # mark other reached expressions as `Any` to indicate they don't throw + frame.src.ssavaluetypes[pc] = Any + end + + pc´ > n && break # can't proceed with the fast-path fall-through + frame.handler_at[pc´] = frame.cur_hand + newstate = stupdate!(s[pc´], changes) + if isa(stmt, GotoNode) && frame.pc´´ < pc´ + # if we are processing a goto node anyways, + # (such as a terminator for a loop, if-else, or try block), + # consider whether we should jump to an older backedge first, + # to try to traverse the statements in approximate dominator order + if newstate !== false + s[pc´] = newstate + end + push!(W, pc´) + pc = frame.pc´´ + elseif newstate !== false + s[pc´] = newstate + pc = pc´ + elseif pc´ in W + pc = pc´ + else + break + end + end + end + frame.dont_work_on_me = false + nothing +end + +# make as much progress on `frame` as possible (by handling cycles) +function typeinf_nocycle(frame::InferenceState) + typeinf_local(frame) + + # If the current frame is part of a cycle, solve the cycle before finishing + no_active_ips_in_callers = false + while !no_active_ips_in_callers + no_active_ips_in_callers = true + for caller in frame.callers_in_cycle + caller.dont_work_on_me && return false # cycle is above us on the stack + if caller.pc´´ <= caller.nstmts # equivalent to `isempty(caller.ip)` + # Note that `typeinf_local(caller)` can potentially modify the other frames + # `frame.callers_in_cycle`, which is why making incremental progress requires the + # outer while loop. + typeinf_local(caller) + no_active_ips_in_callers = false + end + if caller.min_valid < frame.min_valid + caller.min_valid = frame.min_valid + end + if caller.max_valid > frame.max_valid + caller.max_valid = frame.max_valid + end + end + end + return true +end diff --git a/base/compiler/bootstrap.jl b/base/compiler/bootstrap.jl new file mode 100644 index 0000000..b1fb73f --- /dev/null +++ b/base/compiler/bootstrap.jl @@ -0,0 +1,33 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# make sure that typeinf is executed before turning on typeinf_ext +# this ensures that typeinf_ext doesn't recurse before it can add the item to the workq +# especially try to make sure any recursive and leaf functions have concrete signatures, +# since we won't be able to specialize & infer them at runtime + +let fs = Any[typeinf_ext, typeinf, typeinf_edge, pure_eval_call, run_passes], + world = get_world_counter() + for x in T_FFUNC_VAL + push!(fs, x[3]) + end + for i = 1:length(T_IFUNC) + if isassigned(T_IFUNC, i) + x = T_IFUNC[i] + push!(fs, x[3]) + else + println(stderr, "WARNING: tfunc missing for ", reinterpret(IntrinsicFunction, Int32(i))) + end + end + for f in fs + for m in _methods_by_ftype(Tuple{typeof(f), Vararg{Any}}, 10, typemax(UInt)) + # remove any TypeVars from the intersection + typ = Any[m[1].parameters...] + for i = 1:length(typ) + if isa(typ[i], TypeVar) + typ[i] = typ[i].ub + end + end + typeinf_type(m[3], Tuple{typ...}, m[2], Params(world)) + end + end +end diff --git a/base/compiler/compiler.jl b/base/compiler/compiler.jl new file mode 100644 index 0000000..c2d97e4 --- /dev/null +++ b/base/compiler/compiler.jl @@ -0,0 +1,115 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +getfield(getfield(Main, :Core), :eval)(getfield(Main, :Core), :(baremodule Compiler + +using Core.Intrinsics, Core.IR + +import Core: print, println, show, write, unsafe_write, stdout, stderr, + _apply, svec, apply_type, Builtin, IntrinsicFunction, MethodInstance, CodeInstance + +const getproperty = getfield +const setproperty! = setfield! + +ccall(:jl_set_istopmod, Cvoid, (Any, Bool), Compiler, false) + +eval(x) = Core.eval(Compiler, x) +eval(m, x) = Core.eval(m, x) + +include(x) = Core.include(Compiler, x) +include(mod, x) = Core.include(mod, x) + +############# +# from Base # +############# + +# essential files and libraries +include("essentials.jl") +include("ctypes.jl") +include("generator.jl") +include("reflection.jl") +include("options.jl") + +# core operations & types +function return_type end # promotion.jl expects this to exist +is_return_type(@Core.nospecialize(f)) = f === return_type +include("promotion.jl") +include("tuple.jl") +include("pair.jl") +include("traits.jl") +include("range.jl") +include("expr.jl") +include("error.jl") + +# core numeric operations & types +include("bool.jl") +include("number.jl") +include("int.jl") +include("operators.jl") +include("pointer.jl") +include("refvalue.jl") + +# checked arithmetic +const checked_add = + +const checked_sub = - +const SignedInt = Union{Int8,Int16,Int32,Int64,Int128} +const UnsignedInt = Union{UInt8,UInt16,UInt32,UInt64,UInt128} +sub_with_overflow(x::T, y::T) where {T<:SignedInt} = checked_ssub_int(x, y) +sub_with_overflow(x::T, y::T) where {T<:UnsignedInt} = checked_usub_int(x, y) +sub_with_overflow(x::Bool, y::Bool) = (x-y, false) +add_with_overflow(x::T, y::T) where {T<:SignedInt} = checked_sadd_int(x, y) +add_with_overflow(x::T, y::T) where {T<:UnsignedInt} = checked_uadd_int(x, y) +add_with_overflow(x::Bool, y::Bool) = (x+y, false) + +# core array operations +include("indices.jl") +include("array.jl") +include("abstractarray.jl") + +# core structures +include("bitarray.jl") +include("bitset.jl") +include("abstractdict.jl") +include("abstractset.jl") +include("iterators.jl") +using .Iterators: zip, enumerate +using .Iterators: Flatten, Filter, product # for generators +include("namedtuple.jl") + +# core docsystem +include("docs/core.jl") + +# sorting +function sort end +function sort! end +function issorted end +function sortperm end +include("ordering.jl") +using .Order +include("sort.jl") +using .Sort + +############ +# compiler # +############ + +include("compiler/utilities.jl") +include("compiler/validation.jl") + +include("compiler/inferenceresult.jl") +include("compiler/params.jl") +include("compiler/inferencestate.jl") + +include("compiler/typeutils.jl") +include("compiler/typelimits.jl") +include("compiler/typelattice.jl") +include("compiler/tfuncs.jl") + +include("compiler/abstractinterpretation.jl") +include("compiler/typeinfer.jl") +include("compiler/optimize.jl") # TODO: break this up further + extract utilities + +include("compiler/bootstrap.jl") +ccall(:jl_set_typeinf_func, Cvoid, (Any,), typeinf_ext) + +end # baremodule Compiler +)) diff --git a/base/compiler/inferenceresult.jl b/base/compiler/inferenceresult.jl new file mode 100644 index 0000000..a466a00 --- /dev/null +++ b/base/compiler/inferenceresult.jl @@ -0,0 +1,169 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +const EMPTY_VECTOR = Vector{Any}() + +mutable struct InferenceResult + linfo::MethodInstance + argtypes::Vector{Any} + overridden_by_const::BitVector + result # ::Type, or InferenceState if WIP + src #::Union{CodeInfo, OptimizationState, Nothing} # if inferred copy is available + function InferenceResult(linfo::MethodInstance, given_argtypes = nothing) + argtypes, overridden_by_const = matching_cache_argtypes(linfo, given_argtypes) + return new(linfo, argtypes, overridden_by_const, Any, nothing) + end +end + +function is_argtype_match(@nospecialize(given_argtype), + @nospecialize(cache_argtype), + overridden_by_const::Bool) + if isa(given_argtype, Const) || isa(given_argtype, PartialStruct) + return is_lattice_equal(given_argtype, cache_argtype) + end + return !overridden_by_const +end + +# In theory, there could be a `cache` containing a matching `InferenceResult` +# for the provided `linfo` and `given_argtypes`. The purpose of this function is +# to return a valid value for `cache_lookup(linfo, argtypes, cache).argtypes`, +# so that we can construct cache-correct `InferenceResult`s in the first place. +function matching_cache_argtypes(linfo::MethodInstance, given_argtypes::Vector) + @assert isa(linfo.def, Method) # ensure the next line works + nargs::Int = linfo.def.nargs + @assert length(given_argtypes) >= (nargs - 1) + given_argtypes = anymap(widenconditional, given_argtypes) + if linfo.def.isva + isva_given_argtypes = Vector{Any}(undef, nargs) + for i = 1:(nargs - 1) + isva_given_argtypes[i] = given_argtypes[i] + end + isva_given_argtypes[nargs] = tuple_tfunc(given_argtypes[nargs:end]) + given_argtypes = isva_given_argtypes + end + cache_argtypes, overridden_by_const = matching_cache_argtypes(linfo, nothing) + if nargs === length(given_argtypes) + for i in 1:nargs + given_argtype = given_argtypes[i] + cache_argtype = cache_argtypes[i] + if !is_argtype_match(given_argtype, cache_argtype, overridden_by_const[i]) + # prefer the argtype we were given over the one computed from `linfo` + cache_argtypes[i] = given_argtype + overridden_by_const[i] = true + end + end + end + return cache_argtypes, overridden_by_const +end + +function matching_cache_argtypes(linfo::MethodInstance, ::Nothing) + toplevel = !isa(linfo.def, Method) + linfo_argtypes = Any[unwrap_unionall(linfo.specTypes).parameters...] + nargs::Int = toplevel ? 0 : linfo.def.nargs + cache_argtypes = Vector{Any}(undef, nargs) + # First, if we're dealing with a varargs method, then we set the last element of `args` + # to the appropriate `Tuple` type or `PartialStruct` instance. + if !toplevel && linfo.def.isva + if linfo.specTypes == Tuple + if nargs > 1 + linfo_argtypes = svec(Any[Any for i = 1:(nargs - 1)]..., Tuple.parameters[1]) + end + vargtype = Tuple + else + linfo_argtypes_length = length(linfo_argtypes) + if nargs > linfo_argtypes_length + va = linfo_argtypes[linfo_argtypes_length] + if isvarargtype(va) + new_va = rewrap_unionall(unconstrain_vararg_length(va), linfo.specTypes) + vargtype_elements = Any[new_va] + vargtype = Tuple{new_va} + else + vargtype_elements = Any[] + vargtype = Tuple{} + end + else + vargtype_elements = Any[] + for p in linfo_argtypes[nargs:linfo_argtypes_length] + p = isvarargtype(p) ? unconstrain_vararg_length(p) : p + push!(vargtype_elements, rewrap(p, linfo.specTypes)) + end + for i in 1:length(vargtype_elements) + atyp = vargtype_elements[i] + if isa(atyp, DataType) && isdefined(atyp, :instance) + # replace singleton types with their equivalent Const object + vargtype_elements[i] = Const(atyp.instance) + elseif isconstType(atyp) + vargtype_elements[i] = Const(atyp.parameters[1]) + end + end + vargtype = tuple_tfunc(vargtype_elements) + end + end + cache_argtypes[nargs] = vargtype + nargs -= 1 + end + # Now, we propagate type info from `linfo_argtypes` into `cache_argtypes`, improving some + # type info as we go (where possible). Note that if we're dealing with a varargs method, + # we already handled the last element of `cache_argtypes` (and decremented `nargs` so that + # we don't overwrite the result of that work here). + linfo_argtypes_length = length(linfo_argtypes) + if linfo_argtypes_length > 0 + n = linfo_argtypes_length > nargs ? nargs : linfo_argtypes_length + tail_index = n + local lastatype + for i = 1:n + atyp = linfo_argtypes[i] + if i == n && isvarargtype(atyp) + atyp = unwrapva(atyp) + tail_index -= 1 + end + while isa(atyp, TypeVar) + atyp = atyp.ub + end + if isa(atyp, DataType) && isdefined(atyp, :instance) + # replace singleton types with their equivalent Const object + atyp = Const(atyp.instance) + elseif isconstType(atyp) + atyp = Const(atyp.parameters[1]) + else + atyp = rewrap(atyp, linfo.specTypes) + end + i == n && (lastatype = atyp) + cache_argtypes[i] = atyp + end + for i = (tail_index + 1):nargs + cache_argtypes[i] = lastatype + end + else + @assert nargs == 0 "invalid specialization of method" # wrong number of arguments + end + return cache_argtypes, falses(length(cache_argtypes)) +end + +function cache_lookup(linfo::MethodInstance, given_argtypes::Vector{Any}, cache::Vector{InferenceResult}) + method = linfo.def::Method + nargs::Int = method.nargs + method.isva && (nargs -= 1) + length(given_argtypes) >= nargs || return nothing + for cached_result in cache + cached_result.linfo === linfo || continue + cache_match = true + cache_argtypes = cached_result.argtypes + cache_overridden_by_const = cached_result.overridden_by_const + for i in 1:nargs + if !is_argtype_match(given_argtypes[i], + cache_argtypes[i], + cache_overridden_by_const[i]) + cache_match = false + break + end + end + if method.isva && cache_match + cache_match = is_argtype_match(tuple_tfunc(given_argtypes[(nargs + 1):end]), + cache_argtypes[end], + cache_overridden_by_const[end]) + end + cache_match || continue + return cached_result + end + return nothing +end diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl new file mode 100644 index 0000000..4caf6b6 --- /dev/null +++ b/base/compiler/inferencestate.jl @@ -0,0 +1,286 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +const LineNum = Int + +mutable struct InferenceState + params::Params # describes how to compute the result + result::InferenceResult # remember where to put the result + linfo::MethodInstance + sptypes::Vector{Any} # types of static parameter + slottypes::Vector{Any} + mod::Module + currpc::LineNum + + # info on the state of inference and the linfo + src::CodeInfo + min_valid::UInt + max_valid::UInt + nargs::Int + stmt_types::Vector{Any} + stmt_edges::Vector{Any} + # return type + bestguess #::Type + # current active instruction pointers + ip::BitSet + pc´´::LineNum + nstmts::Int + # current exception handler info + cur_hand #::Union{Nothing, Pair{LineNum, prev_handler}} + handler_at::Vector{Any} + n_handlers::Int + # ssavalue sparsity and restart info + ssavalue_uses::Vector{BitSet} + + cycle_backedges::Vector{Tuple{InferenceState, LineNum}} # call-graph backedges connecting from callee to caller + callers_in_cycle::Vector{InferenceState} + parent::Union{Nothing, InferenceState} + + # TODO: move these to InferenceResult / Params? + cached::Bool + limited::Bool + inferred::Bool + dont_work_on_me::Bool + + # src is assumed to be a newly-allocated CodeInfo, that can be modified in-place to contain intermediate results + function InferenceState(result::InferenceResult, src::CodeInfo, + cached::Bool, params::Params) + linfo = result.linfo + code = src.code::Array{Any,1} + toplevel = !isa(linfo.def, Method) + + sp = sptypes_from_meth_instance(linfo::MethodInstance) + + nssavalues = src.ssavaluetypes::Int + src.ssavaluetypes = Any[ NOT_FOUND for i = 1:nssavalues ] + + n = length(code) + s_edges = Any[ nothing for i = 1:n ] + s_types = Any[ nothing for i = 1:n ] + + # initial types + nslots = length(src.slotflags) + argtypes = result.argtypes + nargs = length(argtypes) + s_argtypes = VarTable(undef, nslots) + slottypes = Vector{Any}(undef, nslots) + for i in 1:nslots + at = (i > nargs) ? Bottom : argtypes[i] + s_argtypes[i] = VarState(at, i > nargs) + slottypes[i] = at + end + s_types[1] = s_argtypes + + ssavalue_uses = find_ssavalue_uses(code, nssavalues) + + # exception handlers + cur_hand = nothing + handler_at = Any[ nothing for i=1:n ] + n_handlers = 0 + + W = BitSet() + push!(W, 1) #initial pc to visit + + if !toplevel + meth = linfo.def + inmodule = meth.module + else + inmodule = linfo.def::Module + end + + min_valid = src.min_world + max_valid = src.max_world == typemax(UInt) ? + get_world_counter() : src.max_world + frame = new( + params, result, linfo, + sp, slottypes, inmodule, 0, + src, min_valid, max_valid, + nargs, s_types, s_edges, + Union{}, W, 1, n, + cur_hand, handler_at, n_handlers, + ssavalue_uses, + Vector{Tuple{InferenceState,LineNum}}(), # cycle_backedges + Vector{InferenceState}(), # callers_in_cycle + #=parent=#nothing, + cached, false, false, false) + result.result = frame + cached && push!(params.cache, result) + return frame + end +end + +function InferenceState(result::InferenceResult, cached::Bool, params::Params) + # prepare an InferenceState object for inferring lambda + src = retrieve_code_info(result.linfo) + src === nothing && return nothing + validate_code_in_debug_mode(result.linfo, src, "lowered") + return InferenceState(result, src, cached, params) +end + +function sptypes_from_meth_instance(linfo::MethodInstance) + toplevel = !isa(linfo.def, Method) + if !toplevel && isempty(linfo.sparam_vals) && isa(linfo.def.sig, UnionAll) + # linfo is unspecialized + sp = Any[] + sig = linfo.def.sig + while isa(sig, UnionAll) + push!(sp, sig.var) + sig = sig.body + end + else + sp = collect(Any, linfo.sparam_vals) + end + for i = 1:length(sp) + v = sp[i] + if v isa TypeVar + fromArg = 0 + # if this parameter came from arg::Type{T}, then `arg` is more precise than + # Type{T} where lb<:T<:ub + sig = linfo.def.sig + temp = sig + for j = 1:i-1 + temp = temp.body + end + Pi = temp.var + while temp isa UnionAll + temp = temp.body + end + sigtypes = temp.parameters + for j = 1:length(sigtypes) + tj = sigtypes[j] + if isType(tj) && tj.parameters[1] === Pi + fromArg = j + break + end + end + if fromArg > 0 + ty = fieldtype(linfo.specTypes, fromArg) + else + ub = v.ub + while ub isa TypeVar + ub = ub.ub + end + if has_free_typevars(ub) + ub = Any + end + lb = v.lb + while lb isa TypeVar + lb = lb.lb + end + if has_free_typevars(lb) + lb = Bottom + end + if Any <: ub && lb <: Bottom + ty = Any + else + tv = TypeVar(v.name, lb, ub) + ty = UnionAll(tv, Type{tv}) + end + end + else + ty = Const(v) + end + sp[i] = ty + end + return sp +end + +_topmod(sv::InferenceState) = _topmod(sv.mod) + +# work towards converging the valid age range for sv +function update_valid_age!(min_valid::UInt, max_valid::UInt, sv::InferenceState) + sv.min_valid = max(sv.min_valid, min_valid) + sv.max_valid = min(sv.max_valid, max_valid) + @assert(sv.min_valid <= sv.params.world <= sv.max_valid, + "invalid age range update") + nothing +end + +update_valid_age!(edge::InferenceState, sv::InferenceState) = update_valid_age!(edge.min_valid, edge.max_valid, sv) + +function record_ssa_assign(ssa_id::Int, @nospecialize(new), frame::InferenceState) + old = frame.src.ssavaluetypes[ssa_id] + if old === NOT_FOUND || !(new ⊑ old) + frame.src.ssavaluetypes[ssa_id] = tmerge(old, new) + W = frame.ip + s = frame.stmt_types + for r in frame.ssavalue_uses[ssa_id] + if s[r] !== nothing # s[r] === nothing => unreached statement + if r < frame.pc´´ + frame.pc´´ = r + end + push!(W, r) + end + end + end + nothing +end + +function add_cycle_backedge!(frame::InferenceState, caller::InferenceState, currpc::Int) + update_valid_age!(frame, caller) + backedge = (caller, currpc) + contains_is(frame.cycle_backedges, backedge) || push!(frame.cycle_backedges, backedge) + add_backedge!(frame.linfo, caller) + return frame +end + +# temporarily accumulate our edges to later add as backedges in the callee +function add_backedge!(li::MethodInstance, caller::InferenceState) + isa(caller.linfo.def, Method) || return # don't add backedges to toplevel exprs + if caller.stmt_edges[caller.currpc] === nothing + caller.stmt_edges[caller.currpc] = [] + end + push!(caller.stmt_edges[caller.currpc], li) + nothing +end + +# used to temporarily accumulate our no method errors to later add as backedges in the callee method table +function add_mt_backedge!(mt::Core.MethodTable, @nospecialize(typ), caller::InferenceState) + isa(caller.linfo.def, Method) || return # don't add backedges to toplevel exprs + if caller.stmt_edges[caller.currpc] === nothing + caller.stmt_edges[caller.currpc] = [] + end + push!(caller.stmt_edges[caller.currpc], mt) + push!(caller.stmt_edges[caller.currpc], typ) + nothing +end + +function poison_callstack(infstate::InferenceState, topmost::InferenceState, poison_topmost::Bool) + poison_topmost && (topmost = topmost.parent) + while !(infstate === topmost) + if call_result_unused(infstate) + # If we won't propagate the result any further (since it's typically unused), + # it's OK that we keep and cache the "limited" result in the parents + # (non-typically, this means that we lose the ability to detect a guaranteed StackOverflow in some cases) + # TODO: we might be able to halt progress much more strongly here, + # since now we know we won't be able to keep anything much that we learned. + # We were mainly only here to compute the calling convention return type, + # but in most situations now, we are unlikely to be able to use that information. + break + end + infstate.limited = true + for infstate_cycle in infstate.callers_in_cycle + infstate_cycle.limited = true + end + infstate = infstate.parent + infstate === nothing && return + end +end + +function is_specializable_vararg_slot(@nospecialize(arg), nargs::Int, vargs::Vector{Any}) + return (isa(arg, Slot) && slot_id(arg) == nargs && !isempty(vargs)) +end + +function print_callstack(sv::InferenceState) + while sv !== nothing + print(sv.linfo) + sv.limited && print(" [limited]") + !sv.cached && print(" [uncached]") + println() + for cycle in sv.callers_in_cycle + print(' ', cycle.linfo) + cycle.limited && print(" [limited]") + println() + end + sv = sv.parent + end +end diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl new file mode 100644 index 0000000..c003908 --- /dev/null +++ b/base/compiler/optimize.jl @@ -0,0 +1,444 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +##################### +# OptimizationState # +##################### + +mutable struct OptimizationState + linfo::MethodInstance + calledges::Vector{Any} + src::CodeInfo + mod::Module + nargs::Int + min_valid::UInt + max_valid::UInt + params::Params + sptypes::Vector{Any} # static parameters + slottypes::Vector{Any} + const_api::Bool + function OptimizationState(frame::InferenceState) + s_edges = frame.stmt_edges[1] + if s_edges === nothing + s_edges = [] + frame.stmt_edges[1] = s_edges + end + src = frame.src + return new(frame.linfo, + s_edges::Vector{Any}, + src, frame.mod, frame.nargs, + frame.min_valid, frame.max_valid, + frame.params, frame.sptypes, frame.slottypes, false) + end + function OptimizationState(linfo::MethodInstance, src::CodeInfo, + params::Params) + # prepare src for running optimization passes + # if it isn't already + nssavalues = src.ssavaluetypes + if nssavalues isa Int + src.ssavaluetypes = Any[ Any for i = 1:nssavalues ] + end + nslots = length(src.slotflags) + slottypes = src.slottypes + if slottypes === nothing + slottypes = Any[ Any for i = 1:nslots ] + end + s_edges = [] + # cache some useful state computations + toplevel = !isa(linfo.def, Method) + if !toplevel + meth = linfo.def + inmodule = meth.module + nargs = meth.nargs + else + inmodule = linfo.def::Module + nargs = 0 + end + return new(linfo, + s_edges::Vector{Any}, + src, inmodule, nargs, + UInt(1), get_world_counter(), + params, sptypes_from_meth_instance(linfo), slottypes, false) + end +end + +function OptimizationState(linfo::MethodInstance, params::Params) + src = retrieve_code_info(linfo) + src === nothing && return nothing + return OptimizationState(linfo, src, params) +end + + +############# +# constants # +############# + +# The slot has uses that are not statically dominated by any assignment +# This is implied by `SLOT_USEDUNDEF`. +# If this is not set, all the uses are (statically) dominated by the defs. +# In particular, if a slot has `AssignedOnce && !StaticUndef`, it is an SSA. +const SLOT_STATICUNDEF = 1 # slot might be used before it is defined (structurally) +const SLOT_ASSIGNEDONCE = 16 # slot is assigned to only once +const SLOT_USEDUNDEF = 32 # slot has uses that might raise UndefVarError +# const SLOT_CALLED = 64 + +const IR_FLAG_INBOUNDS = 0x01 + +# known to be always effect-free (in particular nothrow) +const _PURE_BUILTINS = Any[tuple, svec, ===, typeof, nfields] + +# known to be effect-free if the are nothrow +const _PURE_OR_ERROR_BUILTINS = [ + fieldtype, apply_type, isa, UnionAll, + getfield, arrayref, const_arrayref, isdefined, Core.sizeof, + Core.kwfunc, ifelse, Core._typevar, (<:) +] + +const TOP_TUPLE = GlobalRef(Core, :tuple) + +######### +# logic # +######### + +_topmod(sv::OptimizationState) = _topmod(sv.mod) + +function update_valid_age!(min_valid::UInt, max_valid::UInt, sv::OptimizationState) + sv.min_valid = max(sv.min_valid, min_valid) + sv.max_valid = min(sv.max_valid, max_valid) + @assert(sv.min_valid <= sv.params.world <= sv.max_valid, + "invalid age range update") + nothing +end + +function add_backedge!(li::MethodInstance, caller::OptimizationState) + #TODO: deprecate this? + isa(caller.linfo.def, Method) || return # don't add backedges to toplevel exprs + push!(caller.calledges, li) + nothing +end + +function add_backedge!(li::CodeInstance, caller::OptimizationState) + update_valid_age!(min_world(li), max_world(li), caller) + add_backedge!(li.def, caller) + nothing +end + +function isinlineable(m::Method, me::OptimizationState, bonus::Int=0) + # compute the cost (size) of inlining this code + inlineable = false + cost_threshold = me.params.inline_cost_threshold + if m.module === _topmod(m.module) + # a few functions get special treatment + name = m.name + sig = m.sig + if ((name === :+ || name === :* || name === :min || name === :max) && + isa(sig,DataType) && + sig == Tuple{sig.parameters[1],Any,Any,Any,Vararg{Any}}) + inlineable = true + elseif (name === :iterate || name === :unsafe_convert || + name === :cconvert) + cost_threshold *= 4 + end + end + if !inlineable + inlineable = inline_worthy(me.src.code, me.src, me.sptypes, me.slottypes, me.params, cost_threshold + bonus) + end + return inlineable +end + +# These affect control flow within the function (so may not be removed +# if there is no usage within the function), but don't affect the purity +# of the function as a whole. +function stmt_affects_purity(@nospecialize(stmt), ir) + if isa(stmt, GotoNode) || isa(stmt, ReturnNode) + return false + end + if isa(stmt, GotoIfNot) + t = argextype(stmt.cond, ir, ir.sptypes) + return !(t ⊑ Bool) + end + if isa(stmt, Expr) + return stmt.head !== :loopinfo && stmt.head !== :enter + end + return true +end + +# run the optimization work +function optimize(opt::OptimizationState, @nospecialize(result)) + def = opt.linfo.def + nargs = Int(opt.nargs) - 1 + @timeit "optimizer" ir = run_passes(opt.src, nargs, opt) + force_noinline = _any(@nospecialize(x) -> isexpr(x, :meta) && x.args[1] === :noinline, ir.meta) + + # compute inlining and other related optimizations + if (isa(result, Const) || isconstType(result)) + proven_pure = false + # must be proven pure to use const_api; otherwise we might skip throwing errors + # (issue #20704) + # TODO: Improve this analysis; if a function is marked @pure we should really + # only care about certain errors (e.g. method errors and type errors). + if length(ir.stmts) < 10 + proven_pure = true + for i in 1:length(ir.stmts) + stmt = ir.stmts[i] + if stmt_affects_purity(stmt, ir) && !stmt_effect_free(stmt, ir.types[i], ir, ir.sptypes) + proven_pure = false + break + end + end + if proven_pure + for fl in opt.src.slotflags + if (fl & SLOT_USEDUNDEF) != 0 + proven_pure = false + break + end + end + end + end + if proven_pure + opt.src.pure = true + end + + if proven_pure && !coverage_enabled() + # use constant calling convention + # Do not emit `jl_fptr_const_return` if coverage is enabled + # so that we don't need to add coverage support + # to the `jl_call_method_internal` fast path + # Still set pure flag to make sure `inference` tests pass + # and to possibly enable more optimization in the future + if !(isa(result, Const) && !is_inlineable_constant(result.val)) + opt.const_api = true + end + force_noinline || (opt.src.inlineable = true) + end + end + + replace_code_newstyle!(opt.src, ir, nargs) + + # determine and cache inlineability + if !force_noinline + # don't keep ASTs for functions specialized on a Union argument + # TODO: this helps avoid a type-system bug mis-computing sparams during intersection + sig = unwrap_unionall(opt.linfo.specTypes) + if isa(sig, DataType) && sig.name === Tuple.name + for P in sig.parameters + P = unwrap_unionall(P) + if isa(P, Union) + force_noinline = true + break + end + end + else + force_noinline = true + end + if !opt.src.inlineable && result === Union{} + force_noinline = true + end + end + if force_noinline + opt.src.inlineable = false + elseif isa(def, Method) + if opt.src.inlineable && isdispatchtuple(opt.linfo.specTypes) + # obey @inline declaration if a dispatch barrier would not help + else + bonus = 0 + if result ⊑ Tuple && !isbitstype(widenconst(result)) + bonus = opt.params.inline_tupleret_bonus + end + if opt.src.inlineable + # For functions declared @inline, increase the cost threshold 20x + bonus += opt.params.inline_cost_threshold*19 + end + opt.src.inlineable = isinlineable(def, opt, bonus) + end + end + nothing +end + + +# whether `f` is pure for inference +function is_pure_intrinsic_infer(f::IntrinsicFunction) + return !(f === Intrinsics.pointerref || # this one is volatile + f === Intrinsics.pointerset || # this one is never effect-free + f === Intrinsics.llvmcall || # this one is never effect-free + f === Intrinsics.arraylen || # this one is volatile + f === Intrinsics.sqrt_llvm || # this one may differ at runtime (by a few ulps) + f === Intrinsics.cglobal) # cglobal lookup answer changes at runtime +end + +# whether `f` is effect free if nothrow +intrinsic_effect_free_if_nothrow(f) = f === Intrinsics.pointerref || is_pure_intrinsic_infer(f) + +## Computing the cost of a function body + +# saturating sum (inputs are nonnegative), prevents overflow with typemax(Int) below +plus_saturate(x::Int, y::Int) = max(x, y, x+y) + +# known return type +isknowntype(@nospecialize T) = (T === Union{}) || isconcretetype(T) + +function statement_cost(ex::Expr, line::Int, src::CodeInfo, sptypes::Vector{Any}, slottypes::Vector{Any}, params::Params) + head = ex.head + if is_meta_expr_head(head) + return 0 + elseif head === :call + farg = ex.args[1] + ftyp = argextype(farg, src, sptypes, slottypes) + if ftyp === IntrinsicFunction && farg isa SSAValue + # if this comes from code that was already inlined into another function, + # Consts have been widened. try to recover in simple cases. + farg = src.code[farg.id] + if isa(farg, GlobalRef) || isa(farg, QuoteNode) || isa(farg, IntrinsicFunction) || isexpr(farg, :static_parameter) + ftyp = argextype(farg, src, sptypes, slottypes) + end + end + f = singleton_type(ftyp) + if isa(f, IntrinsicFunction) + iidx = Int(reinterpret(Int32, f::IntrinsicFunction)) + 1 + if !isassigned(T_IFUNC_COST, iidx) + # unknown/unhandled intrinsic + return params.inline_nonleaf_penalty + end + return T_IFUNC_COST[iidx] + end + if isa(f, Builtin) + # The efficiency of operations like a[i] and s.b + # depend strongly on whether the result can be + # inferred, so check the type of ex + if f === Main.Core.getfield || f === Main.Core.tuple + # we might like to penalize non-inferrability, but + # tuple iteration/destructuring makes that impossible + # return plus_saturate(argcost, isknowntype(extyp) ? 1 : params.inline_nonleaf_penalty) + return 0 + elseif (f === Main.Core.arrayref || f === Main.Core.const_arrayref) && length(ex.args) >= 3 + atyp = argextype(ex.args[3], src, sptypes, slottypes) + return isknowntype(atyp) ? 4 : params.inline_nonleaf_penalty + end + fidx = find_tfunc(f) + if fidx === nothing + # unknown/unhandled builtin or anonymous function + # Use the generic cost of a direct function call + return 20 + end + return T_FFUNC_COST[fidx] + end + return params.inline_nonleaf_penalty + elseif head === :foreigncall || head === :invoke + # Calls whose "return type" is Union{} do not actually return: + # they are errors. Since these are not part of the typical + # run-time of the function, we omit them from + # consideration. This way, non-inlined error branches do not + # prevent inlining. + extyp = line == -1 ? Any : src.ssavaluetypes[line] + return extyp === Union{} ? 0 : 20 + elseif head === :return + a = ex.args[1] + if a isa Expr + return statement_cost(a, -1, src, sptypes, slottypes, params) + end + return 0 + elseif head === :(=) + if ex.args[1] isa GlobalRef + cost = 20 + else + cost = 0 + end + a = ex.args[2] + if a isa Expr + cost = plus_saturate(cost, statement_cost(a, -1, src, sptypes, slottypes, params)) + end + return cost + elseif head === :copyast + return 100 + elseif head === :enter + # try/catch is a couple function calls, + # but don't inline functions with try/catch + # since these aren't usually performance-sensitive functions, + # and llvm is more likely to miscompile them when these functions get large + return typemax(Int) + elseif head === :gotoifnot + target = ex.args[2]::Int + # loops are generally always expensive + # but assume that forward jumps are already counted for from + # summing the cost of the not-taken branch + return target < line ? 40 : 0 + end + return 0 +end + +function inline_worthy(body::Array{Any,1}, src::CodeInfo, sptypes::Vector{Any}, slottypes::Vector{Any}, + params::Params, cost_threshold::Integer=params.inline_cost_threshold) + bodycost::Int = 0 + for line = 1:length(body) + stmt = body[line] + if stmt isa Expr + thiscost = statement_cost(stmt, line, src, sptypes, slottypes, params)::Int + elseif stmt isa GotoNode + # loops are generally always expensive + # but assume that forward jumps are already counted for from + # summing the cost of the not-taken branch + thiscost = stmt.label < line ? 40 : 0 + else + continue + end + bodycost = plus_saturate(bodycost, thiscost) + bodycost > cost_threshold && return false + end + return true +end + +function is_known_call(e::Expr, @nospecialize(func), src, sptypes::Vector{Any}, slottypes::Vector{Any} = empty_slottypes) + if e.head !== :call + return false + end + f = argextype(e.args[1], src, sptypes, slottypes) + return isa(f, Const) && f.val === func +end + +function renumber_ir_elements!(body::Vector{Any}, changemap::Vector{Int}) + return renumber_ir_elements!(body, changemap, changemap) +end + +function renumber_ir_elements!(body::Vector{Any}, ssachangemap::Vector{Int}, labelchangemap::Vector{Int}) + for i = 2:length(labelchangemap) + labelchangemap[i] += labelchangemap[i - 1] + end + if ssachangemap !== labelchangemap + for i = 2:length(ssachangemap) + ssachangemap[i] += ssachangemap[i - 1] + end + end + (labelchangemap[end] != 0 && ssachangemap[end] != 0) || return + for i = 1:length(body) + el = body[i] + if isa(el, GotoNode) + body[i] = GotoNode(el.label + labelchangemap[el.label]) + elseif isa(el, SSAValue) + body[i] = SSAValue(el.id + ssachangemap[el.id]) + elseif isa(el, Expr) + if el.head === :gotoifnot + cond = el.args[1] + if isa(cond, SSAValue) + el.args[1] = SSAValue(cond.id + ssachangemap[cond.id]) + end + tgt = el.args[2]::Int + el.args[2] = tgt + labelchangemap[tgt] + elseif el.head === :enter + tgt = el.args[1]::Int + el.args[1] = tgt + labelchangemap[tgt] + elseif !is_meta_expr_head(el.head) + if el.head === :(=) && el.args[2] isa Expr && !is_meta_expr_head(el.args[2].head) + el = el.args[2]::Expr + end + args = el.args + for i = 1:length(args) + el = args[i] + if isa(el, SSAValue) + args[i] = SSAValue(el.id + ssachangemap[el.id]) + end + end + end + end + end +end + +include("compiler/ssair/driver.jl") diff --git a/base/compiler/params.jl b/base/compiler/params.jl new file mode 100644 index 0000000..46c0862 --- /dev/null +++ b/base/compiler/params.jl @@ -0,0 +1,72 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +struct Params + cache::Vector{InferenceResult} + world::UInt + global_cache::Bool + + # optimization + inlining::Bool + ipo_constant_propagation::Bool + aggressive_constant_propagation::Bool + inline_cost_threshold::Int # number of CPU cycles beyond which it's not worth inlining + inline_nonleaf_penalty::Int # penalty for dynamic dispatch + inline_tupleret_bonus::Int # extra willingness for non-isbits tuple return types + + # don't consider more than N methods. this trades off between + # compiler performance and generated code performance. + # typically, considering many methods means spending lots of time + # obtaining poor type information. + # It is important for N to be >= the number of methods in the error() + # function, so we can still know that error() is always Bottom. + MAX_METHODS::Int + # the maximum number of union-tuples to swap / expand + # before computing the set of matching methods + MAX_UNION_SPLITTING::Int + # the maximum number of union-tuples to swap / expand + # when inferring a call to _apply + MAX_APPLY_UNION_ENUM::Int + + # parameters limiting large (tuple) types + TUPLE_COMPLEXITY_LIMIT_DEPTH::Int + + # when attempting to inlining _apply, abort the optimization if the tuple + # contains more than this many elements + MAX_TUPLE_SPLAT::Int + + # reasonable defaults + global function CustomParams(world::UInt, + ; + inlining::Bool = inlining_enabled(), + inline_cost_threshold::Int = DEFAULT_PARAMS.inline_cost_threshold, + inline_nonleaf_penalty::Int = DEFAULT_PARAMS.inline_nonleaf_penalty, + inline_tupleret_bonus::Int = DEFAULT_PARAMS.inline_tupleret_bonus, + ipo_constant_propagation::Bool = true, + aggressive_constant_propagation::Bool = false, + max_methods::Int = DEFAULT_PARAMS.MAX_METHODS, + tupletype_depth::Int = DEFAULT_PARAMS.TUPLE_COMPLEXITY_LIMIT_DEPTH, + tuple_splat::Int = DEFAULT_PARAMS.MAX_TUPLE_SPLAT, + union_splitting::Int = DEFAULT_PARAMS.MAX_UNION_SPLITTING, + apply_union_enum::Int = DEFAULT_PARAMS.MAX_APPLY_UNION_ENUM) + return new(Vector{InferenceResult}(), + world, false, + inlining, ipo_constant_propagation, aggressive_constant_propagation, + inline_cost_threshold, inline_nonleaf_penalty, inline_tupleret_bonus, + max_methods, union_splitting, apply_union_enum, tupletype_depth, + tuple_splat) + end + function Params(world::UInt) + world == typemax(UInt) && (world = get_world_counter()) # workaround for bad callers + @assert world <= get_world_counter() + inlining = inlining_enabled() + return new(Vector{InferenceResult}(), + world, true, + #=inlining, ipo_constant_propagation, aggressive_constant_propagation, inline_cost_threshold, inline_nonleaf_penalty,=# + inlining, true, false, 100, 1000, + #=inline_tupleret_bonus, max_methods, union_splitting, apply_union_enum=# + 400, 4, 4, 8, + #=tupletype_depth, tuple_splat=# + 3, 32) + end +end +const DEFAULT_PARAMS = Params(UInt(0)) diff --git a/base/compiler/ssair/domtree.jl b/base/compiler/ssair/domtree.jl new file mode 100644 index 0000000..23ab6c7 --- /dev/null +++ b/base/compiler/ssair/domtree.jl @@ -0,0 +1,327 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +"Represents a Basic Block, in the DomTree" +struct DomTreeNode + # How deep we are in the DomTree + level::Int + # The BB indices in the CFG for all Basic Blocks we immediately dominate + children::Vector{Int} +end +DomTreeNode() = DomTreeNode(1, Vector{Int}()) + +"Data structure that encodes which basic block dominates which." +struct DomTree + # Which basic block immediately dominates each basic block (ordered by BB indices) + # Note: this is the inverse of the nodes, children field + idoms::Vector{Int} + + # The nodes in the tree (ordered by BB indices) + nodes::Vector{DomTreeNode} +end + +""" + Checks if bb1 dominates bb2. + bb1 and bb2 are indexes into the CFG blocks. + bb1 dominates bb2 if the only way to enter bb2 is via bb1. + (Other blocks may be in between, e.g bb1->bbX->bb2). +""" +function dominates(domtree::DomTree, bb1::Int, bb2::Int) + bb1 == bb2 && return true + target_level = domtree.nodes[bb1].level + source_level = domtree.nodes[bb2].level + source_level < target_level && return false + for _ in (source_level - 1):-1:target_level + bb2 = domtree.idoms[bb2] + end + return bb1 == bb2 +end + +bb_unreachable(domtree::DomTree, bb::Int) = bb != 1 && domtree.nodes[bb].level == 1 + +function update_level!(domtree::Vector{DomTreeNode}, node::Int, level::Int) + worklist = Tuple{Int, Int}[(node, level)] + while !isempty(worklist) + (node, level) = pop!(worklist) + domtree[node] = DomTreeNode(level, domtree[node].children) + foreach(domtree[node].children) do child + push!(worklist, (child, level+1)) + end + end +end + +"Iterable data structure that walks though all dominated blocks" +struct DominatedBlocks + domtree::DomTree + worklist::Vector{Int} +end + +"Returns an iterator that walks through all blocks dominated by the basic block at index `root`" +function dominated(domtree::DomTree, root::Int) + doms = DominatedBlocks(domtree, Vector{Int}()) + push!(doms.worklist, root) + doms +end + +function iterate(doms::DominatedBlocks, state::Nothing=nothing) + isempty(doms.worklist) && return nothing + bb = pop!(doms.worklist) + for dominated in doms.domtree.nodes[bb].children + push!(doms.worklist, dominated) + end + return (bb, nothing) +end + +function naive_idoms(cfg::CFG) + nblocks = length(cfg.blocks) + # The extra +1 helps us detect unreachable blocks below + dom_all = BitSet(1:nblocks+1) + dominators = BitSet[n == 1 ? BitSet(1) : copy(dom_all) for n = 1:nblocks] + changed = true + while changed + changed = false + for n = 2:nblocks + if isempty(cfg.blocks[n].preds) + continue + end + firstp, rest = Iterators.peel(Iterators.filter(p->p != 0, cfg.blocks[n].preds)) + new_doms = copy(dominators[firstp]) + for p in rest + intersect!(new_doms, dominators[p]) + end + push!(new_doms, n) + changed = changed || (new_doms != dominators[n]) + dominators[n] = new_doms + end + end + # Compute idoms + idoms = fill(0, nblocks) + for i = 2:nblocks + if dominators[i] == dom_all + idoms[i] = 0 + continue + end + doms = collect(dominators[i]) + for dom in doms + i == dom && continue + hasany = false + for p in doms + if p !== i && p !== dom && dom in dominators[p] + hasany = true; break + end + end + hasany && continue + idoms[i] = dom + end + end + idoms +end + +# Construct Dom Tree +function construct_domtree(cfg::CFG) + idoms = SNCA(cfg) + # Compute children + nblocks = length(cfg.blocks) + domtree = DomTreeNode[DomTreeNode() for _ = 1:nblocks] + for (idx, idom) in Iterators.enumerate(idoms) + (idx == 1 || idom == 0) && continue + push!(domtree[idom].children, idx) + end + # Recursively set level + update_level!(domtree, 1, 1) + DomTree(idoms, domtree) +end + +#================================ [SNCA] ======================================# +# +# This section implements the Semi-NCA (SNCA) dominator tree construction from +# described in Georgiadis' PhD thesis [LG05], which itself is a simplification +# of the Simple Lenguare-Tarjan (SLT) algorithm [LG79]. This algorithm matches +# the algorithm choice in LLVM and seems to be a sweet spot in implementation +# simplicity and efficiency. +# +# [LG05] Linear-Time Algorithms for Dominators and Related Problems +# Loukas Georgiadis, Princeton University, November 2005, pp. 21-23: +# ftp://ftp.cs.princeton.edu/reports/2005/737.pdf +# +# [LT79] A fast algorithm for finding dominators in a flowgraph +# Thomas Lengauer, Robert Endre Tarjan, July 1979, ACM TOPLAS 1-1 +# http://www.dtic.mil/dtic/tr/fulltext/u2/a054144.pdf +# +begin + # We could make these real structs, but probably not worth the extra + # overhead. Still, give them names for documentary purposes. + const BBNumber = UInt + const DFSNumber = UInt + + """ + Keeps the per-BB state of the Semi NCA algorithm. In the original + formulation, there are three separate length `n` arrays, `label`, `semi` and + `ancestor`. Instead, for efficiency, we use one array in a array-of-structs + style setup. + """ + struct Node + semi::DFSNumber + label::DFSNumber + end + + struct DFSTree + # Maps DFS number to BB number + numbering::Vector{BBNumber} + # Maps BB number to DFS number + reverse::Vector{DFSNumber} + # Records parent relationships in the DFS tree (DFS number -> DFS number) + # Storing it this way saves a few lookups in the snca_compress! algorithm + parents::Vector{DFSNumber} + end + length(D::DFSTree) = length(D.numbering) + preorder(D::DFSTree) = OneTo(length(D)) + _drop(xs::AbstractUnitRange, n::Integer) = (first(xs)+n):last(xs) + + function DFSTree(nblocks::Int) + DFSTree( + Vector{BBNumber}(undef, nblocks), + zeros(DFSNumber, nblocks), + Vector{DFSNumber}(undef, nblocks)) + end + + function DFS(cfg::CFG, current_node::BBNumber)::DFSTree + dfs = DFSTree(length(cfg.blocks)) + # TODO: We could reuse the storage in DFSTree for our worklist. We're + # guaranteed for the worklist to be smaller than the remaining space in + # DFSTree + worklist = Tuple{DFSNumber, BBNumber}[(0, current_node)] + dfs_num = 1 + parent = 0 + while !isempty(worklist) + (parent, current_node) = pop!(worklist) + dfs.reverse[current_node] != 0 && continue + dfs.reverse[current_node] = dfs_num + dfs.numbering[dfs_num] = current_node + dfs.parents[dfs_num] = parent + for succ in cfg.blocks[current_node].succs + push!(worklist, (dfs_num, succ)) + end + dfs_num += 1 + end + # If all blocks are reachable, this is a no-op, otherwise, + # we shrink these arrays. + resize!(dfs.numbering, dfs_num - 1) + resize!(dfs.parents, dfs_num - 1) + dfs + end + + """ + Matches the snca_compress algorithm in Figure 2.8 of [LG05], with the + modification suggested in the paper to use `last_linked` to determine + whether an ancestor has been processed rather than storing `0` in the + ancestor array. + """ + function snca_compress!(state::Vector{Node}, ancestors::Vector{DFSNumber}, + v::DFSNumber, last_linked::DFSNumber) + u = ancestors[v] + @assert u < v + if u >= last_linked + snca_compress!(state, ancestors, u, last_linked) + if state[u].label < state[v].label + state[v] = Node(state[v].semi, state[u].label) + end + ancestors[v] = ancestors[u] + end + nothing + end + + function snca_compress_worklist!( + state::Vector{Node}, ancestors::Vector{DFSNumber}, + v::DFSNumber, last_linked::DFSNumber) + # TODO: There is a smarter way to do this + u = ancestors[v] + worklist = Tuple{DFSNumber, DFSNumber}[(u,v)] + @assert u < v + while !isempty(worklist) + u, v = last(worklist) + if u >= last_linked + if ancestors[u] >= last_linked + push!(worklist, (ancestors[u], u)) + continue + end + if state[u].label < state[v].label + state[v] = Node(state[v].semi, state[u].label) + end + ancestors[v] = ancestors[u] + end + pop!(worklist) + end + end + + """ + SNCA(cfg::CFG) + + Determines a map from basic blocks to the block which immediately dominate them. + Expressed as indexes into `cfg.blocks`. + + The main Semi-NCA algrithm. Matches Figure 2.8 in [LG05]. + Note that the pseudocode in [LG05] is not entirely accurate. + The best way to understand what's happening is to read [LT79], then the + description of SLT in in [LG05] (warning: inconsistent notation), then + the description of Semi-NCA. + """ + function SNCA(cfg::CFG) + D = DFS(cfg, BBNumber(1)) + # `label` is initialized to the identity mapping (though + # the paper doesn't make that clear). The rational for this is Lemma + # 2.4 in [LG05] (i.e. Theorem 4 in ). Note however, that we don't + # ever look at `semi` until it is fully initialized, so we could leave + # it uninitialized here if we wanted to. + state = Node[ Node(typemax(DFSNumber), w) for w in preorder(D) ] + # Initialize idoms to parents. Note that while idoms are eventually + # BB indexed, we keep it DFS indexed until a final post-processing + # pass to avoid extra memory references during the O(N^2) phase below. + idoms_dfs = copy(D.parents) + # We abuse the parents array as the ancestors array. + # Semi-NCA does not look at the parents array at all. + # SLT would, but never simultaneously, so we could still + # do this. + ancestors = D.parents + for w::DFSNumber ∈ reverse(_drop(preorder(D), 1)) + # LLVM initializes this to the parent, the paper initializes this to + # `w`, but it doesn't really matter (the parent is a predecessor, + # so at worst we'll discover it below). Save a memory reference here. + semi_w = typemax(DFSNumber) + for v ∈ cfg.blocks[D.numbering[w]].preds + # For the purpose of the domtree, ignore virtual predecessors + # into catch blocks. + v == 0 && continue + vdfs = D.reverse[v] + # Ignore unreachable predecessors + vdfs == 0 && continue + last_linked = DFSNumber(w + 1) + # N.B.: This conditional is missing from the psuedocode + # in figure 2.8 of [LG05]. It corresponds to the + # `ancestor[v] != 0` check in the `eval` implementation in + # figure 2.6 + if vdfs >= last_linked + # For performance, if the number of ancestors is small + # avoid the extra allocation of the worklist. + if length(ancestors) <= 32 + snca_compress!(state, ancestors, vdfs, last_linked) + else + snca_compress_worklist!(state, ancestors, vdfs, last_linked) + end + end + semi_w = min(semi_w, state[vdfs].label) + end + state[w] = Node(semi_w, semi_w) + end + for v ∈ _drop(preorder(D), 1) + idom = idoms_dfs[v] + vsemi = state[v].semi + while idom > vsemi + idom = idoms_dfs[idom] + end + idoms_dfs[v] = idom + end + # Reexpress the idom relationship in BB indexing + idoms_bb = Int[ (i == 1 || D.reverse[i] == 0) ? 0 : D.numbering[idoms_dfs[D.reverse[i]]] for i = 1:length(cfg.blocks) ] + idoms_bb + end +end diff --git a/base/compiler/ssair/driver.jl b/base/compiler/ssair/driver.jl new file mode 100644 index 0000000..cb05e7e --- /dev/null +++ b/base/compiler/ssair/driver.jl @@ -0,0 +1,133 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +using Core: LineInfoNode + +if false + import Base: Base, @show +else + macro show(s) + return :(println(stdout, $(QuoteNode(s)), " = ", $(esc(s)))) + end +end + +include("compiler/ssair/ir.jl") +include("compiler/ssair/domtree.jl") +include("compiler/ssair/slot2ssa.jl") +include("compiler/ssair/queries.jl") +include("compiler/ssair/passes.jl") +include("compiler/ssair/inlining.jl") +include("compiler/ssair/verify.jl") +include("compiler/ssair/legacy.jl") +#@isdefined(Base) && include("compiler/ssair/show.jl") + +function normalize_expr(stmt::Expr) + if stmt.head === :gotoifnot + return GotoIfNot(stmt.args[1], stmt.args[2]::Int) + elseif stmt.head === :return + return (length(stmt.args) == 0) ? ReturnNode(nothing) : ReturnNode(stmt.args[1]) + elseif stmt.head === :unreachable + return ReturnNode() + else + return stmt + end +end + +function normalize(@nospecialize(stmt), meta::Vector{Any}) + if isa(stmt, Expr) + if stmt.head === :meta + args = stmt.args + if length(args) > 0 + push!(meta, stmt) + end + return nothing + elseif stmt.head === :line + return nothing # deprecated - we shouldn't encounter this + else + return normalize_expr(stmt) + end + end + return stmt +end + +function just_construct_ssa(ci::CodeInfo, code::Vector{Any}, nargs::Int, sv::OptimizationState) + # Go through and add an unreachable node after every + # Union{} call. Then reindex labels. + idx = 1 + oldidx = 1 + changemap = fill(0, length(code)) + while idx <= length(code) + if code[idx] isa Expr && ci.ssavaluetypes[idx] === Union{} + if !(idx < length(code) && isexpr(code[idx+1], :unreachable)) + insert!(code, idx + 1, ReturnNode()) + insert!(ci.codelocs, idx + 1, ci.codelocs[idx]) + insert!(ci.ssavaluetypes, idx + 1, Union{}) + if oldidx < length(changemap) + changemap[oldidx + 1] = 1 + end + idx += 1 + end + end + idx += 1 + oldidx += 1 + end + renumber_ir_elements!(code, changemap) + + inbounds_depth = 0 # Number of stacked inbounds + meta = Any[] + flags = fill(0x00, length(code)) + for i = 1:length(code) + stmt = code[i] + if isexpr(stmt, :inbounds) + arg1 = stmt.args[1] + if arg1 === true # push + inbounds_depth += 1 + elseif arg1 === false # clear + inbounds_depth = 0 + elseif inbounds_depth > 0 # pop + inbounds_depth -= 1 + end + stmt = nothing + else + stmt = normalize(stmt, meta) + end + code[i] = stmt + if !(stmt === nothing) + if inbounds_depth > 0 + flags[i] |= IR_FLAG_INBOUNDS + end + end + end + strip_trailing_junk!(ci, code, flags) + cfg = compute_basic_blocks(code) + defuse_insts = scan_slot_def_use(nargs, ci, code) + @timeit "domtree 1" domtree = construct_domtree(cfg) + ir = let code = Any[nothing for _ = 1:length(code)] + IRCode(code, Any[], ci.codelocs, flags, cfg, collect(LineInfoNode, ci.linetable), sv.slottypes, meta, sv.sptypes) + end + @timeit "construct_ssa" ir = construct_ssa!(ci, code, ir, domtree, defuse_insts, nargs, sv.sptypes, sv.slottypes) + return ir +end + +function run_passes(ci::CodeInfo, nargs::Int, sv::OptimizationState) + ir = just_construct_ssa(ci, copy_exprargs(ci.code), nargs, sv) + #@Base.show ("after_construct", ir) + # TODO: Domsorting can produce an updated domtree - no need to recompute here + @timeit "compact 1" ir = compact!(ir) + @timeit "Inlining" ir = ssa_inlining_pass!(ir, ir.linetable, sv) + #@timeit "verify 2" verify_ir(ir) + ir = compact!(ir) + #@Base.show ("before_sroa", ir) + @timeit "domtree 2" domtree = construct_domtree(ir.cfg) + @timeit "SROA" ir = getfield_elim_pass!(ir, domtree) + #@Base.show ir.new_nodes + #@Base.show ("after_sroa", ir) + ir = adce_pass!(ir) + #@Base.show ("after_adce", ir) + @timeit "type lift" ir = type_lift_pass!(ir) + @timeit "compact 3" ir = compact!(ir) + #@Base.show ir + if JLOptions().debug_level == 2 + @timeit "verify 3" (verify_ir(ir); verify_linetable(ir.linetable)) + end + return ir +end diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl new file mode 100644 index 0000000..3287fa3 --- /dev/null +++ b/base/compiler/ssair/inlining.jl @@ -0,0 +1,1314 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +struct InvokeData + entry::Core.TypeMapEntry + types0 + min_valid::UInt + max_valid::UInt +end + +struct Signature + f::Any + ft::Any + atypes::Vector{Any} + atype::Type + Signature(f, ft, atypes) = new(f, ft, atypes) + Signature(f, ft, atypes, atype) = new(f, ft, atypes, atype) +end +with_atype(sig::Signature) = Signature(sig.f, sig.ft, sig.atypes, argtypes_to_type(sig.atypes)) + +struct InliningTodo + idx::Int # The statement to replace + # Properties of the call - these determine how arguments + # need to be rewritten. + isva::Bool + isinvoke::Bool + na::Int + method::Method # The method being inlined + sparams::Vector{Any} # The static parameters we computed for this call site + metharg # ::Type + # The LineTable and IR of the inlinee + linetable::Vector{LineInfoNode} + ir::IRCode + # If the function being inlined is a single basic block we can use a + # simpler inlining algorithm. This flag determines whether that's allowed + linear_inline_eligible::Bool +end +isinvoke(inl::InliningTodo) = inl.isinvoke + +struct ConstantCase + val::Any + method::Method + sparams::Vector{Any} + metharg::Any + ConstantCase(@nospecialize(val), method::Method, sparams::Vector{Any}, @nospecialize(metharg)) = + new(val, method, sparams, metharg) +end + +struct DynamicCase + method::Method + sparams::Vector{Any} + metharg::Any + DynamicCase(method::Method, sparams::Vector{Any}, @nospecialize(metharg)) = + new(method, sparams, metharg) +end + +struct UnionSplit + idx::Int # The statement to replace + fully_covered::Bool + atype # ::Type + cases::Vector{Pair{Any, Any}} + bbs::Vector{Int} + UnionSplit(idx::Int, fully_covered::Bool, @nospecialize(atype), + cases::Vector{Pair{Any, Any}}) = + new(idx, fully_covered, atype, cases, Int[]) +end +isinvoke(inl::UnionSplit) = false + +function ssa_inlining_pass!(ir::IRCode, linetable::Vector{LineInfoNode}, sv::OptimizationState) + # Go through the function, performing simple ininlingin (e.g. replacing call by constants + # and analyzing legality of inlining). + @timeit "analysis" todo = assemble_inline_todo!(ir, sv) + isempty(todo) && return ir + # Do the actual inlining for every call we identified + @timeit "execution" ir = batch_inline!(todo, ir, linetable, sv.src.propagate_inbounds) + return ir +end + +mutable struct CFGInliningState + new_cfg_blocks::Vector{BasicBlock} + inserted_block_ranges::Vector{UnitRange{Int}} + todo_bbs::Vector{Tuple{Int, Int}} + first_bb::Int + bb_rename::Vector{Int} + dead_blocks::Vector{Int} + split_targets::BitSet + merged_orig_blocks::BitSet + cfg::CFG +end + +function CFGInliningState(ir::IRCode) + CFGInliningState( + BasicBlock[], + UnitRange{Int}[], + Tuple{Int, Int}[], + 0, + zeros(Int, length(ir.cfg.blocks)), + Vector{Int}(), + BitSet(), + BitSet(), + ir.cfg + ) +end + +# Tells the inliner that we're now inlining into block `block`, meaning +# all previous blocks have been proceesed and can be added to the new cfg +function inline_into_block!(state::CFGInliningState, block::Int) + if state.first_bb != block + new_range = state.first_bb+1:block + l = length(state.new_cfg_blocks) + state.bb_rename[new_range] = (l+1:l+length(new_range)) + append!(state.new_cfg_blocks, map(copy, state.cfg.blocks[new_range])) + push!(state.merged_orig_blocks, last(new_range)) + end + state.first_bb = block + return +end + +function cfg_inline_item!(item::InliningTodo, state::CFGInliningState, from_unionsplit::Bool=false) + inlinee_cfg = item.ir.cfg + # Figure out if we need to split the BB + need_split_before = false + need_split = true + block = block_for_inst(state.cfg, item.idx) + inline_into_block!(state, block) + + if !isempty(inlinee_cfg.blocks[1].preds) + need_split_before = true + end + + last_block_idx = last(state.cfg.blocks[block].stmts) + if false # TODO: ((idx+1) == last_block_idx && isa(ir[SSAValue(last_block_idx)], GotoNode)) + need_split = false + post_bb_id = -ir[SSAValue(last_block_idx)].label + else + post_bb_id = length(state.new_cfg_blocks) + length(inlinee_cfg.blocks) + (need_split_before ? 1 : 0) + need_split = true #!(idx == last_block_idx) + end + + if !need_split + delete!(state.merged_orig_blocks, last(new_range)) + end + + push!(state.todo_bbs, (length(state.new_cfg_blocks) - 1 + (need_split_before ? 1 : 0), post_bb_id)) + + from_unionsplit || delete!(state.split_targets, length(state.new_cfg_blocks)) + orig_succs = copy(state.new_cfg_blocks[end].succs) + empty!(state.new_cfg_blocks[end].succs) + if need_split_before + l = length(state.new_cfg_blocks) + bb_rename_range = (1+l:length(inlinee_cfg.blocks)+l) + push!(state.new_cfg_blocks[end].succs, length(state.new_cfg_blocks)+1) + append!(state.new_cfg_blocks, inlinee_cfg.blocks) + else + # Merge the last block that was already there with the first block we're adding + l = length(state.new_cfg_blocks) + bb_rename_range = (l:length(inlinee_cfg.blocks)+l-1) + append!(state.new_cfg_blocks[end].succs, inlinee_cfg.blocks[1].succs) + append!(state.new_cfg_blocks, inlinee_cfg.blocks[2:end]) + end + if need_split + push!(state.new_cfg_blocks, BasicBlock(state.cfg.blocks[block].stmts, + Int[], orig_succs)) + from_unionsplit || push!(state.split_targets, length(state.new_cfg_blocks)) + end + new_block_range = (length(state.new_cfg_blocks)-length(inlinee_cfg.blocks)+1):length(state.new_cfg_blocks) + push!(state.inserted_block_ranges, new_block_range) + + # Fixup the edges of the newely added blocks + for (old_block, new_block) in enumerate(bb_rename_range) + if old_block != 1 || need_split_before + p = state.new_cfg_blocks[new_block].preds + let bb_rename_range = bb_rename_range + map!(p, p) do old_pred_block + return old_pred_block == 0 ? 0 : bb_rename_range[old_pred_block] + end + end + end + if new_block != last(new_block_range) + s = state.new_cfg_blocks[new_block].succs + let bb_rename_range = bb_rename_range + map!(s, s) do old_succ_block + return bb_rename_range[old_succ_block] + end + end + end + end + + if need_split_before + push!(state.new_cfg_blocks[first(bb_rename_range)].preds, first(bb_rename_range)-1) + end + + any_edges = false + for (old_block, new_block) in enumerate(bb_rename_range) + if (length(state.new_cfg_blocks[new_block].succs) == 0) + terminator_idx = last(inlinee_cfg.blocks[old_block].stmts) + terminator = item.ir[SSAValue(terminator_idx)] + if isa(terminator, ReturnNode) && isdefined(terminator, :val) + any_edges = true + push!(state.new_cfg_blocks[new_block].succs, post_bb_id) + if need_split + push!(state.new_cfg_blocks[post_bb_id].preds, new_block) + end + end + end + end + + if !any_edges + push!(state.dead_blocks, post_bb_id) + end +end + +function cfg_inline_unionsplit!(item::UnionSplit, state::CFGInliningState) + block = block_for_inst(state.cfg, item.idx) + inline_into_block!(state, block) + from_bbs = Int[] + delete!(state.split_targets, length(state.new_cfg_blocks)) + orig_succs = copy(state.new_cfg_blocks[end].succs) + empty!(state.new_cfg_blocks[end].succs) + for (i, (_, case)) in enumerate(item.cases) + # The condition gets sunk into the previous block + # Add a block for the union-split body + push!(state.new_cfg_blocks, BasicBlock(StmtRange(item.idx, item.idx))) + cond_bb = length(state.new_cfg_blocks)-1 + push!(state.new_cfg_blocks[end].preds, cond_bb) + push!(state.new_cfg_blocks[cond_bb].succs, cond_bb+1) + if isa(case, InliningTodo) && !case.linear_inline_eligible + cfg_inline_item!(case, state, true) + end + bb = length(state.new_cfg_blocks) + push!(from_bbs, bb) + # TODO: Right now we unconditionally generate a fallback block + # in case of subtyping errors - This is probably unnecessary. + if true # i != length(item.cases) || !item.fully_covered + # This block will have the next condition or the final else case + push!(state.new_cfg_blocks, BasicBlock(StmtRange(item.idx, item.idx))) + push!(state.new_cfg_blocks[cond_bb].succs, length(state.new_cfg_blocks)) + push!(state.new_cfg_blocks[end].preds, cond_bb) + push!(item.bbs, length(state.new_cfg_blocks)) + end + end + # The edge from the fallback block. + if !item.fully_covered + push!(from_bbs, length(state.new_cfg_blocks)) + end + # This block will be the block everyone returns to + push!(state.new_cfg_blocks, BasicBlock(StmtRange(item.idx, item.idx), from_bbs, orig_succs)) + join_bb = length(state.new_cfg_blocks) + push!(state.split_targets, join_bb) + push!(item.bbs, join_bb) + for bb in from_bbs + push!(state.new_cfg_blocks[bb].succs, join_bb) + end +end + +function finish_cfg_inline!(state::CFGInliningState) + new_range = (state.first_bb + 1):length(state.cfg.blocks) + l = length(state.new_cfg_blocks) + state.bb_rename[new_range] = (l+1:l+length(new_range)) + append!(state.new_cfg_blocks, state.cfg.blocks[new_range]) + + # Rename edges original bbs + for (orig_bb, bb) in pairs(state.bb_rename) + p, s = state.new_cfg_blocks[bb].preds, state.new_cfg_blocks[bb].succs + map!(p, p) do pred_bb + pred_bb == length(state.bb_rename) && return length(state.new_cfg_blocks) + return state.bb_rename[pred_bb + 1] - 1 + end + if !(orig_bb in state.merged_orig_blocks) + map!(s, s) do succ_bb + return state.bb_rename[succ_bb] + end + end + end + + for bb in collect(state.split_targets) + s = state.new_cfg_blocks[bb].succs + map!(s, s) do succ_bb + return state.bb_rename[succ_bb] + end + end + + # Rename any annotated original bb references + for bb in 1:length(state.new_cfg_blocks) + s = state.new_cfg_blocks[bb].succs + map!(s, s) do succ_bb + return succ_bb < 0 ? state.bb_rename[-succ_bb] : succ_bb + end + end + + # Kill dead blocks + for block in state.dead_blocks + for succ in state.new_cfg_blocks[block].succs + kill_edge!(state.new_cfg_blocks, block, succ) + end + end +end + +function ir_inline_item!(compact::IncrementalCompact, idx::Int, argexprs::Vector{Any}, + linetable::Vector{LineInfoNode}, item::InliningTodo, + boundscheck::Symbol, todo_bbs::Vector{Tuple{Int, Int}}) + # Ok, do the inlining here + inline_cfg = item.ir.cfg + stmt = compact.result[idx] + linetable_offset = length(linetable) + # Append the linetable of the inlined function to our line table + inlined_at = Int(compact.result_lines[idx]) + for entry in item.linetable + push!(linetable, LineInfoNode(entry.method, entry.file, entry.line, + (entry.inlined_at > 0 ? entry.inlined_at + linetable_offset : inlined_at))) + end + if item.isva + vararg = mk_tuplecall!(compact, argexprs[item.na:end], compact.result_lines[idx]) + argexprs = Any[argexprs[1:(item.na - 1)]..., vararg] + end + flag = compact.result_flags[idx] + boundscheck_idx = boundscheck + if boundscheck_idx === :default || boundscheck_idx === :propagate + if (flag & IR_FLAG_INBOUNDS) != 0 + boundscheck_idx = :off + end + end + # If the iterator already moved on to the next basic block, + # temporarily re-open in again. + local return_value + # Special case inlining that maintains the current basic block if there's only one BB in the target + if item.linear_inline_eligible + terminator = item.ir[SSAValue(last(inline_cfg.blocks[1].stmts))] + #compact[idx] = nothing + inline_compact = IncrementalCompact(compact, item.ir, compact.result_idx) + for (idx′, stmt′) in inline_compact + # This dance is done to maintain accurate usage counts in the + # face of rename_arguments! mutating in place - should figure out + # something better eventually. + inline_compact[idx′] = nothing + stmt′ = ssa_substitute!(idx′, stmt′, argexprs, item.method.sig, item.sparams, linetable_offset, boundscheck_idx, compact) + if isa(stmt′, ReturnNode) + isa(stmt′.val, SSAValue) && (compact.used_ssas[stmt′.val.id] += 1) + return_value = SSAValue(idx′) + inline_compact[idx′] = stmt′.val + val = stmt′.val + inline_compact.result_types[idx′] = (isa(val, Argument) || isa(val, Expr)) ? + compact_exprtype(compact, stmt′.val) : + compact_exprtype(inline_compact, stmt′.val) + break + end + inline_compact[idx′] = stmt′ + end + just_fixup!(inline_compact) + compact.result_idx = inline_compact.result_idx + else + bb_offset, post_bb_id = popfirst!(todo_bbs) + # This implements the need_split_before flag above + need_split_before = !isempty(item.ir.cfg.blocks[1].preds) + if need_split_before + finish_current_bb!(compact, 0) + end + pn = PhiNode() + #compact[idx] = nothing + inline_compact = IncrementalCompact(compact, item.ir, compact.result_idx) + for (idx′, stmt′) in inline_compact + inline_compact[idx′] = nothing + stmt′ = ssa_substitute!(idx′, stmt′, argexprs, item.method.sig, item.sparams, linetable_offset, boundscheck_idx, compact) + if isa(stmt′, ReturnNode) + if isdefined(stmt′, :val) + val = stmt′.val + # GlobalRefs can have side effects, but are currently + # allowed in arguments of ReturnNodes + push!(pn.edges, inline_compact.active_result_bb-1) + if isa(val, GlobalRef) || isa(val, Expr) + stmt′ = val + inline_compact.result_types[idx′] = (isa(val, Argument) || isa(val, Expr)) ? + compact_exprtype(compact, val) : + compact_exprtype(inline_compact, val) + insert_node_here!(inline_compact, GotoNode(post_bb_id), + Any, compact.result_lines[idx′], + true) + push!(pn.values, SSAValue(idx′)) + else + push!(pn.values, val) + stmt′ = GotoNode(post_bb_id) + end + + end + elseif isa(stmt′, GotoNode) + stmt′ = GotoNode(stmt′.label + bb_offset) + elseif isa(stmt′, Expr) && stmt′.head === :enter + stmt′ = Expr(:enter, stmt′.args[1] + bb_offset) + elseif isa(stmt′, GotoIfNot) + stmt′ = GotoIfNot(stmt′.cond, stmt′.dest + bb_offset) + elseif isa(stmt′, PhiNode) + stmt′ = PhiNode(Any[edge+bb_offset for edge in stmt′.edges], stmt′.values) + end + inline_compact[idx′] = stmt′ + end + just_fixup!(inline_compact) + compact.result_idx = inline_compact.result_idx + compact.active_result_bb = inline_compact.active_result_bb + for i = 1:length(pn.values) + isassigned(pn.values, i) || continue + if isa(pn.values[i], SSAValue) + compact.used_ssas[pn.values[i].id] += 1 + end + end + if length(pn.edges) == 1 + return_value = pn.values[1] + else + return_value = insert_node_here!(compact, pn, compact_exprtype(compact, SSAValue(idx)), compact.result_lines[idx]) + end + end + return_value +end + +const fatal_type_bound_error = ErrorException("fatal error in type inference (type bound)") + +function ir_inline_unionsplit!(compact::IncrementalCompact, idx::Int, + argexprs::Vector{Any}, linetable::Vector{LineInfoNode}, + item::UnionSplit, boundscheck::Symbol, todo_bbs::Vector{Tuple{Int, Int}}) + stmt, typ, line = compact.result[idx], compact.result_types[idx], compact.result_lines[idx] + atype = item.atype + generic_bb = item.bbs[end-1] + join_bb = item.bbs[end] + bb = compact.active_result_bb + pn = PhiNode() + has_generic = false + @assert length(item.bbs) > length(item.cases) + for ((metharg, case), next_cond_bb) in zip(item.cases, item.bbs) + @assert !isa(metharg, UnionAll) + cond = true + @assert length(atype.parameters) == length(metharg.parameters) + for i in 1:length(atype.parameters) + a, m = atype.parameters[i], metharg.parameters[i] + # If this is always true, we don't need to check for it + a <: m && continue + # Generate isa check + isa_expr = Expr(:call, isa, argexprs[i], m) + ssa = insert_node_here!(compact, isa_expr, Bool, line) + if cond === true + cond = ssa + else + and_expr = Expr(:call, and_int, cond, ssa) + cond = insert_node_here!(compact, and_expr, Bool, line) + end + end + insert_node_here!(compact, GotoIfNot(cond, next_cond_bb), Union{}, line) + bb = next_cond_bb - 1 + finish_current_bb!(compact, 0) + argexprs′ = argexprs + if !isa(case, ConstantCase) + argexprs′ = copy(argexprs) + for i = 1:length(metharg.parameters) + a, m = atype.parameters[i], metharg.parameters[i] + (isa(argexprs[i], SSAValue) || isa(argexprs[i], Argument)) || continue + if !(a <: m) + argexprs′[i] = insert_node_here!(compact, PiNode(argexprs′[i], m), + m, line) + end + end + end + if isa(case, InliningTodo) + val = ir_inline_item!(compact, idx, argexprs′, linetable, case, boundscheck, todo_bbs) + elseif isa(case, MethodInstance) + val = insert_node_here!(compact, Expr(:invoke, case, argexprs′...), typ, line) + else + case = case::ConstantCase + val = case.val + end + if !isempty(compact.result_bbs[bb].preds) + push!(pn.edges, bb) + push!(pn.values, val) + insert_node_here!(compact, GotoNode(join_bb), Union{}, line) + else + insert_node_here!(compact, ReturnNode(), Union{}, line) + end + finish_current_bb!(compact, 0) + end + bb += 1 + # We're now in the fall through block, decide what to do + if item.fully_covered + e = Expr(:call, GlobalRef(Core, :throw), fatal_type_bound_error) + insert_node_here!(compact, e, Union{}, line) + insert_node_here!(compact, ReturnNode(), Union{}, line) + finish_current_bb!(compact, 0) + else + ssa = insert_node_here!(compact, stmt, typ, line) + push!(pn.edges, bb) + push!(pn.values, ssa) + insert_node_here!(compact, GotoNode(join_bb), Union{}, line) + finish_current_bb!(compact, 0) + end + + # We're now in the join block. + compact.ssa_rename[compact.idx-1] = insert_node_here!(compact, pn, typ, line) + nothing +end + +function batch_inline!(todo::Vector{Any}, ir::IRCode, linetable::Vector{LineInfoNode}, propagate_inbounds::Bool) + # Compute the new CFG first (modulo statement ranges, which will be computed below) + state = CFGInliningState(ir) + for item in todo + if isa(item, UnionSplit) + cfg_inline_unionsplit!(item::UnionSplit, state) + else + item = item::InliningTodo + # A linear inline does not modify the CFG + item.linear_inline_eligible && continue + cfg_inline_item!(item, state) + end + end + finish_cfg_inline!(state) + + boundscheck = inbounds_option() + if boundscheck === :default && propagate_inbounds + boundscheck = :propagate + end + + let compact = IncrementalCompact(ir, false) + compact.result_bbs = state.new_cfg_blocks + # This needs to be a minimum and is more of a size hint + nn = 0 + for item in todo + if isa(item, InliningTodo) + nn += (length(item.ir.stmts) + length(item.ir.new_nodes)) + end + end + nnewnodes = length(compact.result) + nn + resize!(compact, nnewnodes) + item = popfirst!(todo) + inline_idx = item.idx + for (idx, stmt) in compact + if compact.idx - 1 == inline_idx + argexprs = copy(stmt.args) + refinish = false + if compact.result_idx == first(compact.result_bbs[compact.active_result_bb].stmts) + compact.active_result_bb -= 1 + refinish = true + end + # At the moment we will allow globalrefs in argument position, turn those into ssa values + for aidx in 1:length(argexprs) + aexpr = argexprs[aidx] + if isa(aexpr, GlobalRef) || isa(aexpr, Expr) + argexprs[aidx] = insert_node_here!(compact, aexpr, compact_exprtype(compact, aexpr), compact.result_lines[idx]) + end + end + if isinvoke(item) + argexprs = rewrite_invoke_exprargs!((node, typ)->insert_node_here!(compact, node, typ, compact.result_lines[idx]), + argexprs) + end + if isa(item, InliningTodo) + compact.ssa_rename[compact.idx-1] = ir_inline_item!(compact, idx, argexprs, linetable, item, boundscheck, state.todo_bbs) + elseif isa(item, UnionSplit) + ir_inline_unionsplit!(compact, idx, argexprs, linetable, item, boundscheck, state.todo_bbs) + end + compact[idx] = nothing + refinish && finish_current_bb!(compact, 0) + if !isempty(todo) + item = popfirst!(todo) + inline_idx = item.idx + else + inline_idx = -1 + end + elseif isa(stmt, GotoNode) + compact[idx] = GotoNode(state.bb_rename[stmt.label]) + elseif isa(stmt, Expr) && stmt.head === :enter + compact[idx] = Expr(:enter, state.bb_rename[stmt.args[1]]) + elseif isa(stmt, GotoIfNot) + compact[idx] = GotoIfNot(stmt.cond, state.bb_rename[stmt.dest]) + elseif isa(stmt, PhiNode) + compact[idx] = PhiNode(Any[edge == length(state.bb_rename) ? length(state.new_cfg_blocks) : state.bb_rename[edge+1]-1 for edge in stmt.edges], stmt.values) + end + end + + ir = finish(compact) + end + return ir +end + +function spec_lambda(@nospecialize(atype), sv::OptimizationState, @nospecialize(invoke_data)) + min_valid = UInt[typemin(UInt)] + max_valid = UInt[typemax(UInt)] + if invoke_data === nothing + mi = ccall(:jl_get_spec_lambda, Any, (Any, UInt, Ptr{UInt}, Ptr{UInt}), atype, sv.params.world, min_valid, max_valid) + else + invoke_data = invoke_data::InvokeData + atype <: invoke_data.types0 || return nothing + mi = ccall(:jl_get_invoke_lambda, Any, (Any, Any), invoke_data.entry, atype) + min_valid[1] = invoke_data.min_valid + max_valid[1] = invoke_data.max_valid + end + mi !== nothing && add_backedge!(mi::MethodInstance, sv) + update_valid_age!(min_valid[1], max_valid[1], sv) + return mi +end + +# This assumes the caller has verified that all arguments to the _apply call are Tuples. +function rewrite_apply_exprargs!(ir::IRCode, idx::Int, argexprs::Vector{Any}, atypes::Vector{Any}) + new_argexprs = Any[argexprs[2]] + new_atypes = Any[atypes[2]] + # loop over original arguments and flatten any known iterators + for i in 3:length(argexprs) + def = argexprs[i] + def_type = atypes[i] + if def_type isa PartialStruct + # def_type.typ <: Tuple is assumed + def_atypes = def_type.fields + else + def_atypes = Any[] + if isa(def_type, Const) # && isa(def_type.val, Union{Tuple, SimpleVector}) is implied + for p in def_type.val + push!(def_atypes, Const(p)) + end + else + ti = widenconst(def_type) + if ti.name === NamedTuple_typename + ti = ti.parameters[2] + end + for p in ti.parameters + if isa(p, DataType) && isdefined(p, :instance) + # replace singleton types with their equivalent Const object + p = Const(p.instance) + elseif isconstType(p) + p = Const(p.parameters[1]) + end + push!(def_atypes, p) + end + end + end + # now push flattened types into new_atypes and getfield exprs into new_argexprs + for j in 1:length(def_atypes) + def_atype = def_atypes[j] + if isa(def_atype, Const) && is_inlineable_constant(def_atype.val) + new_argexpr = quoted(def_atype.val) + else + new_call = Expr(:call, Core.getfield, def, j) + new_argexpr = insert_node!(ir, idx, def_atype, new_call) + end + push!(new_argexprs, new_argexpr) + push!(new_atypes, def_atype) + end + end + return new_argexprs, new_atypes +end + +function rewrite_invoke_exprargs!(inserter, argexprs::Vector{Any}) + argexpr0 = argexprs[2] + argexprs = argexprs[4:end] + pushfirst!(argexprs, argexpr0) + return argexprs +end + +function singleton_type(@nospecialize(ft)) + if isa(ft, Const) + return ft.val + elseif ft isa DataType && isdefined(ft, :instance) + return ft.instance + end + return nothing +end + +function analyze_method!(idx::Int, sig::Signature, @nospecialize(metharg), methsp::SimpleVector, + method::Method, stmt::Expr, sv::OptimizationState, + isinvoke::Bool, invoke_data::Union{InvokeData,Nothing}, @nospecialize(stmttyp)) + f, ft, atypes, atype_unlimited = sig.f, sig.ft, sig.atypes, sig.atype + methsig = method.sig + + # Check whether this call just evaluates to a constant + if isa(f, widenconst(ft)) && + isa(stmttyp, Const) && stmttyp.actual && is_inlineable_constant(stmttyp.val) + return ConstantCase(quoted(stmttyp.val), method, Any[methsp...], metharg) + end + + # Check that we habe the correct number of arguments + na = Int(method.nargs) + npassedargs = length(atypes) + if na != npassedargs && !(na > 0 && method.isva) + # we have a method match only because an earlier + # inference step shortened our call args list, even + # though we have too many arguments to actually + # call this function + return nothing + end + + # Check if we intersect any of this method's ambiguities + # TODO: We could split out the ambiguous case as another "union split" case. + # For now, we just reject the method + if method.ambig !== nothing + for entry::Core.TypeMapEntry in method.ambig + if typeintersect(sig.atype, entry.sig) !== Bottom + return nothing + end + end + end + + # Bail out if any static parameters are left as TypeVar + ok = true + for i = 1:length(methsp) + isa(methsp[i], TypeVar) && return nothing + end + + # See if there exists a specialization for this method signature + mi = specialize_method(method, metharg, methsp, true) # Union{Nothing, MethodInstance} + if !isa(mi, MethodInstance) + return spec_lambda(atype_unlimited, sv, invoke_data) + end + + isconst, src = find_inferred(mi, atypes, sv, stmttyp) + if isconst + add_backedge!(mi, sv) + return ConstantCase(src, method, Any[methsp...], metharg) + end + if src === nothing + return spec_lambda(atype_unlimited, sv, invoke_data) + end + + src_inferred = ccall(:jl_ast_flag_inferred, Bool, (Any,), src) + src_inlineable = ccall(:jl_ast_flag_inlineable, Bool, (Any,), src) + + if !(src_inferred && src_inlineable) + return spec_lambda(atype_unlimited, sv, invoke_data) + end + + # At this point we're committed to performing the inlining, add the backedge + add_backedge!(mi, sv) + + if !isa(src, CodeInfo) + src = ccall(:jl_uncompress_ast, Any, (Any, Ptr{Cvoid}, Any), method, C_NULL, src::Vector{UInt8})::CodeInfo + end + + @timeit "inline IR inflation" begin + ir2 = inflate_ir(src, mi) + # prepare inlining linetable with method instance information + inline_linetable = Vector{LineInfoNode}(undef, length(src.linetable)) + for i = 1:length(src.linetable) + entry = src.linetable[i] + if entry.inlined_at === 0 && entry.method === method + entry = LineInfoNode(mi, entry.file, entry.line, entry.inlined_at) + end + inline_linetable[i] = entry + end + end + #verify_ir(ir2) + + return InliningTodo(idx, + na > 0 && method.isva, + isinvoke, na, + method, Any[methsp...], metharg, + inline_linetable, ir2, linear_inline_eligible(ir2)) +end + +# Neither the product iterator not CartesianIndices are available +# here, so use this poor man's version +struct SimpleCartesian + ranges::Vector{UnitRange{Int}} +end +function iterate(s::SimpleCartesian, state::Vector{Int}=Int[1 for _ in 1:length(s.ranges)]) + state[end] > last(s.ranges[end]) && return nothing + vals = copy(state) + any = false + for i = 1:length(s.ranges) + if state[i] < last(s.ranges[i]) + for j = 1:(i-1) + state[j] = first(s.ranges[j]) + end + state[i] += 1 + any = true + break + end + end + if !any + state[end] += 1 + end + (vals, state) +end + +# Given a signure, iterate over the signatures to union split over +struct UnionSplitSignature + it::SimpleCartesian + typs::Vector{Any} +end + +function UnionSplitSignature(atypes::Vector{Any}) + typs = Any[uniontypes(widenconst(atypes[i])) for i = 1:length(atypes)] + ranges = UnitRange{Int}[1:length(typs[i]) for i = 1:length(typs)] + return UnionSplitSignature(SimpleCartesian(ranges), typs) +end + +function iterate(split::UnionSplitSignature, state::Vector{Int}...) + y = iterate(split.it, state...) + y === nothing && return nothing + idxs, state = y + sig = Any[split.typs[i][j] for (i, j) in enumerate(idxs)] + return (sig, state) +end + +function handle_single_case!(ir::IRCode, stmt::Expr, idx::Int, @nospecialize(case), isinvoke::Bool, todo::Vector{Any}, sv::OptimizationState) + if isa(case, ConstantCase) + ir[SSAValue(idx)] = case.val + elseif isa(case, MethodInstance) + if isinvoke + stmt.args = rewrite_invoke_exprargs!( + (node, typ)->insert_node!(ir, idx, typ, node), + stmt.args) + end + stmt.head = :invoke + pushfirst!(stmt.args, case) + elseif case === nothing + # Do, well, nothing + else + push!(todo, case::InliningTodo) + end + nothing +end + +function is_valid_type_for_apply_rewrite(@nospecialize(typ), params::Params) + if isa(typ, Const) && isa(typ.val, SimpleVector) + length(typ.val) > params.MAX_TUPLE_SPLAT && return false + for p in typ.val + is_inlineable_constant(p) || return false + end + return true + end + typ = widenconst(typ) + if isa(typ, DataType) && typ.name === NamedTuple_typename + typ = typ.parameters[2] + while isa(typ, TypeVar) + typ = typ.ub + end + end + isa(typ, DataType) || return false + if typ.name === Tuple.name + return !isvatuple(typ) && length(typ.parameters) <= params.MAX_TUPLE_SPLAT + else + return false + end +end + +function inline_splatnew!(ir::IRCode, idx::Int) + stmt = ir.stmts[idx] + ty = ir.types[idx] + nf = nfields_tfunc(ty) + if nf isa Const + eargs = stmt.args + tup = eargs[2] + tt = argextype(tup, ir, ir.sptypes) + tnf = nfields_tfunc(tt) + # TODO: hoisting this tnf.val == nf.val check into codegen + # would enable us to almost always do this transform + if tnf isa Const && tnf.val == nf.val + n = tnf.val + new_argexprs = Any[eargs[1]] + for j = 1:n + atype = getfield_tfunc(tt, Const(j)) + new_call = Expr(:call, Core.getfield, tup, j) + new_argexpr = insert_node!(ir, idx, atype, new_call) + push!(new_argexprs, new_argexpr) + end + stmt.head = :new + stmt.args = new_argexprs + end + end + nothing +end + +function call_sig(ir::IRCode, stmt::Expr) + isempty(stmt.args) && return nothing + ft = argextype(stmt.args[1], ir, ir.sptypes) + has_free_typevars(ft) && return nothing + f = singleton_type(ft) + f === Core.Intrinsics.llvmcall && return nothing + f === Core.Intrinsics.cglobal && return nothing + + atypes = Vector{Any}(undef, length(stmt.args)) + atypes[1] = ft + ok = true + for i = 2:length(stmt.args) + a = argextype(stmt.args[i], ir, ir.sptypes) + (a === Bottom || isvarargtype(a)) && return nothing + atypes[i] = a + end + + Signature(f, ft, atypes) +end + +function inline_apply!(ir::IRCode, idx::Int, sig::Signature, params::Params) + stmt = ir.stmts[idx] + while sig.f === Core._apply + atypes = sig.atypes + # Try to figure out the signature of the function being called + # and if rewrite_apply_exprargs can deal with this form + for i = 3:length(atypes) + # TODO: We could basically run the iteration protocol here + if !is_valid_type_for_apply_rewrite(atypes[i], params) + return nothing + end + end + # Independent of whether we can inline, the above analysis allows us to rewrite + # this apply call to a regular call + ft = atypes[2] + if length(atypes) == 3 && ft isa Const && ft.val === Core.tuple && atypes[3] ⊑ Tuple + # rewrite `((t::Tuple)...,)` to `t` + ir.stmts[idx] = stmt.args[3] + return nothing + end + stmt.args, atypes = rewrite_apply_exprargs!(ir, idx, stmt.args, atypes) + has_free_typevars(ft) && return nothing + f = singleton_type(ft) + sig = Signature(f, ft, atypes) + end + sig +end + +# TODO: this test is wrong if we start to handle Unions of function types later +is_builtin(s::Signature) = + isa(s.f, IntrinsicFunction) || + s.ft ⊑ IntrinsicFunction || + isa(s.f, Builtin) || + s.ft ⊑ Builtin + +function inline_invoke!(ir::IRCode, idx::Int, sig::Signature, invoke_data::InvokeData, sv::OptimizationState, todo::Vector{Any}) + stmt = ir.stmts[idx] + calltype = ir.types[idx] + method = invoke_data.entry.func + (metharg, methsp) = ccall(:jl_type_intersection_with_env, Any, (Any, Any), + sig.atype, method.sig)::SimpleVector + methsp = methsp::SimpleVector + result = analyze_method!(idx, sig, metharg, methsp, method, stmt, sv, true, invoke_data, + calltype) + handle_single_case!(ir, stmt, idx, result, true, todo, sv) + update_valid_age!(invoke_data.min_valid, invoke_data.max_valid, sv) + return nothing +end + +# Handles all analysis and inlining of intrinsics and builtins. In particular, +# this method does not access the method table or otherwise process generic +# functions. +function process_simple!(ir::IRCode, idx::Int, params::Params) + stmt = ir.stmts[idx] + stmt isa Expr || return nothing + if stmt.head === :splatnew + inline_splatnew!(ir, idx) + return nothing + end + + stmt.head === :call || return nothing + + sig = call_sig(ir, stmt) + sig === nothing && return nothing + + # Handle _apply + sig = inline_apply!(ir, idx, sig, params) + sig === nothing && return nothing + + # Check if we match any of the early inliners + calltype = ir.types[idx] + res = early_inline_special_case(ir, sig, stmt, params, calltype) + if res !== nothing + ir.stmts[idx] = res + return nothing + end + + # Bail out here if inlining is disabled + params.inlining || return nothing + + # Handle invoke + invoke_data = nothing + if sig.f === Core.invoke && length(sig.atypes) >= 3 + res = compute_invoke_data(sig.atypes, params) + res === nothing && return nothing + (sig, invoke_data) = res + elseif is_builtin(sig) + # No inlining for builtins (other than what was previously handled) + return nothing + end + + sig = with_atype(sig) + + # In :invoke, make sure that the arguments we're passing are a subtype of the + # signature we're invoking. + (invoke_data === nothing || sig.atype <: invoke_data.types0) || return nothing + + # Special case inliners for regular functions + if late_inline_special_case!(ir, sig, idx, stmt) || is_return_type(sig.f) + return nothing + end + return (sig, invoke_data) +end + +function assemble_inline_todo!(ir::IRCode, sv::OptimizationState) + # todo = (inline_idx, (isva, isinvoke, na), method, spvals, inline_linetable, inline_ir, lie) + todo = Any[] + for idx in 1:length(ir.stmts) + r = process_simple!(ir, idx, sv.params) + r === nothing && continue + + stmt = ir.stmts[idx] + calltype = ir.types[idx] + (sig, invoke_data) = r + + # Ok, now figure out what method to call + if invoke_data !== nothing + inline_invoke!(ir, idx, sig, invoke_data, sv, todo) + continue + end + + # Regular case: Perform method matching + min_valid = UInt[typemin(UInt)] + max_valid = UInt[typemax(UInt)] + meth = _methods_by_ftype(sig.atype, sv.params.MAX_METHODS, sv.params.world, min_valid, max_valid) + if meth === false || length(meth) == 0 + # No applicable method, or too many applicable methods + continue + end + update_valid_age!(min_valid[1], max_valid[1], sv) + + cases = Pair{Any, Any}[] + # TODO: This could be better + signature_union = Union{Any[match[1]::Type for match in meth]...} + signature_fully_covered = sig.atype <: signature_union + fully_covered = signature_fully_covered + split_out_sigs = Any[] + + # For any method match that's a dispatch tuple, extract those cases first + for (i, match) in enumerate(meth) + (metharg, methsp, method) = (match[1]::Type, match[2]::SimpleVector, match[3]::Method) + if !isdispatchtuple(metharg) + fully_covered = false + continue + end + case_sig = Signature(sig.f, sig.ft, sig.atypes, metharg) + case = analyze_method!(idx, case_sig, metharg, methsp, method, + stmt, sv, false, nothing, calltype) + if case === nothing + fully_covered = false + continue + end + push!(cases, Pair{Any,Any}(metharg, case)) + push!(split_out_sigs, metharg) + end + + # Now, if profitable union split the atypes into dispatch tuples and match the appropriate method + nu = countunionsplit(sig.atypes) + if nu != 1 && nu <= sv.params.MAX_UNION_SPLITTING + fully_covered = true + for union_sig in UnionSplitSignature(sig.atypes) + metharg′ = argtypes_to_type(union_sig) + if !isdispatchtuple(metharg′) + fully_covered = false + continue + elseif _any(x->x === metharg′, split_out_sigs) + continue + end + # `meth` is in specificity order, so find the first applicable method + found_any = false + for (i, match) in enumerate(meth) + (metharg, methsp, method) = (match[1]::Type, match[2]::SimpleVector, match[3]::Method) + metharg′ <: method.sig || continue + case_sig = Signature(sig.f, sig.ft, sig.atypes, metharg′) + case = analyze_method!(idx, case_sig, metharg′, methsp, method, stmt, sv, false, nothing, + calltype) + if case !== nothing + found_any = true + push!(cases, Pair{Any,Any}(metharg′, case)) + end + break + end + if !found_any + fully_covered = false + continue + end + end + end + + # If we're fully covered and there's only one applicable method, + # we inline, even if the signature is not a dispatch tuple + if signature_fully_covered && length(cases) == 0 && length(meth) == 1 + metharg = meth[1][1]::Type + methsp = meth[1][2]::SimpleVector + method = meth[1][3]::Method + fully_covered = true + case = analyze_method!(idx, sig, metharg, methsp, method, + stmt, sv, false, nothing, calltype) + case === nothing && continue + push!(cases, Pair{Any,Any}(metharg, case)) + end + + # If we only have one case and that case is fully covered, we may either + # be able to do the inlining now (for constant cases), or push it directly + # onto the todo list + if fully_covered && length(cases) == 1 + handle_single_case!(ir, stmt, idx, cases[1][2], false, todo, sv) + continue + end + length(cases) == 0 && continue + push!(todo, UnionSplit(idx, fully_covered, sig.atype, cases)) + end + todo +end + +function mk_tuplecall!(compact::IncrementalCompact, args::Vector{Any}, line_idx::Int32) + e = Expr(:call, TOP_TUPLE, args...) + etyp = tuple_tfunc(Any[compact_exprtype(compact, args[i]) for i in 1:length(args)]) + return insert_node_here!(compact, e, etyp, line_idx) +end + +function linear_inline_eligible(ir::IRCode) + length(ir.cfg.blocks) == 1 || return false + terminator = ir[SSAValue(last(ir.cfg.blocks[1].stmts))] + isa(terminator, ReturnNode) || return false + isdefined(terminator, :val) || return false + return true +end + +function compute_invoke_data(@nospecialize(atypes), params::Params) + ft = widenconst(atypes[2]) + if !isdispatchelem(ft) || has_free_typevars(ft) || (ft <: Builtin) + # TODO: this can be rather aggressive at preventing inlining of closures + # but we need to check that `ft` can't have a subtype at runtime before using the supertype lookup below + return nothing + end + invoke_tt = widenconst(atypes[3]) + if !isType(invoke_tt) || has_free_typevars(invoke_tt) + return nothing + end + invoke_tt = invoke_tt.parameters[1] + if !(isa(unwrap_unionall(invoke_tt), DataType) && invoke_tt <: Tuple) + return nothing + end + invoke_types = rewrap_unionall(Tuple{ft, unwrap_unionall(invoke_tt).parameters...}, invoke_tt) + min_valid = UInt[typemin(UInt)] + max_valid = UInt[typemax(UInt)] + invoke_entry = ccall(:jl_gf_invoke_lookup, Any, (Any, UInt), + invoke_types, params.world) # XXX: min_valid, max_valid + invoke_entry === nothing && return nothing + invoke_data = InvokeData(invoke_entry::Core.TypeMapEntry, invoke_types, min_valid[1], max_valid[1]) + atype0 = atypes[2] + atypes = atypes[4:end] + pushfirst!(atypes, atype0) + f = singleton_type(ft) + return (Signature(f, ft, atypes), invoke_data) +end + +# Check for a number of functions known to be pure +function ispuretopfunction(@nospecialize(f)) + return istopfunction(f, :typejoin) || + istopfunction(f, :isbits) || + istopfunction(f, :isbitstype) || + istopfunction(f, :promote_type) +end + +function early_inline_special_case(ir::IRCode, s::Signature, e::Expr, params::Params, + @nospecialize(etype)) + f, ft, atypes = s.f, s.ft, s.atypes + if (f === typeassert || ft ⊑ typeof(typeassert)) && length(atypes) == 3 + # typeassert(x::S, T) => x, when S<:T + a3 = atypes[3] + if (isType(a3) && !has_free_typevars(a3) && atypes[2] ⊑ a3.parameters[1]) || + (isa(a3, Const) && isa(a3.val, Type) && atypes[2] ⊑ a3.val) + val = e.args[2] + val === nothing && return QuoteNode(val) + return val + end + end + + if params.inlining + if isa(etype, Const) # || isconstType(etype) + val = etype.val + is_inlineable_constant(val) || return nothing + if isa(f, IntrinsicFunction) + if is_pure_intrinsic_infer(f) && + (intrinsic_nothrow(f) || intrinsic_nothrow(f, atypes[2:end])) + return quoted(val) + end + elseif ispuretopfunction(f) || contains_is(_PURE_BUILTINS, f) + return quoted(val) + elseif contains_is(_PURE_OR_ERROR_BUILTINS, f) + if _builtin_nothrow(f, atypes[2:end], etype) + return quoted(val) + end + end + end + end + + return nothing +end + +function late_inline_special_case!(ir::IRCode, sig::Signature, idx::Int, stmt::Expr) + typ = ir.types[idx] + f, ft, atypes = sig.f, sig.ft, sig.atypes + if length(atypes) == 3 && istopfunction(f, :!==) + # special-case inliner for !== that precedes _methods_by_ftype union splitting + # and that works, even though inference generally avoids inferring the `!==` Method + if isa(typ, Const) + ir[SSAValue(idx)] = quoted(typ.val) + return true + end + cmp_call = Expr(:call, GlobalRef(Core, :(===)), stmt.args[2], stmt.args[3]) + cmp_call_ssa = insert_node!(ir, idx, Bool, cmp_call) + not_call = Expr(:call, GlobalRef(Core.Intrinsics, :not_int), cmp_call_ssa) + ir[SSAValue(idx)] = not_call + return true + elseif length(atypes) == 3 && istopfunction(f, :(>:)) + # special-case inliner for issupertype + # that works, even though inference generally avoids inferring the `>:` Method + if isa(typ, Const) + ir[SSAValue(idx)] = quoted(typ.val) + return true + end + subtype_call = Expr(:call, GlobalRef(Core, :(<:)), stmt.args[3], stmt.args[2]) + ir[SSAValue(idx)] = subtype_call + return true + elseif is_return_type(f) + if isconstType(typ) + ir[SSAValue(idx)] = quoted(typ.parameters[1]) + return true + elseif isa(typ, Const) + ir[SSAValue(idx)] = quoted(typ.val) + return true + end + end + return false +end + +function ssa_substitute!(idx::Int, @nospecialize(val), arg_replacements::Vector{Any}, + @nospecialize(spsig), spvals::Vector{Any}, + linetable_offset::Int, boundscheck::Symbol, compact::IncrementalCompact) + compact.result_flags[idx] &= ~IR_FLAG_INBOUNDS + compact.result_lines[idx] += linetable_offset + return ssa_substitute_op!(val, arg_replacements, spsig, spvals, boundscheck) +end + +function ssa_substitute_op!(@nospecialize(val), arg_replacements::Vector{Any}, + @nospecialize(spsig), spvals::Vector{Any}, boundscheck::Symbol) + if isa(val, Argument) + return arg_replacements[val.n] + end + if isa(val, Expr) + e = val::Expr + head = e.head + if head === :static_parameter + return quoted(spvals[e.args[1]]) + elseif head === :cfunction + @assert !isa(spsig, UnionAll) || !isempty(spvals) + e.args[3] = ccall(:jl_instantiate_type_in_env, Any, (Any, Any, Ptr{Any}), e.args[3], spsig, spvals) + e.args[4] = svec(Any[ + ccall(:jl_instantiate_type_in_env, Any, (Any, Any, Ptr{Any}), argt, spsig, spvals) + for argt + in e.args[4] ]...) + elseif head === :foreigncall + @assert !isa(spsig, UnionAll) || !isempty(spvals) + for i = 1:length(e.args) + if i == 2 + e.args[2] = ccall(:jl_instantiate_type_in_env, Any, (Any, Any, Ptr{Any}), e.args[2], spsig, spvals) + elseif i == 3 + argtuple = Any[ + ccall(:jl_instantiate_type_in_env, Any, (Any, Any, Ptr{Any}), argt, spsig, spvals) + for argt + in e.args[3] ] + e.args[3] = svec(argtuple...) + end + end + elseif head === :boundscheck + if boundscheck === :off # inbounds == true + return false + elseif boundscheck === :propagate + return e + else # on or default + return true + end + end + end + urs = userefs(val) + for op in urs + op[] = ssa_substitute_op!(op[], arg_replacements, spsig, spvals, boundscheck) + end + return urs[] +end + +function find_inferred(mi::MethodInstance, @nospecialize(atypes), sv::OptimizationState, @nospecialize(rettype)) + # see if the method has a InferenceResult in the current cache + # or an existing inferred code info store in `.inferred` + haveconst = false + for i in 1:length(atypes) + if has_nontrivial_const_info(atypes[i]) + # have new information from argtypes that wasn't available from the signature + haveconst = true + break + end + end + if haveconst || improvable_via_constant_propagation(rettype) + inf_result = cache_lookup(mi, atypes, sv.params.cache) # Union{Nothing, InferenceResult} + else + inf_result = nothing + end + #XXX: update_valid_age!(min_valid[1], max_valid[1], sv) + if isa(inf_result, InferenceResult) + let inferred_src = inf_result.src + if isa(inferred_src, CodeInfo) + return svec(false, inferred_src) + end + if isa(inferred_src, Const) && is_inlineable_constant(inferred_src.val) + return svec(true, quoted(inferred_src.val),) + end + end + end + + linfo = inf_for_methodinstance(mi, sv.params.world) + if linfo isa CodeInstance + if invoke_api(linfo) == 2 + # in this case function can be inlined to a constant + return svec(true, quoted(linfo.rettype_const)) + end + return svec(false, linfo.inferred) + end + return svec(false, nothing) +end diff --git a/base/compiler/ssair/ir.jl b/base/compiler/ssair/ir.jl new file mode 100644 index 0000000..4518420 --- /dev/null +++ b/base/compiler/ssair/ir.jl @@ -0,0 +1,1316 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +@inline isexpr(@nospecialize(stmt), head::Symbol) = isa(stmt, Expr) && stmt.head === head +@eval Core.UpsilonNode() = $(Expr(:new, Core.UpsilonNode)) +Core.PhiNode() = Core.PhiNode(Any[], Any[]) + +struct Argument + n::Int +end + +struct GotoIfNot + cond::Any + dest::Int + GotoIfNot(@nospecialize(cond), dest::Int) = new(cond, dest) +end + +struct ReturnNode + val::Any + ReturnNode(@nospecialize(val)) = new(val) + # unassigned val indicates unreachable + ReturnNode() = new() +end + +""" +Like UnitRange{Int}, but can handle the `last` field, being temporarily +< first (this can happen during compacting) +""" +struct StmtRange <: AbstractUnitRange{Int} + start::Int + stop::Int +end +first(r::StmtRange) = r.start +last(r::StmtRange) = r.stop +iterate(r::StmtRange, state=0) = (last(r) - first(r) < state) ? nothing : (first(r) + state, state + 1) + +StmtRange(range::UnitRange{Int}) = StmtRange(first(range), last(range)) + +struct BasicBlock + stmts::StmtRange + preds::Vector{Int} + succs::Vector{Int} +end +function BasicBlock(stmts::StmtRange) + return BasicBlock(stmts, Int[], Int[]) +end +function BasicBlock(old_bb, stmts) + return BasicBlock(stmts, old_bb.preds, old_bb.succs) +end +copy(bb::BasicBlock) = BasicBlock(bb.stmts, copy(bb.preds), copy(bb.succs)) + +struct CFG + blocks::Vector{BasicBlock} + index::Vector{Int} # map from instruction => basic-block number + # TODO: make this O(1) instead of O(log(n_blocks))? +end +copy(c::CFG) = CFG(BasicBlock[copy(b) for b in c.blocks], copy(c.index)) + +function block_for_inst(index::Vector{Int}, inst::Int) + return searchsortedfirst(index, inst, lt=(<=)) +end +block_for_inst(cfg::CFG, inst::Int) = block_for_inst(cfg.index, inst) + +function basic_blocks_starts(stmts::Vector{Any}) + jump_dests = BitSet() + push!(jump_dests, 1) # function entry point + # First go through and compute jump destinations + for idx in 1:length(stmts) + stmt = stmts[idx] + # Terminators + if isa(stmt, GotoIfNot) + push!(jump_dests, idx+1) + push!(jump_dests, stmt.dest) + elseif isa(stmt, ReturnNode) + idx < length(stmts) && push!(jump_dests, idx+1) + elseif isa(stmt, GotoNode) + # This is a fake dest to force the next stmt to start a bb + idx < length(stmts) && push!(jump_dests, idx+1) + push!(jump_dests, stmt.label) + elseif isa(stmt, Expr) + if stmt.head === :leave + # :leave terminates a BB + push!(jump_dests, idx+1) + elseif stmt.head === :enter + # :enter starts/ends a BB + push!(jump_dests, idx) + push!(jump_dests, idx+1) + # The catch block is a jump dest + push!(jump_dests, stmt.args[1]::Int) + elseif stmt.head === :gotoifnot + # also tolerate expr form of IR + push!(jump_dests, idx+1) + push!(jump_dests, stmt.args[2]::Int) + elseif stmt.head === :return + # also tolerate expr form of IR + # This is a fake dest to force the next stmt to start a bb + idx < length(stmts) && push!(jump_dests, idx+1) + end + end + if isa(stmt, PhiNode) + for edge in stmt.edges + if edge === idx - 1 + push!(jump_dests, idx) + end + end + end + end + # and add add one more basic block start after the last statement + for i = length(stmts):-1:1 + if stmts[i] !== nothing + push!(jump_dests, i+1) + break + end + end + return jump_dests +end + +function compute_basic_blocks(stmts::Vector{Any}) + bb_starts = basic_blocks_starts(stmts) + # Compute ranges + pop!(bb_starts, 1) + basic_block_index = collect(bb_starts) + blocks = BasicBlock[] + sizehint!(blocks, length(basic_block_index)) + let first = 1 + for last in basic_block_index + push!(blocks, BasicBlock(StmtRange(first, last - 1))) + first = last + end + end + # Compute successors/predecessors + for (num, b) in enumerate(blocks) + terminator = stmts[last(b.stmts)] + if isa(terminator, ReturnNode) || isexpr(terminator, :return) + # return never has any successors + continue + end + if isa(terminator, GotoNode) + block′ = block_for_inst(basic_block_index, terminator.label) + push!(blocks[block′].preds, num) + push!(b.succs, block′) + continue + end + # Conditional Branch + if isa(terminator, GotoIfNot) + block′ = block_for_inst(basic_block_index, terminator.dest) + if block′ == num + 1 + # This GotoIfNot acts like a noop - treat it as such. + # We will drop it during SSA renaming + else + push!(blocks[block′].preds, num) + push!(b.succs, block′) + end + elseif isa(terminator, Expr) + if terminator.head === :enter + # :enter gets a virtual edge to the exception handler and + # the exception handler gets a virtual edge from outside + # the function. + # See the devdocs on exception handling in SSA form (or + # bug Keno to write them, if you're reading this and they + # don't exist) + block′ = block_for_inst(basic_block_index, terminator.args[1]::Int) + push!(blocks[block′].preds, num) + push!(blocks[block′].preds, 0) + push!(b.succs, block′) + elseif terminator.head === :gotoifnot + block′ = block_for_inst(basic_block_index, terminator.args[2]::Int) + if block′ == num + 1 + # This GotoIfNot acts like a noop - treat it as such. + # We will drop it during SSA renaming + else + push!(blocks[block′].preds, num) + push!(b.succs, block′) + end + end + end + # statement fall-through + if num + 1 <= length(blocks) + push!(blocks[num + 1].preds, num) + push!(b.succs, num + 1) + end + end + return CFG(blocks, basic_block_index) +end + +function first_insert_for_bb(code, cfg::CFG, block::Int) + for idx in cfg.blocks[block].stmts + stmt = code[idx] + if !isa(stmt, PhiNode) + return idx + end + end +end + +struct NewNode + # Insertion position (interpretation depends on which array this is in) + pos::Int + # Place the new instruction after this instruction (but in the same BB if this is an implicit terminator) + attach_after::Bool + # The type of the instruction to insert + typ::Any + # The node itself + node::Any + # The index into the line number table of this entry + line::Int32 + + NewNode(pos::Int, attach_after::Bool, @nospecialize(typ), @nospecialize(node), line::Int32) = + new(pos, attach_after, typ, node, line) +end + +struct IRCode + stmts::Vector{Any} + types::Vector{Any} + lines::Vector{Int32} + flags::Vector{UInt8} + argtypes::Vector{Any} + sptypes::Vector{Any} + linetable::Vector{LineInfoNode} + cfg::CFG + new_nodes::Vector{NewNode} + meta::Vector{Any} + + function IRCode(stmts::Vector{Any}, types::Vector{Any}, lines::Vector{Int32}, flags::Vector{UInt8}, + cfg::CFG, linetable::Vector{LineInfoNode}, argtypes::Vector{Any}, meta::Vector{Any}, + sptypes::Vector{Any}) + return new(stmts, types, lines, flags, argtypes, sptypes, linetable, cfg, NewNode[], meta) + end + function IRCode(ir::IRCode, stmts::Vector{Any}, types::Vector{Any}, lines::Vector{Int32}, flags::Vector{UInt8}, + cfg::CFG, new_nodes::Vector{NewNode}) + return new(stmts, types, lines, flags, ir.argtypes, ir.sptypes, ir.linetable, cfg, new_nodes, ir.meta) + end +end +copy(code::IRCode) = IRCode(code, copy_exprargs(code.stmts), copy(code.types), + copy(code.lines), copy(code.flags), copy(code.cfg), copy(code.new_nodes)) + +function getindex(x::IRCode, s::SSAValue) + if s.id <= length(x.stmts) + return x.stmts[s.id] + else + return x.new_nodes[s.id - length(x.stmts)].node + end +end + +function setindex!(x::IRCode, @nospecialize(repl), s::SSAValue) + @assert s.id <= length(x.stmts) + x.stmts[s.id] = repl + nothing +end + + +struct OldSSAValue + id::Int +end + +struct NewSSAValue + id::Int +end + +const AnySSAValue = Union{SSAValue, OldSSAValue, NewSSAValue} + +mutable struct UseRef + stmt::Any + op::Int + UseRef(@nospecialize(a)) = new(a, 0) +end +struct UseRefIterator + use::Tuple{UseRef, Nothing} + relevant::Bool + UseRefIterator(@nospecialize(a), relevant::Bool) = new((UseRef(a), nothing), relevant) +end +getindex(it::UseRefIterator) = it.use[1].stmt + +# TODO: stack-allocation +#struct UseRef +# urs::UseRefIterator +# use::Int +#end + +struct OOBToken +end + +struct UndefToken +end + +function getindex(x::UseRef) + stmt = x.stmt + if isa(stmt, Expr) && stmt.head === :(=) + rhs = stmt.args[2] + if isa(rhs, Expr) + if is_relevant_expr(rhs) + x.op > length(rhs.args) && return OOBToken() + return rhs.args[x.op] + end + end + x.op == 1 || return OOBToken() + return rhs + elseif isa(stmt, Expr) # @assert is_relevant_expr(stmt) + x.op > length(stmt.args) && return OOBToken() + return stmt.args[x.op] + elseif isa(stmt, GotoIfNot) + x.op == 1 || return OOBToken() + return stmt.cond + elseif isa(stmt, ReturnNode) + isdefined(stmt, :val) || return OOBToken() + x.op == 1 || return OOBToken() + return stmt.val + elseif isa(stmt, PiNode) + isdefined(stmt, :val) || return OOBToken() + x.op == 1 || return OOBToken() + return stmt.val + elseif isa(stmt, UpsilonNode) + isdefined(stmt, :val) || return OOBToken() + x.op == 1 || return OOBToken() + return stmt.val + elseif isa(stmt, PhiNode) + x.op > length(stmt.values) && return OOBToken() + isassigned(stmt.values, x.op) || return UndefToken() + return stmt.values[x.op] + elseif isa(stmt, PhiCNode) + x.op > length(stmt.values) && return OOBToken() + isassigned(stmt.values, x.op) || return UndefToken() + return stmt.values[x.op] + else + return OOBToken() + end +end + +function is_relevant_expr(e::Expr) + return e.head in (:call, :invoke, :new, :splatnew, :(=), :(&), + :gc_preserve_begin, :gc_preserve_end, + :foreigncall, :isdefined, :copyast, + :undefcheck, :throw_undef_if_not, + :cfunction, :method, :pop_exception, + #=legacy IR format support=# :gotoifnot, :return) +end + +function setindex!(x::UseRef, @nospecialize(v)) + stmt = x.stmt + if isa(stmt, Expr) && stmt.head === :(=) + rhs = stmt.args[2] + if isa(rhs, Expr) + if is_relevant_expr(rhs) + x.op > length(rhs.args) && throw(BoundsError()) + rhs.args[x.op] = v + return v + end + end + x.op == 1 || throw(BoundsError()) + stmt.args[2] = v + elseif isa(stmt, Expr) # @assert is_relevant_expr(stmt) + x.op > length(stmt.args) && throw(BoundsError()) + stmt.args[x.op] = v + elseif isa(stmt, GotoIfNot) + x.op == 1 || throw(BoundsError()) + x.stmt = GotoIfNot(v, stmt.dest) + elseif isa(stmt, ReturnNode) + x.op == 1 || throw(BoundsError()) + x.stmt = typeof(stmt)(v) + elseif isa(stmt, UpsilonNode) + x.op == 1 || throw(BoundsError()) + x.stmt = typeof(stmt)(v) + elseif isa(stmt, PiNode) + x.op == 1 || throw(BoundsError()) + x.stmt = typeof(stmt)(v, stmt.typ) + elseif isa(stmt, PhiNode) + x.op > length(stmt.values) && throw(BoundsError()) + isassigned(stmt.values, x.op) || throw(BoundsError()) + stmt.values[x.op] = v + elseif isa(stmt, PhiCNode) + x.op > length(stmt.values) && throw(BoundsError()) + isassigned(stmt.values, x.op) || throw(BoundsError()) + stmt.values[x.op] = v + else + throw(BoundsError()) + end + return x +end + +function userefs(@nospecialize(x)) + relevant = (isa(x, Expr) && is_relevant_expr(x)) || + isa(x, GotoIfNot) || isa(x, ReturnNode) || + isa(x, PiNode) || isa(x, PhiNode) || isa(x, PhiCNode) || isa(x, UpsilonNode) + return UseRefIterator(x, relevant) +end + +iterate(it::UseRefIterator) = (it.use[1].op = 0; iterate(it, nothing)) +@noinline function iterate(it::UseRefIterator, ::Nothing) + it.relevant || return nothing + use = it.use[1] + while true + use.op += 1 + y = use[] + y === OOBToken() && return nothing + y === UndefToken() || return it.use + end +end + +# This function is used from the show code, which may have a different +# `push!`/`used` type since it's in Base. +function scan_ssa_use!(push!, used, @nospecialize(stmt)) + if isa(stmt, SSAValue) + push!(used, stmt.id) + end + for useref in userefs(stmt) + val = useref[] + if isa(val, SSAValue) + push!(used, val.id) + end + end +end + +# Manually specialized copy of the above with push! === Compiler.push! +function scan_ssa_use!(used::IdSet, @nospecialize(stmt)) + if isa(stmt, SSAValue) + push!(used, stmt.id) + end + for useref in userefs(stmt) + val = useref[] + if isa(val, SSAValue) + push!(used, val.id) + end + end +end + +function ssamap(f, @nospecialize(stmt)) + urs = userefs(stmt) + for op in urs + val = op[] + if isa(val, SSAValue) + op[] = f(val) + end + end + return urs[] +end + +function foreachssa(f, @nospecialize(stmt)) + for op in userefs(stmt) + val = op[] + if isa(val, SSAValue) + f(val) + end + end +end + +function insert_node!(ir::IRCode, pos::Int, @nospecialize(typ), @nospecialize(val), attach_after::Bool=false) + line = ir.lines[pos] + push!(ir.new_nodes, NewNode(pos, attach_after, typ, val, line)) + return SSAValue(length(ir.stmts) + length(ir.new_nodes)) +end + +# For bootstrapping +function my_sortperm(v) + p = Vector{Int}(undef, length(v)) + for i = 1:length(v) + p[i] = i + end + sort!(p, Sort.DEFAULT_UNSTABLE, Order.Perm(Sort.Forward,v)) + p +end + +mutable struct IncrementalCompact + ir::IRCode + result::Vector{Any} + result_types::Vector{Any} + result_lines::Vector{Int32} + result_flags::Vector{UInt8} + result_bbs::Vector{BasicBlock} + ssa_rename::Vector{Any} + bb_rename_pred::Vector{Int} + bb_rename_succ::Vector{Int} + used_ssas::Vector{Int} + late_fixup::Vector{Int} + # This could be Stateful, but bootstrapping doesn't like that + perm::Vector{Int} + new_nodes_idx::Int + # This supports insertion while compacting + new_new_nodes::Vector{NewNode} # New nodes that were before the compaction point at insertion time + # TODO: Switch these two to a min-heap of some sort + pending_nodes::Vector{NewNode} # New nodes that were after the compaction point at insertion time + pending_perm::Vector{Int} + # State + idx::Int + result_idx::Int + active_result_bb::Int + renamed_new_nodes::Bool + cfg_transforms_enabled::Bool + fold_constant_branches::Bool + function IncrementalCompact(code::IRCode, allow_cfg_transforms::Bool=false) + # Sort by position with attach after nodes affter regular ones + perm = my_sortperm(Int[(code.new_nodes[i].pos*2 + Int(code.new_nodes[i].attach_after)) for i in 1:length(code.new_nodes)]) + new_len = length(code.stmts) + length(code.new_nodes) + result = Array{Any}(undef, new_len) + result_types = Array{Any}(undef, new_len) + result_lines = fill(Int32(0), new_len) + result_flags = fill(0x00, new_len) + used_ssas = fill(0, new_len) + blocks = code.cfg.blocks + if allow_cfg_transforms + bb_rename = Vector{Int}(undef, length(blocks)) + cur_bb = 1 + for i = 1:length(bb_rename) + if i != 1 && length(blocks[i].preds) == 0 + bb_rename[i] = -1 + else + bb_rename[i] = cur_bb + cur_bb += 1 + end + end + for i = 1:length(bb_rename) + bb_rename[i] == -1 && continue + preds, succs = blocks[i].preds, blocks[i].succs + # Rename preds + for j = 1:length(preds) + if preds[j] != 0 + preds[j] = bb_rename[preds[j]] + end + end + # Dead blocks get removed from the predecessor list + filter!(x->x !== -1, preds) + # Rename succs + for j = 1:length(succs); succs[j] = bb_rename[succs[j]]; end + end + let blocks=blocks + result_bbs = BasicBlock[blocks[i] for i = 1:length(blocks) if bb_rename[i] != -1] + end + else + bb_rename = Vector{Int}() + result_bbs = code.cfg.blocks + end + ssa_rename = Any[SSAValue(i) for i = 1:new_len] + late_fixup = Vector{Int}() + new_new_nodes = NewNode[] + pending_nodes = NewNode[] + pending_perm = Int[] + return new(code, result, result_types, result_lines, result_flags, result_bbs, ssa_rename, bb_rename, bb_rename, used_ssas, late_fixup, perm, 1, + new_new_nodes, pending_nodes, pending_perm, + 1, 1, 1, false, allow_cfg_transforms, allow_cfg_transforms) + end + + # For inlining + function IncrementalCompact(parent::IncrementalCompact, code::IRCode, result_offset) + perm = my_sortperm(Int[code.new_nodes[i].pos for i in 1:length(code.new_nodes)]) + new_len = length(code.stmts) + length(code.new_nodes) + ssa_rename = Any[SSAValue(i) for i = 1:new_len] + used_ssas = fill(0, new_len) + late_fixup = Vector{Int}() + bb_rename = Vector{Int}() + new_new_nodes = NewNode[] + pending_nodes = NewNode[] + pending_perm = Int[] + return new(code, parent.result, parent.result_types, parent.result_lines, parent.result_flags, + parent.result_bbs, ssa_rename, bb_rename, bb_rename, parent.used_ssas, + late_fixup, perm, 1, + new_new_nodes, pending_nodes, pending_perm, + 1, result_offset, parent.active_result_bb, false, false, false) + end +end + +struct TypesView + ir::Union{IRCode, IncrementalCompact} +end +types(ir::Union{IRCode, IncrementalCompact}) = TypesView(ir) + +function getindex(compact::IncrementalCompact, idx::Int) + if idx < compact.result_idx + return compact.result[idx] + else + return compact.ir.stmts[idx] + end +end + +function getindex(compact::IncrementalCompact, ssa::SSAValue) + @assert ssa.id < compact.result_idx + return compact.result[ssa.id] +end + +function getindex(compact::IncrementalCompact, ssa::OldSSAValue) + id = ssa.id + if id <= length(compact.ir.stmts) + return compact.ir.stmts[id] + end + id -= length(compact.ir.stmts) + if id <= length(compact.ir.new_nodes) + return compact.ir.new_nodes[id].node + end + id -= length(compact.ir.new_nodes) + return compact.pending_nodes[id].node +end + +function getindex(compact::IncrementalCompact, ssa::NewSSAValue) + return compact.new_new_nodes[ssa.id].node +end + +function count_added_node!(compact::IncrementalCompact, @nospecialize(v)) + needs_late_fixup = isa(v, NewSSAValue) + if isa(v, SSAValue) + compact.used_ssas[v.id] += 1 + else + for ops in userefs(v) + val = ops[] + if isa(val, SSAValue) + compact.used_ssas[val.id] += 1 + elseif isa(val, NewSSAValue) + needs_late_fixup = true + end + end + end + needs_late_fixup +end + +function resort_pending!(compact) + sort!(compact.pending_perm, DEFAULT_STABLE, Order.By(x->compact.pending_nodes[x].pos)) +end + +function insert_node!(compact::IncrementalCompact, before, @nospecialize(typ), @nospecialize(val), attach_after::Bool=false) + if isa(before, SSAValue) + if before.id < compact.result_idx + count_added_node!(compact, val) + line = compact.result_lines[before.id] + push!(compact.new_new_nodes, NewNode(before.id, attach_after, typ, val, line)) + return NewSSAValue(length(compact.new_new_nodes)) + else + line = compact.ir.lines[before.id] + push!(compact.pending_nodes, NewNode(before.id, attach_after, typ, val, line)) + push!(compact.pending_perm, length(compact.pending_nodes)) + resort_pending!(compact) + os = OldSSAValue(length(compact.ir.stmts) + length(compact.ir.new_nodes) + length(compact.pending_nodes)) + push!(compact.ssa_rename, os) + push!(compact.used_ssas, 0) + return os + end + elseif isa(before, OldSSAValue) + pos = before.id + if pos > length(compact.ir.stmts) + #@assert attach_after + entry = compact.pending_nodes[pos - length(compact.ir.stmts) - length(compact.ir.new_nodes)] + pos, attach_after = entry.pos, entry.attach_after + end + line = compact.ir.lines[pos] + push!(compact.pending_nodes, NewNode(pos, attach_after, typ, val, line)) + push!(compact.pending_perm, length(compact.pending_nodes)) + resort_pending!(compact) + os = OldSSAValue(length(compact.ir.stmts) + length(compact.ir.new_nodes) + length(compact.pending_nodes)) + push!(compact.ssa_rename, os) + push!(compact.used_ssas, 0) + return os + elseif isa(before, NewSSAValue) + before_entry = compact.new_new_nodes[before.id] + push!(compact.new_new_nodes, NewNode(before_entry.pos, attach_after, typ, val, before_entry.line)) + return NewSSAValue(length(compact.new_new_nodes)) + else + error("Unsupported") + end +end + +function append_node!(ir, @nospecialize(typ), @nospecialize(node), line) + push!(ir.stmts, node) + push!(ir.types, typ) + push!(ir.lines, line) + push!(ir.flags, 0) + last_bb = ir.cfg.blocks[end] + ir.cfg.blocks[end] = BasicBlock(first(last_bb.stmts):length(ir.stmts), + last_bb.preds, + last_bb.succs) + return SSAValue(length(ir.stmts)) +end + +function insert_node_here!(compact::IncrementalCompact, @nospecialize(val), @nospecialize(typ), ltable_idx::Int32, reverse_affinity::Bool=false) + if compact.result_idx > length(compact.result) + @assert compact.result_idx == length(compact.result) + 1 + resize!(compact, compact.result_idx) + end + refinish = false + if compact.result_idx == first(compact.result_bbs[compact.active_result_bb].stmts) && reverse_affinity + compact.active_result_bb -= 1 + refinish = true + end + compact.result[compact.result_idx] = val + compact.result_types[compact.result_idx] = typ + compact.result_lines[compact.result_idx] = ltable_idx + compact.result_flags[compact.result_idx] = 0x00 + if count_added_node!(compact, val) + push!(compact.late_fixup, compact.result_idx) + end + ret = SSAValue(compact.result_idx) + compact.result_idx += 1 + refinish && finish_current_bb!(compact, 0) + ret +end + +function getindex(view::TypesView, v::OldSSAValue) + id = v.id + if id <= length(view.ir.ir.types) + return view.ir.ir.types[id] + end + id -= length(view.ir.ir.types) + if id <= length(view.ir.ir.new_nodes) + return view.ir.ir.new_nodes[id].typ + end + id -= length(view.ir.ir.new_nodes) + return view.ir.pending_nodes[id].typ +end + +function setindex!(compact::IncrementalCompact, @nospecialize(v), idx::SSAValue) + @assert idx.id < compact.result_idx + (compact.result[idx.id] === v) && return + # Kill count for current uses + for ops in userefs(compact.result[idx.id]) + val = ops[] + if isa(val, SSAValue) + @assert compact.used_ssas[val.id] >= 1 + compact.used_ssas[val.id] -= 1 + end + end + compact.result[idx.id] = v + # Add count for new use + if count_added_node!(compact, v) + push!(compact.late_fixup, idx.id) + end +end + +function setindex!(compact::IncrementalCompact, @nospecialize(v), idx::Int) + if idx < compact.result_idx + compact[SSAValue(idx)] = v + else + compact.ir.stmts[idx] = v + end + return nothing +end + +function getindex(view::TypesView, idx) + isa(idx, SSAValue) && (idx = idx.id) + if isa(view.ir, IncrementalCompact) && idx < view.ir.result_idx + return view.ir.result_types[idx] + elseif isa(view.ir, IncrementalCompact) && view.ir.renamed_new_nodes + if idx <= length(view.ir.result_types) + return view.ir.result_types[idx] + else + return view.ir.new_new_nodes[idx - length(view.ir.result_types)].typ + end + else + ir = isa(view.ir, IncrementalCompact) ? view.ir.ir : view.ir + if idx <= length(ir.types) + return ir.types[idx] + else + return ir.new_nodes[idx - length(ir.types)].typ + end + end +end + +function getindex(view::TypesView, idx::NewSSAValue) + if isa(view.ir, IncrementalCompact) + compact = view.ir + compact.new_new_nodes[idx.id].typ + else + view.ir.new_nodes[idx.id].typ + end +end + +function process_phinode_values(old_values::Vector{Any}, late_fixup::Vector{Int}, + processed_idx::Int, result_idx::Int, + ssa_rename::Vector{Any}, used_ssas::Vector{Int}, + do_rename_ssa::Bool) + values = Vector{Any}(undef, length(old_values)) + for i = 1:length(old_values) + isassigned(old_values, i) || continue + val = old_values[i] + if isa(val, SSAValue) + if do_rename_ssa + if val.id > processed_idx + push!(late_fixup, result_idx) + val = OldSSAValue(val.id) + else + val = renumber_ssa2(val, ssa_rename, used_ssas, do_rename_ssa) + end + else + used_ssas[val.id] += 1 + end + elseif isa(val, OldSSAValue) + if val.id > processed_idx + push!(late_fixup, result_idx) + else + # Always renumber these. do_rename_ssa applies only to actual SSAValues + val = renumber_ssa2(SSAValue(val.id), ssa_rename, used_ssas, true) + end + elseif isa(val, NewSSAValue) + push!(late_fixup, result_idx) + end + values[i] = val + end + return values +end + +function renumber_ssa2(val::SSAValue, ssanums::Vector{Any}, used_ssa::Vector{Int}, do_rename_ssa::Bool) + id = val.id + if id > length(ssanums) + return val + end + if do_rename_ssa + val = ssanums[id] + end + if isa(val, SSAValue) + if used_ssa !== nothing + used_ssa[val.id] += 1 + end + end + return val +end + +function renumber_ssa2!(@nospecialize(stmt), ssanums::Vector{Any}, used_ssa::Vector{Int}, late_fixup::Vector{Int}, result_idx::Int, do_rename_ssa::Bool) + urs = userefs(stmt) + for op in urs + val = op[] + if isa(val, OldSSAValue) || isa(val, NewSSAValue) + push!(late_fixup, result_idx) + end + if isa(val, SSAValue) + val = renumber_ssa2(val, ssanums, used_ssa, do_rename_ssa) + end + if isa(val, OldSSAValue) || isa(val, NewSSAValue) + push!(late_fixup, result_idx) + end + op[] = val + end + return urs[] +end + +# Used in inlining before we start compacting - Only works at the CFG level +function kill_edge!(bbs::Vector{BasicBlock}, from::Int, to::Int) + preds, succs = bbs[to].preds, bbs[from].succs + deleteat!(preds, findfirst(x->x === from, preds)::Int) + deleteat!(succs, findfirst(x->x === to, succs)::Int) + if length(preds) == 0 + for succ in copy(bbs[to].succs) + kill_edge!(bbs, to, succ) + end + end +end + +# N.B.: from and to are non-renamed indices +function kill_edge!(compact::IncrementalCompact, active_bb::Int, from::Int, to::Int) + # Note: We recursively kill as many edges as are obviously dead. However, this + # may leave dead loops in the IR. We kill these later in a CFG cleanup pass (or + # worstcase during codegen). + preds, succs = compact.result_bbs[compact.bb_rename_succ[to]].preds, compact.result_bbs[compact.bb_rename_pred[from]].succs + deleteat!(preds, findfirst(x->x === compact.bb_rename_pred[from], preds)::Int) + deleteat!(succs, findfirst(x->x === compact.bb_rename_succ[to], succs)::Int) + # Check if the block is now dead + if length(preds) == 0 + for succ in copy(compact.result_bbs[compact.bb_rename_succ[to]].succs) + kill_edge!(compact, active_bb, to, findfirst(x->x === succ, compact.bb_rename_pred)) + end + if to < active_bb + # Kill all statements in the block + stmts = compact.result_bbs[compact.bb_rename_succ[to]].stmts + for stmt in stmts + compact.result[stmt] = nothing + end + compact.result[last(stmts)] = ReturnNode() + end + else + # We need to remove this edge from any phi nodes + if to < active_bb + idx = first(compact.result_bbs[compact.bb_rename_succ[to]].stmts) + while idx < length(compact.result) + stmt = compact.result[idx] + stmt === nothing && continue + isa(stmt, PhiNode) || break + i = findfirst(x-> x === compact.bb_rename_pred[from], stmt.edges) + if i !== nothing + deleteat!(stmt.edges, i) + deleteat!(stmt.values, i) + end + idx += 1 + end + else + idx = first(compact.ir.cfg.blocks[to].stmts) + for stmt in CompactPeekIterator(compact, idx) + stmt === nothing && continue + isa(stmt, PhiNode) || break + i = findfirst(x-> x === from, stmt.edges) + if i !== nothing + deleteat!(stmt.edges, i) + deleteat!(stmt.values, i) + end + end + end + end + nothing +end + +function process_node!(compact::IncrementalCompact, result::Vector{Any}, + result_idx::Int, ssa_rename::Vector{Any}, + late_fixup::Vector{Int}, used_ssas::Vector{Int}, @nospecialize(stmt), + idx::Int, processed_idx::Int, active_bb::Int, do_rename_ssa::Bool) + ssa_rename[idx] = SSAValue(result_idx) + if stmt === nothing + ssa_rename[idx] = stmt + elseif isa(stmt, OldSSAValue) + ssa_rename[idx] = ssa_rename[stmt.id] + elseif isa(stmt, GotoNode) && compact.cfg_transforms_enabled + result[result_idx] = GotoNode(compact.bb_rename_succ[stmt.label]) + result_idx += 1 + elseif isa(stmt, GlobalRef) || isa(stmt, GotoNode) + result[result_idx] = stmt + result_idx += 1 + elseif isa(stmt, GotoIfNot) && compact.cfg_transforms_enabled + stmt = renumber_ssa2!(stmt, ssa_rename, used_ssas, late_fixup, result_idx, do_rename_ssa)::GotoIfNot + result[result_idx] = stmt + cond = stmt.cond + if isa(cond, Bool) && compact.fold_constant_branches + if cond + result[result_idx] = nothing + kill_edge!(compact, active_bb, active_bb, stmt.dest) + # Don't increment result_idx => Drop this statement + else + result[result_idx] = GotoNode(compact.bb_rename_succ[stmt.dest]) + kill_edge!(compact, active_bb, active_bb, active_bb+1) + result_idx += 1 + end + else + result[result_idx] = GotoIfNot(cond, compact.bb_rename_succ[stmt.dest]) + result_idx += 1 + end + elseif isa(stmt, Expr) + stmt = renumber_ssa2!(stmt, ssa_rename, used_ssas, late_fixup, result_idx, do_rename_ssa)::Expr + if compact.cfg_transforms_enabled && isexpr(stmt, :enter) + stmt.args[1] = compact.bb_rename_succ[stmt.args[1]::Int] + end + result[result_idx] = stmt + result_idx += 1 + elseif isa(stmt, PiNode) + # As an optimization, we eliminate any trivial pinodes. For performance, we use === + # type equality. We may want to consider using == in either a separate pass or if + # performance turns out ok + stmt = renumber_ssa2!(stmt, ssa_rename, used_ssas, late_fixup, result_idx, do_rename_ssa)::PiNode + pi_val = stmt.val + if isa(pi_val, SSAValue) + if stmt.typ === compact.result_types[pi_val.id] + ssa_rename[idx] = pi_val + return result_idx + end + elseif !isa(pi_val, AnySSAValue) && !isa(pi_val, GlobalRef) + valtyp = isa(pi_val, QuoteNode) ? typeof(pi_val.value) : typeof(pi_val) + if valtyp === stmt.typ + ssa_rename[idx] = pi_val + return result_idx + end + end + result[result_idx] = stmt + result_idx += 1 + elseif isa(stmt, ReturnNode) || isa(stmt, UpsilonNode) || isa(stmt, GotoIfNot) + result[result_idx] = renumber_ssa2!(stmt, ssa_rename, used_ssas, late_fixup, result_idx, do_rename_ssa) + result_idx += 1 + elseif isa(stmt, PhiNode) + values = process_phinode_values(stmt.values, late_fixup, processed_idx, result_idx, ssa_rename, used_ssas, do_rename_ssa) + if length(stmt.edges) == 1 && isassigned(values, 1) && + length(compact.cfg_transforms_enabled ? + compact.result_bbs[compact.bb_rename_succ[active_bb]].preds : + compact.ir.cfg.blocks[active_bb].preds) == 1 + # There's only one predecessor left - just replace it + ssa_rename[idx] = values[1] + else + edges = compact.cfg_transforms_enabled ? map!(i->compact.bb_rename_pred[i], stmt.edges, stmt.edges) : stmt.edges + result[result_idx] = PhiNode(edges, values) + result_idx += 1 + end + elseif isa(stmt, PhiCNode) + result[result_idx] = PhiCNode(process_phinode_values(stmt.values, late_fixup, processed_idx, result_idx, ssa_rename, used_ssas, do_rename_ssa)) + result_idx += 1 + elseif isa(stmt, SSAValue) + # identity assign, replace uses of this ssa value with its result + if do_rename_ssa + stmt = ssa_rename[stmt.id] + end + ssa_rename[idx] = stmt + else + # Constant assign, replace uses of this ssa value with its result + ssa_rename[idx] = stmt + end + return result_idx +end + +function process_node!(compact::IncrementalCompact, result_idx::Int, @nospecialize(stmt), idx::Int, processed_idx::Int, active_bb::Int, do_rename_ssa::Bool) + return process_node!(compact, compact.result, result_idx, compact.ssa_rename, + compact.late_fixup, compact.used_ssas, stmt, idx, processed_idx, active_bb, + do_rename_ssa) +end + +function resize!(compact::IncrementalCompact, nnewnodes) + old_length = length(compact.result) + resize!(compact.result, nnewnodes) + resize!(compact.result_types, nnewnodes) + resize!(compact.result_lines, nnewnodes) + resize!(compact.result_flags, nnewnodes) + resize!(compact.used_ssas, nnewnodes) + for i in (old_length+1):nnewnodes + compact.used_ssas[i] = 0 + end + nothing +end + +function finish_current_bb!(compact, active_bb, old_result_idx=compact.result_idx, unreachable=false) + if compact.active_result_bb > length(compact.result_bbs) + #@assert compact.bb_rename[active_bb] == -1 + return true + end + bb = compact.result_bbs[compact.active_result_bb] + # If this was the last statement in the BB and we decided to skip it, insert a + # dummy `nothing` node, to prevent changing the structure of the CFG + skipped = false + if !compact.cfg_transforms_enabled || active_bb == 0 || active_bb > length(compact.bb_rename_succ) || compact.bb_rename_succ[active_bb] != -1 + if compact.result_idx == first(bb.stmts) + length(compact.result) < old_result_idx && resize!(compact, old_result_idx) + if unreachable + compact.result[old_result_idx] = ReturnNode() + compact.result_types[old_result_idx] = Union{} + else + compact.result[old_result_idx] = nothing + compact.result_types[old_result_idx] = Nothing + end + compact.result_lines[old_result_idx] = 0 + compact.result_flags[old_result_idx] = 0x00 + compact.result_idx = old_result_idx + 1 + elseif compact.cfg_transforms_enabled && compact.result_idx - 1 == first(bb.stmts) + # Optimization: If this BB consists of only a branch, eliminate this bb + end + compact.result_bbs[compact.active_result_bb] = BasicBlock(bb, StmtRange(first(bb.stmts), compact.result_idx-1)) + compact.active_result_bb += 1 + else + skipped = true + end + if compact.active_result_bb <= length(compact.result_bbs) + new_bb = compact.result_bbs[compact.active_result_bb] + compact.result_bbs[compact.active_result_bb] = BasicBlock(new_bb, + StmtRange(compact.result_idx, last(new_bb.stmts))) + end + return skipped +end + +function attach_after_stmt_after(compact::IncrementalCompact, idx::Int) + compact.new_nodes_idx > length(compact.perm) && return false + entry = compact.ir.new_nodes[compact.perm[compact.new_nodes_idx]] + entry.pos == idx && entry.attach_after +end + +function process_newnode!(compact, new_idx, new_node_entry, idx, active_bb, do_rename_ssa) + old_result_idx = compact.result_idx + bb = compact.ir.cfg.blocks[active_bb] + compact.result_types[old_result_idx] = new_node_entry.typ + compact.result_lines[old_result_idx] = new_node_entry.line + result_idx = process_node!(compact, old_result_idx, new_node_entry.node, new_idx, idx - 1, active_bb, do_rename_ssa) + compact.result_idx = result_idx + # If this instruction has reverse affinity and we were at the end of a basic block, + # finish it now. + if new_node_entry.attach_after && idx == last(bb.stmts)+1 && !attach_after_stmt_after(compact, idx-1) + active_bb += 1 + finish_current_bb!(compact, active_bb, old_result_idx) + end + (old_result_idx == result_idx) && return iterate(compact, (idx, active_bb)) + return Pair{Int, Any}(old_result_idx, compact.result[old_result_idx]), (idx, active_bb) +end + +struct CompactPeekIterator + compact::IncrementalCompact + start_idx::Int +end + +entry_at_idx(entry, idx) = entry.attach_after ? entry.pos == idx - 1 : entry.pos == idx +function iterate(it::CompactPeekIterator, (idx, aidx, bidx)::NTuple{3, Int}=(it.start_idx,it.compact.new_nodes_idx,1)) + # TODO: Take advantage of the fact that these arrays are sorted + compact = it.compact + if compact.new_nodes_idx <= length(compact.perm) + for eidx in aidx:length(compact.perm) + if entry_at_idx(compact.ir.new_nodes[compact.perm[eidx]], idx) + entry = compact.ir.new_nodes[compact.perm[eidx]] + return (entry.node, (idx, eidx+1, bidx)) + end + end + end + if !isempty(compact.pending_perm) + for eidx in bidx:length(compact.pending_perm) + if entry_at_idx(compact.pending_nodes[compact.pending_perm[eidx]], idx) + entry = compact.pending_nodes[compact.compact.pending_perm[eidx]] + return (entry.node, (idx, aidx, eidx+1)) + end + end + end + idx > length(compact.ir.stmts) && return nothing + return (compact.ir.stmts[idx], (idx + 1, aidx, bidx)) +end + +function iterate(compact::IncrementalCompact, (idx, active_bb)::Tuple{Int, Int}=(compact.idx, 1)) + # Create label to dodge recursion so that we don't stack overflow + @label restart + + old_result_idx = compact.result_idx + if idx > length(compact.ir.stmts) && (compact.new_nodes_idx > length(compact.perm)) + return nothing + end + if length(compact.result) < old_result_idx + resize!(compact, old_result_idx) + end + bb = compact.ir.cfg.blocks[active_bb] + if compact.cfg_transforms_enabled && active_bb > 1 && active_bb <= length(compact.bb_rename_succ) && length(bb.preds) == 0 + # No predecessors, kill the entire block. + compact.idx = last(bb.stmts) + # Pop any remaining insertion nodes + while compact.new_nodes_idx <= length(compact.perm) + entry = compact.ir.new_nodes[compact.perm[compact.new_nodes_idx]] + if !(entry.attach_after ? entry.pos <= compact.idx - 1 : entry.pos <= compact.idx) + break + end + compact.new_nodes_idx += 1 + end + while !isempty(compact.pending_perm) + entry = compact.pending_nodes[compact.pending_perm[1]]; + if !(entry.attach_after ? entry.pos <= compact.idx - 1 : entry.pos <= compact.idx) + break + end + popfirst!(compact.pending_perm) + end + # Move to next block + compact.idx += 1 + if finish_current_bb!(compact, active_bb, old_result_idx, true) + return iterate(compact, (compact.idx, active_bb + 1)) + else + return Pair{Int, Any}(old_result_idx, compact.result[old_result_idx]), (compact.idx, active_bb + 1) + end + end + if compact.new_nodes_idx <= length(compact.perm) && + (entry = compact.ir.new_nodes[compact.perm[compact.new_nodes_idx]]; + entry.attach_after ? entry.pos == idx - 1 : entry.pos == idx) + new_idx = compact.perm[compact.new_nodes_idx] + compact.new_nodes_idx += 1 + new_node_entry = compact.ir.new_nodes[new_idx] + new_idx += length(compact.ir.stmts) + return process_newnode!(compact, new_idx, new_node_entry, idx, active_bb, true) + elseif !isempty(compact.pending_perm) && + (entry = compact.pending_nodes[compact.pending_perm[1]]; + entry.attach_after ? entry.pos == idx - 1 : entry.pos == idx) + new_idx = popfirst!(compact.pending_perm) + new_node_entry = compact.pending_nodes[new_idx] + new_idx += length(compact.ir.stmts) + length(compact.ir.new_nodes) + return process_newnode!(compact, new_idx, new_node_entry, idx, active_bb, false) + end + # This will get overwritten in future iterations if + # result_idx is not, incremented, but that's ok and expected + compact.result_types[old_result_idx] = compact.ir.types[idx] + compact.result_lines[old_result_idx] = compact.ir.lines[idx] + compact.result_flags[old_result_idx] = compact.ir.flags[idx] + result_idx = process_node!(compact, old_result_idx, compact.ir.stmts[idx], idx, idx, active_bb, true) + stmt_if_any = old_result_idx == result_idx ? nothing : compact.result[old_result_idx] + compact.result_idx = result_idx + if idx == last(bb.stmts) && !attach_after_stmt_after(compact, idx) + finish_current_bb!(compact, active_bb, old_result_idx) + active_bb += 1 + end + compact.idx = idx + 1 + if old_result_idx == compact.result_idx + idx += 1 + @goto restart + end + if !isassigned(compact.result, old_result_idx) + @assert false + end + return Pair{Int, Any}(old_result_idx, compact.result[old_result_idx]), (compact.idx, active_bb) +end + +function maybe_erase_unused!(extra_worklist, compact, idx, callback = x->nothing) + stmt = compact.result[idx] + stmt === nothing && return false + if compact_exprtype(compact, SSAValue(idx)) === Bottom + effect_free = false + else + effect_free = stmt_effect_free(stmt, compact.result_types[idx], compact, compact.ir.sptypes) + end + if effect_free + for ops in userefs(stmt) + val = ops[] + # If the pass we ran inserted new nodes, it's possible for those + # to be outside our used_ssas count. + if isa(val, SSAValue) && val.id <= length(compact.used_ssas) + if compact.used_ssas[val.id] == 1 + if val.id < idx + push!(extra_worklist, val.id) + end + end + compact.used_ssas[val.id] -= 1 + callback(val) + end + end + compact.result[idx] = nothing + return true + end + return false +end + +function fixup_phinode_values!(compact::IncrementalCompact, old_values::Vector{Any}) + values = Vector{Any}(undef, length(old_values)) + for i = 1:length(old_values) + isassigned(old_values, i) || continue + val = old_values[i] + if isa(val, OldSSAValue) + val = compact.ssa_rename[val.id] + if isa(val, SSAValue) + compact.used_ssas[val.id] += 1 + end + elseif isa(val, NewSSAValue) + val = SSAValue(length(compact.result) + val.id) + end + values[i] = val + end + values +end + +function fixup_node(compact::IncrementalCompact, @nospecialize(stmt)) + if isa(stmt, PhiNode) + return PhiNode(stmt.edges, fixup_phinode_values!(compact, stmt.values)) + elseif isa(stmt, PhiCNode) + return PhiCNode(fixup_phinode_values!(compact, stmt.values)) + elseif isa(stmt, NewSSAValue) + return SSAValue(length(compact.result) + stmt.id) + elseif isa(stmt, OldSSAValue) + return compact.ssa_rename[stmt.id] + else + urs = userefs(stmt) + for ur in urs + val = ur[] + if isa(val, NewSSAValue) + ur[] = SSAValue(length(compact.result) + val.id) + elseif isa(val, OldSSAValue) + ur[] = compact.ssa_rename[val.id] + end + end + return urs[] + end +end + +function just_fixup!(compact::IncrementalCompact) + for idx in compact.late_fixup + stmt = compact.result[idx] + new_stmt = fixup_node(compact, stmt) + (stmt !== new_stmt) && (compact.result[idx] = new_stmt) + end + for idx in 1:length(compact.new_new_nodes) + node = compact.new_new_nodes[idx] + new_stmt = fixup_node(compact, node.node) + if node.node !== new_stmt + compact.new_new_nodes[idx] = NewNode( + node.pos, node.attach_after, node.typ, + new_stmt, node.line) + end + end +end + +function simple_dce!(compact::IncrementalCompact) + # Perform simple DCE for unused values + extra_worklist = Int[] + for (idx, nused) in Iterators.enumerate(compact.used_ssas) + idx >= compact.result_idx && break + nused == 0 || continue + maybe_erase_unused!(extra_worklist, compact, idx) + end + while !isempty(extra_worklist) + maybe_erase_unused!(extra_worklist, compact, pop!(extra_worklist)) + end +end + +function non_dce_finish!(compact::IncrementalCompact) + result_idx = compact.result_idx + resize!(compact.result, result_idx-1) + resize!(compact.result_types, result_idx-1) + resize!(compact.result_lines, result_idx-1) + resize!(compact.result_flags, result_idx-1) + just_fixup!(compact) + bb = compact.result_bbs[end] + compact.result_bbs[end] = BasicBlock(bb, + StmtRange(first(bb.stmts), result_idx-1)) + compact.renamed_new_nodes = true + nothing +end + +function finish(compact::IncrementalCompact) + non_dce_finish!(compact) + simple_dce!(compact) + return complete(compact) +end + +function complete(compact::IncrementalCompact) + result_bbs = resize!(compact.result_bbs, compact.active_result_bb-1) + cfg = CFG(result_bbs, Int[first(result_bbs[i].stmts) for i in 2:length(result_bbs)]) + return IRCode(compact.ir, compact.result, compact.result_types, compact.result_lines, compact.result_flags, cfg, compact.new_new_nodes) +end + +function compact!(code::IRCode, allow_cfg_transforms=false) + compact = IncrementalCompact(code, allow_cfg_transforms) + # Just run through the iterator without any processing + foreach(x -> nothing, compact) # x isa Pair{Int, Any} + return finish(compact) +end + +struct BBIdxIter + ir::IRCode +end + +bbidxiter(ir::IRCode) = BBIdxIter(ir) + +function iterate(x::BBIdxIter, (idx, bb)::Tuple{Int, Int}=(1, 1)) + idx > length(x.ir.stmts) && return nothing + active_bb = x.ir.cfg.blocks[bb] + next_bb = bb + if idx == last(active_bb.stmts) + next_bb += 1 + end + return (bb, idx), (idx + 1, next_bb) +end diff --git a/base/compiler/ssair/legacy.jl b/base/compiler/ssair/legacy.jl new file mode 100644 index 0000000..66e8300 --- /dev/null +++ b/base/compiler/ssair/legacy.jl @@ -0,0 +1,100 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +function inflate_ir(ci::CodeInfo, linfo::MethodInstance) + sptypes = sptypes_from_meth_instance(linfo) + if ci.inferred + argtypes, _ = matching_cache_argtypes(linfo, nothing) + else + argtypes = Any[ Any for i = 1:length(ci.slotflags) ] + end + return inflate_ir(ci, sptypes, argtypes) +end + +function inflate_ir(ci::CodeInfo, sptypes::Vector{Any}, argtypes::Vector{Any}) + code = copy_exprargs(ci.code) # TODO: this is a huge hot-spot + for i = 1:length(code) + if isa(code[i], Expr) + code[i] = normalize_expr(code[i]) + end + end + cfg = compute_basic_blocks(code) + for i = 1:length(code) + stmt = code[i] + urs = userefs(stmt) + for op in urs + val = op[] + if isa(val, SlotNumber) + op[] = Argument(val.id) + end + end + stmt = urs[] + # Translate statement edges to bb_edges + if isa(stmt, GotoNode) + code[i] = GotoNode(block_for_inst(cfg, stmt.label)) + elseif isa(stmt, GotoIfNot) + code[i] = GotoIfNot(stmt.cond, block_for_inst(cfg, stmt.dest)) + elseif isa(stmt, PhiNode) + code[i] = PhiNode(Any[block_for_inst(cfg, edge) for edge in stmt.edges], stmt.values) + elseif isa(stmt, Expr) && stmt.head === :enter + stmt.args[1] = block_for_inst(cfg, stmt.args[1]) + code[i] = stmt + else + code[i] = stmt + end + end + ssavaluetypes = ci.ssavaluetypes isa Vector{Any} ? copy(ci.ssavaluetypes) : Any[ Any for i = 1:(ci.ssavaluetypes::Int) ] + ir = IRCode(code, ssavaluetypes, copy(ci.codelocs), copy(ci.ssaflags), cfg, collect(LineInfoNode, ci.linetable), + argtypes, Any[], sptypes) + return ir +end + +function replace_code_newstyle!(ci::CodeInfo, ir::IRCode, nargs::Int) + @assert isempty(ir.new_nodes) + # All but the first `nargs` slots will now be unused + resize!(ci.slotflags, nargs+1) + ci.code = ir.stmts + ci.codelocs = ir.lines + ci.linetable = ir.linetable + ci.ssavaluetypes = ir.types + ci.ssaflags = ir.flags + for metanode in ir.meta + push!(ci.code, metanode) + push!(ci.codelocs, 1) + push!(ci.ssavaluetypes, Any) + push!(ci.ssaflags, 0x00) + end + # Translate BB Edges to statement edges + # (and undo normalization for now) + for i = 1:length(ci.code) + stmt = ci.code[i] + urs = userefs(stmt) + for op in urs + val = op[] + if isa(val, Argument) + op[] = SlotNumber(val.n) + end + end + stmt = urs[] + if isa(stmt, GotoNode) + ci.code[i] = GotoNode(first(ir.cfg.blocks[stmt.label].stmts)) + elseif isa(stmt, GotoIfNot) + ci.code[i] = Expr(:gotoifnot, stmt.cond, first(ir.cfg.blocks[stmt.dest].stmts)) + elseif isa(stmt, PhiNode) + ci.code[i] = PhiNode(Any[last(ir.cfg.blocks[edge].stmts) for edge in stmt.edges], stmt.values) + elseif isa(stmt, ReturnNode) + if isdefined(stmt, :val) + ci.code[i] = Expr(:return, stmt.val) + else + ci.code[i] = Expr(:unreachable) + end + elseif isa(stmt, Expr) && stmt.head === :enter + stmt.args[1] = first(ir.cfg.blocks[stmt.args[1]].stmts) + ci.code[i] = stmt + else + ci.code[i] = stmt + end + end +end + +# used by some tests +inflate_ir(ci::CodeInfo) = inflate_ir(ci, Any[], Any[ Any for i = 1:length(ci.slotflags) ]) diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl new file mode 100644 index 0000000..f30860c --- /dev/null +++ b/base/compiler/ssair/passes.jl @@ -0,0 +1,1148 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" + This struct keeps track of all uses of some mutable struct allocated + in the current function. `uses` are all instances of `getfield` on the + struct. `defs` are all instances of `setfield!` on the struct. The terminology + refers to the uses/defs of the ``slot bundle'' that the mutable struct represents. + + In addition we keep track of all instances of a foreigncall preserve of this mutable + struct. Somewhat counterintuitively, we don't actually need to make sure that the + struct itself is live (or even allocated) at a ccall site. If there are no other places + where the struct escapes (and thus e.g. where its address is taken), it need not be + allocated. We do however, need to make sure to preserve any elements of this struct. +""" +struct SSADefUse + uses::Vector{Int} + defs::Vector{Int} + ccall_preserve_uses::Vector{Int} +end +SSADefUse() = SSADefUse(Int[], Int[], Int[]) + +function try_compute_fieldidx_expr(@nospecialize(typ), @nospecialize(use_expr)) + field = use_expr.args[3] + isa(field, QuoteNode) && (field = field.value) + isa(field, Union{Int, Symbol}) || return nothing + return try_compute_fieldidx(typ, field) +end + +function lift_defuse(cfg::CFG, ssa::SSADefUse) + # We remove from `uses` any block where all uses are dominated + # by a def. This prevents insertion of dead phi nodes at the top + # of such a block if that block happens to be in a loop + ordered = Tuple{Int, Int, Bool}[(x, block_for_inst(cfg, x), true) for x in ssa.uses] + for x in ssa.defs + push!(ordered, (x, block_for_inst(cfg, x), false)) + end + ordered = sort(ordered, by=x->x[1]) + bb_defs = Int[] + bb_uses = Int[] + last_bb = last_def_bb = 0 + for (_, bb, is_use) in ordered + if bb != last_bb && is_use + push!(bb_uses, bb) + end + last_bb = bb + if last_def_bb != bb && !is_use + push!(bb_defs, bb) + last_def_bb = bb + end + end + SSADefUse(bb_uses, bb_defs, Int[]) +end + +function find_curblock(domtree::DomTree, allblocks::Vector{Int}, curblock::Int) + # TODO: This can be much faster by looking at current level and only + # searching for those blocks in a sorted order + while !(curblock in allblocks) + curblock = domtree.idoms[curblock] + end + return curblock +end + +function val_for_def_expr(ir::IRCode, def::Int, fidx::Int) + if isexpr(ir[SSAValue(def)], :new) + return ir[SSAValue(def)].args[1+fidx] + else + # The use is whatever the setfield was + return ir[SSAValue(def)].args[4] + end +end + +function compute_value_for_block(ir::IRCode, domtree::DomTree, allblocks::Vector{Int}, du::SSADefUse, phinodes::IdDict{Int, SSAValue}, fidx::Int, curblock::Int) + curblock = find_curblock(domtree, allblocks, curblock) + def = 0 + for stmt in du.defs + if block_for_inst(ir.cfg, stmt) == curblock + def = max(def, stmt) + end + end + def == 0 ? phinodes[curblock] : val_for_def_expr(ir, def, fidx) +end + +function compute_value_for_use(ir::IRCode, domtree::DomTree, allblocks::Vector{Int}, du::SSADefUse, phinodes::IdDict{Int, SSAValue}, fidx::Int, use_idx::Int) + # Find the first dominating def + curblock = stmtblock = block_for_inst(ir.cfg, use_idx) + curblock = find_curblock(domtree, allblocks, curblock) + defblockdefs = Int[stmt for stmt in du.defs if block_for_inst(ir.cfg, stmt) == curblock] + def = 0 + if !isempty(defblockdefs) + if curblock != stmtblock + # Find the last def in this block + def = 0 + for x in defblockdefs + def = max(def, x) + end + else + # Find the last def before our use + def = 0 + for x in defblockdefs + def = max(def, x >= use_idx ? 0 : x) + end + end + end + if def == 0 + if !haskey(phinodes, curblock) + # If this happens, we need to search the predecessors for defs. Which + # one doesn't matter - if it did, we'd have had a phinode + return compute_value_for_block(ir, domtree, allblocks, du, phinodes, fidx, first(ir.cfg.blocks[stmtblock].preds)) + end + # The use is the phinode + return phinodes[curblock] + else + return val_for_def_expr(ir, def, fidx) + end +end + +function simple_walk(compact::IncrementalCompact, @nospecialize(defssa#=::AnySSAValue=#), pi_callback=(pi, idx)->false) + while true + if isa(defssa, OldSSAValue) && already_inserted(compact, defssa) + rename = compact.ssa_rename[defssa.id] + if isa(rename, AnySSAValue) + defssa = rename + continue + end + return rename + end + def = compact[defssa] + if isa(def, PiNode) + if pi_callback(def, defssa) + return defssa + end + if isa(def.val, SSAValue) + if is_old(compact, defssa) + defssa = OldSSAValue(def.val.id) + else + defssa = def.val + end + else + return def.val + end + elseif isa(def, AnySSAValue) + pi_callback(def, defssa) + if isa(def, SSAValue) && is_old(compact, defssa) + defssa = OldSSAValue(def.id) + else + defssa = def + end + elseif isa(def, Union{PhiNode, PhiCNode, Expr, GlobalRef}) + return defssa + else + return def + end + end +end + +function simple_walk_constraint(compact::IncrementalCompact, @nospecialize(defidx), @nospecialize(typeconstraint) = types(compact)[defidx]) + callback = function (@nospecialize(pi), @nospecialize(idx)) + isa(pi, PiNode) && (typeconstraint = typeintersect(typeconstraint, widenconst(pi.typ))) + return false + end + def = simple_walk(compact, defidx, callback) + return Pair{Any, Any}(def, typeconstraint) +end + +""" + walk_to_defs(compact, val, intermediaries) + +Starting at `val` walk use-def chains to get all the leaves feeding into +this val (pruning those leaves rules out by path conditions). +""" +function walk_to_defs(compact::IncrementalCompact, @nospecialize(defssa), @nospecialize(typeconstraint), visited_phinodes::Vector{Any}=Any[]) + if !isa(defssa, AnySSAValue) || !isa(compact[defssa], PhiNode) + return Any[defssa] + end + # Step 2: Figure out what the struct is defined as + def = compact[defssa] + ## Track definitions through PiNode/PhiNode + found_def = false + ## Track which PhiNodes, SSAValue intermediaries + ## we forwarded through. + visited = IdDict{Any, Any}() + worklist_defs = Any[] + worklist_constraints = Any[] + leaves = Any[] + push!(worklist_defs, defssa) + push!(worklist_constraints, typeconstraint) + while !isempty(worklist_defs) + defssa = pop!(worklist_defs) + typeconstraint = pop!(worklist_constraints) + visited[defssa] = typeconstraint + def = compact[defssa] + if isa(def, PhiNode) + push!(visited_phinodes, defssa) + possible_predecessors = Int[] + for n in 1:length(def.edges) + isassigned(def.values, n) || continue + val = def.values[n] + if is_old(compact, defssa) && isa(val, SSAValue) + val = OldSSAValue(val.id) + end + edge_typ = widenconst(compact_exprtype(compact, val)) + typeintersect(edge_typ, typeconstraint) === Union{} && continue + push!(possible_predecessors, n) + end + for n in possible_predecessors + pred = def.edges[n] + val = def.values[n] + if is_old(compact, defssa) && isa(val, SSAValue) + val = OldSSAValue(val.id) + end + if isa(val, AnySSAValue) + new_def, new_constraint = simple_walk_constraint(compact, val, typeconstraint) + if isa(new_def, AnySSAValue) + if !haskey(visited, new_def) + push!(worklist_defs, new_def) + push!(worklist_constraints, new_constraint) + elseif !(new_constraint <: visited[new_def]) + # We have reached the same definition via a different + # path, with a different type constraint. We may have + # to redo some work here with the wider typeconstraint + push!(worklist_defs, new_def) + push!(worklist_constraints, tmerge(new_constraint, visited[new_def])) + end + continue + end + val = new_def + end + if def == val + # This shouldn't really ever happen, but + # patterns like this can occur in dead code, + # so bail out. + break + else + push!(leaves, val) + end + continue + end + else + push!(leaves, defssa) + end + end + leaves +end + +function process_immutable_preserve(new_preserves::Vector{Any}, compact::IncrementalCompact, def::Expr) + for arg in (isexpr(def, :new) ? def.args : def.args[2:end]) + if !isbitstype(widenconst(compact_exprtype(compact, arg))) + push!(new_preserves, arg) + end + end +end + +function already_inserted(compact::IncrementalCompact, old::OldSSAValue) + id = old.id + if id < length(compact.ir.stmts) + return id < compact.idx + end + id -= length(compact.ir.stmts) + if id < length(compact.ir.new_nodes) + error() + end + id -= length(compact.ir.new_nodes) + @assert id <= length(compact.pending_nodes) + return !(id in compact.pending_perm) +end + +function is_pending(compact::IncrementalCompact, old::OldSSAValue) + return old.id > length(compact.ir.stmts) + length(compact.ir.new_nodes) +end + +function lift_leaves(compact::IncrementalCompact, @nospecialize(stmt), + @nospecialize(result_t), field::Int, leaves::Vector{Any}) + # For every leaf, the lifted value + lifted_leaves = IdDict{Any, Any}() + maybe_undef = false + for leaf in leaves + leaf_key = leaf + if isa(leaf, AnySSAValue) + if isa(leaf, OldSSAValue) && already_inserted(compact, leaf) + leaf = compact.ssa_rename[leaf.id] + if isa(leaf, AnySSAValue) + leaf = simple_walk(compact, leaf) + end + if isa(leaf, AnySSAValue) + def = compact[leaf] + else + def = leaf + end + else + def = compact[leaf] + end + if is_tuple_call(compact, def) && isa(field, Int) && 1 <= field < length(def.args) + lifted = def.args[1+field] + if is_old(compact, leaf) && isa(lifted, SSAValue) + lifted = OldSSAValue(lifted.id) + end + if isa(lifted, GlobalRef) || isa(lifted, Expr) + lifted = insert_node!(compact, leaf, compact_exprtype(compact, lifted), lifted) + def.args[1+field] = lifted + (isa(leaf, SSAValue) && (leaf.id < compact.result_idx)) && push!(compact.late_fixup, leaf.id) + end + lifted_leaves[leaf_key] = RefValue{Any}(lifted) + continue + elseif isexpr(def, :new) + typ = widenconst(types(compact)[leaf]) + if isa(typ, UnionAll) + typ = unwrap_unionall(typ) + end + (isa(typ, DataType) && (!typ.abstract)) || return nothing + @assert !typ.mutable + field = try_compute_fieldidx_expr(typ, stmt) + field === nothing && return nothing + if length(def.args) < 1 + field + ftyp = fieldtype(typ, field) + if !isbitstype(ftyp) + # On this branch, this will be a guaranteed UndefRefError. + # We use the regular undef mechanic to lift this to a boolean slot + maybe_undef = true + lifted_leaves[leaf_key] = nothing + continue + end + return nothing + # Expand the Expr(:new) to include it's element Expr(:new) nodes up until the one we want + compact[leaf] = nothing + for i = (length(def.args) + 1):(1+field) + ftyp = fieldtype(typ, i - 1) + isbits(ftyp) || return nothing + push!(def.args, insert_node!(compact, leaf, result_t, Expr(:new, ftyp))) + end + compact[leaf] = def + end + lifted = def.args[1+field] + if is_old(compact, leaf) && isa(lifted, SSAValue) + lifted = OldSSAValue(lifted.id) + end + if isa(lifted, GlobalRef) || isa(lifted, Expr) + lifted = insert_node!(compact, leaf, compact_exprtype(compact, lifted), lifted) + def.args[1+field] = lifted + (isa(leaf, SSAValue) && (leaf.id < compact.result_idx)) && push!(compact.late_fixup, leaf.id) + end + lifted_leaves[leaf_key] = RefValue{Any}(lifted) + continue + else + typ = compact_exprtype(compact, leaf) + if !isa(typ, Const) + # If the leaf is an old ssa value, insert a getfield here + # We will revisit this getfield later when compaction gets + # to the appropriate point. + # N.B.: This can be a bit dangerous because it can lead to + # infinite loops if we accidentally insert a node just ahead + # of where we are + if is_old(compact, leaf) && (isa(field, Int) || isa(field, Symbol)) + (isa(typ, DataType) && (!typ.abstract)) || return nothing + @assert !typ.mutable + # If there's the potential for an undefref error on access, we cannot insert a getfield + if field > typ.ninitialized && !isbits(fieldtype(typ, field)) + return nothing + lifted_leaves[leaf] = RefValue{Any}(insert_node!(compact, leaf, make_MaybeUndef(result_t), Expr(:call, :unchecked_getfield, SSAValue(leaf.id), field), true)) + maybe_undef = true + else + return nothing + lifted_leaves[leaf] = RefValue{Any}(insert_node!(compact, leaf, result_t, Expr(:call, getfield, SSAValue(leaf.id), field), true)) + end + continue + end + return nothing + end + leaf = typ.val + # Fall through to below + end + elseif isa(leaf, QuoteNode) + leaf = leaf.value + elseif isa(leaf, Union{Argument, Expr}) + return nothing + end + isimmutable(leaf) || return nothing + isdefined(leaf, field) || return nothing + val = getfield(leaf, field) + is_inlineable_constant(val) || return nothing + lifted_leaves[leaf_key] = RefValue{Any}(quoted(val)) + end + lifted_leaves, maybe_undef +end + +make_MaybeUndef(@nospecialize(typ)) = isa(typ, MaybeUndef) ? typ : MaybeUndef(typ) + +function lift_comparison!(compact::IncrementalCompact, idx::Int, + @nospecialize(c1), @nospecialize(c2), stmt::Expr, + lifting_cache::IdDict{Pair{AnySSAValue, Any}, AnySSAValue}) + if isa(c1, Const) + cmp = c1 + typeconstraint = widenconst(c2) + val = stmt.args[3] + else + cmp = c2 + typeconstraint = widenconst(c1) + val = stmt.args[2] + end + + is_type_only = isdefined(typeof(cmp), :instance) + + if isa(val, Union{OldSSAValue, SSAValue}) + val, typeconstraint = simple_walk_constraint(compact, val, typeconstraint) + end + + visited_phinodes = Any[] + leaves = walk_to_defs(compact, val, typeconstraint, visited_phinodes) + + # Let's check if we evaluate the comparison for each one of the leaves + lifted_leaves = IdDict{Any, Any}() + for leaf in leaves + r = egal_tfunc(compact_exprtype(compact, leaf), cmp) + if isa(r, Const) + lifted_leaves[leaf] = RefValue{Any}(r.val) + else + # TODO: In some cases it might be profitable to hoist the === + # here. + return + end + end + + lifted_val = perform_lifting!(compact, visited_phinodes, cmp, lifting_cache, Bool, lifted_leaves, val) + @assert lifted_val !== nothing + + #global assertion_counter + #assertion_counter::Int += 1 + #insert_node_here!(compact, Expr(:assert_egal, Symbol(string("assert_egal_", assertion_counter)), SSAValue(idx), lifted_val), nothing, 0, true) + #return + compact[idx] = lifted_val.x +end + +struct LiftedPhi + ssa::AnySSAValue + node::Any + need_argupdate::Bool +end + +function is_old(compact, @nospecialize(old_node_ssa)) + isa(old_node_ssa, OldSSAValue) && + !is_pending(compact, old_node_ssa) && + !already_inserted(compact, old_node_ssa) +end + +function perform_lifting!(compact::IncrementalCompact, + visited_phinodes::Vector{Any}, @nospecialize(cache_key), + lifting_cache::IdDict{Pair{AnySSAValue, Any}, AnySSAValue}, + @nospecialize(result_t), lifted_leaves::IdDict{Any, Any}, @nospecialize(stmt_val)) + reverse_mapping = IdDict{Any, Any}(ssa => id for (id, ssa) in enumerate(visited_phinodes)) + + # Insert PhiNodes + lifted_phis = LiftedPhi[] + for item in visited_phinodes + if (item, cache_key) in keys(lifting_cache) + ssa = lifting_cache[Pair{AnySSAValue, Any}(item, cache_key)] + push!(lifted_phis, LiftedPhi(ssa, compact[ssa], false)) + continue + end + n = PhiNode() + ssa = insert_node!(compact, item, result_t, n) + lifting_cache[Pair{AnySSAValue, Any}(item, cache_key)] = ssa + push!(lifted_phis, LiftedPhi(ssa, n, true)) + end + + # Fix up arguments + for (old_node_ssa, lf) in zip(visited_phinodes, lifted_phis) + old_node = compact[old_node_ssa] + new_node = lf.node + lf.need_argupdate || continue + for i = 1:length(old_node.edges) + edge = old_node.edges[i] + isassigned(old_node.values, i) || continue + val = old_node.values[i] + orig_val = val + if is_old(compact, old_node_ssa) && isa(val, SSAValue) + val = OldSSAValue(val.id) + end + if isa(val, Union{NewSSAValue, SSAValue, OldSSAValue}) + val = simple_walk(compact, val) + end + if val in keys(lifted_leaves) + push!(new_node.edges, edge) + lifted_val = lifted_leaves[val] + if lifted_val === nothing + resize!(new_node.values, length(new_node.values)+1) + continue + end + lifted_val = lifted_val.x + if isa(lifted_val, Union{NewSSAValue, SSAValue, OldSSAValue}) + lifted_val = simple_walk(compact, lifted_val, (pi, idx)->true) + end + push!(new_node.values, lifted_val) + elseif isa(val, Union{NewSSAValue, SSAValue, OldSSAValue}) && val in keys(reverse_mapping) + push!(new_node.edges, edge) + push!(new_node.values, lifted_phis[reverse_mapping[val]].ssa) + else + # Probably ignored by path condition, skip this + end + end + end + + for lf in lifted_phis + count_added_node!(compact, lf.node) + end + + # Fixup the stmt itself + if isa(stmt_val, Union{SSAValue, OldSSAValue}) + stmt_val = simple_walk(compact, stmt_val) + end + + if stmt_val in keys(lifted_leaves) + stmt_val = lifted_leaves[stmt_val] + else + isa(stmt_val, Union{SSAValue, OldSSAValue}) && stmt_val in keys(reverse_mapping) + stmt_val = RefValue{Any}(lifted_phis[reverse_mapping[stmt_val]].ssa) + end + + return stmt_val +end + +assertion_counter = 0 +function getfield_elim_pass!(ir::IRCode, domtree::DomTree) + compact = IncrementalCompact(ir) + insertions = Vector{Any}() + defuses = IdDict{Int, Tuple{IdSet{Int}, SSADefUse}}() + lifting_cache = IdDict{Pair{AnySSAValue, Any}, AnySSAValue}() + revisit_worklist = Int[] + #ndone, nmax = 0, 200 + for (idx, stmt) in compact + isa(stmt, Expr) || continue + #ndone >= nmax && continue + #ndone += 1 + result_t = compact_exprtype(compact, SSAValue(idx)) + is_getfield = is_setfield = false + is_ccall = false + is_unchecked = false + # Step 1: Check whether the statement we're looking at is a getfield/setfield! + if is_known_call(stmt, setfield!, compact) + is_setfield = true + 4 <= length(stmt.args) <= 5 || continue + elseif is_known_call(stmt, getfield, compact) + is_getfield = true + 3 <= length(stmt.args) <= 4 || continue + elseif is_known_call(stmt, isa, compact) + # TODO + continue + elseif is_known_call(stmt, typeassert, compact) + # Canonicalize + # X = typeassert(Y, T)::S + # into + # typeassert(Y, T) + # X = PiNode(Y, S) + # N.B.: Inference may have a more precise type for `S`, than + # just T, but from here on out, there's no problem with + # using just using that. + # so subsequent analysis only has to deal with the latter + # form. TODO: This isn't the best place to put this. + # Also, we should probably have a version of typeassert + # that's defined not to return its value to make life easier + # for the backend. + pi = insert_node_here!(compact, + PiNode(stmt.args[2], compact.result_types[idx]), compact.result_types[idx], + compact.result_lines[idx], true) + compact.ssa_rename[compact.idx-1] = pi + continue + elseif is_known_call(stmt, (===), compact) + c1 = compact_exprtype(compact, stmt.args[2]) + c2 = compact_exprtype(compact, stmt.args[3]) + if !(isa(c1, Const) || isa(c2, Const)) + continue + end + (isa(c1, Const) && isa(c2, Const)) && continue + lift_comparison!(compact, idx, c1, c2, stmt, lifting_cache) + continue + elseif isexpr(stmt, :call) && stmt.args[1] === :unchecked_getfield + is_getfield = true + is_unchecked = true + elseif isexpr(stmt, :foreigncall) + nccallargs = length(stmt.args[3]::SimpleVector) + new_preserves = Any[] + old_preserves = stmt.args[(6+nccallargs):end] + for (pidx, preserved_arg) in enumerate(old_preserves) + isa(preserved_arg, SSAValue) || continue + let intermediaries = IdSet() + callback = function(@nospecialize(pi), ssa::AnySSAValue) + push!(intermediaries, ssa.id) + return false + end + def = simple_walk(compact, preserved_arg, callback) + isa(def, SSAValue) || continue + defidx = def.id + def = compact[defidx] + if is_tuple_call(compact, def) + process_immutable_preserve(new_preserves, compact, def) + old_preserves[pidx] = nothing + continue + elseif isexpr(def, :new) + typ = widenconst(compact_exprtype(compact, SSAValue(defidx))) + if isa(typ, UnionAll) + typ = unwrap_unionall(typ) + end + if typ isa DataType && !typ.mutable + process_immutable_preserve(new_preserves, compact, def) + old_preserves[pidx] = nothing + continue + end + else + continue + end + mid, defuse = get!(defuses, defidx, (IdSet{Int}(), SSADefUse())) + push!(defuse.ccall_preserve_uses, idx) + union!(mid, intermediaries) + end + continue + end + if !isempty(new_preserves) + old_preserves = filter(ssa->ssa !== nothing, old_preserves) + new_expr = Expr(:foreigncall, stmt.args[1:(6+nccallargs-1)]..., + old_preserves..., new_preserves...) + compact[idx] = new_expr + end + continue + else + continue + end + ## Normalize the field argument to getfield/setfield + field = stmt.args[3] + isa(field, QuoteNode) && (field = field.value) + isa(field, Union{Int, Symbol}) || continue + + struct_typ = unwrap_unionall(widenconst(compact_exprtype(compact, stmt.args[2]))) + isa(struct_typ, DataType) || continue + + def, typeconstraint = stmt.args[2], struct_typ + + if struct_typ.mutable + isa(def, SSAValue) || continue + let intermediaries = IdSet() + callback = function(@nospecialize(pi), ssa::AnySSAValue) + push!(intermediaries, ssa.id) + return false + end + def = simple_walk(compact, def, callback) + # Mutable stuff here + isa(def, SSAValue) || continue + mid, defuse = get!(defuses, def.id, (IdSet{Int}(), SSADefUse())) + if is_setfield + push!(defuse.defs, idx) + else + push!(defuse.uses, idx) + end + union!(mid, intermediaries) + end + continue + elseif is_setfield + continue + end + + if isa(def, Union{OldSSAValue, SSAValue}) + def, typeconstraint = simple_walk_constraint(compact, def, typeconstraint) + end + + visited_phinodes = Any[] + leaves = walk_to_defs(compact, def, typeconstraint, visited_phinodes) + + isempty(leaves) && continue + + field = try_compute_fieldidx_expr(struct_typ, stmt) + field === nothing && continue + + r = lift_leaves(compact, stmt, result_t, field, leaves) + r === nothing && continue + lifted_leaves, any_undef = r + + if any_undef + result_t = make_MaybeUndef(result_t) + end + +# @Base.show result_t +# @Base.show stmt +# for (k,v) in lifted_leaves +# @Base.show (k, v) +# if isa(k, AnySSAValue) +# @Base.show compact[k] +# end +# if isa(v, RefValue) && isa(v.x, AnySSAValue) +# @Base.show compact[v.x] +# end +# end + val = perform_lifting!(compact, visited_phinodes, field, lifting_cache, result_t, lifted_leaves, stmt.args[2]) + + # Insert the undef check if necessary + if any_undef && !is_unchecked + if val === nothing + insert_node!(compact, SSAValue(idx), Nothing, Expr(:throw_undef_if_not, Symbol("##getfield##"), false)) + else + insert_node!(compact, SSAValue(idx), Nothing, Expr(:undefcheck, Symbol("##getfield##"), val.x)) + end + else + @assert val !== nothing + end + + global assertion_counter + assertion_counter::Int += 1 + #insert_node_here!(compact, Expr(:assert_egal, Symbol(string("assert_egal_", assertion_counter)), SSAValue(idx), val), nothing, 0, true) + #continue + compact[idx] = val === nothing ? nothing : val.x + end + + + non_dce_finish!(compact) + # Copy the use count, `simple_dce!` may modify it and for our predicate + # below we need it consistent with the state of the IR here (after tracking + # phi node arguments, but before dce). + used_ssas = copy(compact.used_ssas) + simple_dce!(compact) + ir = complete(compact) + # Now go through any mutable structs and see which ones we can eliminate + for (idx, (intermediaries, defuse)) in defuses + intermediaries = collect(intermediaries) + # Check if there are any uses we did not account for. If so, the variable + # escapes and we cannot eliminate the allocation. This works, because we're guaranteed + # not to include any intermediaries that have dead uses. As a result, missing uses will only ever + # show up in the nuses_total count. + nleaves = length(defuse.uses) + length(defuse.defs) + length(defuse.ccall_preserve_uses) + nuses = 0 + for idx in intermediaries + nuses += used_ssas[idx] + end + nuses_total = used_ssas[idx] + nuses - length(intermediaries) + nleaves == nuses_total || continue + # Find the type for this allocation + defexpr = ir[SSAValue(idx)] + isexpr(defexpr, :new) || continue + typ = ir.types[idx] + if isa(typ, UnionAll) + typ = unwrap_unionall(typ) + end + # Could still end up here if we tried to setfield! and immutable, which would + # error at runtime, but is not illegal to have in the IR. + typ.mutable || continue + # Partition defuses by field + fielddefuse = SSADefUse[SSADefUse() for _ = 1:fieldcount(typ)] + ok = true + for use in defuse.uses + stmt = ir[SSAValue(use)] + # We may have discovered above that this use is dead + # after the getfield elim of immutables. In that case, + # it would have been deleted. That's fine, just ignore + # the use in that case. + stmt === nothing && continue + field = try_compute_fieldidx_expr(typ, stmt) + field === nothing && (ok = false; break) + push!(fielddefuse[field].uses, use) + end + ok || continue + for use in defuse.defs + field = try_compute_fieldidx_expr(typ, ir[SSAValue(use)]) + field === nothing && (ok = false; break) + push!(fielddefuse[field].defs, use) + end + ok || continue + # Check that the defexpr has defined values for all the fields + # we're accessing. In the future, we may want to relax this, + # but we should come up with semantics for well defined semantics + # for uninitialized fields first. + for (fidx, du) in pairs(fielddefuse) + isempty(du.uses) && continue + if fidx + 1 > length(defexpr.args) + ok = false + break + end + end + ok || continue + preserve_uses = IdDict{Int, Vector{Any}}((idx=>Any[] for idx in IdSet{Int}(defuse.ccall_preserve_uses))) + # Everything accounted for. Go field by field and perform idf + for (fidx, du) in pairs(fielddefuse) + ftyp = fieldtype(typ, fidx) + if !isempty(du.uses) + push!(du.defs, idx) + ldu = compute_live_ins(ir.cfg, du) + phiblocks = Int[] + if !isempty(ldu.live_in_bbs) + phiblocks = idf(ir.cfg, ldu, domtree) + end + phinodes = IdDict{Int, SSAValue}() + for b in phiblocks + n = PhiNode() + phinodes[b] = insert_node!(ir, first(ir.cfg.blocks[b].stmts), ftyp, n) + end + # Now go through all uses and rewrite them + allblocks = sort(vcat(phiblocks, ldu.def_bbs)) + for stmt in du.uses + ir[SSAValue(stmt)] = compute_value_for_use(ir, domtree, allblocks, du, phinodes, fidx, stmt) + end + if !isbitstype(fieldtype(typ, fidx)) + for (use, list) in preserve_uses + push!(list, compute_value_for_use(ir, domtree, allblocks, du, phinodes, fidx, use)) + end + end + for b in phiblocks + for p in ir.cfg.blocks[b].preds + n = ir[phinodes[b]] + push!(n.edges, p) + push!(n.values, compute_value_for_block(ir, domtree, + allblocks, du, phinodes, fidx, p)) + end + end + end + for stmt in du.defs + stmt == idx && continue + ir[SSAValue(stmt)] = nothing + end + continue + end + isempty(defuse.ccall_preserve_uses) && continue + push!(intermediaries, idx) + # Insert the new preserves + for (use, new_preserves) in preserve_uses + useexpr = ir[SSAValue(use)] + nccallargs = length(useexpr.args[3]::SimpleVector) + old_preserves = filter(ssa->!isa(ssa, SSAValue) || !(ssa.id in intermediaries), useexpr.args[(6+nccallargs):end]) + new_expr = Expr(:foreigncall, useexpr.args[1:(6+nccallargs-1)]..., + old_preserves..., new_preserves...) + ir[SSAValue(use)] = new_expr + end + end + ir +end + +function adce_erase!(phi_uses, extra_worklist, compact, idx) + if isa(compact.result[idx], PhiNode) + maybe_erase_unused!(extra_worklist, compact, idx, val->phi_uses[val.id]-=1) + else + maybe_erase_unused!(extra_worklist, compact, idx) + end +end + +function count_uses(@nospecialize(stmt), uses::Vector{Int}) + for ur in userefs(stmt) + use = ur[] + if isa(use, SSAValue) + uses[use.id] += 1 + end + end +end + +function mark_phi_cycles(compact::IncrementalCompact, safe_phis::BitSet, phi::Int) + worklist = Int[] + push!(worklist, phi) + while !isempty(worklist) + phi = pop!(worklist) + push!(safe_phis, phi) + for ur in userefs(compact.result[phi]) + val = ur[] + isa(val, SSAValue) || continue + isa(compact[val], PhiNode) || continue + (val.id in safe_phis) && continue + push!(worklist, val.id) + end + end +end + +function adce_pass!(ir::IRCode) + phi_uses = fill(0, length(ir.stmts) + length(ir.new_nodes)) + all_phis = Int[] + compact = IncrementalCompact(ir) + for (idx, stmt) in compact + if isa(stmt, PhiNode) + push!(all_phis, idx) + end + end + non_dce_finish!(compact) + for phi in all_phis + count_uses(compact.result[phi]::PhiNode, phi_uses) + end + # Perform simple DCE for unused values + extra_worklist = Int[] + for (idx, nused) in Iterators.enumerate(compact.used_ssas) + idx >= compact.result_idx && break + nused == 0 || continue + adce_erase!(phi_uses, extra_worklist, compact, idx) + end + while !isempty(extra_worklist) + adce_erase!(phi_uses, extra_worklist, compact, pop!(extra_worklist)) + end + # Go back and erase any phi cycles + changed = true + while changed + changed = false + safe_phis = BitSet() + for phi in all_phis + # Save any phi cycles that have non-phi uses + if compact.used_ssas[phi] - phi_uses[phi] != 0 + mark_phi_cycles(compact, safe_phis, phi) + end + end + for phi in all_phis + if !(phi in safe_phis) + push!(extra_worklist, phi) + end + end + while !isempty(extra_worklist) + if adce_erase!(phi_uses, extra_worklist, compact, pop!(extra_worklist)) + changed = true + end + end + end + return complete(compact) +end + +function type_lift_pass!(ir::IRCode) + type_ctx_uses = Vector{Vector{Int}}[] + has_non_type_ctx_uses = IdSet{Int}() + lifted_undef = IdDict{Int, Any}() + for (idx, stmt) in pairs(ir.stmts) + stmt isa Expr || continue + if (stmt.head === :isdefined || stmt.head === :undefcheck) + val = (stmt.head === :isdefined) ? stmt.args[1] : stmt.args[2] + # undef can only show up by being introduced in a phi + # node (or an UpsilonNode() argument to a PhiC node), + # so lift all these nodes that have maybe undef values + processed = IdDict{Int, Union{SSAValue, Bool}}() + while isa(val, SSAValue) && isa(ir.stmts[val.id], PiNode) + val = ir.stmts[val.id].val + end + if !isa(val, SSAValue) || (!isa(ir.stmts[val.id], PhiNode) && !isa(ir.stmts[val.id], PhiCNode)) + (isa(val, GlobalRef) || isexpr(val, :static_parameter)) && continue + if stmt.head === :undefcheck + ir.stmts[idx] = nothing + else + ir.stmts[idx] = true + end + continue + end + stmt_id = val.id + worklist = Tuple{Int, Int, SSAValue, Int}[(stmt_id, 0, SSAValue(0), 0)] + def = ir.stmts[stmt_id] + if !haskey(lifted_undef, stmt_id) + first = true + while !isempty(worklist) + item, w_up_id, which, use = pop!(worklist) + def = ir.stmts[item] + if isa(def, PhiNode) + edges = copy(def.edges) + values = Vector{Any}(undef, length(edges)) + new_phi = length(values) == 0 ? false : insert_node!(ir, item, Bool, PhiNode(edges, values)) + else + values = Vector{Any}(undef, length(def.values)) + new_phi = length(values) == 0 ? false : insert_node!(ir, item, Bool, PhiCNode(values)) + end + processed[item] = new_phi + if first + lifted_undef[stmt_id] = new_phi + first = false + end + local id::Int = 0 + for i = 1:length(values) + if !isassigned(def.values, i) + val = false + elseif !isa(def.values[i], SSAValue) + val = true + else + up_id = id = def.values[i].id + @label restart + if !isa(ir.types[id], MaybeUndef) + val = true + else + if isa(ir.stmts[id], UpsilonNode) + up = ir.stmts[id] + if !isdefined(up, :val) + val = false + elseif !isa(up.val, SSAValue) + val = true + else + id = up.val.id + @goto restart + end + else + while isa(ir.stmts[id], PiNode) + id = ir.stmts[id].val.id + end + if isa(ir.stmts[id], Union{PhiNode, PhiCNode}) + if haskey(processed, id) + val = processed[id] + else + push!(worklist, (id, up_id, new_phi, i)) + continue + end + else + val = true + end + end + end + end + if isa(def, PhiNode) + values[i] = val + else + values[i] = insert_node!(ir, up_id, Bool, UpsilonNode(val)) + end + end + if which !== SSAValue(0) + phi = ir[which] + if isa(phi, PhiNode) + phi.values[use] = new_phi + else + phi = phi::PhiCNode + ir[which].values[use] = insert_node!(ir, w_up_id, Bool, UpsilonNode(new_phi)) + end + end + end + end + if stmt.head === :isdefined + ir.stmts[idx] = lifted_undef[stmt_id] + else + ir.stmts[idx] = Expr(:throw_undef_if_not, stmt.args[1], lifted_undef[stmt_id]) + end + end + end + ir +end + +function cfg_simplify!(ir::IRCode) + bbs = ir.cfg.blocks + merge_into = zeros(Int, length(bbs)) + merged_succ = zeros(Int, length(bbs)) + + # Walk the CFG at from the entry block and aggressively combine blocks + for (idx, bb) in enumerate(bbs) + if length(bb.succs) == 1 + succ = bb.succs[1] + if length(bbs[succ].preds) == 1 + merge_into[succ] = idx + merged_succ[idx] = succ + end + end + end + max_bb_num = 1 + bb_rename_succ = zeros(Int, length(bbs)) + # Lay out the basic blocks + for i = 1:length(bbs) + if merge_into[i] != 0 + bb_rename_succ[i] = -1 + continue + end + # Drop unreachable blocks + if i != 1 && length(ir.cfg.blocks[i].preds) == 0 + bb_rename_succ[i] = -1 + end + bb_rename_succ[i] != 0 && continue + curr = i + while true + bb_rename_succ[curr] = max_bb_num + max_bb_num += 1 + # Now walk the chain of blocks we merged. + # If we end in something that may fall through, + # we have to schedule that block next + while merged_succ[curr] != 0 + curr = merged_succ[curr] + end + terminator = ir.stmts[ir.cfg.blocks[curr].stmts[end]] + if isa(terminator, GotoNode) || isa(terminator, ReturnNode) + break + end + curr += 1 + end + end + bb_rename_pred = zeros(Int, length(bbs)) + for i = 1:length(bbs) + if merged_succ[i] != 0 + bb_rename_pred[i] = -1 + continue + end + bbnum = i + while merge_into[bbnum] != 0 + bbnum = merge_into[bbnum] + end + bb_rename_pred[i] = bb_rename_succ[bbnum] + end + result_bbs = Int[findfirst(j->i==j, bb_rename_succ) for i = 1:max_bb_num-1] + result_bbs_lengths = zeros(Int, max_bb_num-1) + for (idx, orig_bb) in enumerate(result_bbs) + ms = orig_bb + while ms != 0 + result_bbs_lengths[idx] += length(bbs[ms].stmts) + ms = merged_succ[ms] + end + end + bb_starts = Vector{Int}(undef, 1+length(result_bbs_lengths)) + bb_starts[1] = 1 + for i = 1:length(result_bbs_lengths) + bb_starts[i+1] = bb_starts[i] + result_bbs_lengths[i] + end + # Look at the original successor + function compute_succs(i) + orig_bb = result_bbs[i] + while merged_succ[orig_bb] != 0 + orig_bb = merged_succ[orig_bb] + end + map(i->bb_rename_succ[i], bbs[orig_bb].succs) + end + + function compute_preds(i) + orig_bb = result_bbs[i] + preds = bbs[orig_bb].preds + map(preds) do pred + while merge_into[pred] != 0 + pred = merge_into[pred] + end + bb_rename_succ[pred] + end + end + cresult_bbs = BasicBlock[BasicBlock( + StmtRange(bb_starts[i], i+1 > length(bb_starts) ? length(compact.result) : bb_starts[i+1]-1), + compute_preds(i), compute_succs(i)) for i = 1:length(result_bbs)] + compact = IncrementalCompact(ir, true) + # We're messing with the CFG. We don't want compaction to do + # so independently + compact.fold_constant_branches = false + compact.bb_rename_succ = bb_rename_succ + compact.bb_rename_pred = bb_rename_pred + compact.result_bbs = cresult_bbs + result_idx = 1 + for (idx, orig_bb) in enumerate(result_bbs) + ms = orig_bb + while ms != 0 + for i in bbs[ms].stmts + stmt = ir.stmts[i] + compact.result[compact.result_idx] = nothing + compact.result_types[compact.result_idx] = ir.types[i] + compact.result_lines[compact.result_idx] = ir.lines[i] + compact.result_flags[compact.result_idx] = ir.flags[i] + # If we merged a basic block, we need remove the trailing GotoNode (if any) + if isa(stmt, GotoNode) && merged_succ[ms] != 0 + # Do nothing + else + process_node!(compact, compact.result_idx, stmt, i, i, ms, true) + end + # We always increase the result index to ensure a predicatable + # placement of the resulting nodes. + compact.result_idx += 1 + end + ms = merged_succ[ms] + end + end + + compact.active_result_bb = length(bb_starts) + return finish(compact) +end diff --git a/base/compiler/ssair/queries.jl b/base/compiler/ssair/queries.jl new file mode 100644 index 0000000..d015500 --- /dev/null +++ b/base/compiler/ssair/queries.jl @@ -0,0 +1,88 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" +Determine whether a statement is side-effect-free, i.e. may be removed if it has no uses. +""" +function stmt_effect_free(@nospecialize(stmt), @nospecialize(rt), src, sptypes::Vector{Any}) + isa(stmt, PiNode) && return true + isa(stmt, PhiNode) && return true + isa(stmt, ReturnNode) && return false + isa(stmt, GotoNode) && return false + isa(stmt, GotoIfNot) && return false + isa(stmt, Slot) && return false # Slots shouldn't occur in the IR at this point, but let's be defensive here + isa(stmt, GlobalRef) && return isdefined(stmt.mod, stmt.name) + if isa(stmt, Expr) + e = stmt::Expr + head = e.head + if head === :static_parameter + etyp = sptypes[e.args[1]] + # if we aren't certain enough about the type, it might be an UndefVarError at runtime + return isa(etyp, Const) + end + ea = e.args + if head === :call + f = argextype(ea[1], src, sptypes) + f = singleton_type(f) + f === nothing && return false + is_return_type(f) && return true + if isa(f, IntrinsicFunction) + intrinsic_effect_free_if_nothrow(f) || return false + return intrinsic_nothrow(f) || + intrinsic_nothrow(f, + Any[argextype(ea[i], src, sptypes) for i = 2:length(ea)]) + end + contains_is(_PURE_BUILTINS, f) && return true + contains_is(_PURE_OR_ERROR_BUILTINS, f) || return false + rt === Bottom && return false + return _builtin_nothrow(f, Any[argextype(ea[i], src, sptypes) for i = 2:length(ea)], rt) + elseif head === :new + a = ea[1] + typ = argextype(a, src, sptypes) + # `Expr(:new)` of unknown type could raise arbitrary TypeError. + typ, isexact = instanceof_tfunc(typ) + isexact || return false + isconcretedispatch(typ) || return false + typ = typ::DataType + fieldcount(typ) >= length(ea) - 1 || return false + for fld_idx in 1:(length(ea) - 1) + eT = argextype(ea[fld_idx + 1], src, sptypes) + fT = fieldtype(typ, fld_idx) + eT ⊑ fT || return false + end + return true + elseif head === :isdefined || head === :the_exception || head === :copyast || head === :inbounds || head === :boundscheck + return true + else + # e.g. :loopinfo + return false + end + end + return true +end + +function abstract_eval_ssavalue(s::SSAValue, src::IRCode) + return types(src)[s] +end + +function abstract_eval_ssavalue(s::SSAValue, src::IncrementalCompact) + return types(src)[s] +end + +function compact_exprtype(compact::IncrementalCompact, @nospecialize(value)) + if isa(value, AnySSAValue) + return types(compact)[value] + elseif isa(value, Argument) + return compact.ir.argtypes[value.n] + end + return argextype(value, compact.ir, compact.ir.sptypes) +end + +is_tuple_call(ir::IRCode, @nospecialize(def)) = isa(def, Expr) && is_known_call(def, tuple, ir, ir.sptypes) +is_tuple_call(compact::IncrementalCompact, @nospecialize(def)) = isa(def, Expr) && is_known_call(def, tuple, compact) +function is_known_call(e::Expr, @nospecialize(func), src::IncrementalCompact) + if e.head !== :call + return false + end + f = compact_exprtype(src, e.args[1]) + return singleton_type(f) === func +end diff --git a/base/compiler/ssair/show.jl b/base/compiler/ssair/show.jl new file mode 100644 index 0000000..d3bd8e9 --- /dev/null +++ b/base/compiler/ssair/show.jl @@ -0,0 +1,753 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +@nospecialize + +if Pair != Base.Pair +import Base: Base, IOContext, string, join, sprint +IOContext(io::IO, KV::Pair) = IOContext(io, Base.Pair(KV[1], KV[2])) +length(s::String) = Base.length(s) +^(s::String, i::Int) = Base.:^(s, i) +end + +import Base: show_unquoted +using Base: printstyled, with_output_color, prec_decl + +function Base.show(io::IO, cfg::CFG) + for (idx, block) in enumerate(cfg.blocks) + print(io, idx, "\t=>\t") + join(io, block.succs, ", ") + println(io) + end +end + +function print_stmt(io::IO, idx::Int, @nospecialize(stmt), used::BitSet, maxlength_idx::Int, color::Bool, show_type::Bool) + if idx in used + idx_s = string(idx) + pad = " "^(maxlength_idx - length(idx_s) + 1) + print(io, "%", idx_s, pad, "= ") + else + print(io, " "^(maxlength_idx + 4)) + end + # TODO: `indent` is supposed to be the full width of the leader for correct alignment + indent = 16 + if !color && stmt isa PiNode + # when the outer context is already colored (green, for pending nodes), don't use the usual coloring printer + print(io, "π (") + show_unquoted(io, stmt.val, indent) + print(io, ", ") + print(io, stmt.typ) + print(io, ")") + elseif isexpr(stmt, :invoke) + # TODO: why is this here, and not in Base.show_unquoted + print(io, "invoke ") + linfo = stmt.args[1] + show_unquoted(io, stmt.args[2], indent) + print(io, "(") + # XXX: this is wrong if `sig` is not a concretetype method + # more correct would be to use `fieldtype(sig, i)`, but that would obscure / discard Varargs information in show + sig = linfo.specTypes == Tuple ? Core.svec() : Base.unwrap_unionall(linfo.specTypes).parameters::Core.SimpleVector + print_arg(i) = sprint() do io + show_unquoted(io, stmt.args[i], indent) + if (i - 1) <= length(sig) + print(io, "::", sig[i - 1]) + end + end + join(io, (print_arg(i) for i = 3:length(stmt.args)), ", ") + print(io, ")") + # given control flow information, we prefer to print these with the basic block #, instead of the ssa % + elseif isexpr(stmt, :enter) && length(stmt.args) == 1 && stmt.args[1] isa Int + print(io, "\$(Expr(:enter, #", stmt.args[1]::Int, "))") + elseif stmt isa GotoNode + print(io, "goto #", stmt.label) + elseif stmt isa PhiNode + show_unquoted_phinode(io, stmt, indent, "#") + elseif stmt isa GotoIfNot + show_unquoted_gotoifnot(io, stmt, indent, "#") + # everything else in the IR, defer to the generic AST printer + else + show_unquoted(io, stmt, indent, show_type ? prec_decl : 0) + end + nothing +end + +show_unquoted(io::IO, val::Argument, indent::Int, prec::Int) = show_unquoted(io, Core.SlotNumber(val.n), indent, prec) + +show_unquoted(io::IO, stmt::PhiNode, indent::Int, ::Int) = show_unquoted_phinode(io, stmt, indent, "%") +function show_unquoted_phinode(io::IO, stmt::PhiNode, indent::Int, prefix::String) + args = map(1:length(stmt.edges)) do i + e = stmt.edges[i] + v = !isassigned(stmt.values, i) ? "#undef" : + sprint() do io′ + show_unquoted(io′, stmt.values[i], indent) + end + return "$prefix$e => $v" + end + print(io, "φ ", '(') + join(io, args, ", ") + print(io, ')') +end + +function show_unquoted(io::IO, stmt::PhiCNode, indent::Int, ::Int) + print(io, "φᶜ (") + first = true + for v in stmt.values + first ? (first = false) : print(io, ", ") + show_unquoted(io, v, indent) + end + print(io, ")") +end + +function show_unquoted(io::IO, stmt::PiNode, indent::Int, ::Int) + print(io, "π (") + show_unquoted(io, stmt.val, indent) + print(io, ", ") + printstyled(io, stmt.typ, color=:cyan) + print(io, ")") +end + +function show_unquoted(io::IO, stmt::UpsilonNode, indent::Int, ::Int) + print(io, "ϒ (") + isdefined(stmt, :val) ? + show_unquoted(io, stmt.val, indent) : + print(io, "#undef") + print(io, ")") +end + +function show_unquoted(io::IO, stmt::ReturnNode, indent::Int, ::Int) + if !isdefined(stmt, :val) + print(io, "unreachable") + else + print(io, "return ") + show_unquoted(io, stmt.val, indent) + end +end + +show_unquoted(io::IO, stmt::GotoIfNot, indent::Int, ::Int) = show_unquoted_gotoifnot(io, stmt, indent, "%") +function show_unquoted_gotoifnot(io::IO, stmt::GotoIfNot, indent::Int, prefix::String) + print(io, "goto ", prefix, stmt.dest, " if not ") + show_unquoted(io, stmt.cond, indent) +end + +function compute_inlining_depth(linetable::Vector, iline::Int32) + iline == 0 && return 1 + depth = -1 + while iline != 0 + depth += 1 + lineinfo = linetable[iline]::LineInfoNode + iline = lineinfo.inlined_at + end + return depth +end + +function should_print_ssa_type(@nospecialize node) + if isa(node, Expr) + return !(node.head in (:gc_preserve_begin, :gc_preserve_end, :meta, :return, :enter, :leave)) + end + return !isa(node, PiNode) && !isa(node, GotoIfNot) && + !isa(node, GotoNode) && !isa(node, ReturnNode) && + !isa(node, QuoteNode) +end + +function default_expr_type_printer(io::IO, @nospecialize(typ), used::Bool) + printstyled(io, "::", typ, color=(used ? :cyan : :light_black)) + nothing +end + +normalize_method_name(m::Method) = m.name +normalize_method_name(m::MethodInstance) = (m.def::Method).name +normalize_method_name(m::Symbol) = m +normalize_method_name(m) = Symbol("") +@noinline method_name(m::LineInfoNode) = normalize_method_name(m.method) + +# converts the linetable for line numbers +# into a list in the form: +# 1 outer-most-frame +# 2 inlined-frame +# 3 innermost-frame +function compute_loc_stack(linetable::Vector, line::Int32) + stack = Int[] + while line != 0 + entry = linetable[line]::LineInfoNode + pushfirst!(stack, line) + line = entry.inlined_at + end + return stack +end + +""" + Compute line number annotations for an IRCode + +This functions compute three sets of annotations for each IR line. Take the following +example (taken from `@code_typed sin(1.0)`): + +``` + ** *** ********** + 35 6 ── %10 = :(Base.mul_float)(%%2, %%2)::Float64 │╻╷ sin_kernel + │ %11 = :(Base.mul_float)(%10, %10)::Float64 ││╻ * +``` + +The three annotations are indicated with `*`. The first one is the line number of the +active function (printed once whenver the outer most line number changes). The second +is the inlining indicator. The number of lines indicate the level of nesting, with a +half-size line (╷) indicating the start of a scope and a full size line (│) indicating +a continuing scope. The last annotation is the most complicated one. It is a heuristic +way to print the name of the entered scope. What it attempts to do is print the outermost +scope that hasn't been printed before. Let's work a number of examples to see the impacts +and tradeoffs involved. + +``` +f() = leaf_function() # Delibarately not defined to end up in the IR verbatim +g() = f() +h() = g() +top_function() = h() +``` + +After inlining, we end up with: +``` +1 1 ─ %1 = :(Main.leaf_function)()::Any │╻╷╷ h + └── return %1 │ +``` + +We see that the only function printed is the outermost function. This certainly loses +some information, but the idea is that the outermost function would have the most +semantic meaning (in the context of the function we're looking at). + +On the other hand, let's see what happens when we redefine f: +``` +function f() + leaf_function() + leaf_function() + leaf_function() +end +``` + +We get: +``` +1 1 ─ :(Main.leaf_function)()::Any │╻╷╷ h + │ :(Main.leaf_function)()::Any ││┃│ g + │ %3 = :(Main.leaf_function)()::Any │││┃ f + └── return %3 │ +``` + +Even though we were in the `f` scope since the first statement, it tooks us two statements +to catch up and print the intermediate scopes. Which scope is printed is indicated both +by the indentation of the method name and by an increased thickness of the appropriate +line for the scope. +""" +function compute_ir_line_annotations(code::Union{IRCode, CodeInfo}) + loc_annotations = String[] + loc_methods = String[] + loc_lineno = String[] + cur_group = 1 + last_line = 0 + last_lineno = 0 + last_stack = [] + last_printed_depth = 0 + stmts = (code isa IRCode ? code.stmts : code.code) + linetable = code.linetable + lines = (code isa IRCode ? code.lines : code.codelocs) + for idx in eachindex(stmts) + buf = IOBuffer() + # N.B.: The line array length not matching is invalid, + # but let's be robust here + if idx > length(lines) + line = Int32(0) + print(buf, "!") + else + line = lines[idx] + print(buf, "│") + end + depth = compute_inlining_depth(linetable, line) + iline = line + lineno = 0 + loc_method = "" + if line != 0 + stack = compute_loc_stack(linetable, line) + lineno = linetable[stack[1]].line + x = min(length(last_stack), length(stack)) + if length(stack) != 0 + # Compute the last depth that was in common + first_mismatch = findfirst(i->last_stack[i] != stack[i], 1:x) + # If the first mismatch is the last stack frame, that might just + # be a line number mismatch in inner most frame. Ignore those + if length(last_stack) == length(stack) && first_mismatch == length(stack) + last_entry, entry = linetable[last_stack[end]], linetable[stack[end]] + if method_name(last_entry) === method_name(entry) && last_entry.file === entry.file + first_mismatch = nothing + end + end + last_depth = something(first_mismatch, x+1)-1 + if min(depth, last_depth) > last_printed_depth + printing_depth = min(depth, last_printed_depth + 1) + last_printed_depth = printing_depth + elseif length(stack) > length(last_stack) || first_mismatch !== nothing + printing_depth = min(depth, last_depth + 1) + last_printed_depth = printing_depth + else + printing_depth = 0 + end + stole_one = false + if printing_depth != 0 + for _ in 1:(printing_depth-1) + print(buf, "│") + end + if printing_depth <= last_depth-1 && first_mismatch === nothing + print(buf, "┃") + for _ in printing_depth+1:min(depth, last_depth) + print(buf, "│") + end + else + stole_one = true + print(buf, "╻") + end + else + for _ in 1:min(depth, last_depth) + print(buf, "│") + end + end + print(buf, "╷"^max(0, depth - last_depth - stole_one)) + if printing_depth != 0 + if length(stack) == printing_depth + loc_method = line + else + loc_method = stack[printing_depth + 1] + end + loc_method = method_name(linetable[loc_method]) + end + loc_method = string(" "^printing_depth, loc_method) + end + last_stack = stack + entry = linetable[line] + end + push!(loc_annotations, String(take!(buf))) + push!(loc_lineno, (lineno != 0 && lineno != last_lineno) ? string(lineno) : "") + push!(loc_methods, loc_method) + last_line = line + (lineno != 0) && (last_lineno = lineno) + end + return (loc_annotations, loc_methods, loc_lineno) +end + +Base.show(io::IO, code::IRCode) = show_ir(io, code) + + +lineinfo_disabled(io::IO, linestart::String, lineidx::Int32) = "" + +function DILineInfoPrinter(linetable::Vector, showtypes::Bool=false) + context = LineInfoNode[] + context_depth = Ref(0) + indent(s::String) = s^(max(context_depth[], 1) - 1) + function emit_lineinfo_update(io::IO, linestart::String, lineidx::Int32) + # internal configuration options: + linecolor = :yellow + collapse = showtypes ? false : true + indent_all = true + # convert lineidx to a vector + if lineidx < 0 + # sentinel value: reset internal (and external) state + pops = indent("└") + if !isempty(pops) + print(io, linestart) + printstyled(io, pops; color=linecolor) + println(io) + end + empty!(context) + context_depth[] = 0 + elseif lineidx > 0 # just skip over lines with no debug info at all + DI = LineInfoNode[] + while lineidx != 0 + entry = linetable[lineidx]::LineInfoNode + push!(DI, entry) + lineidx = entry.inlined_at + end + # FOR DEBUGGING, or if you just like very excessive output: + # this prints out the context in full for every statement + #empty!(context) + #context_depth[] = 0 + nframes = length(DI) + nctx = 0 + pop_skips = 0 + # compute the size of the matching prefix in the inlining information stack + for i = 1:min(length(context), nframes) + CtxLine = context[i] + FrameLine = DI[nframes - i + 1] + CtxLine === FrameLine || break + nctx = i + end + update_line_only = false + if collapse && 0 < nctx + # check if we're adding more frames with the same method name, + # if so, drop all existing calls to it from the top of the context + # AND check if instead the context was previously printed that way + # but now has removed the recursive frames + let method = method_name(context[nctx]) + if (nctx < nframes && method_name(DI[nframes - nctx]) === method) || + (nctx < length(context) && method_name(context[nctx + 1]) === method) + update_line_only = true + while nctx > 0 && method_name(context[nctx]) === method + nctx -= 1 + end + end + end + end + # examine what frames we're returning from + if nctx < length(context) + # compute the new inlining depth + if collapse + npops = 1 + let Prev = method_name(context[nctx + 1]) + for i = (nctx + 2):length(context) + Next = method_name(context[i]) + Prev === Next || (npops += 1) + Prev = Next + end + end + else + npops = length(context) - nctx + end + # look at the first non-matching element to see if we are only changing the line number + if !update_line_only && nctx < nframes + let CtxLine = context[nctx + 1], + FrameLine = DI[nframes - nctx] + if CtxLine.file === FrameLine.file && + method_name(CtxLine) === method_name(FrameLine) + update_line_only = true + end + end + end + resize!(context, nctx) + update_line_only && (npops -= 1) + if npops > 0 + context_depth[] -= npops + print(io, linestart) + printstyled(io, indent("│"), "└"^npops; color=linecolor) + println(io) + end + end + # see what change we made to the outermost line number + if update_line_only + frame = DI[nframes - nctx] + nctx += 1 + push!(context, frame) + if frame.line != typemax(frame.line) && frame.line != 0 + print(io, linestart) + Base.with_output_color(linecolor, io) do io + print(io, indent("│"), " @ ", frame.file, ":", frame.line, " within `", method_name(frame), "'") + if collapse + method = method_name(frame) + while nctx < nframes + frame = DI[nframes - nctx] + method_name(frame) === method || break + nctx += 1 + push!(context, frame) + print(io, " @ ", frame.file, ":", frame.line) + end + end + end + println(io) + end + end + # now print the rest of the new frames + while nctx < nframes + frame = DI[nframes - nctx] + nctx += 1 + started = false + if showtypes && !isa(frame.method, Symbol) && nctx != 1 + print(io, linestart) + Base.with_output_color(linecolor, io) do io + print(io, indent("│")) + print(io, "┌ invoke ", frame.method) + println(io) + end + started = true + end + print(io, linestart) + Base.with_output_color(linecolor, io) do io + print(io, indent("│")) + push!(context, frame) + context_depth[] += 1 + nctx != 1 && print(io, started ? "│" : "┌") + print(io, " @ ", frame.file) + if frame.line != typemax(frame.line) && frame.line != 0 + print(io, ":", frame.line) + end + print(io, " within `", method_name(frame), "'") + if collapse + method = method_name(frame) + while nctx < nframes + frame = DI[nframes - nctx] + method_name(frame) === method || break + nctx += 1 + push!(context, frame) + print(io, " @ ", frame.file, ":", frame.line) + end + end + end + println(io) + end + # FOR DEBUGGING `collapse`: + # this double-checks the computation of context_depth + #let Prev = method_name(context[1]), + # depth2 = 1 + # for i = 2:nctx + # Next = method_name(context[i]) + # (collapse && Prev === Next) || (depth2 += 1) + # Prev = Next + # end + # @assert context_depth[] == depth2 + #end + end + indent_all || return "" + return sprint(io -> printstyled(io, indent("│"), color=linecolor), context=io) + end + return emit_lineinfo_update +end + + +function show_ir(io::IO, code::IRCode, expr_type_printer=default_expr_type_printer; verbose_linetable=false) + cols = displaysize(io)[2] + used = BitSet() + stmts = code.stmts + types = code.types + cfg = code.cfg + max_bb_idx_size = length(string(length(cfg.blocks))) + for stmt in stmts + scan_ssa_use!(push!, used, stmt) + end + bb_idx = 1 + new_nodes = code.new_nodes + if any(i -> !isassigned(code.new_nodes, i), 1:length(code.new_nodes)) + printstyled(io, "ERROR: New node array has unset entry\n", color=:red) + new_nodes = new_nodes[filter(i -> isassigned(code.new_nodes, i), 1:length(code.new_nodes))] + end + for nn in new_nodes + scan_ssa_use!(push!, used, nn.node) + end + perm = sortperm(new_nodes, by = x->x.pos) + new_nodes_perm = Iterators.Stateful(perm) + + if isempty(used) + maxlength_idx = 0 + else + maxused = maximum(used) + maxlength_idx = length(string(maxused)) + end + if !verbose_linetable + (loc_annotations, loc_methods, loc_lineno) = compute_ir_line_annotations(code) + max_loc_width = maximum(length(str) for str in loc_annotations) + max_lineno_width = maximum(length(str) for str in loc_lineno) + max_method_width = maximum(length(str) for str in loc_methods) + end + max_depth = maximum(compute_inlining_depth(code.linetable, line) for line in code.lines) + last_stack = [] + for idx in eachindex(stmts) + if !isassigned(stmts, idx) + # This is invalid, but do something useful rather + # than erroring, to make debugging easier + printstyled(io, "#UNDEF\n", color=:red) + continue + end + stmt = stmts[idx] + # Compute BB guard rail + if bb_idx > length(cfg.blocks) + # Even if invariants are violated, try our best to still print + bbrange = (length(cfg.blocks) == 0 ? 1 : last(cfg.blocks[end].stmts) + 1):typemax(Int) + bb_idx_str = "!" + bb_type = "─" + else + bbrange = cfg.blocks[bb_idx].stmts + bbrange = bbrange.start:bbrange.stop + bb_idx_str = string(bb_idx) + bb_type = length(cfg.blocks[bb_idx].preds) <= 1 ? "─" : "┄" + end + bb_pad = max_bb_idx_size - length(bb_idx_str) + bb_start_str = string(bb_idx_str, " ", bb_type, "─"^bb_pad, " ") + bb_guard_rail_cont = string("│ ", " "^max_bb_idx_size) + if idx == first(bbrange) + bb_guard_rail = bb_start_str + else + bb_guard_rail = bb_guard_rail_cont + end + # Print linetable information + if verbose_linetable + stack = compute_loc_stack(code.linetable, code.lines[idx]) + # We need to print any stack frames that did not exist in the last stack + ndepth = max(1, length(stack)) + rail = string(" "^(max_depth+1-ndepth), "│"^ndepth) + start_column = cols - max_depth - 10 + for (i, x) in enumerate(stack) + if i > length(last_stack) || last_stack[i] != x + entry = code.linetable[x] + printstyled(io, "\e[$(start_column)G$(rail)\e[1G", color = :light_black) + print(io, bb_guard_rail) + ssa_guard = " "^(maxlength_idx + 4 + (i - 1)) + entry_label = "$(ssa_guard)$(method_name(entry)) at $(entry.file):$(entry.line) " + hline = string("─"^(start_column-length(entry_label)-length(bb_guard_rail)+max_depth-i), "┐") + printstyled(io, string(entry_label, hline), "\n"; color=:light_black) + bb_guard_rail = bb_guard_rail_cont + end + end + printstyled(io, "\e[$(start_column)G$(rail)\e[1G", color = :light_black) + last_stack = stack + else + annotation = loc_annotations[idx] + loc_method = loc_methods[idx] + lineno = loc_lineno[idx] + # Print location information right aligned. If the line below is too long, it'll overwrite this, + # but that's what we want. + if get(io, :color, false) + method_start_column = cols - max_method_width - max_loc_width - 2 + filler = " "^(max_loc_width-length(annotation)) + printstyled(io, "\e[$(method_start_column)G$(annotation)$(filler)$(loc_method)\e[1G", color = :light_black) + end + printstyled(io, lineno, " "^(max_lineno_width - length(lineno) + 1); color = :light_black) + end + idx != last(bbrange) && print(io, bb_guard_rail) + print_sep = false + if idx == last(bbrange) + print_sep = true + end + floop = true + # print new nodes first in the right position + while !isempty(new_nodes_perm) && new_nodes[Iterators.peek(new_nodes_perm)].pos == idx + node_idx = popfirst!(new_nodes_perm) + new_node = new_nodes[node_idx] + node_idx += length(stmts) + if !floop && !verbose_linetable + print(io, " "^(max_lineno_width + 1)) + end + if print_sep + if idx == first(bbrange) && floop + print(io, bb_start_str) + else + print(io, "│ ", " "^max_bb_idx_size) + end + end + print_sep = true + floop = false + show_type = should_print_ssa_type(new_node.node) + with_output_color(:green, io) do io′ + print_stmt(io′, node_idx, new_node.node, used, maxlength_idx, false, show_type) + end + if show_type + expr_type_printer(io, new_node.typ, node_idx in used) + end + println(io) + end + if !floop && !verbose_linetable + print(io, " "^(max_lineno_width + 1)) + end + if print_sep + if idx == first(bbrange) && floop + print(io, bb_start_str) + elseif idx == last(bbrange) + print(io, "└", "─"^(1 + max_bb_idx_size), " ") + else + print(io, "│ ", " "^max_bb_idx_size) + end + end + if idx == last(bbrange) + bb_idx += 1 + end + show_type = should_print_ssa_type(stmt) + print_stmt(io, idx, stmt, used, maxlength_idx, true, show_type) + if !isassigned(types, idx) + # This is an error, but can happen if passes don't update their type information + printstyled(io, "::#UNDEF", color=:red) + elseif show_type + typ = types[idx] + expr_type_printer(io, typ, idx in used) + end + println(io) + end +end + +function show_ir(io::IO, code::CodeInfo, line_info_preprinter=DILineInfoPrinter(code.linetable), line_info_postprinter=default_expr_type_printer) + cols = displaysize(io)[2] + used = BitSet() + stmts = code.code + types = code.ssavaluetypes + cfg = compute_basic_blocks(stmts) + max_bb_idx_size = length(string(length(cfg.blocks))) + for stmt in stmts + scan_ssa_use!(push!, used, stmt) + end + bb_idx = 1 + + if isempty(used) + maxlength_idx = 0 + else + maxused = maximum(used) + maxlength_idx = length(string(maxused)) + end + for idx in eachindex(stmts) + if !isassigned(stmts, idx) + # This is invalid, but do something useful rather + # than erroring, to make debugging easier + printstyled(io, "#UNDEF\n", color=:red) + continue + end + stmt = stmts[idx] + # Compute BB guard rail + if bb_idx > length(cfg.blocks) + # If invariants are violated, print a special leader + linestart = " "^(max_bb_idx_size + 2) # not inside a basic block bracket + inlining_indent = line_info_preprinter(io, linestart, code.codelocs[idx]) + printstyled(io, "!!! ", "─"^max_bb_idx_size, color=:light_black) + else + bbrange = cfg.blocks[bb_idx].stmts + bbrange = bbrange.start:bbrange.stop + # Print line info update + linestart = idx == first(bbrange) ? " " : sprint(io -> printstyled(io, "│ ", color=:light_black), context=io) + linestart *= " "^max_bb_idx_size + inlining_indent = line_info_preprinter(io, linestart, code.codelocs[idx]) + if idx == first(bbrange) + bb_idx_str = string(bb_idx) + bb_pad = max_bb_idx_size - length(bb_idx_str) + bb_type = length(cfg.blocks[bb_idx].preds) <= 1 ? "─" : "┄" + printstyled(io, bb_idx_str, " ", bb_type, "─"^bb_pad, color=:light_black) + elseif idx == last(bbrange) # print separator + printstyled(io, "└", "─"^(1 + max_bb_idx_size), color=:light_black) + else + printstyled(io, "│ ", " "^max_bb_idx_size, color=:light_black) + end + if idx == last(bbrange) + bb_idx += 1 + end + end + print(io, inlining_indent, " ") + # convert statement index to labels, as expected by print_stmt + if stmt isa Expr + if stmt.head === :gotoifnot && length(stmt.args) == 2 && stmt.args[2] isa Int + stmt = GotoIfNot(stmt.args[1], block_for_inst(cfg, stmt.args[2]::Int)) + elseif stmt.head === :enter && length(stmt.args) == 1 && stmt.args[1] isa Int + stmt = Expr(:enter, block_for_inst(cfg, stmt.args[1]::Int)) + end + elseif isa(stmt, GotoIfNot) + stmt = GotoIfNot(stmt.cond, block_for_inst(cfg, stmt.dest)) + elseif stmt isa GotoNode + stmt = GotoNode(block_for_inst(cfg, stmt.label)) + elseif stmt isa PhiNode + e = stmt.edges + stmt = PhiNode(Any[block_for_inst(cfg, e[i]) for i in 1:length(e)], stmt.values) + end + show_type = types isa Vector{Any} && should_print_ssa_type(stmt) + print_stmt(io, idx, stmt, used, maxlength_idx, true, show_type) + if types isa Vector{Any} # ignore types for pre-inference code + if !isassigned(types, idx) + # This is an error, but can happen if passes don't update their type information + printstyled(io, "::#UNDEF", color=:red) + elseif show_type + typ = types[idx] + line_info_postprinter(io, typ, idx in used) + end + end + println(io) + end + let linestart = " "^(max_bb_idx_size + 2) + line_info_preprinter(io, linestart, typemin(Int32)) + end + nothing +end + +@specialize diff --git a/base/compiler/ssair/slot2ssa.jl b/base/compiler/ssair/slot2ssa.jl new file mode 100644 index 0000000..a15c037 --- /dev/null +++ b/base/compiler/ssair/slot2ssa.jl @@ -0,0 +1,870 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +mutable struct SlotInfo + defs::Vector{Int} + uses::Vector{Int} + any_newvar::Bool +end +SlotInfo() = SlotInfo(Int[], Int[], false) + +function scan_entry!(result::Vector{SlotInfo}, idx::Int, @nospecialize(stmt)) + # NewVarNodes count as defs for the purpose + # of liveness analysis (i.e. they kill use chains) + if isa(stmt, NewvarNode) + result[slot_id(stmt.slot)].any_newvar = true + push!(result[slot_id(stmt.slot)].defs, idx) + return + elseif isexpr(stmt, :(=)) + if isa(stmt.args[1], SlotNumber) + push!(result[slot_id(stmt.args[1])].defs, idx) + end + stmt = stmt.args[2] + end + if isa(stmt, Union{SlotNumber, TypedSlot}) + push!(result[slot_id(stmt)].uses, idx) + return + end + for op in userefs(stmt) + val = op[] + if isa(val, Union{SlotNumber, TypedSlot}) + push!(result[slot_id(val)].uses, idx) + end + end +end + + +function lift_defuse(cfg::CFG, defuse) + map(defuse) do slot + SlotInfo( + Int[block_for_inst(cfg, x) for x in slot.defs], + Int[block_for_inst(cfg, x) for x in slot.uses], + slot.any_newvar + ) + end +end + +@inline slot_id(s) = isa(s, SlotNumber) ? (s::SlotNumber).id : (s::TypedSlot).id +function scan_slot_def_use(nargs::Int, ci::CodeInfo, code::Vector{Any}) + nslots = length(ci.slotflags) + result = SlotInfo[SlotInfo() for i = 1:nslots] + # Set defs for arguments + for var in result[1:(1+nargs)] + push!(var.defs, 0) + end + for (idx, stmt) in Iterators.enumerate(code) + scan_entry!(result, idx, stmt) + end + result +end + +function renumber_ssa(stmt::SSAValue, ssanums::Vector{Any}, new_ssa::Bool=false, used_ssa::Union{Nothing, Vector{Int}}=nothing) + id = stmt.id + if id > length(ssanums) + return stmt + end + val = ssanums[id] + if isa(val, SSAValue) && used_ssa !== nothing + used_ssa[val.id] += 1 + end + return val +end + +function renumber_ssa!(@nospecialize(stmt), ssanums::Vector{Any}, new_ssa::Bool=false, used_ssa::Union{Nothing, Vector{Int}}=nothing) + isa(stmt, SSAValue) && return renumber_ssa(stmt, ssanums, new_ssa, used_ssa) + return ssamap(val->renumber_ssa(val, ssanums, new_ssa, used_ssa), stmt) +end + +function make_ssa!(ci::CodeInfo, code::Vector{Any}, idx, slot, @nospecialize(typ)) + (idx == 0) && return Argument(slot) + stmt = code[idx] + @assert isexpr(stmt, :(=)) + code[idx] = stmt.args[2] + ci.ssavaluetypes[idx] = typ + idx +end + +struct UndefToken +end +const undef_token = UndefToken() + +function new_to_regular(@nospecialize(stmt), new_offset::Int) + if isa(stmt, NewSSAValue) + return SSAValue(stmt.id + new_offset) + end + urs = userefs(stmt) + for op in urs + val = op[] + if isa(val, NewSSAValue) + op[] = SSAValue(val.id + new_offset) + end + end + return urs[] +end + +function fixup_slot!(ir::IRCode, ci::CodeInfo, idx::Int, slot::Int, @nospecialize(stmt::Union{SlotNumber, TypedSlot}), @nospecialize(ssa)) + # We don't really have the information here to get rid of these. + # We'll do so later + if ssa === undef_token + insert_node!(ir, idx, Any, Expr(:throw_undef_if_not, ci.slotnames[slot], false)) + return undef_token + end + if !isa(ssa, Argument) && !(ssa === nothing) && ((ci.slotflags[slot] & SLOT_USEDUNDEF) != 0) + insert_node!(ir, idx, Any, Expr(:undefcheck, ci.slotnames[slot], ssa)) + end + if isa(stmt, SlotNumber) + return ssa + elseif isa(stmt, TypedSlot) + return NewSSAValue(insert_node!(ir, idx, stmt.typ, PiNode(ssa, stmt.typ)).id - length(ir.stmts)) + end +end + +function fixemup!(cond, rename, ir::IRCode, ci::CodeInfo, idx::Int, @nospecialize(stmt)) + if isa(stmt, Union{SlotNumber, TypedSlot}) && cond(stmt) + return fixup_slot!(ir, ci, idx, slot_id(stmt), stmt, rename(stmt)) + end + if isexpr(stmt, :(=)) + stmt.args[2] = fixemup!(cond, rename, ir, ci, idx, stmt.args[2]) + return stmt + end + if isa(stmt, PhiNode) + for i = 1:length(stmt.edges) + isassigned(stmt.values, i) || continue + val = stmt.values[i] + isa(val, Union{SlotNumber, TypedSlot}) || continue + cond(val) || continue + bb_idx = block_for_inst(ir.cfg, stmt.edges[i]) + from_bb_terminator = last(ir.cfg.blocks[bb_idx].stmts) + stmt.values[i] = fixup_slot!(ir, ci, from_bb_terminator, slot_id(val), val, rename(val)) + end + return stmt + end + if isexpr(stmt, :isdefined) + val = stmt.args[1] + if isa(val, Union{SlotNumber, TypedSlot}) + slot = slot_id(val) + if (ci.slotflags[slot] & SLOT_USEDUNDEF) == 0 + return true + else + ssa = rename(val) + if ssa === undef_token + return false + elseif !isa(ssa, SSAValue) && !isa(ssa, NewSSAValue) + return true + end + end + stmt.args[1] = ssa + end + return stmt + end + urs = userefs(stmt) + for op in urs + val = op[] + if isa(val, Union{SlotNumber, TypedSlot}) && cond(val) + x = fixup_slot!(ir, ci, idx, slot_id(val), val, rename(val)) + # We inserted an undef error node. Delete subsequent statement + # to avoid confusing the optimizer + if x === undef_token + return nothing + end + op[] = x + end + end + return urs[] +end + +function fixup_uses!(ir::IRCode, ci::CodeInfo, code, uses::Vector{Int}, slot, @nospecialize(ssa)) + for use in uses + code[use] = fixemup!(stmt->slot_id(stmt)==slot, stmt->ssa, ir, ci, use, code[use]) + end +end + +function rename_uses!(ir::IRCode, ci::CodeInfo, idx::Int, @nospecialize(stmt), renames::Vector{Any}) + return fixemup!(stmt->true, stmt->renames[slot_id(stmt)], ir, ci, idx, stmt) +end + +function strip_trailing_junk!(ci::CodeInfo, code::Vector{Any}, flags::Vector{UInt8}) + # Remove `nothing`s at the end, we don't handle them well + # (we expect the last instruction to be a terminator) + for i = length(code):-1:1 + if code[i] !== nothing + resize!(code, i) + resize!(ci.ssavaluetypes, i) + resize!(ci.codelocs, i) + resize!(flags, i) + break + end + end + # If the last instruction is not a terminator, add one. This can + # happen for implicit return on dead branches. + term = code[end] + if !isa(term, GotoIfNot) && !isa(term, GotoNode) && !isa(term, ReturnNode) + push!(code, ReturnNode()) + push!(ci.ssavaluetypes, Union{}) + push!(ci.codelocs, 0) + push!(flags, 0x00) + end + nothing +end + +struct DelayedTyp + phi::NewSSAValue +end + +# maybe use expr_type? +function typ_for_val(@nospecialize(x), ci::CodeInfo, sptypes::Vector{Any}, idx::Int, slottypes::Vector{Any}) + if isa(x, Expr) + if x.head === :static_parameter + return sptypes[x.args[1]] + elseif x.head === :boundscheck + return Bool + elseif x.head === :copyast + return typ_for_val(x.args[1], ci, sptypes, idx, slottypes) + end + return ci.ssavaluetypes[idx] + end + isa(x, GlobalRef) && return abstract_eval_global(x.mod, x.name) + isa(x, SSAValue) && return ci.ssavaluetypes[x.id] + isa(x, Argument) && return slottypes[x.n] + isa(x, NewSSAValue) && return DelayedTyp(x) + isa(x, QuoteNode) && return Const(x.value) + isa(x, Union{Symbol, PiNode, PhiNode, SlotNumber, TypedSlot}) && error("unexpected val type") + return Const(x) +end + +struct BlockLiveness + def_bbs::Vector{Int} + live_in_bbs::Vector{Int} +end + +# Run iterated dominance frontier +# +# The algorithm we have here essentially follows LLVM, which itself is a +# a cleaned up version of the linear-time algorithm described in +# +# A Linear Time Algorithm for Placing phi-Nodes (by Sreedhar and Gao) +# +# The algorithm here, is quite straightforward. Suppose we have a CFG: +# +# A -> B -> D -> F +# \-> C -------/ +# +# and a corresponding dominator tree: +# +# A +# |- B - D +# |- C +# |- F +# +# Now, for every definition of our slot, we simply walk down the dominator +# tree and look for any edges that leave the sub-domtree rooted by our definition. +# +# E.g. in our example above, if we have a definition in `B`, we look at its successors, +# which is only `D`, which is dominated by `B` and hence doesn't need a phi node. +# Then we descend down the subtree rooted at `B` and end up in `D`. `D` has a successor +# `F`, which is not part of the current subtree, (i.e. not dominated by `B`), so it +# needs a phi node. +# +# Now, the key insight of that algorithm is that we have two defs, in blocks `A` and `B`, +# and `A` dominates `B`, then we do not need to recurse into `B`, because the set of +# potential backedges from a subtree rooted at `B` (to outside the subtree) is a strict +# subset of those backedges from a subtree rooted at `A` (out outside the subtree rooted +# at `A`). Note however that this does not work the other way. Thus, the algorithm +# needs to make sure that we always visit `B` before `A`. +function idf(cfg::CFG, liveness::BlockLiveness, domtree::DomTree) + # This should be a priority queue, but TODO - sorted array for now + defs = liveness.def_bbs + pq = Tuple{Int, Int}[(defs[i], domtree.nodes[defs[i]].level) for i in 1:length(defs)] + sort!(pq, by=x->x[2]) + phiblocks = Int[] + # This bitset makes sure we only add a phi node to a given block once. + processed = BitSet() + # This bitset implements the `key insight` mentioned above. In particular, it prevents + # us from visiting a subtree that we have already visited before. + visited = BitSet() + while !isempty(pq) + # We pop from the end of the array - i.e. the element with the highest level. + node, level = pop!(pq) + worklist = Int[] + push!(worklist, node) + while !isempty(worklist) + active = pop!(worklist) + for succ in cfg.blocks[active].succs + # Check whether the current root (`node`) dominates succ. + # We are guaranteed that `node` dominates `active`, since + # we've arrived at `active` by following dominator tree edges. + # If `succ`'s level is less than or equal to that of `node`, + # it cannot possibly be dominated by `node`. On the other hand, + # since at this point we know that there is an edge from `node`'s + # subtree to `succ`, we know that if succ's level is greater than + # that of `node`, it must be dominated by `node`. + succ_level = domtree.nodes[succ].level + succ_level > level && continue + # We don't dominate succ. We need to place a phinode, + # unless liveness said otherwise. + succ in processed && continue + push!(processed, succ) + if !(succ in liveness.live_in_bbs) + continue + end + push!(phiblocks, succ) + # Basically: Consider the phi node we just added another + # def of this value. N.B.: This needs to retain the invariant that it + # is processed before any of its parents in the dom tree. This is guaranteed, + # because succ_level <= level, which is the greatest level we have currently + # processed. Thus, we have not yet processed any subtrees of level < succ_level. + if !(succ in defs) + push!(pq, (succ, succ_level)) + sort!(pq, by=x->x[2]) + end + end + # Recurse down the current subtree + for child in domtree.nodes[active].children + child in visited && continue + push!(visited, child) + push!(worklist, child) + end + end + end + phiblocks +end + +function rename_incoming_edge(old_edge, old_to, result_order, bb_rename) + new_edge_from = bb_rename[old_edge] + if old_edge == old_to - 1 + # Could have been a crit edge break + if new_edge_from < length(result_order) && result_order[new_edge_from + 1] == 0 + new_edge_from += 1 + end + end + new_edge_from +end + +function rename_outgoing_edge(old_to, old_from, result_order, bb_rename) + new_edge_to = bb_rename[old_to] + if old_from == old_to - 1 + # Could have been a crit edge break + if bb_rename[old_from] < length(result_order) && result_order[bb_rename[old_from]+1] == 0 + new_edge_to = bb_rename[old_from] + 1 + end + end + new_edge_to +end + +function rename_phinode_edges(node, bb, result_order, bb_rename) + new_values = Any[] + new_edges = Any[] + for (idx, edge) in pairs(node.edges) + (edge == 0 || haskey(bb_rename, edge)) || continue + new_edge_from = edge == 0 ? 0 : rename_incoming_edge(edge, bb, result_order, bb_rename) + push!(new_edges, new_edge_from) + if isassigned(node.values, idx) + push!(new_values, node.values[idx]) + else + resize!(new_values, length(new_values)+1) + end + end + return PhiNode(new_edges, new_values) +end + +""" + Sort the basic blocks in `ir` into domtree order (i.e. if bb`` is higher in + the domtree than bb2, it will come first in the linear order). The resulting + ir has the property that a linear traversal of basic blocks will also be a + RPO traversal and in particular, any use of an SSA value must come after (by linear + order) its definition. +""" +function domsort_ssa!(ir::IRCode, domtree::DomTree) + # First compute the new order of basic blocks + result_order = Int[] + stack = Int[] + node = 1 + ncritbreaks = 0 + nnewfallthroughs = 0 + while node !== -1 + push!(result_order, node) + cs = domtree.nodes[node].children + terminator = ir.stmts[last(ir.cfg.blocks[node].stmts)] + iscondbr = isa(terminator, GotoIfNot) + let old_node = node + 1 + if length(cs) >= 1 + # Adding the nodes in reverse sorted order attempts to retain + # the original source order of the nodes as much as possible. + # This is not required for correctness, but is easier on the humans + if old_node in cs + # Schedule the fall through node first, + # so we can retain the fall through + append!(stack, reverse(sort(filter(x -> (x != old_node), cs)))) + node = node + 1 + else + append!(stack, reverse(sort(cs))) + node = pop!(stack) + end + else + if isempty(stack) + node = -1 + else + node = pop!(stack) + end + end + if node != old_node && !isa(terminator, Union{GotoNode, ReturnNode}) + if isa(terminator, GotoIfNot) + # Need to break the critical edge + ncritbreaks += 1 + push!(result_order, 0) + else + nnewfallthroughs += 1 + end + end + end + end + bb_rename = IdDict{Int,Int}(i=>x for (x, i) in pairs(result_order) if i !== 0) + new_bbs = Vector{BasicBlock}(undef, length(result_order)) + nstmts = 0 + for i in result_order + if i !== 0 + nstmts += length(ir.cfg.blocks[i].stmts) + end + end + result_stmts = Vector{Any}(undef, nstmts + ncritbreaks + nnewfallthroughs) + result_types = Any[Any for i = 1:length(result_stmts)] + result_ltable = fill(Int32(0), length(result_stmts)) + result_flags = fill(0x00, length(result_stmts)) + inst_rename = Vector{Any}(undef, length(ir.stmts)) + for i = 1:length(ir.new_nodes) + push!(inst_rename, SSAValue(nstmts + i + ncritbreaks + nnewfallthroughs)) + end + bb_start_off = 0 + crit_edge_breaks_fixup = Tuple{Int, Int}[] + for (new_bb, bb) in pairs(result_order) + if bb == 0 + @assert isa(result_stmts[bb_start_off+1], GotoNode) + # N.B.: The .label has already been renamed when it was created. + new_bbs[new_bb] = BasicBlock((bb_start_off+1):(bb_start_off+1), [new_bb-1], [result_stmts[bb_start_off+1].label]) + bb_start_off += 1 + continue + end + old_inst_range = ir.cfg.blocks[bb].stmts + inst_range = (bb_start_off+1):(bb_start_off+length(old_inst_range)) + for (nidx, idx) in zip(inst_range, old_inst_range) + inst_rename[idx] = SSAValue(nidx) + stmt = ir.stmts[idx] + if isa(stmt, PhiNode) + result_stmts[nidx] = rename_phinode_edges(stmt, bb, result_order, bb_rename) + else + result_stmts[nidx] = stmt + end + result_types[nidx] = ir.types[idx] + result_ltable[nidx] = ir.lines[idx] + result_flags[nidx] = ir.flags[idx] + end + # Now fix up the terminator + terminator = result_stmts[inst_range[end]] + if isa(terminator, GotoNode) + # Convert to implicit fall through + if bb_rename[terminator.label] == new_bb + 1 + result_stmts[inst_range[end]] = nothing + else + result_stmts[inst_range[end]] = GotoNode(bb_rename[terminator.label]) + end + elseif isa(terminator, GotoIfNot) + # Check if we need to break the critical edge + if bb_rename[bb + 1] != new_bb + 1 + @assert result_order[new_bb + 1] == 0 + # Add an explicit goto node in the next basic block (we accounted for this above) + result_stmts[inst_range[end]+1] = GotoNode(bb_rename[bb+1]) + end + result_stmts[inst_range[end]] = GotoIfNot(terminator.cond, bb_rename[terminator.dest]) + elseif !isa(terminator, ReturnNode) + if isa(terminator, Expr) && terminator.head === :enter + terminator.args[1] = bb_rename[terminator.args[1]] + end + if bb_rename[bb + 1] != new_bb + 1 + # Add an explicit goto node + result_stmts[inst_range[end]+1] = GotoNode(bb_rename[bb+1]) + inst_range = first(inst_range):(last(inst_range)+1) + end + end + bb_start_off += length(inst_range) + local new_preds, new_succs + let bb = bb, bb_rename = bb_rename, result_order = result_order + new_preds = Int[rename_incoming_edge(i, bb, result_order, bb_rename) for i in ir.cfg.blocks[bb].preds if haskey(bb_rename, i)] + new_succs = Int[rename_outgoing_edge(i, bb, result_order, bb_rename) for i in ir.cfg.blocks[bb].succs if haskey(bb_rename, i)] + end + new_bbs[new_bb] = BasicBlock(inst_range, new_preds, new_succs) + end + result_stmts = Any[renumber_ssa!(result_stmts[i], inst_rename, true) for i in 1:length(result_stmts)] + cfg = CFG(new_bbs, Int[first(bb.stmts) for bb in new_bbs[2:end]]) + new_new_nodes = Vector{NewNode}(undef, length(ir.new_nodes)) + for i = 1:length(ir.new_nodes) + entry = ir.new_nodes[i] + new_new_nodes[i] = NewNode(inst_rename[entry.pos].id, entry.attach_after, entry.typ, + renumber_ssa!(isa(entry.node, PhiNode) ? + rename_phinode_edges(entry.node, block_for_inst(ir.cfg, entry.pos), result_order, bb_rename) : entry.node, + inst_rename, true), + entry.line) + end + new_ir = IRCode(ir, result_stmts, result_types, result_ltable, result_flags, cfg, new_new_nodes) + return new_ir +end + +function compute_live_ins(cfg::CFG, defuse) + # We remove from `uses` any block where all uses are dominated + # by a def. This prevents insertion of dead phi nodes at the top + # of such a block if that block happens to be in a loop + ordered = Tuple{Int, Int, Bool}[(x, block_for_inst(cfg, x), true) for x in defuse.uses] + for x in defuse.defs + push!(ordered, (x, block_for_inst(cfg, x), false)) + end + ordered = sort(ordered, by=x->x[1]) + bb_defs = Int[] + bb_uses = Int[] + last_bb = last_def_bb = 0 + for (_, bb, is_use) in ordered + if bb != last_bb && is_use + push!(bb_uses, bb) + end + last_bb = bb + if last_def_bb != bb && !is_use + push!(bb_defs, bb) + last_def_bb = bb + end + end + # To obtain live ins from bb_uses, recursively add predecessors + extra_liveins = BitSet() + worklist = Int[] + for bb in bb_uses + append!(worklist, filter(p->p != 0 && !(p in bb_defs), cfg.blocks[bb].preds)) + end + while !isempty(worklist) + elem = pop!(worklist) + (elem in bb_uses || elem in extra_liveins) && continue + push!(extra_liveins, elem) + append!(worklist, filter(p->p != 0 && !(p in bb_defs), cfg.blocks[elem].preds)) + end + append!(bb_uses, extra_liveins) + BlockLiveness(bb_defs, bb_uses) +end + +function recompute_type(node::Union{PhiNode, PhiCNode}, ci::CodeInfo, ir::IRCode, sptypes::Vector{Any}, slottypes::Vector{Any}) + new_typ = Union{} + for i = 1:length(node.values) + if isa(node, PhiNode) && !isassigned(node.values, i) + if !isa(new_typ, MaybeUndef) + new_typ = MaybeUndef(new_typ) + end + continue + end + typ = typ_for_val(node.values[i], ci, sptypes, -1, slottypes) + was_maybe_undef = false + if isa(typ, MaybeUndef) + typ = typ.typ + was_maybe_undef = true + end + @assert !isa(typ, MaybeUndef) + while isa(typ, DelayedTyp) + typ = types(ir)[typ.phi::NewSSAValue] + end + new_typ = tmerge(new_typ, was_maybe_undef ? MaybeUndef(typ) : typ) + end + return new_typ +end + +function construct_ssa!(ci::CodeInfo, code::Vector{Any}, ir::IRCode, domtree::DomTree, defuse, nargs::Int, sptypes::Vector{Any}, + slottypes::Vector{Any}) + cfg = ir.cfg + left = Int[] + catch_entry_blocks = Tuple{Int, Int}[] + for (idx, stmt) in pairs(code) + if isexpr(stmt, :enter) + push!(catch_entry_blocks, (block_for_inst(cfg, idx), block_for_inst(cfg, stmt.args[1]))) + end + end + + exc_handlers = IdDict{Int, Tuple{Int, Int}}() + # Record the correct exception handler for all cricitcal sections + for (enter_block, exc) in catch_entry_blocks + exc_handlers[enter_block+1] = (enter_block, exc) + # TODO: Cut off here if the terminator is a leave corresponding to this enter + for block in dominated(domtree, enter_block+1) + exc_handlers[block] = (enter_block, exc) + end + end + + phi_slots = Vector{Int}[Vector{Int}() for _ = 1:length(ir.cfg.blocks)] + phi_nodes = Vector{Pair{NewSSAValue,PhiNode}}[Vector{Pair{NewSSAValue,PhiNode}}() for _ = 1:length(cfg.blocks)] + phi_ssas = SSAValue[] + phicnodes = IdDict{Int, Vector{Tuple{SlotNumber, NewSSAValue, PhiCNode}}}() + for (_, exc) in catch_entry_blocks + phicnodes[exc] = Vector{Tuple{SlotNumber, NewSSAValue, PhiCNode}}() + end + @timeit "idf" for (idx, slot) in Iterators.enumerate(defuse) + # No uses => no need for phi nodes + isempty(slot.uses) && continue + # TODO: Restore this optimization + if false # length(slot.defs) == 1 && slot.any_newvar + if slot.defs[] == 0 + typ = slottypes[idx] + ssaval = Argument(idx) + fixup_uses!(ir, ci, code, slot.uses, idx, ssaval) + elseif isa(code[slot.defs[]], NewvarNode) + typ = MaybeUndef(Union{}) + ssaval = nothing + for use in slot.uses[] + insert_node!(ir, use, Union{}, Expr(:throw_undef_if_not, ci.slotnames[idx], false)) + end + fixup_uses!(ir, ci, code, slot.uses, idx, nothing) + else + val = code[slot.defs[]].args[2] + typ = typ_for_val(val, ci, sptypes, slot.defs[], slottypes) + ssaval = SSAValue(make_ssa!(ci, code, slot.defs[], idx, typ)) + fixup_uses!(ir, ci, code, slot.uses, idx, ssaval) + end + continue + end + @timeit "liveness" (live = compute_live_ins(cfg, slot)) + for li in live.live_in_bbs + cidx = findfirst(x->x[2] == li, catch_entry_blocks) + if cidx !== nothing + # The slot is live-in into this block. We need to + # Create a PhiC node in the catch entry block and + # an upsilon node in the corresponding enter block + node = PhiCNode(Any[]) + phic_ssa = NewSSAValue(insert_node!(ir, first_insert_for_bb(code, cfg, li), Union{}, node).id - length(ir.stmts)) + push!(phicnodes[li], (SlotNumber(idx), phic_ssa, node)) + # Inform IDF that we now have a def in the catch block + if !(li in live.def_bbs) + push!(live.def_bbs, li) + end + end + end + phiblocks = idf(cfg, live, domtree) + for block in phiblocks + push!(phi_slots[block], idx) + node = PhiNode() + ssa = NewSSAValue(insert_node!(ir, first_insert_for_bb(code, cfg, block), Union{}, node).id - length(ir.stmts)) + push!(phi_nodes[block], ssa=>node) + end + push!(left, idx) + end + # Perform SSA renaming + initial_incoming_vals = Any[ + if 0 in defuse[x].defs + Argument(x) + elseif !defuse[x].any_newvar + undef_token + else + SSAValue(-2) + end for x in 1:length(ci.slotflags) + ] + worklist = Tuple{Int, Int, Vector{Any}}[(1, 0, initial_incoming_vals)] + visited = BitSet() + type_refine_phi = BitSet() + @timeit "SSA Rename" while !isempty(worklist) + (item::Int, pred, incoming_vals) = pop!(worklist) + # Rename existing phi nodes first, because their uses occur on the edge + # TODO: This isn't necessary if inlining stops replacing arguments by slots. + for idx in cfg.blocks[item].stmts + stmt = code[idx] + if isexpr(stmt, :(=)) + stmt = stmt.args[2] + end + isa(stmt, PhiNode) || continue + for (edgeidx, edge) in pairs(stmt.edges) + from_bb = edge == 0 ? 0 : block_for_inst(cfg, edge) + from_bb == pred || continue + isassigned(stmt.values, edgeidx) || break + stmt.values[edgeidx] = rename_uses!(ir, ci, edge, stmt.values[edgeidx], incoming_vals) + break + end + end + # Insert phi nodes if necessary + for (idx, slot) in Iterators.enumerate(phi_slots[item]) + ssaval, node = phi_nodes[item][idx] + incoming_val = incoming_vals[slot] + if incoming_val == SSAValue(-1) + # Optimistically omit this path. + # Liveness analysis would probably have prevented us from inserting this phi node + continue + end + push!(node.edges, pred) + if incoming_val == undef_token + resize!(node.values, length(node.values)+1) + else + push!(node.values, incoming_val) + end + # TODO: Remove the next line, it shouldn't be necessary + push!(type_refine_phi, ssaval.id) + if isa(incoming_val, NewSSAValue) + push!(type_refine_phi, ssaval.id) + end + typ = incoming_val == undef_token ? MaybeUndef(Union{}) : typ_for_val(incoming_val, ci, sptypes, -1, slottypes) + old_entry = ir.new_nodes[ssaval.id] + if isa(typ, DelayedTyp) + push!(type_refine_phi, ssaval.id) + end + new_typ = isa(typ, DelayedTyp) ? Union{} : tmerge(old_entry.typ, typ) + ir.new_nodes[ssaval.id] = NewNode(old_entry.pos, old_entry.attach_after, new_typ, node, old_entry.line) + incoming_vals[slot] = ssaval + end + (item in visited) && continue + # Record phi_C nodes if necessary + if haskey(phicnodes, item) + for (slot, ssa, _) in phicnodes[item] + incoming_vals[slot_id(slot)] = ssa + end + end + # Record initial upsilon nodes if necessary + eidx = findfirst(x->x[1] == item, catch_entry_blocks) + if eidx !== nothing + for (slot, _, node) in phicnodes[catch_entry_blocks[eidx][2]] + ival = incoming_vals[slot_id(slot)] + ivalundef = ival === undef_token + unode = ivalundef ? UpsilonNode() : UpsilonNode(ival) + typ = ivalundef ? MaybeUndef(Union{}) : typ_for_val(ival, ci, sptypes, -1, slottypes) + push!(node.values, + NewSSAValue(insert_node!(ir, first_insert_for_bb(code, cfg, item), + typ, unode, true).id - length(ir.stmts))) + end + end + push!(visited, item) + for idx in cfg.blocks[item].stmts + stmt = code[idx] + (isa(stmt, PhiNode) || (isexpr(stmt, :(=)) && isa(stmt.args[2], PhiNode))) && continue + if isa(stmt, NewvarNode) + incoming_vals[slot_id(stmt.slot)] = undef_token + code[idx] = nothing + else + stmt = rename_uses!(ir, ci, idx, stmt, incoming_vals) + if stmt === nothing && isa(code[idx], Union{ReturnNode, GotoIfNot}) && idx == last(cfg.blocks[item].stmts) + # preserve the CFG + stmt = ReturnNode() + end + code[idx] = stmt + # Record a store + if isexpr(stmt, :(=)) && isa(stmt.args[1], SlotNumber) + id = slot_id(stmt.args[1]) + val = stmt.args[2] + typ = typ_for_val(val, ci, sptypes, idx, slottypes) + # Having undef_token appear on the RHS is possible if we're on a dead branch. + # Do something reasonable here, by marking the LHS as undef as well. + if val !== undef_token + incoming_vals[id] = SSAValue(make_ssa!(ci, code, idx, id, typ)) + else + code[idx] = nothing + incoming_vals[id] = undef_token + end + eidx = item + while haskey(exc_handlers, eidx) + (eidx, exc) = exc_handlers[eidx] + cidx = findfirst(x->slot_id(x[1]) == id, phicnodes[exc]) + if cidx !== nothing + node = UpsilonNode(incoming_vals[id]) + if incoming_vals[id] === undef_token + node = UpsilonNode() + typ = MaybeUndef(Union{}) + end + push!(phicnodes[exc][cidx][3].values, + NewSSAValue(insert_node!(ir, idx, typ, node, true).id - length(ir.stmts))) + end + end + end + end + end + for succ in cfg.blocks[item].succs + push!(worklist, (succ, item, copy(incoming_vals))) + end + end + # Delete any instruction in unreachable blocks (except for terminators) + for bb in setdiff(BitSet(1:length(cfg.blocks)), visited) + for idx in cfg.blocks[bb].stmts + if isa(code[idx], Union{GotoNode, GotoIfNot, ReturnNode}) + code[idx] = ReturnNode() + else + code[idx] = nothing + end + end + end + # Convert into IRCode form + new_code = ir.stmts + ssavalmap = Any[SSAValue(-1) for _ in 1:(length(ci.ssavaluetypes)+1)] + result_types = Any[Any for _ in 1:length(new_code)] + # Detect statement positions for assignments and construct array + for (bb, idx) in bbidxiter(ir) + stmt = code[idx] + # Convert GotoNode/GotoIfNot/PhiNode to BB addressing + if isa(stmt, GotoNode) + new_code[idx] = GotoNode(block_for_inst(cfg, stmt.label)) + elseif isa(stmt, GotoIfNot) + new_dest = block_for_inst(cfg, stmt.dest) + if new_dest == bb+1 + # Drop this node - it's a noop + new_code[idx] = stmt.cond + else + new_code[idx] = GotoIfNot(stmt.cond, new_dest) + end + elseif isexpr(stmt, :enter) + new_code[idx] = Expr(:enter, block_for_inst(cfg, stmt.args[1])) + ssavalmap[idx] = SSAValue(idx) # Slot to store token for pop_exception + elseif isexpr(stmt, :leave) || isexpr(stmt, :(=)) || isexpr(stmt, :return) || + isexpr(stmt, :meta) || isa(stmt, NewvarNode) + new_code[idx] = stmt + else + ssavalmap[idx] = SSAValue(idx) + result_types[idx] = ci.ssavaluetypes[idx] + if isa(stmt, PhiNode) + edges = Any[edge == 0 ? 0 : block_for_inst(cfg, edge) for edge in stmt.edges] + new_code[idx] = PhiNode(edges, stmt.values) + else + new_code[idx] = stmt + end + end + end + for (_, nodes) in phicnodes + for (_, ssa, node) in nodes + new_typ = Union{} + # TODO: This could just be the ones that depend on other phis + push!(type_refine_phi, ssa.id) + new_idx = ssa.id + node = ir.new_nodes[new_idx] + for i = 1:length(node.node.values) + orig_typ = typ = typ_for_val(node.node.values[i], ci, sptypes, -1, slottypes) + @assert !isa(typ, MaybeUndef) + while isa(typ, DelayedTyp) + typ = types(ir)[typ.phi::NewSSAValue] + end + new_typ = tmerge(new_typ, typ) + end + ir.new_nodes[new_idx] = NewNode(node.pos, node.attach_after, new_typ, node.node, node.line) + end + end + # This is a bit awkward, because it basically duplicates what type + # inference does. Ideally, we'd just use this representation earlier + # to make sure phi nodes have accurate types + changed = true + while changed + changed = false + for new_idx in type_refine_phi + node = ir.new_nodes[new_idx] + new_typ = recompute_type(node.node, ci, ir, sptypes, slottypes) + if !(node.typ ⊑ new_typ) || !(new_typ ⊑ node.typ) + ir.new_nodes[new_idx] = NewNode(node.pos, node.attach_after, new_typ, node.node, node.line) + changed = true + end + end + end + result_types = Any[isa(result_types[i], DelayedTyp) ? types(ir)[result_types[i].phi::NewSSAValue] : result_types[i] for i in 1:length(result_types)] + new_nodes = NewNode[let node = ir.new_nodes[i] + typ = isa(node.typ, DelayedTyp) ? types(ir)[node.typ.phi::NewSSAValue] : node.typ + NewNode(node.pos, node.attach_after, typ, node.node, node.line) + end for i in 1:length(ir.new_nodes)] + # Renumber SSA values + new_code = Any[new_to_regular(renumber_ssa!(new_code[i], ssavalmap), length(ir.stmts)) for i in 1:length(new_code)] + new_nodes = NewNode[let node = new_nodes[i] + NewNode(node.pos, node.attach_after, node.typ, + new_to_regular(renumber_ssa!(node.node, ssavalmap), length(ir.stmts)), + node.line) + end for i in 1:length(new_nodes)] + ir = IRCode(ir, new_code, result_types, ir.lines, ir.flags, ir.cfg, new_nodes) + @timeit "domsort" ir = domsort_ssa!(ir, domtree) + return ir +end diff --git a/base/compiler/ssair/verify.jl b/base/compiler/ssair/verify.jl new file mode 100644 index 0000000..d8297f9 --- /dev/null +++ b/base/compiler/ssair/verify.jl @@ -0,0 +1,213 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +if !isdefined(@__MODULE__, Symbol("@verify_error")) + macro verify_error(arg) + arg isa String && return esc(:(println(stderr, $arg))) + (arg isa Expr && arg.head === :string) || error("verify_error macro expected a string expression") + pushfirst!(arg.args, GlobalRef(Core, :stderr)) + pushfirst!(arg.args, :println) + arg.head = :call + return esc(arg) + end +end + +function check_op(ir::IRCode, domtree::DomTree, @nospecialize(op), use_bb::Int, use_idx::Int) + if isa(op, SSAValue) + if op.id > length(ir.stmts) + def_bb = block_for_inst(ir.cfg, ir.new_nodes[op.id - length(ir.stmts)].pos) + else + def_bb = block_for_inst(ir.cfg, op.id) + end + if (def_bb == use_bb) + if op.id > length(ir.stmts) + @assert ir.new_nodes[op.id - length(ir.stmts)].pos <= use_idx + else + if op.id >= use_idx + @verify_error "Def ($(op.id)) does not dominate use ($(use_idx)) in same BB" + error() + end + end + else + if !dominates(domtree, def_bb, use_bb) && !(bb_unreachable(domtree, def_bb) && bb_unreachable(domtree, use_bb)) + #@Base.show ir + @verify_error "Basic Block $def_bb does not dominate block $use_bb (tried to use value $(op.id))" + error() + end + end + elseif isa(op, Union{OldSSAValue, NewSSAValue}) + #@Base.show ir + @verify_error "Left over SSA marker" + error() + elseif isa(op, Union{SlotNumber, TypedSlot}) + @verify_error "Left over slot detected in converted IR" + error() + end +end + +function count_int(val::Int, arr::Vector{Int}) + n = 0 + for x in arr + if x === val + n += 1 + end + end + n +end + +function verify_ir(ir::IRCode) + # For now require compact IR + # @assert isempty(ir.new_nodes) + # Verify CFG + last_end = 0 + # Verify statements + domtree = construct_domtree(ir.cfg) + for (idx, block) in pairs(ir.cfg.blocks) + if first(block.stmts) != last_end + 1 + #ranges = [(idx,first(bb.stmts),last(bb.stmts)) for (idx, bb) in pairs(ir.cfg.blocks)] + @verify_error "First statement of BB $idx ($(first(block.stmts))) does not match end of previous ($last_end)" + error() + end + last_end = last(block.stmts) + terminator = ir.stmts[last_end] + + bb_unreachable(domtree, idx) && continue + for p in block.preds + p == 0 && continue + c = count_int(idx, ir.cfg.blocks[p].succs) + if c == 0 + @verify_error "Predecessor $p of block $idx not in successor list" + error() + elseif c == 2 + if count_int(p, block.preds) != 2 + @verify_error "Double edge from $p to $idx not correctly accounted" + error() + end + end + end + if isa(terminator, ReturnNode) + if !isempty(block.succs) + @verify_error "Block $idx ends in return or unreachable, but has successors" + error() + end + elseif isa(terminator, GotoNode) + if length(block.succs) != 1 || block.succs[1] != terminator.label + @verify_error "Block $idx successors ($(block.succs)), does not match GotoNode terminator" + error() + end + elseif isa(terminator, GotoIfNot) + if terminator.dest == idx + 1 + @verify_error "Block $idx terminator forms a double edge to block $(idx+1)" + error() + end + if length(block.succs) != 2 || (block.succs != [terminator.dest, idx+1] && block.succs != [idx+1, terminator.dest]) + @verify_error "Block $idx successors ($(block.succs)), does not match GotoIfNot terminator" + error() + end + elseif isexpr(terminator, :enter) + @label enter_check + if length(block.succs) != 2 || (block.succs != [terminator.args[1], idx+1] && block.succs != [idx+1, terminator.args[1]]) + @verify_error "Block $idx successors ($(block.succs)), does not match :enter terminator" + error() + end + else + if length(block.succs) != 1 || block.succs[1] != idx + 1 + # As a special case, we allow extra statements in the BB of an :enter + # statement, until we can do proper CFG manipulations during compaction. + for stmt in ir.stmts[first(block.stmts):last(block.stmts)] + if isexpr(stmt, :enter) + terminator = stmt + @goto enter_check + end + isa(stmt, PhiNode) || break + end + @verify_error "Block $idx successors ($(block.succs)), does not match fall-through terminator ($terminator)" + error() + end + end + for s in block.succs + if !(idx in ir.cfg.blocks[s].preds) + #@Base.show ir.cfg + #@Base.show ir + #@Base.show ir.argtypes + @verify_error "Successor $s of block $idx not in predecessor list" + error() + end + end + end + for (bb, idx) in bbidxiter(ir) + # We allow invalid IR in dead code to avoid passes having to detect when + # they're generating dead code. + bb_unreachable(domtree, bb) && continue + stmt = ir.stmts[idx] + stmt === nothing && continue + if isa(stmt, PhiNode) + @assert length(stmt.edges) == length(stmt.values) + for i = 1:length(stmt.edges) + edge = stmt.edges[i] + if !(edge == 0 && bb == 1) && !(edge in ir.cfg.blocks[bb].preds) + #@Base.show ir.argtypes + #@Base.show ir + @verify_error "Edge $edge of φ node $idx not in predecessor list" + error() + end + edge == 0 && continue + isassigned(stmt.values, i) || continue + val = stmt.values[i] + phiT = ir.types[idx] + if isa(val, SSAValue) + if !(types(ir)[val] ⊑ phiT) + #@verify_error """ + # PhiNode $idx, has operand $(val.id), whose type is not a sub lattice element. + # PhiNode type was $phiT + # Value type was $(ir.types[val.id]) + #""" + #error() + end + elseif isa(val, GlobalRef) || isa(val, Expr) + @verify_error "GlobalRefs and Exprs are not allowed as PhiNode values" + error() + end + check_op(ir, domtree, val, edge, last(ir.cfg.blocks[stmt.edges[i]].stmts)+1) + end + elseif isa(stmt, PhiCNode) + for i = 1:length(stmt.values) + val = stmt.values[i] + if !isa(val, SSAValue) + @verify_error "Operand $i of PhiC node $idx must be an SSA Value." + error() + end + if !isa(ir[val], UpsilonNode) + @verify_error "Operand $i of PhiC node $idx must reference an Upsilon node." + error() + end + end + else + if isa(stmt, Expr) || isa(stmt, ReturnNode) # TODO: make sure everything has line info + if !(stmt isa ReturnNode && !isdefined(stmt, :val)) # not actually a return node, but an unreachable marker + if ir.lines[idx] <= 0 + #@verify_error "Missing line number information for statement $idx of $ir" + end + end + end + if isa(stmt, Expr) && stmt.head === :(=) + if stmt.args[1] isa SSAValue + @verify_error "SSAValue as assignment LHS" + error() + end + end + for op in userefs(stmt) + op = op[] + check_op(ir, domtree, op, bb, idx) + end + end + end +end + +function verify_linetable(linetable::Vector{LineInfoNode}) + for i in 1:length(linetable) + line = linetable[i] + if i <= line.inlined_at + @verify_error "Misordered linetable" + end + end +end diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl new file mode 100644 index 0000000..a9543bc --- /dev/null +++ b/base/compiler/tfuncs.jl @@ -0,0 +1,1482 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +############# +# constants # +############# + +@nospecialize + +const AbstractEvalConstant = Const + +const _NAMEDTUPLE_NAME = NamedTuple.body.body.name + +const INT_INF = typemax(Int) # integer infinity + +const N_IFUNC = reinterpret(Int32, arraylen) + 1 +const T_IFUNC = Vector{Tuple{Int, Int, Any}}(undef, N_IFUNC) +const T_IFUNC_COST = Vector{Int}(undef, N_IFUNC) +const T_FFUNC_KEY = Vector{Any}() +const T_FFUNC_VAL = Vector{Tuple{Int, Int, Any}}() +const T_FFUNC_COST = Vector{Int}() +function find_tfunc(@nospecialize f) + for i = 1:length(T_FFUNC_KEY) + if T_FFUNC_KEY[i] === f + return i + end + end +end + +const DATATYPE_NAME_FIELDINDEX = fieldindex(DataType, :name) +const DATATYPE_PARAMETERS_FIELDINDEX = fieldindex(DataType, :parameters) +const DATATYPE_TYPES_FIELDINDEX = fieldindex(DataType, :types) +const DATATYPE_SUPER_FIELDINDEX = fieldindex(DataType, :super) +const DATATYPE_MUTABLE_FIELDINDEX = fieldindex(DataType, :mutable) +const DATATYPE_INSTANCE_FIELDINDEX = fieldindex(DataType, :instance) + +const TYPENAME_NAME_FIELDINDEX = fieldindex(Core.TypeName, :name) +const TYPENAME_MODULE_FIELDINDEX = fieldindex(Core.TypeName, :module) +const TYPENAME_WRAPPER_FIELDINDEX = fieldindex(Core.TypeName, :wrapper) + +########## +# tfuncs # +########## + +# Note that in most places in the compiler here, we'll assume that T=Type{S} is well-formed, +# and implies that `S <: Type`, not `1::Type{1}`, for example. +# This means that isType(T) implies we can call subtype on T.parameters[1], etc. + +function add_tfunc(f::IntrinsicFunction, minarg::Int, maxarg::Int, @nospecialize(tfunc), cost::Int) + idx = reinterpret(Int32, f) + 1 + T_IFUNC[idx] = (minarg, maxarg, tfunc) + T_IFUNC_COST[idx] = cost +end +# TODO: add @nospecialize on `f` and declare its type as `Builtin` when that's supported +function add_tfunc(f::Function, minarg::Int, maxarg::Int, @nospecialize(tfunc), cost::Int) + push!(T_FFUNC_KEY, f) + push!(T_FFUNC_VAL, (minarg, maxarg, tfunc)) + push!(T_FFUNC_COST, cost) +end + +add_tfunc(throw, 1, 1, (@nospecialize(x)) -> Bottom, 0) + +# the inverse of typeof_tfunc +# returns (type, isexact) +# if isexact is false, the actual runtime type may (will) be a subtype of t +function instanceof_tfunc(@nospecialize(t)) + if isa(t, Const) + if isa(t.val, Type) + return t.val, true + end + return Bottom, true + end + t = widenconst(t) + if t === Bottom || t === typeof(Bottom) || typeintersect(t, Type) === Bottom + return Bottom, true + elseif isType(t) + tp = t.parameters[1] + return tp, !has_free_typevars(tp) + elseif isa(t, UnionAll) + t′ = unwrap_unionall(t) + t′′, isexact = instanceof_tfunc(t′) + tr = rewrap_unionall(t′′, t) + if t′′ isa DataType && !has_free_typevars(tr) + # a real instance must be within the declared bounds of the type, + # so we can intersect with the original wrapper. + tr = typeintersect(tr, t′′.name.wrapper) + end + return tr, isexact + elseif isa(t, Union) + ta, isexact_a = instanceof_tfunc(t.a) + tb, isexact_b = instanceof_tfunc(t.b) + ta === Union{} && return tb, isexact_b + tb === Union{} && return ta, isexact_a + ta == tb && return ta, isexact_a && isexact_b + return Union{ta, tb}, false # at runtime, will be exactly one of these + end + return Any, false +end +bitcast_tfunc(@nospecialize(t), @nospecialize(x)) = instanceof_tfunc(t)[1] +math_tfunc(@nospecialize(x)) = widenconst(x) +math_tfunc(@nospecialize(x), @nospecialize(y)) = widenconst(x) +math_tfunc(@nospecialize(x), @nospecialize(y), @nospecialize(z)) = widenconst(x) +fptoui_tfunc(@nospecialize(t), @nospecialize(x)) = bitcast_tfunc(t, x) +fptosi_tfunc(@nospecialize(t), @nospecialize(x)) = bitcast_tfunc(t, x) + + ## conversion ## +add_tfunc(bitcast, 2, 2, bitcast_tfunc, 1) +add_tfunc(sext_int, 2, 2, bitcast_tfunc, 1) +add_tfunc(zext_int, 2, 2, bitcast_tfunc, 1) +add_tfunc(trunc_int, 2, 2, bitcast_tfunc, 1) +add_tfunc(fptoui, 2, 2, fptoui_tfunc, 1) +add_tfunc(fptosi, 2, 2, fptosi_tfunc, 1) +add_tfunc(uitofp, 2, 2, bitcast_tfunc, 1) +add_tfunc(sitofp, 2, 2, bitcast_tfunc, 1) +add_tfunc(fptrunc, 2, 2, bitcast_tfunc, 1) +add_tfunc(fpext, 2, 2, bitcast_tfunc, 1) + ## arithmetic ## +add_tfunc(neg_int, 1, 1, math_tfunc, 1) +add_tfunc(add_int, 2, 2, math_tfunc, 1) +add_tfunc(sub_int, 2, 2, math_tfunc, 1) +add_tfunc(mul_int, 2, 2, math_tfunc, 4) +add_tfunc(sdiv_int, 2, 2, math_tfunc, 30) +add_tfunc(udiv_int, 2, 2, math_tfunc, 30) +add_tfunc(srem_int, 2, 2, math_tfunc, 30) +add_tfunc(urem_int, 2, 2, math_tfunc, 30) +add_tfunc(add_ptr, 2, 2, math_tfunc, 1) +add_tfunc(sub_ptr, 2, 2, math_tfunc, 1) +add_tfunc(neg_float, 1, 1, math_tfunc, 1) +add_tfunc(add_float, 2, 2, math_tfunc, 1) +add_tfunc(sub_float, 2, 2, math_tfunc, 1) +add_tfunc(mul_float, 2, 2, math_tfunc, 4) +add_tfunc(div_float, 2, 2, math_tfunc, 20) +add_tfunc(rem_float, 2, 2, math_tfunc, 20) +add_tfunc(fma_float, 3, 3, math_tfunc, 5) +add_tfunc(muladd_float, 3, 3, math_tfunc, 5) + ## fast arithmetic ## +add_tfunc(neg_float_fast, 1, 1, math_tfunc, 1) +add_tfunc(add_float_fast, 2, 2, math_tfunc, 1) +add_tfunc(sub_float_fast, 2, 2, math_tfunc, 1) +add_tfunc(mul_float_fast, 2, 2, math_tfunc, 2) +add_tfunc(div_float_fast, 2, 2, math_tfunc, 10) +add_tfunc(rem_float_fast, 2, 2, math_tfunc, 10) + ## bitwise operators ## +add_tfunc(and_int, 2, 2, math_tfunc, 1) +add_tfunc(or_int, 2, 2, math_tfunc, 1) +add_tfunc(xor_int, 2, 2, math_tfunc, 1) +add_tfunc(not_int, 1, 1, math_tfunc, 1) +add_tfunc(shl_int, 2, 2, math_tfunc, 1) +add_tfunc(lshr_int, 2, 2, math_tfunc, 1) +add_tfunc(ashr_int, 2, 2, math_tfunc, 1) +add_tfunc(bswap_int, 1, 1, math_tfunc, 1) +add_tfunc(ctpop_int, 1, 1, math_tfunc, 1) +add_tfunc(ctlz_int, 1, 1, math_tfunc, 1) +add_tfunc(cttz_int, 1, 1, math_tfunc, 1) +add_tfunc(checked_sdiv_int, 2, 2, math_tfunc, 40) +add_tfunc(checked_udiv_int, 2, 2, math_tfunc, 40) +add_tfunc(checked_srem_int, 2, 2, math_tfunc, 40) +add_tfunc(checked_urem_int, 2, 2, math_tfunc, 40) + ## functions ## +add_tfunc(abs_float, 1, 1, math_tfunc, 2) +add_tfunc(copysign_float, 2, 2, math_tfunc, 2) +add_tfunc(flipsign_int, 2, 2, math_tfunc, 1) +add_tfunc(ceil_llvm, 1, 1, math_tfunc, 10) +add_tfunc(floor_llvm, 1, 1, math_tfunc, 10) +add_tfunc(trunc_llvm, 1, 1, math_tfunc, 10) +add_tfunc(rint_llvm, 1, 1, math_tfunc, 10) +add_tfunc(sqrt_llvm, 1, 1, math_tfunc, 20) + ## same-type comparisons ## +cmp_tfunc(@nospecialize(x), @nospecialize(y)) = Bool +add_tfunc(eq_int, 2, 2, cmp_tfunc, 1) +add_tfunc(ne_int, 2, 2, cmp_tfunc, 1) +add_tfunc(slt_int, 2, 2, cmp_tfunc, 1) +add_tfunc(ult_int, 2, 2, cmp_tfunc, 1) +add_tfunc(sle_int, 2, 2, cmp_tfunc, 1) +add_tfunc(ule_int, 2, 2, cmp_tfunc, 1) +add_tfunc(eq_float, 2, 2, cmp_tfunc, 2) +add_tfunc(ne_float, 2, 2, cmp_tfunc, 2) +add_tfunc(lt_float, 2, 2, cmp_tfunc, 2) +add_tfunc(le_float, 2, 2, cmp_tfunc, 2) +add_tfunc(fpiseq, 2, 2, cmp_tfunc, 1) +add_tfunc(fpislt, 2, 2, cmp_tfunc, 1) +add_tfunc(eq_float_fast, 2, 2, cmp_tfunc, 1) +add_tfunc(ne_float_fast, 2, 2, cmp_tfunc, 1) +add_tfunc(lt_float_fast, 2, 2, cmp_tfunc, 1) +add_tfunc(le_float_fast, 2, 2, cmp_tfunc, 1) + + ## checked arithmetic ## +chk_tfunc(@nospecialize(x), @nospecialize(y)) = Tuple{widenconst(x), Bool} +add_tfunc(checked_sadd_int, 2, 2, chk_tfunc, 10) +add_tfunc(checked_uadd_int, 2, 2, chk_tfunc, 10) +add_tfunc(checked_ssub_int, 2, 2, chk_tfunc, 10) +add_tfunc(checked_usub_int, 2, 2, chk_tfunc, 10) +add_tfunc(checked_smul_int, 2, 2, chk_tfunc, 10) +add_tfunc(checked_umul_int, 2, 2, chk_tfunc, 10) + ## other, misc intrinsics ## +add_tfunc(Core.Intrinsics.llvmcall, 3, INT_INF, + (@nospecialize(fptr), @nospecialize(rt), @nospecialize(at), a...) -> instanceof_tfunc(rt)[1], 10) +cglobal_tfunc(@nospecialize(fptr)) = Ptr{Cvoid} +cglobal_tfunc(@nospecialize(fptr), @nospecialize(t)) = (isType(t) ? Ptr{t.parameters[1]} : Ptr) +cglobal_tfunc(@nospecialize(fptr), t::Const) = (isa(t.val, Type) ? Ptr{t.val} : Ptr) +add_tfunc(Core.Intrinsics.cglobal, 1, 2, cglobal_tfunc, 5) + +function ifelse_tfunc(@nospecialize(cnd), @nospecialize(x), @nospecialize(y)) + if isa(cnd, Const) + if cnd.val === true + return x + elseif cnd.val === false + return y + else + return Bottom + end + elseif isa(cnd, Conditional) + # optimized (if applicable) in abstract_call + elseif !(Bool ⊑ cnd) + return Bottom + end + return tmerge(x, y) +end +add_tfunc(ifelse, 3, 3, ifelse_tfunc, 1) + +function egal_tfunc(@nospecialize(x), @nospecialize(y)) + xx = widenconditional(x) + yy = widenconditional(y) + if isa(x, Conditional) && isa(yy, Const) + yy.val === false && return Conditional(x.var, x.elsetype, x.vtype) + yy.val === true && return x + return Const(false) + elseif isa(y, Conditional) && isa(xx, Const) + xx.val === false && return Conditional(y.var, y.elsetype, y.vtype) + xx.val === true && return y + return Const(false) + elseif isa(xx, Const) && isa(yy, Const) + return Const(xx.val === yy.val) + elseif typeintersect(widenconst(xx), widenconst(yy)) === Bottom + return Const(false) + elseif (isa(xx, Const) && y === typeof(xx.val) && isdefined(y, :instance)) || + (isa(yy, Const) && x === typeof(yy.val) && isdefined(x, :instance)) + return Const(true) + end + return Bool +end +add_tfunc(===, 2, 2, egal_tfunc, 1) + +function isdefined_nothrow(argtypes::Array{Any, 1}) + length(argtypes) == 2 || return false + return typeintersect(widenconst(argtypes[1]), Module) === Union{} ? + (argtypes[2] ⊑ Symbol || argtypes[2] ⊑ Int) : + argtypes[2] ⊑ Symbol +end +function isdefined_tfunc(@nospecialize(args...)) + arg1 = args[1] + if isa(arg1, Const) + a1 = typeof(arg1.val) + else + a1 = widenconst(arg1) + end + if isType(a1) + return Bool + end + a1 = unwrap_unionall(a1) + if isa(a1, DataType) && !a1.abstract + if a1 === Module + length(args) == 2 || return Bottom + sym = args[2] + Symbol <: widenconst(sym) || return Bottom + if isa(sym, Const) && isa(sym.val, Symbol) && isa(arg1, Const) && isdefined(arg1.val, sym.val) + return Const(true) + end + elseif length(args) == 2 && isa(args[2], Const) + val = args[2].val + idx::Int = 0 + if isa(val, Symbol) + idx = fieldindex(a1, val, false) + elseif isa(val, Int) + idx = val + else + return Bottom + end + if 1 <= idx <= a1.ninitialized + return Const(true) + elseif a1.name === _NAMEDTUPLE_NAME + if isconcretetype(a1) + return Const(false) + end + elseif idx <= 0 || (!isvatuple(a1) && idx > fieldcount(a1)) + return Const(false) + elseif !isvatuple(a1) && isbitstype(fieldtype(a1, idx)) + return Const(true) + elseif isa(arg1, Const) + arg1v = (arg1::Const).val + if isimmutable(arg1v) || isdefined(arg1v, idx) || (isa(arg1v, DataType) && is_dt_const_field(idx)) + return Const(isdefined(arg1v, idx)) + end + end + end + end + Bool +end + +add_tfunc(isdefined, 1, 2, isdefined_tfunc, 1) +function sizeof_nothrow(@nospecialize(x)) + if isa(x, Const) + x = x.val + if !isa(x, Type) || x === DataType + return true + end + elseif isa(x, Conditional) + return true + else + x = widenconst(x) + end + if isa(x, Union) + return sizeof_nothrow(x.a) && sizeof_nothrow(x.b) + end + isconstType(x) && (x = x.parameters[1]) # since sizeof(typeof(x)) == sizeof(x) + x === DataType && return false + return isconcretetype(x) || isprimitivetype(x) +end +function _const_sizeof(@nospecialize(x)) + # Constant Vector does not have constant size + isa(x, Vector) && return Int + size = try + Core.sizeof(x) + catch ex + # Might return + # "argument is an abstract type; size is indeterminate" or + # "type does not have a fixed size" + isa(ex, ErrorException) || rethrow() + return Int + end + return Const(size) +end +function sizeof_tfunc(@nospecialize(x),) + isa(x, Const) && return _const_sizeof(x.val) + isa(x, Conditional) && return _const_sizeof(Bool) + isconstType(x) && return _const_sizeof(x.parameters[1]) + x = widenconst(x) + if isa(x, Union) + return tmerge(sizeof_tfunc(x.a), sizeof_tfunc(x.b)) + end + x !== DataType && isconcretetype(x) && return _const_sizeof(x) + isprimitivetype(x) && return _const_sizeof(x) + return Int +end +add_tfunc(Core.sizeof, 1, 1, sizeof_tfunc, 0) +function nfields_tfunc(@nospecialize(x)) + isa(x, Const) && return Const(nfields(x.val)) + isa(x, Conditional) && return Const(0) + x = widenconst(x) + if isa(x, DataType) && !x.abstract && !(x.name === Tuple.name && isvatuple(x)) + if !(x.name === _NAMEDTUPLE_NAME && !isconcretetype(x)) + return Const(isdefined(x, :types) ? length(x.types) : length(x.name.names)) + end + end + return Int +end +add_tfunc(nfields, 1, 1, nfields_tfunc, 0) +add_tfunc(Core._expr, 1, INT_INF, (@nospecialize args...)->Expr, 100) +function typevar_tfunc(@nospecialize(n), @nospecialize(lb_arg), @nospecialize(ub_arg)) + lb = Union{} + ub = Any + ub_certain = lb_certain = true + if isa(n, Const) + isa(n.val, Symbol) || return Union{} + if isa(lb_arg, Const) + lb = lb_arg.val + elseif isType(lb_arg) + lb = lb_arg.parameters[1] + lb_certain = false + else + return TypeVar + end + if isa(ub_arg, Const) + ub = ub_arg.val + elseif isType(ub_arg) + ub = ub_arg.parameters[1] + ub_certain = false + else + return TypeVar + end + tv = TypeVar(n.val, lb, ub) + return PartialTypeVar(tv, lb_certain, ub_certain) + end + return TypeVar +end +function typebound_nothrow(b) + b = widenconst(b) + (b ⊑ TypeVar) && return true + if isType(b) + b = unwrap_unionall(b.parameters[1]) + b === Union{} && return true + return !isa(b, DataType) || b.name != _va_typename + end + return false +end +function typevar_nothrow(n, lb, ub) + (n ⊑ Symbol) || return false + typebound_nothrow(lb) || return false + typebound_nothrow(ub) || return false + return true +end +add_tfunc(Core._typevar, 3, 3, typevar_tfunc, 100) +add_tfunc(applicable, 1, INT_INF, (@nospecialize(f), args...)->Bool, 100) +add_tfunc(Core.Intrinsics.arraylen, 1, 1, @nospecialize(x)->Int, 4) +add_tfunc(arraysize, 2, 2, (@nospecialize(a), @nospecialize(d))->Int, 4) +add_tfunc(pointerref, 3, 3, + function (@nospecialize(a), @nospecialize(i), @nospecialize(align)) + a = widenconst(a) + if a <: Ptr + if isa(a,DataType) && isa(a.parameters[1],Type) + return a.parameters[1] + elseif isa(a,UnionAll) && !has_free_typevars(a) + unw = unwrap_unionall(a) + if isa(unw,DataType) + return rewrap_unionall(unw.parameters[1], a) + end + end + end + return Any + end, 4) +add_tfunc(pointerset, 4, 4, (@nospecialize(a), @nospecialize(v), @nospecialize(i), @nospecialize(align)) -> a, 5) + +# more accurate typeof_tfunc for vararg tuples abstract only in length +function typeof_concrete_vararg(t::DataType) + np = length(t.parameters) + for i = 1:np + p = t.parameters[i] + if i == np && isvarargtype(p) + pp = unwrap_unionall(p) + if isconcretetype(pp.parameters[1]) && pp.parameters[2] isa TypeVar + return rewrap_unionall(Type{Tuple{t.parameters[1:np-1]..., pp}}, p) + end + elseif !isconcretetype(p) + break + end + end + return nothing +end + +function typeof_tfunc(@nospecialize(t)) + isa(t, Const) && return Const(typeof(t.val)) + t = widenconst(t) + if isType(t) + tp = t.parameters[1] + if hasuniquerep(tp) + return Const(typeof(tp)) + end + elseif isa(t, DataType) + if isconcretetype(t) + return Const(t) + elseif t === Any + return DataType + else + if t.name === Tuple.name + tt = typeof_concrete_vararg(t) + tt === nothing || return tt + end + return Type{<:t} + end + elseif isa(t, Union) + a = widenconst(typeof_tfunc(t.a)) + b = widenconst(typeof_tfunc(t.b)) + return Union{a, b} + elseif isa(t, TypeVar) && !(Any === t.ub) + return typeof_tfunc(t.ub) + elseif isa(t, UnionAll) + u = unwrap_unionall(t) + if isa(u, DataType) && !u.abstract + if u.name === Tuple.name + uu = typeof_concrete_vararg(u) + if uu !== nothing + return rewrap_unionall(uu, t) + end + else + return rewrap_unionall(Type{u}, t) + end + end + return rewrap_unionall(widenconst(typeof_tfunc(u)), t) + end + return DataType # typeof(anything)::DataType +end +add_tfunc(typeof, 1, 1, typeof_tfunc, 0) + +function typeassert_tfunc(@nospecialize(v), @nospecialize(t)) + t = instanceof_tfunc(t)[1] + t === Any && return v + if isa(v, Const) + if !has_free_typevars(t) && !isa(v.val, t) + return Bottom + end + return v + elseif isa(v, Conditional) + if !(Bool <: t) + return Bottom + end + return v + end + return typeintersect(widenconst(v), t) +end +add_tfunc(typeassert, 2, 2, typeassert_tfunc, 4) + +function isa_tfunc(@nospecialize(v), @nospecialize(tt)) + t, isexact = instanceof_tfunc(tt) + if t === Bottom + # check if t could be equivalent to typeof(Bottom), since that's valid in `isa`, but the set of `v` is empty + # if `t` cannot have instances, it's also invalid on the RHS of isa + if typeintersect(widenconst(tt), Type) === Union{} + return Union{} + end + return Const(false) + end + if !has_free_typevars(t) + if v ⊑ t + if isexact && isnotbrokensubtype(v, t) + return Const(true) + end + else + if isa(v, Const) || isa(v, Conditional) + # this and the `isdispatchelem` below test for knowledge of a + # leaftype appearing on the LHS (ensuring the isa is precise) + return Const(false) + end + v = widenconst(v) + isdispatchelem(v) && return Const(false) + if typeintersect(v, t) === Bottom + # similar to `isnotbrokensubtype` check above, `typeintersect(v, t)` + # can't be trusted for kind types so we do an extra check here + if !iskindtype(v) + return Const(false) + end + end + end + end + # TODO: handle non-leaftype(t) by testing against lower and upper bounds + return Bool +end +add_tfunc(isa, 2, 2, isa_tfunc, 0) + +function subtype_tfunc(@nospecialize(a), @nospecialize(b)) + a, isexact_a = instanceof_tfunc(a) + b, isexact_b = instanceof_tfunc(b) + if !has_free_typevars(a) && !has_free_typevars(b) + if a <: b + if isexact_b || a === Bottom + return Const(true) + end + else + if isexact_a || (b !== Bottom && typeintersect(a, b) === Union{}) + return Const(false) + end + end + end + return Bool +end +add_tfunc(<:, 2, 2, subtype_tfunc, 0) + +is_dt_const_field(fld::Int) = ( + fld == DATATYPE_NAME_FIELDINDEX || + fld == DATATYPE_PARAMETERS_FIELDINDEX || + fld == DATATYPE_TYPES_FIELDINDEX || + fld == DATATYPE_SUPER_FIELDINDEX || + fld == DATATYPE_MUTABLE_FIELDINDEX || + fld == DATATYPE_INSTANCE_FIELDINDEX + ) +function const_datatype_getfield_tfunc(@nospecialize(sv), fld::Int) + if fld == DATATYPE_INSTANCE_FIELDINDEX + return isdefined(sv, fld) ? Const(getfield(sv, fld)) : Union{} + elseif is_dt_const_field(fld) + return Const(getfield(sv, fld)) + end + return nothing +end + +function fieldcount_noerror(@nospecialize t) + if t isa UnionAll || t isa Union + t = argument_datatype(t) + if t === nothing + return nothing + end + t = t::DataType + elseif t == Union{} + return 0 + end + if !(t isa DataType) + return nothing + end + if t.name === NamedTuple.body.body.name + names, types = t.parameters + if names isa Tuple + return length(names) + end + if types isa DataType && types <: Tuple + return fieldcount_noerror(types) + end + abstr = true + else + abstr = t.abstract || (t.name === Tuple.name && isvatuple(t)) + end + if abstr + return nothing + end + return isdefined(t, :types) ? length(t.types) : length(t.name.names) +end + + +function try_compute_fieldidx(typ::DataType, @nospecialize(field)) + if isa(field, Symbol) + field = fieldindex(typ, field, false) + field == 0 && return nothing + elseif isa(field, Integer) + max_fields = fieldcount_noerror(typ) + max_fields === nothing && return nothing + (1 <= field <= max_fields) || return nothing + else + return nothing + end + return field +end + +function getfield_nothrow(argtypes::Vector{Any}) + 2 <= length(argtypes) <= 3 || return false + length(argtypes) == 2 && return getfield_nothrow(argtypes[1], argtypes[2], Const(true)) + return getfield_nothrow(argtypes[1], argtypes[2], argtypes[3]) +end +function getfield_nothrow(@nospecialize(s00), @nospecialize(name), @nospecialize(inbounds)) + bounds_check_disabled = isa(inbounds, Const) && inbounds.val === false + # If we don't have invounds and don't know the field, don't even bother + if !bounds_check_disabled + isa(name, Const) || return false + end + + # If we have s00 being a const, we can potentially refine our type-based analysis above + if isa(s00, Const) || isconstType(s00) + if !isa(s00, Const) + sv = s00.parameters[1] + else + sv = s00.val + end + if isa(name, Const) + if !isa(name.val, Symbol) + isa(sv, Module) && return false + isa(name.val, Int) || return false + end + return isdefined(sv, name.val) + end + if bounds_check_disabled && !isa(sv, Module) + # If bounds checking is disabled and all fields are assigned, + # we may assume that we don't throw + for i = 1:fieldcount(typeof(sv)) + isdefined(sv, i) || return false + end + return true + end + return false + end + + s = unwrap_unionall(widenconst(s00)) + if isa(s, Union) + return getfield_nothrow(rewrap(s.a, s00), name, inbounds) && + getfield_nothrow(rewrap(s.b, s00), name, inbounds) + elseif isa(s, DataType) + # Can't say anything about abstract types + s.abstract && return false + # If all fields are always initialized, and bounds check is disabled, we can assume + # we don't throw + if bounds_check_disabled && !isvatuple(s) && s.name !== NamedTuple.body.body.name && fieldcount(s) == s.ninitialized + return true + end + # Else we need to know what the field is + isa(name, Const) || return false + field = try_compute_fieldidx(s, name.val) + field === nothing && return false + field <= s.ninitialized && return true + end + + return false +end + +getfield_tfunc(@nospecialize(s00), @nospecialize(name), @nospecialize(inbounds)) = + getfield_tfunc(s00, name) +function getfield_tfunc(@nospecialize(s00), @nospecialize(name)) + s = unwrap_unionall(s00) + if isa(s, Union) + return tmerge(getfield_tfunc(rewrap(s.a,s00), name), + getfield_tfunc(rewrap(s.b,s00), name)) + elseif isa(s, Conditional) + return Bottom # Bool has no fields + elseif isa(s, Const) || isconstType(s) + if !isa(s, Const) + sv = s.parameters[1] + else + sv = s.val + end + if isa(name, Const) + nv = name.val + if isa(sv, UnionAll) + if nv === :var || nv === 1 + return Const(sv.var) + elseif nv === :body || nv === 2 + return Const(sv.body) + end + elseif isa(sv, DataType) + idx = nv + if isa(idx, Symbol) + idx = fieldindex(DataType, idx, false) + end + if isa(idx, Int) + t = const_datatype_getfield_tfunc(sv, idx) + t === nothing || return t + end + elseif isa(sv, Core.TypeName) + fld = isa(nv, Symbol) ? fieldindex(Core.TypeName, nv, false) : nv + if (fld == TYPENAME_NAME_FIELDINDEX || + fld == TYPENAME_MODULE_FIELDINDEX || + fld == TYPENAME_WRAPPER_FIELDINDEX) + return AbstractEvalConstant(getfield(sv, fld)) + end + end + if isa(sv, Module) && isa(nv, Symbol) + return abstract_eval_global(sv, nv) + end + if !(isa(nv,Symbol) || isa(nv,Int)) + return Bottom + end + if (isa(sv, SimpleVector) || isimmutable(sv)) && isdefined(sv, nv) + return AbstractEvalConstant(getfield(sv, nv)) + end + end + s = typeof(sv) + elseif isa(s, PartialStruct) + if isa(name, Const) + nv = name.val + if isa(nv, Symbol) + nv = fieldindex(widenconst(s), nv, false) + end + if isa(nv, Int) && 1 <= nv <= length(s.fields) + return s.fields[nv] + end + end + s = widenconst(s) + end + if isType(s) || !isa(s, DataType) || s.abstract + return Any + end + if s <: Tuple && name ⊑ Symbol + return Bottom + end + if s <: Module + if name ⊑ Int + return Bottom + end + return Any + end + if s.name === _NAMEDTUPLE_NAME && !isconcretetype(s) + if isa(name, Const) && isa(name.val, Symbol) + if isa(s.parameters[1], Tuple) + name = Const(Int(ccall(:jl_field_index, Cint, (Any, Any, Cint), s, name.val, false)+1)) + else + name = Int + end + elseif Symbol ⊑ name + name = Int + end + _ts = s.parameters[2] + while isa(_ts, TypeVar) + _ts = _ts.ub + end + _ts = rewrap_unionall(_ts, s00) + if !(_ts <: Tuple) + return Any + end + return getfield_tfunc(_ts, name) + end + ftypes = datatype_fieldtypes(s) + if isempty(ftypes) + return Bottom + end + if isa(name, Conditional) + return Bottom # can't index fields with Bool + end + if !isa(name, Const) + if !(Int <: name || Symbol <: name) + return Bottom + end + if length(ftypes) == 1 + return rewrap_unionall(unwrapva(ftypes[1]), s00) + end + # union together types of all fields + t = Bottom + for _ft in ftypes + t = tmerge(t, rewrap_unionall(unwrapva(_ft), s00)) + t === Any && break + end + return t + end + fld = name.val + if isa(fld, Symbol) + fld = fieldindex(s, fld, false) + end + if !isa(fld, Int) + return Bottom + end + nf = length(ftypes) + if s <: Tuple && fld >= nf && isvarargtype(ftypes[nf]) + return rewrap_unionall(unwrapva(ftypes[nf]), s00) + end + if fld < 1 || fld > nf + return Bottom + end + if isconstType(s00) + sp = s00.parameters[1] + elseif isa(s00, Const) + sp = s00.val + else + sp = nothing + end + if isa(sp, DataType) + t = const_datatype_getfield_tfunc(sp, fld) + t !== nothing && return t + end + R = ftypes[fld] + if isempty(s.parameters) + return R + end + return rewrap_unionall(R, s00) +end +add_tfunc(getfield, 2, 3, getfield_tfunc, 1) +add_tfunc(setfield!, 3, 3, (@nospecialize(o), @nospecialize(f), @nospecialize(v)) -> v, 3) +fieldtype_tfunc(@nospecialize(s0), @nospecialize(name), @nospecialize(inbounds)) = + fieldtype_tfunc(s0, name) + +function fieldtype_nothrow(@nospecialize(s0), @nospecialize(name)) + s0 === Bottom && return true # unreachable + if s0 === Any || s0 === Type || DataType ⊑ s0 || UnionAll ⊑ s0 + # We have no idea + return false + end + + if !isa(name, Const) || (!isa(name.val, Symbol) && !isa(name.val, Int)) + # Due to bounds checking, we can't say anything unless we know what + # the name is. + return false + end + + su = unwrap_unionall(s0) + if isa(su, Union) + return fieldtype_nothrow(rewrap_unionall(su.a, s0), name) && + fieldtype_nothrow(rewrap_unionall(su.b, s0), name) + end + + s, exact = instanceof_tfunc(s0) + s === Bottom && return false # always + return _fieldtype_nothrow(s, exact, name) +end + +function _fieldtype_nothrow(@nospecialize(s), exact::Bool, name::Const) + u = unwrap_unionall(s) + if isa(u, Union) + a = _fieldtype_nothrow(u.a, exact, name) + b = _fieldtype_nothrow(u.b, exact, name) + return exact ? (a || b) : (a && b) + end + u isa DataType || return false + u.abstract && return false + if u.name === _NAMEDTUPLE_NAME && !isconcretetype(u) + # TODO: better approximate inference + return false + end + fld = name.val + if isa(fld, Symbol) + fld = fieldindex(u, fld, false) + end + isa(fld, Int) || return false + ftypes = datatype_fieldtypes(u) + nf = length(ftypes) + (fld >= 1 && fld <= nf) || return false + if u.name === Tuple.name && fld >= nf && isvarargtype(ftypes[nf]) + # The length of the tuple will be determined at runtime, we can't say + # anything + return false + end + return true +end + +function fieldtype_tfunc(@nospecialize(s0), @nospecialize(name)) + if s0 === Bottom + return Bottom + end + if s0 === Any || s0 === Type || DataType ⊑ s0 || UnionAll ⊑ s0 + return Type + end + # fieldtype only accepts Types, errors on `Module` + if isa(s0, Const) && (!(isa(s0.val, DataType) || isa(s0.val, UnionAll) || isa(s0.val, Union)) || s0.val === Module) + return Bottom + end + if s0 == Type{Module} || s0 == Type{Union{}} || isa(s0, Conditional) + return Bottom + end + + su = unwrap_unionall(s0) + if isa(su, Union) + return tmerge(fieldtype_tfunc(rewrap(su.a, s0), name), + fieldtype_tfunc(rewrap(su.b, s0), name)) + end + + s, exact = instanceof_tfunc(s0) + s === Bottom && return Bottom + return _fieldtype_tfunc(s, exact, name) +end + +function _fieldtype_tfunc(@nospecialize(s), exact::Bool, @nospecialize(name)) + exact = exact && !has_free_typevars(s) + u = unwrap_unionall(s) + if isa(u, Union) + return tmerge(_fieldtype_tfunc(rewrap(u.a, s), exact, name), + _fieldtype_tfunc(rewrap(u.b, s), exact, name)) + end + u isa DataType || return Type + u.abstract && return Type + if u.name === _NAMEDTUPLE_NAME && !isconcretetype(u) + # TODO: better approximate inference + return Type + end + ftypes = datatype_fieldtypes(u) + if isempty(ftypes) + return Bottom + end + + if !isa(name, Const) + name = widenconst(name) + if !(Int <: name || Symbol <: name) + return Bottom + end + t = Bottom + for i in 1:length(ftypes) + ft1 = unwrapva(ftypes[i]) + exactft1 = exact || (!has_free_typevars(ft1) && u.name !== Tuple.name) + ft1 = rewrap_unionall(ft1, s) + if exactft1 + if hasuniquerep(ft1) + ft1 = Const(ft1) # ft unique via type cache + else + ft1 = Type{ft1} + end + else + ft1 = Type{ft} where ft<:ft1 + end + t = tmerge(t, ft1) + t === Any && break + end + return t + end + + fld = name.val + if isa(fld, Symbol) + fld = fieldindex(u, fld, false) + end + if !isa(fld, Int) + return Bottom + end + nf = length(ftypes) + if u.name === Tuple.name && fld >= nf && isvarargtype(ftypes[nf]) + ft = unwrapva(ftypes[nf]) + elseif fld < 1 || fld > nf + return Bottom + else + ft = ftypes[fld] + end + + exactft = exact || (!has_free_typevars(ft) && u.name !== Tuple.name) + ft = rewrap_unionall(ft, s) + if exactft + if hasuniquerep(ft) + return Const(ft) # ft unique via type cache + end + return Type{ft} + end + return Type{<:ft} +end +add_tfunc(fieldtype, 2, 3, fieldtype_tfunc, 0) + +function apply_type_nothrow(argtypes::Array{Any, 1}, @nospecialize(rt)) + rt === Type && return false + length(argtypes) >= 1 || return false + headtypetype = argtypes[1] + if isa(headtypetype, Const) + headtype = headtypetype.val + elseif isconstType(headtypetype) + headtype = headtypetype.parameters[1] + else + return false + end + # We know the apply_type is well formed. Oherwise our rt would have been + # Bottom (or Type). + (headtype === Union) && return true + isa(rt, Const) && return true + u = headtype + for i = 2:length(argtypes) + isa(u, UnionAll) || return false + ai = widenconditional(argtypes[i]) + if ai === TypeVar + # We don't know anything about the bounds of this typevar, but as + # long as the UnionAll is not constrained, that's ok. + if !(u.var.lb === Union{} && u.var.ub === Any) + return false + end + elseif isa(ai, Const) && isa(ai.val, Type) + ai = ai.val + if has_free_typevars(u.var.lb) || has_free_typevars(u.var.ub) + return false + end + if !(u.var.lb <: ai <: u.var.ub) + return false + end + else + return false + end + u = u.body + end + return true +end + +const _tvarnames = Symbol[:_A, :_B, :_C, :_D, :_E, :_F, :_G, :_H, :_I, :_J, :_K, :_L, :_M, + :_N, :_O, :_P, :_Q, :_R, :_S, :_T, :_U, :_V, :_W, :_X, :_Y, :_Z] + +# TODO: handle e.g. apply_type(T, R::Union{Type{Int32},Type{Float64}}) +function apply_type_tfunc(@nospecialize(headtypetype), @nospecialize args...) + if isa(headtypetype, Const) + headtype = headtypetype.val + elseif isconstType(headtypetype) + headtype = headtypetype.parameters[1] + else + return Type + end + largs = length(args) + if headtype === Union + largs == 0 && return Const(Bottom) + hasnonType = false + for i = 1:largs + ai = args[i] + if isa(ai, Const) + if !isa(ai.val, Type) + if isa(ai.val, TypeVar) + hasnonType = true + else + return Bottom + end + end + else + if !isType(ai) + if !isa(ai, Type) || typeintersect(ai, Type) !== Bottom || typeintersect(ai, TypeVar) !== Bottom + hasnonType = true + else + return Bottom + end + end + end + end + largs == 1 && return isa(args[1], Type) ? typeintersect(args[1], Type) : Type + hasnonType && return Type + ty = Union{} + allconst = true + for i = 1:largs + ai = args[i] + if isType(ai) + aty = ai.parameters[1] + allconst &= hasuniquerep(aty) + else + aty = (ai::Const).val + end + ty = Union{ty, aty} + end + return allconst ? Const(ty) : Type{ty} + end + istuple = (headtype == Tuple) + if !istuple && !isa(headtype, UnionAll) + return Union{} + end + uncertain = false + canconst = true + tparams = Any[] + outervars = Any[] + varnamectr = 1 + for i = 1:largs + ai = widenconditional(args[i]) + if isType(ai) + aip1 = ai.parameters[1] + canconst &= !has_free_typevars(aip1) + push!(tparams, aip1) + elseif isa(ai, Const) && (isa(ai.val, Type) || isa(ai.val, TypeVar) || valid_tparam(ai.val)) + push!(tparams, ai.val) + elseif isa(ai, PartialTypeVar) + canconst = false + push!(tparams, ai.tv) + else + uncertain = true + # These blocks improve type info but make compilation a bit slower. + # XXX + #unw = unwrap_unionall(ai) + #isT = isType(unw) + #if isT && isa(ai,UnionAll) && contains_is(outervars, ai.var) + # ai = rename_unionall(ai) + # unw = unwrap_unionall(ai) + #end + if istuple + if i == largs + push!(tparams, Vararg) + # XXX + #elseif isT + # push!(tparams, rewrap_unionall(unw.parameters[1], ai)) + else + push!(tparams, Any) + end + # XXX + #elseif isT + # push!(tparams, unw.parameters[1]) + # while isa(ai, UnionAll) + # push!(outervars, ai.var) + # ai = ai.body + # end + else + tvname = varnamectr <= length(_tvarnames) ? _tvarnames[varnamectr] : :_Z + varnamectr += 1 + v = TypeVar(tvname) + push!(tparams, v) + push!(outervars, v) + end + end + end + local appl + try + appl = apply_type(headtype, tparams...) + catch ex + # type instantiation might fail if one of the type parameters + # doesn't match, which could happen if a type estimate is too coarse + return Type{<:headtype} + end + !uncertain && canconst && return Const(appl) + if isvarargtype(headtype) + return Type + end + if istuple + return Type{<:appl} + end + ans = Type{appl} + for i = length(outervars):-1:1 + ans = UnionAll(outervars[i], ans) + end + return ans +end +add_tfunc(apply_type, 1, INT_INF, apply_type_tfunc, 10) + +function invoke_tfunc(@nospecialize(ft), @nospecialize(types), @nospecialize(argtype), sv::InferenceState) + argtype = typeintersect(types, argtype) + argtype === Bottom && return Bottom + argtype isa DataType || return Any # other cases are not implemented below + isdispatchelem(ft) || return Any # check that we might not have a subtype of `ft` at runtime, before doing supertype lookup below + types = rewrap_unionall(Tuple{ft, unwrap_unionall(types).parameters...}, types) + argtype = Tuple{ft, argtype.parameters...} + entry = ccall(:jl_gf_invoke_lookup, Any, (Any, UInt), types, sv.params.world) + if entry === nothing + return Any + end + # XXX: update_valid_age!(min_valid[1], max_valid[1], sv) + meth = entry.func + (ti, env) = ccall(:jl_type_intersection_with_env, Any, (Any, Any), argtype, meth.sig)::SimpleVector + rt, edge = typeinf_edge(meth::Method, ti, env, sv) + edge !== nothing && add_backedge!(edge::MethodInstance, sv) + return rt +end + +# convert the dispatch tuple type argtype to the real (concrete) type of +# the tuple of those values +function tuple_tfunc(atypes::Vector{Any}) + atypes = anymap(widenconditional, atypes) + all_are_const = true + for i in 1:length(atypes) + if !isa(atypes[i], Const) + all_are_const = false + break + end + end + if all_are_const + return Const(tuple(Any[atypes[i].val for i in 1:length(atypes)]...)) + end + params = Vector{Any}(undef, length(atypes)) + anyinfo = false + for i in 1:length(atypes) + x = atypes[i] + # TODO ignore singleton Const (don't forget to update cache logic if you implement this) + if !anyinfo + anyinfo = !isa(x, Type) || isType(x) + end + if isa(x, Const) + params[i] = typeof(x.val) + else + x = widenconst(x) + if isType(x) + xparam = x.parameters[1] + if hasuniquerep(xparam) || xparam === Bottom + params[i] = typeof(xparam) + else + params[i] = Type + end + else + params[i] = x + end + end + end + typ = Tuple{params...} + # replace a singleton type with its equivalent Const object + isdefined(typ, :instance) && return Const(typ.instance) + return anyinfo ? PartialStruct(typ, atypes) : typ +end + +function array_type_undefable(@nospecialize(a)) + if isa(a, Union) + return array_type_undefable(a.a) || array_type_undefable(a.b) + elseif isa(a, UnionAll) + return true + else + etype = (a::DataType).parameters[1] + return !(etype isa Type && (isbitstype(etype) || isbitsunion(etype))) + end +end + +function array_builtin_common_nothrow(argtypes::Array{Any,1}, first_idx_idx::Int) + length(argtypes) >= 4 || return false + atype = argtypes[2] + (argtypes[1] ⊑ Bool && atype ⊑ Array) || return false + for i = first_idx_idx:length(argtypes) + argtypes[i] ⊑ Int || return false + end + # If we could potentially throw undef ref errors, bail out now. + atype = widenconst(atype) + array_type_undefable(atype) && return false + # If we have @inbounds (first argument is false), we're allowed to assume + # we don't throw bounds errors. + (isa(argtypes[1], Const) && !argtypes[1].val) && return true + # Else we can't really say anything here + # TODO: In the future we may be able to track the shapes of arrays though + # inference. + return false +end + +# Query whether the given builtin is guaranteed not to throw given the argtypes +function _builtin_nothrow(@nospecialize(f), argtypes::Array{Any,1}, @nospecialize(rt)) + if f === arrayset + array_builtin_common_nothrow(argtypes, 4) || return true + # Additionally check element type compatibility + a = widenconst(argtypes[2]) + # Check that we can determine the element type + (isa(a, DataType) && isa(a.parameters[1], Type)) || return false + # Check that the element type is compatible with the element we're assigning + (argtypes[3] ⊑ a.parameters[1]::Type) || return false + return true + elseif f === arrayref || f === const_arrayref + return array_builtin_common_nothrow(argtypes, 3) + elseif f === Core._expr + length(argtypes) >= 1 || return false + return argtypes[1] ⊑ Symbol + elseif f === Core._typevar + length(argtypes) == 3 || return false + return typevar_nothrow(argtypes[1], argtypes[2], argtypes[3]) + elseif f === invoke + return false + elseif f === getfield + return getfield_nothrow(argtypes) + elseif f === fieldtype + length(argtypes) == 2 || return false + return fieldtype_nothrow(argtypes[1], argtypes[2]) + elseif f === apply_type + return apply_type_nothrow(argtypes, rt) + elseif f === isa + length(argtypes) == 2 || return false + return argtypes[2] ⊑ Type + elseif f === (<:) + length(argtypes) == 2 || return false + return argtypes[1] ⊑ Type && argtypes[2] ⊑ Type + elseif f === UnionAll + return length(argtypes) == 2 && + (argtypes[1] ⊑ TypeVar && argtypes[2] ⊑ Type) + elseif f === isdefined + return isdefined_nothrow(argtypes) + elseif f === Core.sizeof + length(argtypes) == 1 || return false + return sizeof_nothrow(argtypes[1]) + elseif f === Core.kwfunc + length(argtypes) == 1 || return false + return isa(rt, Const) + elseif f === Core.ifelse + length(argtypes) == 3 || return false + return argtypes[1] ⊑ Bool + end + return false +end + +function builtin_nothrow(@nospecialize(f), argtypes::Array{Any, 1}, @nospecialize(rt)) + rt === Bottom && return false + contains_is(_PURE_BUILTINS, f) && return true + return _builtin_nothrow(f, argtypes, rt) +end + +function builtin_tfunction(@nospecialize(f), argtypes::Array{Any,1}, + sv::Union{InferenceState,Nothing}, params::Params = sv.params) + isva = !isempty(argtypes) && isvarargtype(argtypes[end]) + if f === tuple + return tuple_tfunc(argtypes) + elseif f === svec + return SimpleVector + elseif f === arrayset + if length(argtypes) < 4 + isva && return Any + return Bottom + end + return argtypes[2] + elseif f === arrayref || f === const_arrayref + if length(argtypes) < 3 + isva && return Any + return Bottom + end + a = widenconst(argtypes[2]) + if a <: Array + if isa(a, DataType) && (isa(a.parameters[1], Type) || isa(a.parameters[1], TypeVar)) + # TODO: the TypeVar case should not be needed here + a = a.parameters[1] + return isa(a, TypeVar) ? a.ub : a + elseif isa(a, UnionAll) && !has_free_typevars(a) + unw = unwrap_unionall(a) + if isa(unw, DataType) + return rewrap_unionall(unw.parameters[1], a) + end + end + end + return Any + elseif f === Expr + if length(argtypes) < 1 && !isva + return Bottom + end + return Expr + elseif f === invoke + if length(argtypes) > 1 && sv !== nothing && (isa(argtypes[1], Const) || isa(argtypes[1], Type)) + if isa(argtypes[1], Const) + ft = Core.Typeof(argtypes[1].val) + else + ft = argtypes[1] + end + sig = argtypes[2] + if isa(sig, Const) + sigty = sig.val + elseif isType(sig) + sigty = sig.parameters[1] + else + sigty = nothing + end + if isa(sigty, Type) && !has_free_typevars(sigty) && sigty <: Tuple + return invoke_tfunc(ft, sigty, argtypes_to_type(argtypes[3:end]), sv) + end + end + return Any + end + if isva + return Any + end + if isa(f, IntrinsicFunction) + if is_pure_intrinsic_infer(f) && _all(@nospecialize(a) -> isa(a, Const), argtypes) + argvals = anymap(a::Const -> a.val, argtypes) + try + return Const(f(argvals...)) + catch + end + end + iidx = Int(reinterpret(Int32, f::IntrinsicFunction)) + 1 + if iidx < 0 || iidx > length(T_IFUNC) + # invalid intrinsic + return Any + end + tf = T_IFUNC[iidx] + else + fidx = find_tfunc(f) + if fidx === nothing + # unknown/unhandled builtin function + return Any + end + tf = T_FFUNC_VAL[fidx] + end + tf = tf::Tuple{Int, Int, Any} + if !(tf[1] <= length(argtypes) <= tf[2]) + # wrong # of args + return Bottom + end + return tf[3](argtypes...) +end + +# Query whether the given intrinsic is nothrow +intrinsic_nothrow(f::IntrinsicFunction) = !( + f === Intrinsics.checked_sdiv_int || + f === Intrinsics.checked_udiv_int || + f === Intrinsics.checked_srem_int || + f === Intrinsics.checked_urem_int || + f === Intrinsics.cglobal + ) + +function intrinsic_nothrow(f::IntrinsicFunction, argtypes::Array{Any, 1}) + # TODO: We could do better for cglobal + f === Intrinsics.cglobal && return false + if f === Intrinsics.checked_udiv_int || f === Intrinsics.checked_urem_int || f === Intrinsics.checked_srem_int || f === Intrinsics.checked_sdiv_int + # Nothrow as long as the second argument is guaranteed not to be zero + isa(argtypes[2], Const) || return false + den_val = argtypes[2].val + den_val !== zero(typeof(den_val)) || return false + end + if f === Intrinsics.checked_sdiv_int + # Nothrow as long as we additionally don't do typemin(T)/-1 + return den_val !== -1 || (isa(argtypes[1], Const) && + argtypes[1].val !== typemin(typeof(den_val))) + end + if f === Intrinsics.pointerref + # Nothrow as long as the types are ok. N.B.: dereferencability is not + # modeled here, but can cause errors (e.g. ReadOnlyMemoryError). We follow LLVM here + # in that it is legal to remove unused non-volatile loads. + length(argtypes) == 3 || return false + return argtypes[1] ⊑ Ptr && argtypes[2] ⊑ Int && argtypes[3] ⊑ Int + end + return true +end + +# TODO: this function is a very buggy and poor model of the return_type function +# since abstract_call_gf_by_type is a very inaccurate model of _method and of typeinf_type, +# while this assumes that it is an absolutely precise and accurate and exact model of both +function return_type_tfunc(argtypes::Vector{Any}, vtypes::VarTable, sv::InferenceState) + if length(argtypes) == 3 + tt = argtypes[3] + if isa(tt, Const) || (isType(tt) && !has_free_typevars(tt)) + aft = argtypes[2] + if isa(aft, Const) || (isType(aft) && !has_free_typevars(aft)) || + (isconcretetype(aft) && !(aft <: Builtin)) + af_argtype = isa(tt, Const) ? tt.val : tt.parameters[1] + if isa(af_argtype, DataType) && af_argtype <: Tuple + argtypes_vec = Any[aft, af_argtype.parameters...] + if contains_is(argtypes_vec, Union{}) + return Const(Union{}) + end + astype = argtypes_to_type(argtypes_vec) + if isa(aft, Const) + rt = abstract_call(aft.val, nothing, argtypes_vec, vtypes, sv, -1) + elseif isconstType(aft) + rt = abstract_call(aft.parameters[1], nothing, argtypes_vec, vtypes, sv, -1) + else + rt = abstract_call_gf_by_type(nothing, argtypes_vec, astype, sv, -1) + end + if isa(rt, Const) + # output was computed to be constant + return Const(typeof(rt.val)) + elseif hasuniquerep(rt) || rt === Bottom + # output type was known for certain + return Const(rt) + elseif (isa(tt, Const) || isconstType(tt)) && + (isa(aft, Const) || isconstType(aft)) + # input arguments were known for certain + # XXX: this doesn't imply we know anything about rt + return Const(rt) + elseif isType(rt) + return Type{rt} + else + return Type{<:widenconst(rt)} + end + end + end + end + end + return nothing +end + +# N.B.: typename maps type equivalence classes to a single value +function typename_static(@nospecialize(t)) + t isa Const && return _typename(t.val) + t isa Conditional && return Bool.name + t = unwrap_unionall(widenconst(t)) + return isType(t) ? _typename(t.parameters[1]) : Core.TypeName +end + +@specialize diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl new file mode 100644 index 0000000..74f8695 --- /dev/null +++ b/base/compiler/typeinfer.jl @@ -0,0 +1,649 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# build (and start inferring) the inference frame for the linfo +function typeinf(result::InferenceResult, cached::Bool, params::Params) + frame = InferenceState(result, cached, params) + frame === nothing && return false + cached && (result.linfo.inInference = true) + return typeinf(frame) +end + +function typeinf(frame::InferenceState) + typeinf_nocycle(frame) || return false # frame is now part of a higher cycle + # with no active ip's, frame is done + frames = frame.callers_in_cycle + isempty(frames) && push!(frames, frame) + for caller in frames + @assert !(caller.dont_work_on_me) + caller.dont_work_on_me = true + end + for caller in frames + finish(caller) + end + # collect results for the new expanded frame + results = InferenceResult[ frames[i].result for i in 1:length(frames) ] + # empty!(frames) + min_valid = frame.min_valid + max_valid = frame.max_valid + cached = frame.cached + if cached || frame.parent !== nothing + for caller in results + opt = caller.src + if opt isa OptimizationState + optimize(opt, caller.result) + finish(opt.src) + # finish updating the result struct + validate_code_in_debug_mode(opt.linfo, opt.src, "optimized") + if opt.const_api + if caller.result isa Const + caller.src = caller.result + else + @assert isconstType(caller.result) + caller.src = Const(caller.result.parameters[1]) + end + elseif opt.src.inferred + caller.src = opt.src::CodeInfo # stash a copy of the code (for inlining) + else + caller.src = nothing + end + if min_valid < opt.min_valid + min_valid = opt.min_valid + end + if max_valid > opt.max_valid + max_valid = opt.max_valid + end + end + end + end + if max_valid == get_world_counter() + max_valid = typemax(UInt) + end + for caller in frames + caller.min_valid = min_valid + caller.max_valid = max_valid + caller.src.min_world = min_valid + caller.src.max_world = max_valid + if cached + cache_result(caller.result, min_valid, max_valid) + end + if max_valid == typemax(UInt) + # if we aren't cached, we don't need this edge + # but our caller might, so let's just make it anyways + for caller in frames + store_backedges(caller) + end + end + # finalize and record the linfo result + caller.inferred = true + end + return true +end + +# inference completed on `me` +# update the MethodInstance and notify the edges +function cache_result(result::InferenceResult, min_valid::UInt, max_valid::UInt) + def = result.linfo.def + toplevel = !isa(result.linfo.def, Method) + if toplevel + min_valid = UInt(0) + max_valid = UInt(0) + end + + # check if the existing linfo metadata is also sufficient to describe the current inference result + # to decide if it is worth caching this + already_inferred = !result.linfo.inInference + if inf_for_methodinstance(result.linfo, min_valid, max_valid) isa CodeInstance + already_inferred = true + end + + # TODO: also don't store inferred code if we've previously decided to interpret this function + if !already_inferred + inferred_result = result.src + if inferred_result isa Const + # use constant calling convention + rettype_const = (result.src::Const).val + const_flags = 0x3 + else + if isa(result.result, Const) + rettype_const = (result.result::Const).val + const_flags = 0x2 + elseif isconstType(result.result) + rettype_const = result.result.parameters[1] + const_flags = 0x2 + else + rettype_const = nothing + const_flags = 0x00 + end + if !toplevel && inferred_result isa CodeInfo + cache_the_tree = result.src.inferred && + (result.src.inlineable || + ccall(:jl_isa_compileable_sig, Int32, (Any, Any), result.linfo.specTypes, def) != 0) + if cache_the_tree + # compress code for non-toplevel thunks + nslots = length(inferred_result.slotflags) + resize!(inferred_result.slottypes, nslots) + resize!(inferred_result.slotnames, nslots) + inferred_result = ccall(:jl_compress_ast, Any, (Any, Any), def, inferred_result) + else + inferred_result = nothing + end + end + end + if !isa(inferred_result, Union{CodeInfo, Vector{UInt8}}) + inferred_result = nothing + end + ccall(:jl_set_method_inferred, Ref{CodeInstance}, (Any, Any, Any, Any, Int32, UInt, UInt), + result.linfo, widenconst(result.result), rettype_const, inferred_result, + const_flags, min_valid, max_valid) + end + result.linfo.inInference = false + nothing +end + +function finish(me::InferenceState) + # prepare to run optimization passes on fulltree + if me.limited && me.cached && me.parent !== nothing + # a top parent will be cached still, but not this intermediate work + # we can throw everything else away now + me.cached = false + me.linfo.inInference = false + me.src.inlineable = false + else + # annotate fulltree with type information + type_annotate!(me) + run_optimizer = (me.cached || me.parent !== nothing) + if run_optimizer + # construct the optimizer for later use, if we're building this IR to cache it + # (otherwise, we'll run the optimization passes later, outside of inference) + opt = OptimizationState(me) + me.result.src = opt + end + end + me.result.result = me.bestguess + nothing +end + +function finish(src::CodeInfo) + # convert all type information into the form consumed by the cache for inlining and code-generation + widen_all_consts!(src) + src.inferred = true + nothing +end + +# record the backedges +function store_backedges(frame::InferenceState) + toplevel = !isa(frame.linfo.def, Method) + if !toplevel && (frame.cached || frame.parent !== nothing) + caller = frame.result.linfo + for edges in frame.stmt_edges + store_backedges(caller, edges) + end + store_backedges(caller, frame.src.edges) + frame.src.edges = nothing + end +end + +store_backedges(caller, edges::Nothing) = nothing +function store_backedges(caller, edges::Vector) + i = 1 + while i <= length(edges) + to = edges[i] + if isa(to, MethodInstance) + ccall(:jl_method_instance_add_backedge, Cvoid, (Any, Any), to, caller) + i += 1 + else + typeassert(to, Core.MethodTable) + typ = edges[i + 1] + ccall(:jl_method_table_add_backedge, Cvoid, (Any, Any, Any), to, typ, caller) + i += 2 + end + end +end + +# widen all Const elements in type annotations +function widen_all_consts!(src::CodeInfo) + for i = 1:length(src.ssavaluetypes) + src.ssavaluetypes[i] = widenconst(src.ssavaluetypes[i]) + end + + for i = 1:length(src.code) + x = src.code[i] + if isa(x, PiNode) + src.code[i] = PiNode(x.val, widenconst(x.typ)) + end + end + + src.rettype = widenconst(src.rettype) + + return src +end + +function annotate_slot_load!(e::Expr, vtypes::VarTable, sv::InferenceState, undefs::Array{Bool,1}) + head = e.head + i0 = 1 + if is_meta_expr_head(head) || head === :const + return + end + if head === :(=) || head === :method + i0 = 2 + end + for i = i0:length(e.args) + subex = e.args[i] + if isa(subex, Expr) + annotate_slot_load!(subex, vtypes, sv, undefs) + elseif isa(subex, Slot) + e.args[i] = visit_slot_load!(subex, vtypes, sv, undefs) + end + end +end + +function visit_slot_load!(sl::Slot, vtypes::VarTable, sv::InferenceState, undefs::Array{Bool,1}) + id = slot_id(sl) + s = vtypes[id] + vt = widenconditional(s.typ) + if s.undef + # find used-undef variables + undefs[id] = true + end + # add type annotations where needed + if !(sv.slottypes[id] ⊑ vt) + return TypedSlot(id, vt) + end + return sl +end + +function record_slot_assign!(sv::InferenceState) + # look at all assignments to slots + # and union the set of types stored there + # to compute a lower bound on the storage required + states = sv.stmt_types + body = sv.src.code::Vector{Any} + slottypes = sv.slottypes::Vector{Any} + for i = 1:length(body) + expr = body[i] + st_i = states[i] + # find all reachable assignments to locals + if isa(st_i, VarTable) && isa(expr, Expr) && expr.head === :(=) + lhs = expr.args[1] + rhs = expr.args[2] + if isa(lhs, Slot) + vt = widenconst(sv.src.ssavaluetypes[i]) + if vt !== Bottom + id = slot_id(lhs) + otherTy = slottypes[id] + if otherTy === Bottom + slottypes[id] = vt + elseif otherTy === Any + slottypes[id] = Any + else + slottypes[id] = tmerge(otherTy, vt) + end + end + end + end + end +end + +# annotate types of all symbols in AST +function type_annotate!(sv::InferenceState) + # delete dead statements only if we're building this IR to cache it + # (otherwise, we'll run the optimization passes later, outside of inference) + run_optimizer = (sv.cached || sv.parent !== nothing) + + # remove all unused ssa values + gt = sv.src.ssavaluetypes + for j = 1:length(gt) + if gt[j] === NOT_FOUND + gt[j] = Union{} + end + gt[j] = widenconditional(gt[j]) + end + + # compute the required type for each slot + # to hold all of the items assigned into it + record_slot_assign!(sv) + sv.src.slottypes = sv.slottypes + sv.src.rettype = sv.bestguess + + # annotate variables load types + # remove dead code optimization + # and compute which variables may be used undef + src = sv.src + states = sv.stmt_types + nargs = sv.nargs + nslots = length(states[1]::Array{Any,1}) + undefs = fill(false, nslots) + body = src.code::Array{Any,1} + nexpr = length(body) + + # replace gotoifnot with its condition if the branch target is unreachable + for i = 1:nexpr + expr = body[i] + if isa(expr, Expr) && expr.head === :gotoifnot + tgt = expr.args[2]::Int + if !isa(states[tgt], VarTable) + body[i] = expr.args[1] + end + end + end + + i = 1 + oldidx = 0 + changemap = fill(0, nexpr) + + while i <= nexpr + oldidx += 1 + st_i = states[i] + expr = body[i] + if isa(st_i, VarTable) + # st_i === nothing => unreached statement (see issue #7836) + if isa(expr, Expr) + annotate_slot_load!(expr, st_i, sv, undefs) + elseif isa(expr, Slot) + body[i] = visit_slot_load!(expr, st_i, sv, undefs) + end + else + if isa(expr, Expr) && is_meta_expr_head(expr.head) + # keep any lexically scoped expressions + elseif run_optimizer + deleteat!(body, i) + deleteat!(states, i) + deleteat!(src.ssavaluetypes, i) + deleteat!(src.codelocs, i) + nexpr -= 1 + if oldidx < length(changemap) + changemap[oldidx + 1] = -1 + end + continue + else + body[i] = Const(expr) # annotate that this statement actually is dead + end + end + i += 1 + end + + if run_optimizer + renumber_ir_elements!(body, changemap) + end + + # finish marking used-undef variables + for j = 1:nslots + if undefs[j] + src.slotflags[j] |= SLOT_USEDUNDEF | SLOT_STATICUNDEF + end + end + nothing +end + +# at the end, all items in b's cycle +# will now be added to a's cycle +function union_caller_cycle!(a::InferenceState, b::InferenceState) + callers_in_cycle = b.callers_in_cycle + b.parent = a.parent + b.callers_in_cycle = a.callers_in_cycle + contains_is(a.callers_in_cycle, b) || push!(a.callers_in_cycle, b) + if callers_in_cycle !== a.callers_in_cycle + for caller in callers_in_cycle + if caller !== b + caller.parent = a.parent + caller.callers_in_cycle = a.callers_in_cycle + push!(a.callers_in_cycle, caller) + end + end + end + return +end + +function merge_call_chain!(parent::InferenceState, ancestor::InferenceState, child::InferenceState, limited::Bool) + # add backedge of parent <- child + # then add all backedges of parent <- parent.parent + # and merge all of the callers into ancestor.callers_in_cycle + # and ensure that walking the parent list will get the same result (DAG) from everywhere + while true + add_cycle_backedge!(child, parent, parent.currpc) + union_caller_cycle!(ancestor, child) + child = parent + parent = child.parent + child === ancestor && break + end + if limited + for caller in ancestor.callers_in_cycle + caller.limited = true + end + end +end + +# Walk through `linfo`'s upstream call chain, starting at `parent`. If a parent +# frame matching `linfo` is encountered, then there is a cycle in the call graph +# (i.e. `linfo` is a descendant callee of itself). Upon encountering this cycle, +# we "resolve" it by merging the call chain, which entails unioning each intermediary +# frame's `callers_in_cycle` field and adding the appropriate backedges. Finally, +# we return `linfo`'s pre-existing frame. If no cycles are found, `nothing` is +# returned instead. +function resolve_call_cycle!(linfo::MethodInstance, parent::InferenceState) + frame = parent + uncached = false + limited = false + while isa(frame, InferenceState) + uncached |= !frame.cached # ensure we never add an uncached frame to a cycle + limited |= frame.limited + if frame.linfo === linfo + if uncached + # our attempt to speculate into a constant call lead to an undesired self-cycle + # that cannot be converged: poison our call-stack (up to the discovered duplicate frame) + # with the limited flag and abort (set return type to Any) now + poison_callstack(parent, frame, false) + return true + end + merge_call_chain!(parent, frame, frame, limited) + return frame + end + for caller in frame.callers_in_cycle + if caller.linfo === linfo + if uncached + poison_callstack(parent, frame, false) + return true + end + merge_call_chain!(parent, frame, caller, limited) + return caller + end + end + frame = frame.parent + end + return false +end + +# compute (and cache) an inferred AST and return the current best estimate of the result type +function typeinf_edge(method::Method, @nospecialize(atypes), sparams::SimpleVector, caller::InferenceState) + mi = specialize_method(method, atypes, sparams)::MethodInstance + code = inf_for_methodinstance(mi, caller.params.world) + if code isa CodeInstance # return existing rettype if the code is already inferred + update_valid_age!(min_world(code), max_world(code), caller) + if isdefined(code, :rettype_const) + return Const(code.rettype_const), mi + else + return code.rettype, mi + end + end + if !caller.cached && caller.parent === nothing + # this caller exists to return to the user + # (if we asked resolve_call_cyle, it might instead detect that there is a cycle that it can't merge) + frame = false + else + frame = resolve_call_cycle!(mi, caller) + end + if frame === false + # completely new + mi.inInference = true + result = InferenceResult(mi) + frame = InferenceState(result, #=cached=#true, caller.params) # always use the cache for edge targets + if frame === nothing + # can't get the source for this, so we know nothing + mi.inInference = false + return Any, nothing + end + if caller.cached || caller.limited # don't involve uncached functions in cycle resolution + frame.parent = caller + end + typeinf(frame) + update_valid_age!(frame, caller) + return widenconst_bestguess(frame.bestguess), frame.inferred ? mi : nothing + elseif frame === true + # unresolvable cycle + return Any, nothing + end + # return the current knowledge about this cycle + frame = frame::InferenceState + update_valid_age!(frame, caller) + return widenconst_bestguess(frame.bestguess), nothing +end + +function widenconst_bestguess(bestguess) + !isa(bestguess, Const) && !isa(bestguess, Type) && return widenconst(bestguess) + return bestguess +end + +#### entry points for inferring a MethodInstance given a type signature #### + +# compute an inferred AST and return type +function typeinf_code(method::Method, @nospecialize(atypes), sparams::SimpleVector, run_optimizer::Bool, params::Params) + mi = specialize_method(method, atypes, sparams)::MethodInstance + ccall(:jl_typeinf_begin, Cvoid, ()) + result = InferenceResult(mi) + frame = InferenceState(result, false, params) + frame === nothing && return (nothing, Any) + if typeinf(frame) && run_optimizer + opt = OptimizationState(frame) + optimize(opt, result.result) + opt.src.inferred = true + end + ccall(:jl_typeinf_end, Cvoid, ()) + frame.inferred || return (nothing, Any) + return (frame.src, widenconst(result.result)) +end + +# compute (and cache) an inferred AST and return type +function typeinf_ext(mi::MethodInstance, params::Params) + method = mi.def::Method + for i = 1:2 # test-and-lock-and-test + i == 2 && ccall(:jl_typeinf_begin, Cvoid, ()) + code = inf_for_methodinstance(mi, params.world) + if code isa CodeInstance + # see if this code already exists in the cache + inf = code.inferred + if invoke_api(code) == 2 + i == 2 && ccall(:jl_typeinf_end, Cvoid, ()) + tree = ccall(:jl_new_code_info_uninit, Ref{CodeInfo}, ()) + tree.code = Any[ Expr(:return, quoted(code.rettype_const)) ] + nargs = Int(method.nargs) + tree.slotnames = ccall(:jl_uncompress_argnames, Vector{Symbol}, (Any,), method.slot_syms) + tree.slotflags = fill(0x00, nargs) + tree.ssavaluetypes = 1 + tree.codelocs = Int32[1] + tree.linetable = [LineInfoNode(method, method.file, Int(method.line), 0)] + tree.inferred = true + tree.ssaflags = UInt8[0] + tree.pure = true + tree.inlineable = true + tree.parent = mi + tree.rettype = typeof(code.rettype_const) + tree.min_world = code.min_world + tree.max_world = code.max_world + return tree + elseif isa(inf, CodeInfo) + i == 2 && ccall(:jl_typeinf_end, Cvoid, ()) + if !(inf.min_world == code.min_world && + inf.max_world == code.max_world && + inf.rettype === code.rettype) + inf = copy(inf) + inf.min_world = code.min_world + inf.max_world = code.max_world + inf.rettype = code.rettype + end + return inf + elseif isa(inf, Vector{UInt8}) + i == 2 && ccall(:jl_typeinf_end, Cvoid, ()) + inf = _uncompressed_ast(code, inf) + return inf + end + end + end + mi.inInference = true + frame = InferenceState(InferenceResult(mi), #=cached=#true, params) + frame === nothing && return nothing + typeinf(frame) + ccall(:jl_typeinf_end, Cvoid, ()) + frame.src.inferred || return nothing + return frame.src +end + +# compute (and cache) an inferred AST and return the inferred return type +function typeinf_type(method::Method, @nospecialize(atypes), sparams::SimpleVector, params::Params) + if contains_is(unwrap_unionall(atypes).parameters, Union{}) + return Union{} # don't ask: it does weird and unnecessary things, if it occurs during bootstrap + end + mi = specialize_method(method, atypes, sparams)::MethodInstance + for i = 1:2 # test-and-lock-and-test + i == 2 && ccall(:jl_typeinf_begin, Cvoid, ()) + code = inf_for_methodinstance(mi, params.world) + if code isa CodeInstance + # see if this rettype already exists in the cache + i == 2 && ccall(:jl_typeinf_end, Cvoid, ()) + return code.rettype + end + end + frame = InferenceResult(mi) + typeinf(frame, true, params) + ccall(:jl_typeinf_end, Cvoid, ()) + frame.result isa InferenceState && return nothing + return widenconst(frame.result) +end + +@timeit function typeinf_ext(linfo::MethodInstance, world::UInt) + if isa(linfo.def, Method) + # method lambda - infer this specialization via the method cache + src = typeinf_ext(linfo, Params(world)) + else + src = linfo.uninferred::CodeInfo + if !src.inferred + # toplevel lambda - infer directly + ccall(:jl_typeinf_begin, Cvoid, ()) + if !src.inferred + result = InferenceResult(linfo) + frame = InferenceState(result, src, #=cached=#true, Params(world)) + typeinf(frame) + @assert frame.inferred # TODO: deal with this better + src = frame.src + end + ccall(:jl_typeinf_end, Cvoid, ()) + end + end + return src +end + + +function return_type(@nospecialize(f), @nospecialize(t)) + world = ccall(:jl_get_tls_world_age, UInt, ()) + return ccall(:jl_call_in_typeinf_world, Any, (Ptr{Ptr{Cvoid}}, Cint), Any[_return_type, f, t, world], 4) +end + +function _return_type(@nospecialize(f), @nospecialize(t), world) + params = Params(world) + rt = Union{} + if isa(f, Builtin) + rt = builtin_tfunction(f, Any[t.parameters...], nothing, params) + if isa(rt, TypeVar) + rt = rt.ub + else + rt = widenconst(rt) + end + else + for m in _methods(f, t, -1, params.world) + ty = typeinf_type(m[3], m[1], m[2], params) + ty === nothing && return Any + rt = tmerge(rt, ty) + rt === Any && break + end + end + return rt +end diff --git a/base/compiler/typelattice.jl b/base/compiler/typelattice.jl new file mode 100644 index 0000000..931d56b --- /dev/null +++ b/base/compiler/typelattice.jl @@ -0,0 +1,326 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +##################### +# structs/constants # +##################### + +# The type of a value might be constant +struct Const + val + actual::Bool # if true, we obtained `val` by actually calling a @pure function + Const(@nospecialize(v)) = new(v, false) + Const(@nospecialize(v), a::Bool) = new(v, a) +end + +# The type of this value might be Bool. +# However, to enable a limited amount of back-propagagation, +# we also keep some information about how this Bool value was created. +# In particular, if you branch on this value, then may assume that in +# the true branch, the type of `var` will be limited by `vtype` and in +# the false branch, it will be limited by `elsetype`. Example: +# ``` +# cond = isa(x::Union{Int, Float}, Int)::Conditional(x, Int, Float) +# if cond +# # May assume x is `Int` now +# else +# # May assume x is `Float` now +# end +# ``` +struct Conditional + var::Slot + vtype + elsetype + function Conditional( + var, + @nospecialize(vtype), + @nospecialize(nottype)) + return new(var, vtype, nottype) + end +end + +struct PartialTypeVar + tv::TypeVar + # N.B.: Currently unused, but would allow turning something back + # into Const, if the bounds are pulled out of this TypeVar + lb_certain::Bool + ub_certain::Bool + PartialTypeVar(tv::TypeVar, lb_certain::Bool, ub_certain::Bool) = new(tv, lb_certain, ub_certain) +end + +# Wraps a type and represents that the value may also be undef at this point. +# (only used in optimize, not abstractinterpret) +struct MaybeUndef + typ + MaybeUndef(@nospecialize(typ)) = new(typ) +end + +# The type of a variable load is either a value or an UndefVarError +# (only used in abstractinterpret, doesn't appear in optimize) +struct VarState + typ + undef::Bool + VarState(@nospecialize(typ), undef::Bool) = new(typ, undef) +end + +const VarTable = Array{Any,1} + +struct StateUpdate + var::Union{Slot,SSAValue} + vtype::VarState + state::VarTable +end + +struct PartialStruct + typ + fields::Vector{Any} # elements are other type lattice members +end + +struct NotFound end + +const NOT_FOUND = NotFound() + +################# +# lattice logic # +################# + +function issubconditional(a::Conditional, b::Conditional) + avar = a.var + bvar = b.var + if (isa(avar, Slot) && isa(bvar, Slot) && slot_id(avar) === slot_id(bvar)) || + (isa(avar, SSAValue) && isa(bvar, SSAValue) && avar === bvar) + if a.vtype ⊑ b.vtype + if a.elsetype ⊑ b.elsetype + return true + end + end + end + return false +end + +maybe_extract_const_bool(c::Const) = isa(c.val, Bool) ? c.val : nothing +function maybe_extract_const_bool(c::Conditional) + (c.vtype === Bottom && !(c.elsetype === Bottom)) && return false + (c.elsetype === Bottom && !(c.vtype === Bottom)) && return true + nothing +end +maybe_extract_const_bool(@nospecialize c) = nothing + +function ⊑(@nospecialize(a), @nospecialize(b)) + if isa(a, MaybeUndef) && !isa(b, MaybeUndef) + return false + end + isa(a, MaybeUndef) && (a = a.typ) + isa(b, MaybeUndef) && (b = b.typ) + (a === NOT_FOUND || b === Any) && return true + (a === Any || b === NOT_FOUND) && return false + a === Union{} && return true + b === Union{} && return false + if isa(a, Conditional) + if isa(b, Conditional) + return issubconditional(a, b) + elseif isa(b, Const) && isa(b.val, Bool) + return maybe_extract_const_bool(a) === b.val + end + a = Bool + elseif isa(b, Conditional) + return false + end + if isa(a, PartialStruct) + if isa(b, PartialStruct) + if !(length(a.fields) == length(b.fields) && a.typ <: b.typ) + return false + end + for i in 1:length(b.fields) + # XXX: let's handle varargs later + ⊑(a.fields[i], b.fields[i]) || return false + end + return true + end + return isa(b, Type) && a.typ <: b + elseif isa(b, PartialStruct) + if isa(a, Const) + nfields(a.val) == length(b.fields) || return false + widenconst(b).name === widenconst(a).name || return false + # We can skip the subtype check if b is a Tuple, since in that + # case, the ⊑ of the elements is sufficient. + if b.typ.name !== Tuple.name && !(widenconst(a) <: widenconst(b)) + return false + end + for i in 1:nfields(a.val) + # XXX: let's handle varargs later + isdefined(a.val, i) || return false + ⊑(Const(getfield(a.val, i)), b.fields[i]) || return false + end + return true + end + return false + end + if isa(a, Const) + if isa(b, Const) + return a.val === b.val + end + # TODO: `b` could potentially be a `PartialTypeVar` here, in which case we might be + # able to return `true` in more cases; in the meantime, just returning this is the + # most conservative option. + return isa(b, Type) && isa(a.val, b) + elseif isa(b, Const) + if isa(a, DataType) && isdefined(a, :instance) + return a.instance === b.val + end + return false + elseif !(isa(a, Type) || isa(a, TypeVar)) || + !(isa(b, Type) || isa(b, TypeVar)) + return a === b + else + return a <: b + end +end + +# Check if two lattice elements are partial order equivalent. This is basically +# `a ⊑ b && b ⊑ a` but with extra performance optimizations. +function is_lattice_equal(@nospecialize(a), @nospecialize(b)) + a === b && return true + if isa(a, PartialStruct) + isa(b, PartialStruct) || return false + length(a.fields) == length(b.fields) || return false + widenconst(a) == widenconst(b) || return false + for i in 1:length(a.fields) + is_lattice_equal(a.fields[i], b.fields[i]) || return false + end + return true + end + isa(b, PartialStruct) && return false + a isa Const && return false + b isa Const && return false + return a ⊑ b && b ⊑ a +end + +widenconst(c::Conditional) = Bool +function widenconst(c::Const) + if isa(c.val, Type) + if isvarargtype(c.val) + return Type + end + return Type{c.val} + else + return typeof(c.val) + end +end +widenconst(m::MaybeUndef) = widenconst(m.typ) +widenconst(c::PartialTypeVar) = TypeVar +widenconst(t::PartialStruct) = t.typ +widenconst(@nospecialize(t)) = t + +issubstate(a::VarState, b::VarState) = (a.typ ⊑ b.typ && a.undef <= b.undef) + +function smerge(sa::Union{NotFound,VarState}, sb::Union{NotFound,VarState}) + sa === sb && return sa + sa === NOT_FOUND && return sb + sb === NOT_FOUND && return sa + issubstate(sa, sb) && return sb + issubstate(sb, sa) && return sa + return VarState(tmerge(sa.typ, sb.typ), sa.undef | sb.undef) +end + +@inline tchanged(@nospecialize(n), @nospecialize(o)) = o === NOT_FOUND || (n !== NOT_FOUND && !(n ⊑ o)) +@inline schanged(@nospecialize(n), @nospecialize(o)) = (n !== o) && (o === NOT_FOUND || (n !== NOT_FOUND && !issubstate(n, o))) + +widenconditional(@nospecialize typ) = typ +function widenconditional(typ::Conditional) + if typ.vtype == Union{} + return Const(false) + elseif typ.elsetype == Union{} + return Const(true) + else + return Bool + end +end + +function stupdate!(state::Nothing, changes::StateUpdate) + newst = copy(changes.state) + if isa(changes.var, Slot) + changeid = slot_id(changes.var::Slot) + newst[changeid] = changes.vtype + # remove any Conditional for this Slot from the vtable + for i = 1:length(newst) + newtype = newst[i] + if isa(newtype, VarState) + newtypetyp = newtype.typ + if isa(newtypetyp, Conditional) && slot_id(newtypetyp.var) == changeid + newst[i] = VarState(widenconditional(newtypetyp), newtype.undef) + end + end + end + end + return newst +end + +function stupdate!(state::VarTable, changes::StateUpdate) + if !isa(changes.var, Slot) + return stupdate!(state, changes.state) + end + newstate = false + changeid = slot_id(changes.var::Slot) + for i = 1:length(state) + if i == changeid + newtype = changes.vtype + else + newtype = changes.state[i] + end + oldtype = state[i] + # remove any Conditional for this Slot from the vtable + if isa(newtype, VarState) + newtypetyp = newtype.typ + if isa(newtypetyp, Conditional) && slot_id(newtypetyp.var) == changeid + newtype = VarState(widenconditional(newtypetyp), newtype.undef) + end + end + if schanged(newtype, oldtype) + newstate = state + state[i] = smerge(oldtype, newtype) + end + end + return newstate +end + +function stupdate!(state::VarTable, changes::VarTable) + newstate = false + for i = 1:length(state) + newtype = changes[i] + oldtype = state[i] + if schanged(newtype, oldtype) + newstate = state + state[i] = smerge(oldtype, newtype) + end + end + return newstate +end + +stupdate!(state::Nothing, changes::VarTable) = copy(changes) + +stupdate!(state::Nothing, changes::Nothing) = false + +function stupdate1!(state::VarTable, change::StateUpdate) + if !isa(change.var, Slot) + return false + end + changeid = slot_id(change.var::Slot) + # remove any Conditional for this Slot from the catch block vtable + for i = 1:length(state) + oldtype = state[i] + if isa(oldtype, VarState) + oldtypetyp = oldtype.typ + if isa(oldtypetyp, Conditional) && slot_id(oldtypetyp.var) == changeid + state[i] = VarState(widenconditional(oldtypetyp), oldtype.undef) + end + end + end + # and update the type of it + newtype = change.vtype + oldtype = state[changeid] + if schanged(newtype, oldtype) + state[changeid] = smerge(oldtype, newtype) + return true + end + return false +end diff --git a/base/compiler/typelimits.jl b/base/compiler/typelimits.jl new file mode 100644 index 0000000..48f38ff --- /dev/null +++ b/base/compiler/typelimits.jl @@ -0,0 +1,490 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +######################### +# limitation parameters # +######################### + +const MAX_TYPEUNION_COMPLEXITY = 3 +const MAX_TYPEUNION_LENGTH = 3 +const MAX_INLINE_CONST_SIZE = 256 + +######################### +# limitation heuristics # +######################### + +# limit the complexity of type `t` to be simpler than the comparison type `compare` +# no new values may be introduced, so the parameter `source` encodes the set of all values already present +# the outermost tuple type is permitted to have up to `allowed_tuplelen` parameters +function limit_type_size(@nospecialize(t), @nospecialize(compare), @nospecialize(source), allowed_tupledepth::Int, allowed_tuplelen::Int) + source = svec(unwrap_unionall(compare), unwrap_unionall(source)) + source[1] === source[2] && (source = svec(source[1])) + type_more_complex(t, compare, source, 1, allowed_tupledepth, allowed_tuplelen) || return t + r = _limit_type_size(t, compare, source, 1, allowed_tuplelen) + @assert t <: r + #@assert r === _limit_type_size(r, t, source) # this monotonicity constraint is slightly stronger than actually required, + # since we only actually need to demonstrate that repeated application would reaches a fixed point, + #not that it is already at the fixed point + return r +end + +# try to find `type` somewhere in `comparison` type +# at a minimum nesting depth of `mindepth` +function is_derived_type(@nospecialize(t), @nospecialize(c), mindepth::Int) + if t === c + return mindepth <= 1 + end + if isa(c, Union) + # see if it is one of the elements of the union + return is_derived_type(t, c.a, mindepth) || is_derived_type(t, c.b, mindepth) + elseif isa(c, UnionAll) + # see if it is derived from the body + # also handle the var here, since this construct bounds the mindepth to the smallest possible value + return is_derived_type(t, c.var.ub, mindepth) || is_derived_type(t, c.body, mindepth) + elseif isa(c, DataType) + if mindepth > 0 + mindepth -= 1 + end + if isa(t, DataType) + # see if it is one of the supertypes of a parameter + super = supertype(c) + while super !== Any + t === super && return true + super = supertype(super) + end + end + # see if it was extracted from a type parameter + cP = c.parameters + for p in cP + is_derived_type(t, p, mindepth) && return true + end + if isconcretetype(c) && isbitstype(c) + # see if it was extracted from a fieldtype + # however, only look through types that can be inlined + # to ensure monotonicity of derivation + # since we know that for immutable, concrete, bits types, + # the field types must have been constructed prior to the type, + # it cannot have a reference cycle in the type graph + cF = c.types + for f in cF + # often a parameter is also a field type; avoid searching twice + if !contains_is(c.parameters, f) + is_derived_type(t, f, mindepth) && return true + end + end + end + end + return false +end + +function is_derived_type_from_any(@nospecialize(t), sources::SimpleVector, mindepth::Int) + for s in sources + is_derived_type(t, s, mindepth) && return true + end + return false +end + +# The goal of this function is to return a type of greater "size" and less "complexity" than +# both `t` or `c` over the lattice defined by `sources`, `depth`, and `allowed_tuplelen`. +function _limit_type_size(@nospecialize(t), @nospecialize(c), sources::SimpleVector, depth::Int, allowed_tuplelen::Int) + if t === c + return t # quick egal test + elseif t === Union{} + return t # easy case + elseif isa(t, DataType) && isempty(t.parameters) + return t # fast path: unparameterized are always simple + else + ut = unwrap_unionall(t) + if isa(ut, DataType) && ut.name !== _va_typename && isa(c, Type) && c !== Union{} && c <: t + return t # t is already wider than the comparison in the type lattice + elseif is_derived_type_from_any(ut, sources, depth) + return t # t isn't something new + end + end + # peel off (and ignore) wrappers - they contribute no useful information, so we don't need to consider their size + # first attempt to turn `c` into a type that contributes meaningful information + # by peeling off meaningless non-matching wrappers of comparison one at a time + # then unwrap `t` + if isa(c, TypeVar) + if isa(t, TypeVar) && t.ub === c.ub && (t.lb === Union{} || t.lb === c.lb) + return t # it's ok to change the name, or widen `lb` to Union{}, so we can handle this immediately here + end + return _limit_type_size(t, c.ub, sources, depth, allowed_tuplelen) + end + if isa(c, UnionAll) + return _limit_type_size(t, c.body, sources, depth, allowed_tuplelen) + end + if isa(t, UnionAll) + tbody = _limit_type_size(t.body, c, sources, depth, allowed_tuplelen) + tbody === t.body && return t + return UnionAll(t.var, tbody) + elseif isa(t, TypeVar) + # don't have a matching TypeVar in comparison, so we keep just the upper bound + return _limit_type_size(t.ub, c, sources, depth, allowed_tuplelen) + elseif isa(t, Union) + if isa(c, Union) + a = _limit_type_size(t.a, c.a, sources, depth, allowed_tuplelen) + b = _limit_type_size(t.b, c.b, sources, depth, allowed_tuplelen) + return Union{a, b} + end + elseif isa(t, DataType) + if isa(c, DataType) + tP = t.parameters + cP = c.parameters + if t.name === c.name && !isempty(cP) + if isvarargtype(t) + VaT = _limit_type_size(tP[1], cP[1], sources, depth + 1, 0) + N = tP[2] + if isa(N, TypeVar) || N === cP[2] + return Vararg{VaT, N} + end + return Vararg{VaT} + elseif t.name === Tuple.name + # for covariant datatypes (Tuple), + # apply type-size limit element-wise + ltP = length(tP) + lcP = length(cP) + np = min(ltP, max(lcP, allowed_tuplelen)) + Q = Any[ tP[i] for i in 1:np ] + if ltP > np + # combine tp[np:end] into tP[np] using Vararg + Q[np] = tuple_tail_elem(Bottom, Any[ tP[i] for i in np:ltP ]) + end + for i = 1:np + # now apply limit element-wise to Q + # padding out the comparison as needed to allowed_tuplelen elements + if i <= lcP + cPi = cP[i] + elseif isvarargtype(cP[lcP]) + cPi = cP[lcP] + else + cPi = Any + end + Q[i] = _limit_type_size(Q[i], cPi, sources, depth + 1, 0) + end + return Tuple{Q...} + end + elseif isvarargtype(c) + # Tuple{Vararg{T}} --> Tuple{T} is OK + return _limit_type_size(t, cP[1], sources, depth, 0) + end + end + if isType(t) # allow taking typeof as Type{...}, but ensure it doesn't start nesting + tt = unwrap_unionall(t.parameters[1]) + if isa(tt, DataType) && !isType(tt) + is_derived_type_from_any(tt, sources, depth) && return t + end + end + if isvarargtype(t) + # never replace Vararg with non-Vararg + return Vararg + end + if allowed_tuplelen < 1 && t.name === Tuple.name + return Any + end + widert = t.name.wrapper + if !(t <: widert) + # This can happen when a typevar has bounds too wide for its context, e.g. + # `Complex{T} where T` is not a subtype of `Complex`. In that case widen even + # faster to something safe to ensure the result is a supertype of the input. + return Any + end + return widert + end + return Any +end + +function type_more_complex(@nospecialize(t), @nospecialize(c), sources::SimpleVector, depth::Int, tupledepth::Int, allowed_tuplelen::Int) + # detect cases where the comparison is trivial + if t === c + return false + elseif t === Union{} + return false # Bottom is as simple as they come + elseif isa(t, DataType) && isempty(t.parameters) + return false # fastpath: unparameterized types are always finite + elseif tupledepth > 0 && isa(unwrap_unionall(t), DataType) && isa(c, Type) && c !== Union{} && c <: t + return false # t is already wider than the comparison in the type lattice + elseif tupledepth > 0 && is_derived_type_from_any(unwrap_unionall(t), sources, depth) + return false # t isn't something new + end + # peel off wrappers + if isa(c, UnionAll) + # allow wrapping type with fewer UnionAlls than comparison if in a covariant context + if !isa(t, UnionAll) && tupledepth == 0 + return true + end + t = unwrap_unionall(t) + c = unwrap_unionall(c) + end + # rules for various comparison types + if isa(c, TypeVar) + tupledepth = 1 # allow replacing a TypeVar with a concrete value (since we know the UnionAll must be in covariant position) + if isa(t, TypeVar) + return !(t.lb === Union{} || t.lb === c.lb) || # simplify lb towards Union{} + type_more_complex(t.ub, c.ub, sources, depth + 1, tupledepth, 0) + end + c.lb === Union{} || return true + return type_more_complex(t, c.ub, sources, depth, tupledepth, 0) + elseif isa(c, Union) + if isa(t, Union) + return type_more_complex(t.a, c.a, sources, depth, tupledepth, allowed_tuplelen) || + type_more_complex(t.b, c.b, sources, depth, tupledepth, allowed_tuplelen) + end + return type_more_complex(t, c.a, sources, depth, tupledepth, allowed_tuplelen) && + type_more_complex(t, c.b, sources, depth, tupledepth, allowed_tuplelen) + elseif isa(t, Int) && isa(c, Int) + return t !== 1 && !(0 <= t < c) # alternatively, could use !(abs(t) <= abs(c) || abs(t) < n) for some n + end + # base case for data types + if isa(t, DataType) + tP = t.parameters + if isa(c, DataType) && t.name === c.name + cP = c.parameters + length(cP) < length(tP) && return true + length(cP) > length(tP) && !isvarargtype(tP[end]) && depth == 1 && return false + ntail = length(cP) - length(tP) # assume parameters were dropped from the tuple head + # allow creating variation within a nested tuple, but only so deep + if t.name === Tuple.name && tupledepth > 0 + tupledepth -= 1 + elseif !isvarargtype(t) + tupledepth = 0 + end + isgenerator = (t.name.name === :Generator && t.name.module === _topmod(t.name.module)) + for i = 1:length(tP) + tPi = tP[i] + cPi = cP[i + ntail] + if isgenerator + let tPi = unwrap_unionall(tPi), + cPi = unwrap_unionall(cPi) + if isa(tPi, DataType) && isa(cPi, DataType) && + !tPi.abstract && !cPi.abstract && + sym_isless(cPi.name.name, tPi.name.name) + # allow collect on (anonymous) Generators to nest, provided that their functions are appropriately ordered + # TODO: is there a better way? + continue + end + end + end + type_more_complex(tPi, cPi, sources, depth + 1, tupledepth, 0) && return true + end + return false + elseif isvarargtype(c) + return type_more_complex(t, unwrapva(c), sources, depth, tupledepth, 0) + end + if isType(t) # allow taking typeof any source type anywhere as Type{...}, as long as it isn't nesting Type{Type{...}} + tt = unwrap_unionall(t.parameters[1]) + if isa(tt, DataType) && !isType(tt) + is_derived_type_from_any(tt, sources, depth) || return true + return false + end + end + end + return true +end + +# pick a wider type that contains both typea and typeb, +# with some limits on how "large" it can get, +# but without losing too much precision in common cases +# and also trying to be mostly associative and commutative +function tmerge(@nospecialize(typea), @nospecialize(typeb)) + typea ⊑ typeb && return typeb + typeb ⊑ typea && return typea + # type-lattice for MaybeUndef wrapper + if isa(typea, MaybeUndef) || isa(typeb, MaybeUndef) + return MaybeUndef(tmerge( + isa(typea, MaybeUndef) ? typea.typ : typea, + isa(typeb, MaybeUndef) ? typeb.typ : typeb)) + end + # type-lattice for Conditional wrapper + if isa(typea, Conditional) && isa(typeb, Const) + if typeb.val === true + typeb = Conditional(typea.var, Any, Union{}) + elseif typeb.val === false + typeb = Conditional(typea.var, Union{}, Any) + end + end + if isa(typeb, Conditional) && isa(typea, Const) + if typea.val === true + typea = Conditional(typeb.var, Any, Union{}) + elseif typea.val === false + typea = Conditional(typeb.var, Union{}, Any) + end + end + if isa(typea, Conditional) && isa(typeb, Conditional) + if typea.var === typeb.var + vtype = tmerge(typea.vtype, typeb.vtype) + elsetype = tmerge(typea.elsetype, typeb.elsetype) + if vtype != elsetype + return Conditional(typea.var, vtype, elsetype) + end + end + val = maybe_extract_const_bool(typea) + if val isa Bool && val === maybe_extract_const_bool(typeb) + return Const(val) + end + return Bool + end + if (isa(typea, PartialStruct) || isa(typea, Const)) && + (isa(typeb, PartialStruct) || isa(typeb, Const)) && + widenconst(typea) === widenconst(typeb) + + typea_nfields = nfields_tfunc(typea) + typeb_nfields = nfields_tfunc(typeb) + if !isa(typea_nfields, Const) || !isa(typea_nfields, Const) || typea_nfields.val !== typeb_nfields.val + return widenconst(typea) + end + + type_nfields = typea_nfields.val::Int + fields = Vector{Any}(undef, type_nfields) + anyconst = false + for i = 1:type_nfields + fields[i] = tmerge(getfield_tfunc(typea, Const(i)), + getfield_tfunc(typeb, Const(i))) + anyconst |= has_nontrivial_const_info(fields[i]) + end + return anyconst ? PartialStruct(widenconst(typea), fields) : + widenconst(typea) + end + # no special type-inference lattice, join the types + typea, typeb = widenconst(typea), widenconst(typeb) + typea === typeb && return typea + if !(isa(typea, Type) || isa(typea, TypeVar)) || + !(isa(typeb, Type) || isa(typeb, TypeVar)) + # XXX: this should never happen + return Any + end + # it's always ok to form a Union of two concrete types + if (isconcretetype(typea) || isType(typea)) && (isconcretetype(typeb) || isType(typeb)) + return Union{typea, typeb} + end + # collect the list of types from past tmerge calls returning Union + # and then reduce over that list + types = Any[] + _uniontypes(typea, types) + _uniontypes(typeb, types) + typenames = Vector{Core.TypeName}(undef, length(types)) + for i in 1:length(types) + # check that we will be able to analyze (and simplify) everything + # bail if everything isn't a well-formed DataType + ti = types[i] + uw = unwrap_unionall(ti) + (uw isa DataType && ti <: uw.name.wrapper) || return Any + typenames[i] = uw.name + end + # see if any of the union elements have the same TypeName + # in which case, simplify this tmerge by replacing it with + # the widest possible version of itself (the wrapper) + for i in 1:length(types) + ti = types[i] + for j in (i + 1):length(types) + if typenames[i] === typenames[j] + tj = types[j] + if ti <: tj + types[i] = Union{} + break + elseif tj <: ti + types[j] = Union{} + typenames[j] = Any.name + else + if typenames[i] === Tuple.name + # try to widen Tuple slower: make a single non-concrete Tuple containing both + # converge the Tuple element-wise if they are the same length + # see 4ee2b41552a6bc95465c12ca66146d69b354317b, be59686f7613a2ccfd63491c7b354d0b16a95c05, + widen = tuplemerge(unwrap_unionall(ti)::DataType, unwrap_unionall(tj)::DataType) + widen = rewrap_unionall(rewrap_unionall(widen, ti), tj) + else + widen = typenames[i].wrapper + end + types[i] = Union{} + types[j] = widen + break + end + end + end + end + u = Union{types...} + if unionlen(u) <= MAX_TYPEUNION_LENGTH && unioncomplexity(u) <= MAX_TYPEUNION_COMPLEXITY + # don't let type unions get too big, if the above didn't reduce it enough + return u + end + # finally, just return the widest possible type + return Any +end + +# the inverse of switchtupleunion, with limits on max element union size +function tuplemerge(a::DataType, b::DataType) + @assert a.name === b.name === Tuple.name "assertion failure" + ap, bp = a.parameters, b.parameters + lar = length(ap)::Int + lbr = length(bp)::Int + va = lar > 0 && isvarargtype(ap[lar]) + vb = lbr > 0 && isvarargtype(bp[lbr]) + if lar == lbr && !va && !vb + lt = lar + vt = false + else + lt = 0 # or min(lar - va, lbr - vb) + vt = true + end + # combine the common elements + p = Vector{Any}(undef, lt + vt) + for i = 1:lt + ui = Union{ap[i], bp[i]} + if unionlen(ui) <= MAX_TYPEUNION_LENGTH && unioncomplexity(ui) <= MAX_TYPEUNION_COMPLEXITY + p[i] = ui + else + p[i] = Any + end + end + # merge the remaining tail into a single, simple Tuple{Vararg{T}} (#22120) + if vt + tail = Union{} + for loop_b = (false, true) + for i = (lt + 1):(loop_b ? lbr : lar) + ti = unwrapva(loop_b ? bp[i] : ap[i]) + while ti isa TypeVar + ti = ti.ub + end + # compare (ti <-> tail), (wrapper ti <-> tail), (ti <-> wrapper tail), then (wrapper ti <-> wrapper tail) + # until we find the first element that contains the other in the pair + # TODO: this result would be more stable (and more associative and more commutative) + # if we either joined all of the element wrappers first into a wide-tail, then picked between that or an exact tail, + # or (equivalently?) iteratively took super-types until reaching a common wrapper + # e.g. consider the results of `tuplemerge(Tuple{Complex}, Tuple{Number, Int})` and of + # `tuplemerge(Tuple{Int}, Tuple{String}, Tuple{Int, String})` + if !(ti <: tail) + if tail <: ti + tail = ti # widen to ti + else + uw = unwrap_unionall(tail) + if uw isa DataType && tail <: uw.name.wrapper + # widen tail to wrapper(tail) + tail = uw.name.wrapper + if !(ti <: tail) + #assert !(tail <: ti) + uw = unwrap_unionall(ti) + if uw isa DataType && ti <: uw.name.wrapper + # widen ti to wrapper(ti) + ti = uw.name.wrapper + #assert !(ti <: tail) + if tail <: ti + tail = ti + else + tail = Any # couldn't find common super-type + end + else + tail = Any # couldn't analyze type + end + end + else + tail = Any # couldn't analyze type + end + end + end + tail === Any && return Tuple # short-circuit loop + end + end + @assert !(tail === Union{}) + p[lt + 1] = Vararg{tail} + end + return Tuple{p...} +end diff --git a/base/compiler/typeutils.jl b/base/compiler/typeutils.jl new file mode 100644 index 0000000..89c9c8c --- /dev/null +++ b/base/compiler/typeutils.jl @@ -0,0 +1,174 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +##################### +# lattice utilities # +##################### + +function rewrap(@nospecialize(t), @nospecialize(u)) + if isa(t, TypeVar) || isa(t, Type) + return rewrap_unionall(t, u) + end + return t +end + +isType(@nospecialize t) = isa(t, DataType) && t.name === _TYPE_NAME + +# true if Type{T} is inlineable as constant T +# requires that T is a singleton, s.t. T == S implies T === S +isconstType(@nospecialize t) = isType(t) && hasuniquerep(t.parameters[1]) + +# test whether type T has a unique representation, s.t. T == S implies T === S +function hasuniquerep(@nospecialize t) + # typeof(Bottom) is special since even though it is a leaftype, + # at runtime, it might be Type{Union{}} instead, so don't attempt inference of it + t === typeof(Union{}) && return false + t === Union{} && return true + isa(t, TypeVar) && return false # TypeVars are identified by address, not equality + iskindtype(typeof(t)) || return true # non-types are always compared by egal in the type system + isconcretetype(t) && return true # these are also interned and pointer comparable + if isa(t, DataType) && t.name !== Tuple.name && !isvarargtype(t) # invariant DataTypes + return _all(hasuniquerep, t.parameters) + end + return false +end + +function has_nontrivial_const_info(@nospecialize t) + isa(t, PartialStruct) && return true + return isa(t, Const) && !isdefined(typeof(t.val), :instance) && !(isa(t.val, Type) && hasuniquerep(t.val)) +end + +# Subtyping currently intentionally answers certain queries incorrectly for kind types. For +# some of these queries, this check can be used to somewhat protect against making incorrect +# decisions based on incorrect subtyping. Note that this check, itself, is broken for +# certain combinations of `a` and `b` where one/both isa/are `Union`/`UnionAll` type(s)s. +isnotbrokensubtype(@nospecialize(a), @nospecialize(b)) = (!iskindtype(b) || !isType(a) || hasuniquerep(a.parameters[1])) + +argtypes_to_type(argtypes::Array{Any,1}) = Tuple{anymap(widenconst, argtypes)...} + +function isknownlength(t::DataType) + isvatuple(t) || return true + return length(t.parameters) > 0 && isa(unwrap_unionall(t.parameters[end]).parameters[2], Int) +end + +# test if non-Type, non-TypeVar `x` can be used to parameterize a type +function valid_tparam(@nospecialize(x)) + if isa(x, Tuple) + for t in x + isa(t, Symbol) || isbits(t) || return false + end + return true + end + return isa(x, Symbol) || isbits(x) +end + +# return an upper-bound on type `a` with type `b` removed +# such that `return <: a` && `Union{return, b} == Union{a, b}` +function typesubtract(@nospecialize(a), @nospecialize(b)) + if a <: b && isnotbrokensubtype(a, b) + return Bottom + end + if isa(a, Union) + return Union{typesubtract(a.a, b), + typesubtract(a.b, b)} + end + return a # TODO: improve this bound? +end + +function tvar_extent(@nospecialize t) + while t isa TypeVar + t = t.ub + end + return t +end + +_typename(@nospecialize a) = Union{} +_typename(a::TypeVar) = Core.TypeName +function _typename(a::Union) + ta = _typename(a.a) + tb = _typename(a.b) + ta === tb && return ta # same type-name + (ta === Union{} || tb === Union{}) && return Union{} # threw an error + (ta isa Const && tb isa Const) && return Union{} # will throw an error (different type-names) + return Core.TypeName # uncertain result +end +_typename(union::UnionAll) = _typename(union.body) +_typename(a::DataType) = Const(a.name) + +function tuple_tail_elem(@nospecialize(init), ct::Vector{Any}) + # FIXME: this is broken: it violates subtyping relations and creates invalid types with free typevars + tmerge_maybe_vararg(@nospecialize(a), @nospecialize(b)) = tmerge(a, tvar_extent(unwrapva(b))) + t = init + for x in ct + t = tmerge_maybe_vararg(t, x) + end + return Vararg{widenconst(t)} +end + +function countunionsplit(atypes::Union{SimpleVector,Vector{Any}}) + nu = 1 + for ti in atypes + if isa(ti, Union) + nu, ovf = Core.Intrinsics.checked_smul_int(nu, unionlen(ti::Union)) + if ovf + return typemax(Int) + end + end + end + return nu +end + +# take a Tuple where one or more parameters are Unions +# and return an array such that those Unions are removed +# and `Union{return...} == ty` +function switchtupleunion(@nospecialize(ty)) + tparams = (unwrap_unionall(ty)::DataType).parameters + return _switchtupleunion(Any[tparams...], length(tparams), [], ty) +end + +function _switchtupleunion(t::Vector{Any}, i::Int, tunion::Vector{Any}, @nospecialize(origt)) + if i == 0 + tpl = rewrap_unionall(Tuple{t...}, origt) + push!(tunion, tpl) + else + ti = t[i] + if isa(ti, Union) + for ty in uniontypes(ti::Union) + t[i] = ty + _switchtupleunion(t, i - 1, tunion, origt) + end + t[i] = ti + else + _switchtupleunion(t, i - 1, tunion, origt) + end + end + return tunion +end + +# unioncomplexity estimates the number of calls to `tmerge` to obtain the given type by +# counting the Union instances, taking also into account those hidden in a Tuple or UnionAll +function unioncomplexity(u::Union) + inner = max(unioncomplexity(u.a), unioncomplexity(u.b)) + return inner == 0 ? 0 : 1 + inner +end +function unioncomplexity(t::DataType) + t.name === Tuple.name || return 0 + c = 1 + for ti in t.parameters + ci = unioncomplexity(ti) + if ci > c + c = ci + end + end + return c +end +unioncomplexity(u::UnionAll) = max(unioncomplexity(u.body), unioncomplexity(u.var.ub)) +unioncomplexity(@nospecialize(x)) = 0 + +function improvable_via_constant_propagation(@nospecialize(t)) + if isconcretetype(t) && t <: Tuple + for p in t.parameters + p === DataType && return true + end + end + return false +end diff --git a/base/compiler/utilities.jl b/base/compiler/utilities.jl new file mode 100644 index 0000000..b53030f --- /dev/null +++ b/base/compiler/utilities.jl @@ -0,0 +1,235 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +########### +# generic # +########### + +if !isdefined(@__MODULE__, Symbol("@timeit")) + # This is designed to allow inserting timers when loading a second copy + # of inference for performing performance experiments. + macro timeit(args...) + esc(args[end]) + end +end + +# avoid cycle due to over-specializing `any` when used by inference +function _any(@nospecialize(f), a) + for x in a + f(x) && return true + end + return false +end + +function _all(@nospecialize(f), a) + for x in a + f(x) || return false + end + return true +end + +function contains_is(itr, @nospecialize(x)) + for y in itr + if y === x + return true + end + end + return false +end + +anymap(f::Function, a::Array{Any,1}) = Any[ f(a[i]) for i in 1:length(a) ] + +########### +# scoping # +########### + +_topmod(m::Module) = ccall(:jl_base_relative_to, Any, (Any,), m)::Module + +function istopfunction(@nospecialize(f), name::Symbol) + tn = typeof(f).name + if tn.mt.name === name + top = _topmod(tn.module) + return isdefined(top, name) && isconst(top, name) && f === getfield(top, name) + end + return false +end + +####### +# AST # +####### + +# Meta expression head, these generally can't be deleted even when they are +# in a dead branch but can be ignored when analyzing uses/liveness. +is_meta_expr_head(head::Symbol) = (head === :inbounds || head === :boundscheck || head === :meta || head === :loopinfo) + +sym_isless(a::Symbol, b::Symbol) = ccall(:strcmp, Int32, (Ptr{UInt8}, Ptr{UInt8}), a, b) < 0 + +function is_self_quoting(@nospecialize(x)) + return isa(x,Number) || isa(x,AbstractString) || isa(x,Tuple) || isa(x,Type) || + isa(x,Char) || x === nothing || isa(x,Function) +end + +function quoted(@nospecialize(x)) + return is_self_quoting(x) ? x : QuoteNode(x) +end + +function is_inlineable_constant(@nospecialize(x)) + x isa Type && return true + return isbits(x) && Core.sizeof(x) <= MAX_INLINE_CONST_SIZE +end + +########################### +# MethodInstance/CodeInfo # +########################### + +function invoke_api(li::CodeInstance) + return ccall(:jl_invoke_api, Cint, (Any,), li) +end + +function get_staged(li::MethodInstance) + may_invoke_generator(li) || return nothing + try + # user code might throw errors – ignore them + return ccall(:jl_code_for_staged, Any, (Any,), li)::CodeInfo + catch + return nothing + end +end + +function retrieve_code_info(linfo::MethodInstance) + m = linfo.def::Method + c = nothing + if isdefined(m, :generator) + # user code might throw errors – ignore them + c = get_staged(linfo) + end + if c === nothing && isdefined(m, :source) + src = m.source + if isa(src, Array{UInt8,1}) + c = ccall(:jl_uncompress_ast, Any, (Any, Ptr{Cvoid}, Any), m, C_NULL, src) + else + c = copy(src::CodeInfo) + end + end + if c isa CodeInfo + c.parent = linfo + return c + end +end + +function inf_for_methodinstance(mi::MethodInstance, min_world::UInt, max_world::UInt=min_world) + return ccall(:jl_rettype_inferred, Any, (Any, UInt, UInt), mi, min_world, max_world)::Union{Nothing, CodeInstance} +end + + +# get a handle to the unique specialization object representing a particular instantiation of a call +function specialize_method(method::Method, @nospecialize(atypes), sparams::SimpleVector, preexisting::Bool=false) + if preexisting + if method.specializations !== nothing + # check cached specializations + # for an existing result stored there + return ccall(:jl_specializations_lookup, Any, (Any, Any), method, atypes) + end + return nothing + end + return ccall(:jl_specializations_get_linfo, Ref{MethodInstance}, (Any, Any, Any), method, atypes, sparams) +end + +# This function is used for computing alternate limit heuristics +function method_for_inference_heuristics(method::Method, @nospecialize(sig), sparams::SimpleVector) + if isdefined(method, :generator) && method.generator.expand_early && may_invoke_generator(method, sig, sparams) + method_instance = specialize_method(method, sig, sparams, false) + if isa(method_instance, MethodInstance) + cinfo = get_staged(method_instance) + if isa(cinfo, CodeInfo) + method2 = cinfo.method_for_inference_limit_heuristics + if method2 isa Method + return method2 + end + end + end + end + return nothing +end + +argextype(@nospecialize(x), state) = argextype(x, state.src, state.sptypes, state.slottypes) + +const empty_slottypes = Any[] + +function argextype(@nospecialize(x), src, sptypes::Vector{Any}, slottypes::Vector{Any} = empty_slottypes) + if isa(x, Expr) + if x.head === :static_parameter + return sptypes[x.args[1]] + elseif x.head === :boundscheck + return Bool + elseif x.head === :copyast + return argextype(x.args[1], src, sptypes, slottypes) + end + @assert false "argextype only works on argument-position values" + elseif isa(x, SlotNumber) + return slottypes[(x::SlotNumber).id] + elseif isa(x, TypedSlot) + return (x::TypedSlot).typ + elseif isa(x, SSAValue) + return abstract_eval_ssavalue(x::SSAValue, src) + elseif isa(x, Argument) + return isa(src, IncrementalCompact) ? src.ir.argtypes[x.n] : src.argtypes[x.n] + elseif isa(x, QuoteNode) + return AbstractEvalConstant((x::QuoteNode).value) + elseif isa(x, GlobalRef) + return abstract_eval_global(x.mod, (x::GlobalRef).name) + elseif isa(x, PhiNode) + return Any + elseif isa(x, PiNode) + return x.typ + else + return AbstractEvalConstant(x) + end +end + +################### +# SSAValues/Slots # +################### + +function find_ssavalue_uses(body::Vector{Any}, nvals::Int) + uses = BitSet[ BitSet() for i = 1:nvals ] + for line in 1:length(body) + e = body[line] + if isa(e, SSAValue) + push!(uses[e.id], line) + elseif isa(e, Expr) + find_ssavalue_uses(e, uses, line) + end + end + return uses +end + +function find_ssavalue_uses(e::Expr, uses::Vector{BitSet}, line::Int) + head = e.head + is_meta_expr_head(head) && return + skiparg = (head === :(=)) + for a in e.args + if skiparg + skiparg = false + elseif isa(a, SSAValue) + push!(uses[a.id], line) + elseif isa(a, Expr) + find_ssavalue_uses(a, uses, line) + end + end +end + +# using a function to ensure we can infer this +@inline slot_id(s) = isa(s, SlotNumber) ? (s::SlotNumber).id : (s::TypedSlot).id + +########### +# options # +########### + +inlining_enabled() = (JLOptions().can_inline == 1) +coverage_enabled() = (JLOptions().code_coverage != 0) +function inbounds_option() + opt_check_bounds = JLOptions().check_bounds + opt_check_bounds == 0 && return :default + opt_check_bounds == 1 && return :on + return :off +end diff --git a/base/compiler/validation.jl b/base/compiler/validation.jl new file mode 100644 index 0000000..e02327c --- /dev/null +++ b/base/compiler/validation.jl @@ -0,0 +1,236 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Expr head => argument count bounds +const VALID_EXPR_HEADS = IdDict{Any,Any}( + :call => 1:typemax(Int), + :invoke => 2:typemax(Int), + :static_parameter => 1:1, + :gotoifnot => 2:2, + :(&) => 1:1, + :(=) => 2:2, + :method => 1:4, + :const => 1:1, + :new => 1:typemax(Int), + :splatnew => 2:2, + :return => 1:1, + :unreachable => 0:0, + :the_exception => 0:0, + :enter => 1:1, + :leave => 1:1, + :pop_exception => 1:1, + :inbounds => 1:1, + :boundscheck => 0:0, + :copyast => 1:1, + :meta => 0:typemax(Int), + :global => 1:1, + :foreigncall => 5:typemax(Int), # name, RT, AT, nreq, cconv, args..., roots... + :cfunction => 5:5, + :isdefined => 1:1, + :loopinfo => 0:typemax(Int), + :gc_preserve_begin => 0:typemax(Int), + :gc_preserve_end => 0:typemax(Int), + :thunk => 1:1, + :throw_undef_if_not => 2:2 +) + +# @enum isn't defined yet, otherwise I'd use it for this +const INVALID_EXPR_HEAD = "invalid expression head" +const INVALID_EXPR_NARGS = "invalid number of expression args" +const INVALID_LVALUE = "invalid LHS value" +const INVALID_RVALUE = "invalid RHS value" +const INVALID_RETURN = "invalid argument to :return" +const INVALID_CALL_ARG = "invalid :call argument" +const EMPTY_SLOTNAMES = "slotnames field is empty" +const SLOTFLAGS_MISMATCH = "length(slotnames) < length(slotflags)" +const SSAVALUETYPES_MISMATCH = "not all SSAValues in AST have a type in ssavaluetypes" +const SSAVALUETYPES_MISMATCH_UNINFERRED = "uninferred CodeInfo ssavaluetypes field does not equal the number of present SSAValues" +const NON_TOP_LEVEL_METHOD = "encountered `Expr` head `:method` in non-top-level code (i.e. `nargs` > 0)" +const NON_TOP_LEVEL_GLOBAL = "encountered `Expr` head `:global` in non-top-level code (i.e. `nargs` > 0)" +const SIGNATURE_NARGS_MISMATCH = "method signature does not match number of method arguments" +const SLOTNAMES_NARGS_MISMATCH = "CodeInfo for method contains fewer slotnames than the number of method arguments" + +struct InvalidCodeError <: Exception + kind::AbstractString + meta::Any +end +InvalidCodeError(kind::AbstractString) = InvalidCodeError(kind, nothing) + +function validate_code_in_debug_mode(linfo::MethodInstance, src::CodeInfo, kind::String) + if JLOptions().debug_level == 2 + # this is a debug build of julia, so let's validate linfo + errors = validate_code(linfo, src) + if !isempty(errors) + for e in errors + if linfo.def isa Method + println(stderr, "WARNING: Encountered invalid ", kind, " code for method ", + linfo.def, ": ", e) + else + println(stderr, "WARNING: Encountered invalid ", kind, " code for top level expression in ", + linfo.def, ": ", e) + end + end + end + end +end + +""" + validate_code!(errors::Vector{>:InvalidCodeError}, c::CodeInfo) + +Validate `c`, logging any violation by pushing an `InvalidCodeError` into `errors`. +""" +function validate_code!(errors::Vector{>:InvalidCodeError}, c::CodeInfo, is_top_level::Bool = false) + function validate_val!(@nospecialize(x)) + if isa(x, Expr) + if x.head === :call || x.head === :invoke + f = x.args[1] + if f isa GlobalRef && (f.name === :cglobal) && x.head === :call + # TODO: these are not yet linearized + else + for arg in x.args + if !is_valid_argument(arg) + push!(errors, InvalidCodeError(INVALID_CALL_ARG, arg)) + else + validate_val!(arg) + end + end + end + end + elseif isa(x, SSAValue) + id = x.id + !in(id, ssavals) && push!(ssavals, id) + end + end + + ssavals = BitSet() + lhs_slotnums = BitSet() + for x in c.code + if isa(x, Expr) + head = x.head + if !is_top_level + head === :method && push!(errors, InvalidCodeError(NON_TOP_LEVEL_METHOD)) + head === :global && push!(errors, InvalidCodeError(NON_TOP_LEVEL_GLOBAL)) + end + narg_bounds = get(VALID_EXPR_HEADS, head, -1:-1) + nargs = length(x.args) + if narg_bounds == -1:-1 + push!(errors, InvalidCodeError(INVALID_EXPR_HEAD, (head, x))) + elseif !in(nargs, narg_bounds) + push!(errors, InvalidCodeError(INVALID_EXPR_NARGS, (head, nargs, x))) + elseif head === :(=) + lhs, rhs = x.args + if !is_valid_lvalue(lhs) + push!(errors, InvalidCodeError(INVALID_LVALUE, lhs)) + elseif isa(lhs, SlotNumber) && !in(lhs.id, lhs_slotnums) + n = lhs.id + push!(lhs_slotnums, n) + end + if !is_valid_rvalue(rhs) + push!(errors, InvalidCodeError(INVALID_RVALUE, rhs)) + end + validate_val!(lhs) + validate_val!(rhs) + elseif head === :gotoifnot + if !is_valid_argument(x.args[1]) + push!(errors, InvalidCodeError(INVALID_CALL_ARG, x.args[1])) + end + validate_val!(x.args[1]) + elseif head === :return + if !is_valid_return(x.args[1]) + push!(errors, InvalidCodeError(INVALID_RETURN, x.args[1])) + end + validate_val!(x.args[1]) + elseif head === :call || head === :invoke || head === :gc_preserve_end || head === :meta || + head === :inbounds || head === :foreigncall || head === :cfunction || + head === :const || head === :enter || head === :leave || head === :pop_exception || + head === :method || head === :global || head === :static_parameter || + head === :new || head === :splatnew || head === :thunk || head === :loopinfo || + head === :throw_undef_if_not || head === :unreachable + validate_val!(x) + else + # TODO: nothing is actually in statement position anymore + #push!(errors, InvalidCodeError("invalid statement", x)) + end + elseif isa(x, NewvarNode) + elseif isa(x, GotoNode) + elseif x === nothing + elseif isa(x, SlotNumber) + elseif isa(x, GlobalRef) + elseif isa(x, LineNumberNode) + elseif isa(x, PiNode) + elseif isa(x, PhiCNode) + elseif isa(x, PhiNode) + elseif isa(x, UpsilonNode) + else + #push!(errors, InvalidCodeError("invalid statement", x)) + end + end + nslotnames = length(c.slotnames) + nslotflags = length(c.slotflags) + nssavals = length(c.code) + !is_top_level && nslotnames == 0 && push!(errors, InvalidCodeError(EMPTY_SLOTNAMES)) + nslotnames < nslotflags && push!(errors, InvalidCodeError(SLOTFLAGS_MISMATCH, (nslotnames, nslotflags))) + if c.inferred + nssavaluetypes = length(c.ssavaluetypes) + nssavaluetypes < nssavals && push!(errors, InvalidCodeError(SSAVALUETYPES_MISMATCH, (nssavals, nssavaluetypes))) + else + c.ssavaluetypes != nssavals && push!(errors, InvalidCodeError(SSAVALUETYPES_MISMATCH_UNINFERRED, (nssavals, c.ssavaluetypes))) + end + return errors +end + +""" + validate_code!(errors::Vector{>:InvalidCodeError}, mi::MethodInstance, + c::Union{Nothing,CodeInfo} = Core.Compiler.retrieve_code_info(mi)) + +Validate `mi`, logging any violation by pushing an `InvalidCodeError` into `errors`. + +If `isa(c, CodeInfo)`, also call `validate_code!(errors, c)`. It is assumed that `c` is +the `CodeInfo` instance associated with `mi`. +""" +function validate_code!(errors::Vector{>:InvalidCodeError}, mi::Core.MethodInstance, + c::Union{Nothing,CodeInfo} = Core.Compiler.retrieve_code_info(mi)) + is_top_level = mi.def isa Module + if is_top_level + mnargs = 0 + else + m = mi.def::Method + mnargs = m.nargs + n_sig_params = length(Core.Compiler.unwrap_unionall(m.sig).parameters) + if (m.isva ? (n_sig_params < (mnargs - 1)) : (n_sig_params != mnargs)) + push!(errors, InvalidCodeError(SIGNATURE_NARGS_MISMATCH, (m.isva, n_sig_params, mnargs))) + end + end + if isa(c, CodeInfo) + mnargs > length(c.slotnames) && push!(errors, InvalidCodeError(SLOTNAMES_NARGS_MISMATCH)) + validate_code!(errors, c, is_top_level) + end + return errors +end + +validate_code(args...) = validate_code!(Vector{InvalidCodeError}(), args...) + +is_valid_lvalue(@nospecialize(x)) = isa(x, Slot) || isa(x, GlobalRef) + +function is_valid_argument(@nospecialize(x)) + if isa(x, Slot) || isa(x, SSAValue) || isa(x, GlobalRef) || isa(x, QuoteNode) || + (isa(x,Expr) && (x.head in (:static_parameter, :boundscheck))) || + isa(x, Number) || isa(x, AbstractString) || isa(x, AbstractChar) || isa(x, Tuple) || + isa(x, Type) || isa(x, Core.Box) || isa(x, Module) || x === nothing + return true + end + # TODO: consider being stricter about what needs to be wrapped with QuoteNode + return !(isa(x,Expr) || isa(x,Symbol) || isa(x,GotoNode) || + isa(x,LineNumberNode) || isa(x,NewvarNode)) +end + +function is_valid_rvalue(@nospecialize(x)) + is_valid_argument(x) && return true + if isa(x, Expr) && x.head in (:new, :splatnew, :the_exception, :isdefined, :call, :invoke, :foreigncall, :cfunction, :gc_preserve_begin, :copyast) + return true + end + return false +end + +is_valid_return(@nospecialize(x)) = is_valid_argument(x) || (isa(x, Expr) && x.head === :lambda) + +is_flag_set(byte::UInt8, flag::UInt8) = (byte & flag) == flag diff --git a/base/complex.jl b/base/complex.jl new file mode 100644 index 0000000..aaa4246 --- /dev/null +++ b/base/complex.jl @@ -0,0 +1,1026 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" + Complex{T<:Real} <: Number + +Complex number type with real and imaginary part of type `T`. + +`ComplexF16`, `ComplexF32` and `ComplexF64` are aliases for +`Complex{Float16}`, `Complex{Float32}` and `Complex{Float64}` respectively. +""" +struct Complex{T<:Real} <: Number + re::T + im::T +end +Complex(x::Real, y::Real) = Complex(promote(x,y)...) +Complex(x::Real) = Complex(x, zero(x)) + +""" + im + +The imaginary unit. + +# Examples +```jldoctest +julia> im * im +-1 + 0im +``` +""" +const im = Complex(false, true) + +const ComplexF64 = Complex{Float64} +const ComplexF32 = Complex{Float32} +const ComplexF16 = Complex{Float16} + +Complex{T}(x::Real) where {T<:Real} = Complex{T}(x,0) +Complex{T}(z::Complex) where {T<:Real} = Complex{T}(real(z),imag(z)) +(::Type{T})(z::Complex) where {T<:Real} = + isreal(z) ? T(real(z))::T : throw(InexactError(nameof(T), T, z)) + +Complex(z::Complex) = z + +promote_rule(::Type{Complex{T}}, ::Type{S}) where {T<:Real,S<:Real} = + Complex{promote_type(T,S)} +promote_rule(::Type{Complex{T}}, ::Type{Complex{S}}) where {T<:Real,S<:Real} = + Complex{promote_type(T,S)} + +widen(::Type{Complex{T}}) where {T} = Complex{widen(T)} + +float(::Type{Complex{T}}) where {T<:AbstractFloat} = Complex{T} +float(::Type{Complex{T}}) where {T} = Complex{float(T)} + +""" + real(z) + +Return the real part of the complex number `z`. + +# Examples +```jldoctest +julia> real(1 + 3im) +1 +``` +""" +real(z::Complex) = z.re + +""" + imag(z) + +Return the imaginary part of the complex number `z`. + +# Examples +```jldoctest +julia> imag(1 + 3im) +3 +``` +""" +imag(z::Complex) = z.im +real(x::Real) = x +imag(x::Real) = zero(x) + +""" + reim(z) + +Return both the real and imaginary parts of the complex number `z`. + +# Examples +```jldoctest +julia> reim(1 + 3im) +(1, 3) +``` +""" +reim(z) = (real(z), imag(z)) + +""" + real(T::Type) + +Return the type that represents the real part of a value of type `T`. +e.g: for `T == Complex{R}`, returns `R`. +Equivalent to `typeof(real(zero(T)))`. + +# Examples +```jldoctest +julia> real(Complex{Int}) +Int64 + +julia> real(Float64) +Float64 +``` +""" +real(T::Type) = typeof(real(zero(T))) +real(::Type{T}) where {T<:Real} = T +real(::Type{Complex{T}}) where {T<:Real} = T + +""" + isreal(x) -> Bool + +Test whether `x` or all its elements are numerically equal to some real number +including infinities and NaNs. `isreal(x)` is true if `isequal(x, real(x))` +is true. + +# Examples +```jldoctest +julia> isreal(5.) +true + +julia> isreal(Inf + 0im) +true + +julia> isreal([4.; complex(0,1)]) +false +``` +""" +isreal(x::Real) = true +isreal(z::Complex) = iszero(imag(z)) +isinteger(z::Complex) = isreal(z) & isinteger(real(z)) +isfinite(z::Complex) = isfinite(real(z)) & isfinite(imag(z)) +isnan(z::Complex) = isnan(real(z)) | isnan(imag(z)) +isinf(z::Complex) = isinf(real(z)) | isinf(imag(z)) +iszero(z::Complex) = iszero(real(z)) & iszero(imag(z)) +isone(z::Complex) = isone(real(z)) & iszero(imag(z)) + +""" + complex(r, [i]) + +Convert real numbers or arrays to complex. `i` defaults to zero. + +# Examples +```jldoctest +julia> complex(7) +7 + 0im + +julia> complex([1, 2, 3]) +3-element Array{Complex{Int64},1}: + 1 + 0im + 2 + 0im + 3 + 0im +``` +""" +complex(z::Complex) = z +complex(x::Real) = Complex(x) +complex(x::Real, y::Real) = Complex(x, y) + +""" + complex(T::Type) + +Return an appropriate type which can represent a value of type `T` as a complex number. +Equivalent to `typeof(complex(zero(T)))`. + +# Examples +```jldoctest +julia> complex(Complex{Int}) +Complex{Int64} + +julia> complex(Int) +Complex{Int64} +``` +""" +complex(::Type{T}) where {T<:Real} = Complex{T} +complex(::Type{Complex{T}}) where {T<:Real} = Complex{T} + +flipsign(x::Complex, y::Real) = ifelse(signbit(y), -x, x) + +function show(io::IO, z::Complex) + r, i = reim(z) + compact = get(io, :compact, false) + show(io, r) + if signbit(i) && !isnan(i) + i = -i + print(io, compact ? "-" : " - ") + else + print(io, compact ? "+" : " + ") + end + show(io, i) + if !(isa(i,Integer) && !isa(i,Bool) || isa(i,AbstractFloat) && isfinite(i)) + print(io, "*") + end + print(io, "im") +end +show(io::IO, z::Complex{Bool}) = + print(io, z == im ? "im" : "Complex($(z.re),$(z.im))") + +function show_unquoted(io::IO, z::Complex, ::Int, prec::Int) + if operator_precedence(:+) <= prec + print(io, "(") + show(io, z) + print(io, ")") + else + show(io, z) + end +end + +function read(s::IO, ::Type{Complex{T}}) where T<:Real + r = read(s,T) + i = read(s,T) + Complex{T}(r,i) +end +function write(s::IO, z::Complex) + write(s,real(z),imag(z)) +end + +## byte order swaps: real and imaginary part are swapped individually +bswap(z::Complex) = Complex(bswap(real(z)), bswap(imag(z))) + +## equality and hashing of complex numbers ## + +==(z::Complex, w::Complex) = (real(z) == real(w)) & (imag(z) == imag(w)) +==(z::Complex, x::Real) = isreal(z) && real(z) == x +==(x::Real, z::Complex) = isreal(z) && real(z) == x + +isequal(z::Complex, w::Complex) = isequal(real(z),real(w)) & isequal(imag(z),imag(w)) + +in(x::Complex, r::AbstractRange{<:Real}) = isreal(x) && real(x) in r + +if UInt === UInt64 + const h_imag = 0x32a7a07f3e7cd1f9 +else + const h_imag = 0x3e7cd1f9 +end +const hash_0_imag = hash(0, h_imag) + +function hash(z::Complex, h::UInt) + # TODO: with default argument specialization, this would be better: + # hash(real(z), h ⊻ hash(imag(z), h ⊻ h_imag) ⊻ hash(0, h ⊻ h_imag)) + hash(real(z), h ⊻ hash(imag(z), h_imag) ⊻ hash_0_imag) +end + +## generic functions of complex numbers ## + +""" + conj(z) + +Compute the complex conjugate of a complex number `z`. + +# Examples +```jldoctest +julia> conj(1 + 3im) +1 - 3im +``` +""" +conj(z::Complex) = Complex(real(z),-imag(z)) +abs(z::Complex) = hypot(real(z), imag(z)) +abs2(z::Complex) = real(z)*real(z) + imag(z)*imag(z) +function inv(z::Complex) + c, d = reim(z) + (isinf(c) | isinf(d)) && return complex(copysign(zero(c), c), flipsign(-zero(d), d)) + complex(c, -d)/(c * c + d * d) +end +inv(z::Complex{<:Integer}) = inv(float(z)) + ++(z::Complex) = Complex(+real(z), +imag(z)) +-(z::Complex) = Complex(-real(z), -imag(z)) ++(z::Complex, w::Complex) = Complex(real(z) + real(w), imag(z) + imag(w)) +-(z::Complex, w::Complex) = Complex(real(z) - real(w), imag(z) - imag(w)) +*(z::Complex, w::Complex) = Complex(real(z) * real(w) - imag(z) * imag(w), + real(z) * imag(w) + imag(z) * real(w)) + +muladd(z::Complex, w::Complex, x::Complex) = + Complex(muladd(real(z), real(w), real(x)) - imag(z)*imag(w), # TODO: use mulsub given #15985 + muladd(real(z), imag(w), muladd(imag(z), real(w), imag(x)))) + +# handle Bool and Complex{Bool} +# avoid type signature ambiguity warnings ++(x::Bool, z::Complex{Bool}) = Complex(x + real(z), imag(z)) ++(z::Complex{Bool}, x::Bool) = Complex(real(z) + x, imag(z)) +-(x::Bool, z::Complex{Bool}) = Complex(x - real(z), - imag(z)) +-(z::Complex{Bool}, x::Bool) = Complex(real(z) - x, imag(z)) +*(x::Bool, z::Complex{Bool}) = Complex(x * real(z), x * imag(z)) +*(z::Complex{Bool}, x::Bool) = Complex(real(z) * x, imag(z) * x) + ++(x::Bool, z::Complex) = Complex(x + real(z), imag(z)) ++(z::Complex, x::Bool) = Complex(real(z) + x, imag(z)) +-(x::Bool, z::Complex) = Complex(x - real(z), - imag(z)) +-(z::Complex, x::Bool) = Complex(real(z) - x, imag(z)) +*(x::Bool, z::Complex) = Complex(x * real(z), x * imag(z)) +*(z::Complex, x::Bool) = Complex(real(z) * x, imag(z) * x) + ++(x::Real, z::Complex{Bool}) = Complex(x + real(z), imag(z)) ++(z::Complex{Bool}, x::Real) = Complex(real(z) + x, imag(z)) +function -(x::Real, z::Complex{Bool}) + # we don't want the default type for -(Bool) + re = x-real(z) + Complex(re, - oftype(re, imag(z))) +end +-(z::Complex{Bool}, x::Real) = Complex(real(z) - x, imag(z)) +*(x::Real, z::Complex{Bool}) = Complex(x * real(z), x * imag(z)) +*(z::Complex{Bool}, x::Real) = Complex(real(z) * x, imag(z) * x) + +# adding or multiplying real & complex is common ++(x::Real, z::Complex) = Complex(x + real(z), imag(z)) ++(z::Complex, x::Real) = Complex(x + real(z), imag(z)) +function -(x::Real, z::Complex) + # we don't want the default type for -(Bool) + re = x - real(z) + Complex(re, - oftype(re, imag(z))) +end +-(z::Complex, x::Real) = Complex(real(z) - x, imag(z)) +*(x::Real, z::Complex) = Complex(x * real(z), x * imag(z)) +*(z::Complex, x::Real) = Complex(x * real(z), x * imag(z)) + +muladd(x::Real, z::Complex, y::Number) = muladd(z, x, y) +muladd(z::Complex, x::Real, y::Real) = Complex(muladd(real(z),x,y), imag(z)*x) +muladd(z::Complex, x::Real, w::Complex) = + Complex(muladd(real(z),x,real(w)), muladd(imag(z),x,imag(w))) +muladd(x::Real, y::Real, z::Complex) = Complex(muladd(x,y,real(z)), imag(z)) +muladd(z::Complex, w::Complex, x::Real) = + Complex(muladd(real(z), real(w), x) - imag(z)*imag(w), # TODO: use mulsub given #15985 + muladd(real(z), imag(w), imag(z) * real(w))) + +/(a::R, z::S) where {R<:Real,S<:Complex} = (T = promote_type(R,S); a*inv(T(z))) +/(z::Complex, x::Real) = Complex(real(z)/x, imag(z)/x) + +function /(a::Complex{T}, b::Complex{T}) where T<:Real + are = real(a); aim = imag(a); bre = real(b); bim = imag(b) + if abs(bre) <= abs(bim) + if isinf(bre) && isinf(bim) + r = sign(bre)/sign(bim) + else + r = bre / bim + end + den = bim + r*bre + Complex((are*r + aim)/den, (aim*r - are)/den) + else + if isinf(bre) && isinf(bim) + r = sign(bim)/sign(bre) + else + r = bim / bre + end + den = bre + r*bim + Complex((are + aim*r)/den, (aim - are*r)/den) + end +end + +inv(z::Complex{<:Union{Float16,Float32}}) = + oftype(z, inv(widen(z))) + +/(z::Complex{T}, w::Complex{T}) where {T<:Union{Float16,Float32}} = + oftype(z, widen(z)*inv(widen(w))) + +# robust complex division for double precision +# variables are scaled & unscaled to avoid over/underflow, if necessary +# based on arxiv.1210.4539 +# a + i*b +# p + i*q = --------- +# c + i*d +function /(z::ComplexF64, w::ComplexF64) + a, b = reim(z); c, d = reim(w) + absa = abs(a); absb = abs(b); ab = absa >= absb ? absa : absb # equiv. to max(abs(a),abs(b)) but without NaN-handling (faster) + absc = abs(c); absd = abs(d); cd = absc >= absd ? absc : absd + + halfov = 0.5*floatmax(Float64) # overflow threshold + twounϵ = floatmin(Float64)*2.0/eps(Float64) # underflow threshold + + # actual division operations + if ab>=halfov || ab<=twounϵ || cd>=halfov || cd<=twounϵ # over/underflow case + p,q = scaling_cdiv(a,b,c,d,ab,cd) # scales a,b,c,d before division (unscales after) + else + p,q = cdiv(a,b,c,d) + end + + return ComplexF64(p,q) +end + +# sub-functionality for /(z::ComplexF64, w::ComplexF64) +@inline function cdiv(a::Float64, b::Float64, c::Float64, d::Float64) + if abs(d)<=abs(c) + p,q = robust_cdiv1(a,b,c,d) + else + p,q = robust_cdiv1(b,a,d,c) + q = -q + end + return p,q +end +@noinline function scaling_cdiv(a::Float64, b::Float64, c::Float64, d::Float64, ab::Float64, cd::Float64) + # this over/underflow functionality is outlined for performance, cf. #29688 + a,b,c,d,s = scaleargs_cdiv(a,b,c,d,ab,cd) + p,q = cdiv(a,b,c,d) + return p*s,q*s +end +function scaleargs_cdiv(a::Float64, b::Float64, c::Float64, d::Float64, ab::Float64, cd::Float64) + ϵ = eps(Float64) + halfov = 0.5*floatmax(Float64) + twounϵ = floatmin(Float64)*2.0/ϵ + bs = 2.0/(ϵ*ϵ) + + # scaling + s = 1.0 + if ab >= halfov + a*=0.5; b*=0.5; s*=2.0 # scale down a,b + elseif ab <= twounϵ + a*=bs; b*=bs; s/=bs # scale up a,b + end + if cd >= halfov + c*=0.5; d*=0.5; s*=0.5 # scale down c,d + elseif cd <= twounϵ + c*=bs; d*=bs; s*=bs # scale up c,d + end + + return a,b,c,d,s +end +@inline function robust_cdiv1(a::Float64, b::Float64, c::Float64, d::Float64) + r = d/c + t = 1.0/(c+d*r) + p = robust_cdiv2(a,b,c,d,r,t) + q = robust_cdiv2(b,-a,c,d,r,t) + return p,q +end +function robust_cdiv2(a::Float64, b::Float64, c::Float64, d::Float64, r::Float64, t::Float64) + if r != 0 + br = b*r + return (br != 0 ? (a+br)*t : a*t + (b*t)*r) + else + return (a + d*(b/c)) * t + end +end + +function inv(w::ComplexF64) + c, d = reim(w) + (isinf(c) | isinf(d)) && return complex(copysign(0.0, c), flipsign(-0.0, d)) + half = 0.5 + two = 2.0 + cd = max(abs(c), abs(d)) + ov = floatmax(c) + un = floatmin(c) + ϵ = eps(Float64) + bs = two/(ϵ*ϵ) + s = 1.0 + cd >= half*ov && (c=half*c; d=half*d; s=s*half) # scale down c,d + cd <= un*two/ϵ && (c=c*bs; d=d*bs; s=s*bs ) # scale up c,d + if abs(d)<=abs(c) + r = d/c + t = 1.0/(c+d*r) + p = t + q = -r * t + else + c, d = d, c + r = d/c + t = 1.0/(c+d*r) + p = r * t + q = -t + end + return ComplexF64(p*s,q*s) # undo scaling +end + +function ssqs(x::T, y::T) where T<:AbstractFloat + k::Int = 0 + ρ = x*x + y*y + if !isfinite(ρ) && (isinf(x) || isinf(y)) + ρ = convert(T, Inf) + elseif isinf(ρ) || (ρ==0 && (x!=0 || y!=0)) || ρ= 0 +# return Complex(r, iz/r/2) +# end +# return Complex(abs(iz)/r/2, copysign(r,iz)) +# end + +# compute exp(im*theta) +function cis(theta::Real) + s, c = sincos(theta) + Complex(c, s) +end + +""" + cis(z) + +Return ``\\exp(iz)``. + +# Examples +```jldoctest +julia> cis(π) ≈ -1 +true +``` +""" +function cis(z::Complex) + v = exp(-imag(z)) + s, c = sincos(real(z)) + Complex(v * c, v * s) +end + +""" + angle(z) + +Compute the phase angle in radians of a complex number `z`. + +# Examples +```jldoctest +julia> rad2deg(angle(1 + im)) +45.0 + +julia> rad2deg(angle(1 - im)) +-45.0 + +julia> rad2deg(angle(-1 - im)) +-135.0 +``` +""" +angle(z::Complex) = atan(imag(z), real(z)) + +function log(z::Complex{T}) where T<:AbstractFloat + T1::T = 1.25 + T2::T = 3 + ln2::T = log(convert(T,2)) #0.6931471805599453 + x, y = reim(z) + ρ, k = ssqs(x,y) + ax = abs(x) + ay = abs(y) + if ax < ay + θ, β = ax, ay + else + θ, β = ay, ax + end + if k==0 && (0.5 < β*β) && (β <= T1 || ρ < T2) + ρρ = log1p((β-1)*(β+1)+θ*θ)/2 + else + ρρ = log(ρ)/2 + k*ln2 + end + Complex(ρρ, angle(z)) +end +log(z::Complex) = log(float(z)) + +# function log(z::Complex) +# ar = abs(real(z)) +# ai = abs(imag(z)) +# if ar < ai +# r = ar/ai +# re = log(ai) + log1p(r*r)/2 +# else +# if ar == 0 +# re = isnan(ai) ? ai : -inv(ar) +# elseif isinf(ai) +# re = oftype(ar,Inf) +# else +# r = ai/ar +# re = log(ar) + log1p(r*r)/2 +# end +# end +# Complex(re, angle(z)) +# end + +function log10(z::Complex) + a = log(z) + a/log(oftype(real(a),10)) +end +function log2(z::Complex) + a = log(z) + a/log(oftype(real(a),2)) +end + +function exp(z::Complex) + zr, zi = reim(z) + if isnan(zr) + Complex(zr, zi==0 ? zi : zr) + elseif !isfinite(zi) + if zr == Inf + Complex(-zr, oftype(zr,NaN)) + elseif zr == -Inf + Complex(-zero(zr), copysign(zero(zi), zi)) + else + Complex(oftype(zr,NaN), oftype(zi,NaN)) + end + else + er = exp(zr) + if iszero(zi) + Complex(er, zi) + else + s, c = sincos(zi) + Complex(er * c, er * s) + end + end +end + +function expm1(z::Complex{T}) where T<:Real + Tf = float(T) + zr,zi = reim(z) + if isnan(zr) + Complex(zr, zi==0 ? zi : zr) + elseif !isfinite(zi) + if zr == Inf + Complex(-zr, oftype(zr,NaN)) + elseif zr == -Inf + Complex(-one(zr), copysign(zero(zi), zi)) + else + Complex(oftype(zr,NaN), oftype(zi,NaN)) + end + else + erm1 = expm1(zr) + if zi == 0 + Complex(erm1, zi) + else + er = erm1+one(erm1) + if isfinite(er) + wr = erm1 - 2 * er * (sin(convert(Tf, 0.5) * zi))^2 + return Complex(wr, er * sin(zi)) + else + s, c = sincos(zi) + return Complex(er * c, er * s) + end + end + end +end + +function log1p(z::Complex{T}) where T + zr,zi = reim(z) + if isfinite(zr) + isinf(zi) && return log(z) + # This is based on a well-known trick for log1p of real z, + # allegedly due to Kahan, only modified to handle real(u) <= 0 + # differently to avoid inaccuracy near z==-2 and for correct branch cut + u = one(float(T)) + z + u == 1 ? convert(typeof(u), z) : real(u) <= 0 ? log(u) : log(u)*z/(u-1) + elseif isnan(zr) + Complex(zr, zr) + elseif isfinite(zi) + Complex(T(Inf), copysign(zr > 0 ? zero(T) : convert(T, pi), zi)) + else + Complex(T(Inf), T(NaN)) + end +end + +function exp2(z::Complex{T}) where T<:AbstractFloat + er = exp2(real(z)) + theta = imag(z) * log(convert(T, 2)) + s, c = sincos(theta) + Complex(er * c, er * s) +end +exp2(z::Complex) = exp2(float(z)) + +function exp10(z::Complex{T}) where T<:AbstractFloat + er = exp10(real(z)) + theta = imag(z) * log(convert(T, 10)) + s, c = sincos(theta) + Complex(er * c, er * s) +end +exp10(z::Complex) = exp10(float(z)) + +# _cpow helper function to avoid method ambiguity with ^(::Complex,::Real) +function _cpow(z::Union{T,Complex{T}}, p::Union{T,Complex{T}}) where {T<:AbstractFloat} + if isreal(p) + pᵣ = real(p) + if isinteger(pᵣ) && abs(pᵣ) < typemax(Int32) + # |p| < typemax(Int32) serves two purposes: it prevents overflow + # when converting p to Int, and it also turns out to be roughly + # the crossover point for exp(p*log(z)) or similar to be faster. + if iszero(pᵣ) # fix signs of imaginary part for z^0 + zer = flipsign(copysign(zero(T),pᵣ), imag(z)) + return Complex(one(T), zer) + end + ip = convert(Int, pᵣ) + if isreal(z) + zᵣ = real(z) + if ip < 0 + iszero(z) && return Complex(T(NaN),T(NaN)) + re = power_by_squaring(inv(zᵣ), -ip) + im = -imag(z) + else + re = power_by_squaring(zᵣ, ip) + im = imag(z) + end + # slightly tricky to get the correct sign of zero imag. part + return Complex(re, ifelse(iseven(ip) & signbit(zᵣ), -im, im)) + else + return ip < 0 ? power_by_squaring(inv(z), -ip) : power_by_squaring(z, ip) + end + elseif isreal(z) + # (note: if both z and p are complex with ±0.0 imaginary parts, + # the sign of the ±0.0 imaginary part of the result is ambiguous) + if iszero(real(z)) + return pᵣ > 0 ? complex(z) : Complex(T(NaN),T(NaN)) # 0 or NaN+NaN*im + elseif real(z) > 0 + return Complex(real(z)^pᵣ, z isa Real ? ifelse(real(z) < 1, -imag(p), imag(p)) : flipsign(imag(z), pᵣ)) + else + zᵣ = real(z) + rᵖ = (-zᵣ)^pᵣ + if isfinite(pᵣ) + # figuring out the sign of 0.0 when p is a complex number + # with zero imaginary part and integer/2 real part could be + # improved here, but it's not clear if it's worth it… + return rᵖ * complex(cospi(pᵣ), flipsign(sinpi(pᵣ),imag(z))) + else + iszero(rᵖ) && return zero(Complex{T}) # no way to get correct signs of 0.0 + return Complex(T(NaN),T(NaN)) # non-finite phase angle or NaN input + end + end + else + rᵖ = abs(z)^pᵣ + ϕ = pᵣ*angle(z) + end + elseif isreal(z) + iszero(z) && return real(p) > 0 ? complex(z) : Complex(T(NaN),T(NaN)) # 0 or NaN+NaN*im + zᵣ = real(z) + pᵣ, pᵢ = reim(p) + if zᵣ > 0 + rᵖ = zᵣ^pᵣ + ϕ = pᵢ*log(zᵣ) + else + r = -zᵣ + θ = copysign(T(π),imag(z)) + rᵖ = r^pᵣ * exp(-pᵢ*θ) + ϕ = pᵣ*θ + pᵢ*log(r) + end + else + pᵣ, pᵢ = reim(p) + r = abs(z) + θ = angle(z) + rᵖ = r^pᵣ * exp(-pᵢ*θ) + ϕ = pᵣ*θ + pᵢ*log(r) + end + + if isfinite(ϕ) + return rᵖ * cis(ϕ) + else + iszero(rᵖ) && return zero(Complex{T}) # no way to get correct signs of 0.0 + return Complex(T(NaN),T(NaN)) # non-finite phase angle or NaN input + end +end +_cpow(z, p) = _cpow(float(z), float(p)) +^(z::Complex{T}, p::Complex{T}) where T<:Real = _cpow(z, p) +^(z::Complex{T}, p::T) where T<:Real = _cpow(z, p) +^(z::T, p::Complex{T}) where T<:Real = _cpow(z, p) + +^(z::Complex, n::Bool) = n ? z : one(z) +^(z::Complex, n::Integer) = z^Complex(n) + +^(z::Complex{<:AbstractFloat}, n::Bool) = n ? z : one(z) # to resolve ambiguity +^(z::Complex{<:Integer}, n::Bool) = n ? z : one(z) # to resolve ambiguity + +^(z::Complex{<:AbstractFloat}, n::Integer) = + n>=0 ? power_by_squaring(z,n) : power_by_squaring(inv(z),-n) +^(z::Complex{<:Integer}, n::Integer) = power_by_squaring(z,n) # DomainError for n<0 + +function ^(z::Complex{T}, p::S) where {T<:Real,S<:Real} + P = promote_type(T,S) + return Complex{P}(z) ^ P(p) +end +function ^(z::T, p::Complex{S}) where {T<:Real,S<:Real} + P = promote_type(T,S) + return P(z) ^ Complex{P}(p) +end + +function sin(z::Complex{T}) where T + F = float(T) + zr, zi = reim(z) + if zr == 0 + Complex(F(zr), sinh(zi)) + elseif !isfinite(zr) + if zi == 0 || isinf(zi) + Complex(F(NaN), F(zi)) + else + Complex(F(NaN), F(NaN)) + end + else + s, c = sincos(zr) + Complex(s * cosh(zi), c * sinh(zi)) + end +end + + +function cos(z::Complex{T}) where T + F = float(T) + zr, zi = reim(z) + if zr == 0 + Complex(cosh(zi), isnan(zi) ? F(zr) : -flipsign(F(zr),zi)) + elseif !isfinite(zr) + if zi == 0 + Complex(F(NaN), isnan(zr) ? zero(F) : -flipsign(F(zi),zr)) + elseif isinf(zi) + Complex(F(Inf), F(NaN)) + else + Complex(F(NaN), F(NaN)) + end + else + s, c = sincos(zr) + Complex(c * cosh(zi), -s * sinh(zi)) + end +end + + +function tan(z::Complex) + zr, zi = reim(z) + w = tanh(Complex(-zi, zr)) + Complex(imag(w), -real(w)) +end + +function asin(z::Complex) + zr, zi = reim(z) + if isinf(zr) && isinf(zi) + return Complex(copysign(oftype(zr,pi)/4, zr),zi) + elseif isnan(zi) && isinf(zr) + return Complex(zi, oftype(zr, Inf)) + end + ξ = zr == 0 ? zr : + !isfinite(zr) ? oftype(zr,pi)/2 * sign(zr) : + atan(zr, real(sqrt(1-z)*sqrt(1+z))) + η = asinh(copysign(imag(sqrt(conj(1-z))*sqrt(1+z)), imag(z))) + Complex(ξ,η) +end + +function acos(z::Complex{<:AbstractFloat}) + zr, zi = reim(z) + if isnan(zr) + if isinf(zi) return Complex(zr, -zi) + else return Complex(zr, zr) end + elseif isnan(zi) + if isinf(zr) return Complex(zi, abs(zr)) + elseif zr==0 return Complex(oftype(zr,pi)/2, zi) + else return Complex(zi, zi) end + elseif zr==zi==0 + return Complex(oftype(zr,pi)/2, -zi) + elseif zr==Inf && zi===0.0 + return Complex(zi, -zr) + elseif zr==-Inf && zi===-0.0 + return Complex(oftype(zi,pi), -zr) + end + ξ = 2*atan(real(sqrt(1-z)), real(sqrt(1+z))) + η = asinh(imag(sqrt(conj(1+z))*sqrt(1-z))) + if isinf(zr) && isinf(zi) ξ -= oftype(η,pi)/4 * sign(zr) end + Complex(ξ,η) +end +acos(z::Complex) = acos(float(z)) + +function atan(z::Complex) + w = atanh(Complex(-imag(z),real(z))) + Complex(imag(w),-real(w)) +end + +function sinh(z::Complex) + zr, zi = reim(z) + w = sin(Complex(zi, zr)) + Complex(imag(w),real(w)) +end + +function cosh(z::Complex) + zr, zi = reim(z) + cos(Complex(zi,-zr)) +end + +function tanh(z::Complex{T}) where T<:AbstractFloat + Ω = prevfloat(typemax(T)) + ξ, η = reim(z) + if isnan(ξ) && η==0 return Complex(ξ, η) end + if 4*abs(ξ) > asinh(Ω) #Overflow? + Complex(copysign(one(T),ξ), + copysign(zero(T),η*(isfinite(η) ? sin(2*abs(η)) : one(η)))) + else + t = tan(η) + β = 1+t*t #sec(η)^2 + s = sinh(ξ) + ρ = sqrt(1 + s*s) #cosh(ξ) + if isinf(t) + Complex(ρ/s,1/t) + else + Complex(β*ρ*s,t)/(1+β*s*s) + end + end +end +tanh(z::Complex) = tanh(float(z)) + +function asinh(z::Complex) + w = asin(Complex(-imag(z),real(z))) + Complex(imag(w),-real(w)) +end + +function acosh(z::Complex) + zr, zi = reim(z) + if isnan(zr) || isnan(zi) + if isinf(zr) || isinf(zi) + return Complex(oftype(zr, Inf), oftype(zi, NaN)) + else + return Complex(oftype(zr, NaN), oftype(zi, NaN)) + end + elseif zr==-Inf && zi===-0.0 #Edge case is wrong - WHY? + return Complex(oftype(zr,Inf), oftype(zi, -pi)) + end + ξ = asinh(real(sqrt(conj(z-1))*sqrt(z+1))) + η = 2*atan(imag(sqrt(z-1)),real(sqrt(z+1))) + if isinf(zr) && isinf(zi) + η -= oftype(η,pi)/4 * sign(zi) * sign(zr) + end + Complex(ξ, η) +end + +function atanh(z::Complex{T}) where T<:AbstractFloat + Ω = prevfloat(typemax(T)) + θ = sqrt(Ω)/4 + ρ = 1/θ + x, y = reim(z) + ax = abs(x) + ay = abs(y) + if ax > θ || ay > θ #Prevent overflow + if isnan(y) + if isinf(x) + return Complex(copysign(zero(x),x), y) + else + return Complex(real(1/z), y) + end + end + if isinf(y) + return Complex(copysign(zero(x),x), copysign(oftype(y,pi)/2, y)) + end + return Complex(real(1/z), copysign(oftype(y,pi)/2, y)) + end + β = copysign(one(T), x) + z *= β + x, y = reim(z) + if x == 1 + if y == 0 + ξ = oftype(x, Inf) + η = y + else + ym = ay+ρ + ξ = log(sqrt(sqrt(4+y*y))/sqrt(ym)) + η = copysign(oftype(y,pi)/2 + atan(ym/2), y)/2 + end + else #Normal case + ysq = (ay+ρ)^2 + if x == 0 + ξ = x + else + ξ = log1p(4x/((1-x)^2 + ysq))/4 + end + η = angle(Complex((1-x)*(1+x)-ysq, 2y))/2 + end + β * Complex(ξ, η) +end +atanh(z::Complex) = atanh(float(z)) + +#Rounding complex numbers +#Requires two different RoundingModes for the real and imaginary components +""" + round(z::Complex[, RoundingModeReal, [RoundingModeImaginary]]) + round(z::Complex[, RoundingModeReal, [RoundingModeImaginary]]; digits=, base=10) + round(z::Complex[, RoundingModeReal, [RoundingModeImaginary]]; sigdigits=, base=10) + +Return the nearest integral value of the same type as the complex-valued `z` to `z`, +breaking ties using the specified [`RoundingMode`](@ref)s. The first +[`RoundingMode`](@ref) is used for rounding the real components while the +second is used for rounding the imaginary components. + +# Example +```jldoctest +julia> round(3.14 + 4.5im) +3.0 + 4.0im +``` +""" +function round(z::Complex, rr::RoundingMode=RoundNearest, ri::RoundingMode=rr; kwargs...) + Complex(round(real(z), rr; kwargs...), + round(imag(z), ri; kwargs...)) +end + + +float(z::Complex{<:AbstractFloat}) = z +float(z::Complex) = Complex(float(real(z)), float(imag(z))) + +big(::Type{Complex{T}}) where {T<:Real} = Complex{big(T)} +big(z::Complex{T}) where {T<:Real} = Complex{big(T)}(z) + +## Array operations on complex numbers ## + +complex(A::AbstractArray{<:Complex}) = A + +function complex(A::AbstractArray{T}) where T + if !isconcretetype(T) + error("`complex` not defined on abstractly-typed arrays; please convert to a more specific type") + end + convert(AbstractArray{typeof(complex(zero(T)))}, A) +end diff --git a/base/condition.jl b/base/condition.jl new file mode 100644 index 0000000..17d6b12 --- /dev/null +++ b/base/condition.jl @@ -0,0 +1,163 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## thread/task locking abstraction + +@noinline function concurrency_violation() + # can be useful for debugging + #try; error(); catch; ccall(:jlbacktrace, Cvoid, ()); end + error("concurrency violation detected") +end + +""" + AbstractLock + +Abstract supertype describing types that +implement the synchronization primitives: +[`lock`](@ref), [`trylock`](@ref), [`unlock`](@ref), and [`islocked`](@ref). +""" +abstract type AbstractLock end +function lock end +function unlock end +function trylock end +function islocked end +unlockall(l::AbstractLock) = unlock(l) # internal function for implementing `wait` +relockall(l::AbstractLock, token::Nothing) = lock(l) # internal function for implementing `wait` +assert_havelock(l::AbstractLock, tid::Integer) = + (islocked(l) && tid == Threads.threadid()) ? nothing : concurrency_violation() +assert_havelock(l::AbstractLock, tid::Task) = + (islocked(l) && tid === current_task()) ? nothing : concurrency_violation() +assert_havelock(l::AbstractLock, tid::Nothing) = concurrency_violation() + +""" + AlwaysLockedST + +This struct does not implement a real lock, but instead +pretends to be always locked on the original thread it was allocated on, +and simply ignores all other interactions. +It also does not synchronize tasks; for that use a real lock such as [`RecursiveLock`](@ref). +This can be used in the place of a real lock to, instead, simply and cheaply assert +that the operation is only occurring on a single cooperatively-scheduled thread. +It is thus functionally equivalent to allocating a real, recursive, task-unaware lock +immediately calling `lock` on it, and then never calling a matching `unlock`, +except that calling `lock` from another thread will throw a concurrency violation exception. +""" +struct AlwaysLockedST <: AbstractLock + ownertid::Int16 + AlwaysLockedST() = new(Threads.threadid()) +end +assert_havelock(l::AlwaysLockedST) = assert_havelock(l, l.ownertid) +lock(l::AlwaysLockedST) = assert_havelock(l) +unlock(l::AlwaysLockedST) = assert_havelock(l) +trylock(l::AlwaysLockedST) = l.ownertid == Threads.threadid() +islocked(::AlwaysLockedST) = true + + +## condition variables + +""" + GenericCondition + +Abstract implementation of a condition object +for synchonizing tasks objects with a given lock. +""" +struct GenericCondition{L<:AbstractLock} + waitq::InvasiveLinkedList{Task} + lock::L + + GenericCondition{L}() where {L<:AbstractLock} = new{L}(InvasiveLinkedList{Task}(), L()) + GenericCondition{L}(l::L) where {L<:AbstractLock} = new{L}(InvasiveLinkedList{Task}(), l) + GenericCondition(l::AbstractLock) = new{typeof(l)}(InvasiveLinkedList{Task}(), l) +end + +assert_havelock(c::GenericCondition) = assert_havelock(c.lock) +lock(c::GenericCondition) = lock(c.lock) +unlock(c::GenericCondition) = unlock(c.lock) +trylock(c::GenericCondition) = trylock(c.lock) +islocked(c::GenericCondition) = islocked(c.lock) + +lock(f, c::GenericCondition) = lock(f, c.lock) +unlock(f, c::GenericCondition) = unlock(f, c.lock) + +""" + wait([x]) + +Block the current task until some event occurs, depending on the type of the argument: + +* [`Channel`](@ref): Wait for a value to be appended to the channel. +* [`Condition`](@ref): Wait for [`notify`](@ref) on a condition. +* `Process`: Wait for a process or process chain to exit. The `exitcode` field of a process + can be used to determine success or failure. +* [`Task`](@ref): Wait for a `Task` to finish. If the task fails with an exception, a + `TaskFailedException` (which wraps the failed task) is thrown. +* [`RawFD`](@ref): Wait for changes on a file descriptor (see the `FileWatching` package). + +If no argument is passed, the task blocks for an undefined period. A task can only be +restarted by an explicit call to [`schedule`](@ref) or [`yieldto`](@ref). + +Often `wait` is called within a `while` loop to ensure a waited-for condition is met before +proceeding. +""" +function wait(c::GenericCondition) + ct = current_task() + assert_havelock(c) + push!(c.waitq, ct) + token = unlockall(c.lock) + try + return wait() + catch + ct.queue === nothing || list_deletefirst!(ct.queue, ct) + rethrow() + finally + relockall(c.lock, token) + end +end + +""" + notify(condition, val=nothing; all=true, error=false) + +Wake up tasks waiting for a condition, passing them `val`. If `all` is `true` (the default), +all waiting tasks are woken, otherwise only one is. If `error` is `true`, the passed value +is raised as an exception in the woken tasks. + +Return the count of tasks woken up. Return 0 if no tasks are waiting on `condition`. +""" +notify(c::GenericCondition, @nospecialize(arg = nothing); all=true, error=false) = notify(c, arg, all, error) +function notify(c::GenericCondition, @nospecialize(arg), all, error) + assert_havelock(c) + cnt = 0 + while !isempty(c.waitq) + t = popfirst!(c.waitq) + schedule(t, arg, error=error) + cnt += 1 + all || break + end + return cnt +end + +notify_error(c::GenericCondition, err) = notify(c, err, true, true) + +n_waiters(c::GenericCondition) = length(c.waitq) + +""" + isempty(condition) + +Return `true` if no tasks are waiting on the condition, `false` otherwise. +""" +isempty(c::GenericCondition) = isempty(c.waitq) + + +# default (Julia v1.0) is currently single-threaded +# (although it uses MT-safe versions, when possible) +""" + Condition() + +Create an edge-triggered event source that tasks can wait for. Tasks that call [`wait`](@ref) on a +`Condition` are suspended and queued. Tasks are woken up when [`notify`](@ref) is later called on +the `Condition`. Edge triggering means that only tasks waiting at the time [`notify`](@ref) is +called can be woken up. For level-triggered notifications, you must keep extra state to keep +track of whether a notification has happened. The [`Channel`](@ref) and [`Threads.Event`](@ref) types do +this, and can be used for level-triggered events. + +This object is NOT thread-safe. See [`Threads.Condition`](@ref) for a thread-safe version. +""" +const Condition = GenericCondition{AlwaysLockedST} diff --git a/base/coreio.jl b/base/coreio.jl new file mode 100644 index 0000000..2796c53 --- /dev/null +++ b/base/coreio.jl @@ -0,0 +1,32 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +print(xs...) = print(stdout::IO, xs...) +println(xs...) = println(stdout::IO, xs...) +println(io::IO) = print(io, '\n') + +function show end +function repr end + +struct DevNull <: IO end +const devnull = DevNull() +isreadable(::DevNull) = false +iswritable(::DevNull) = true +isopen(::DevNull) = true +read(::DevNull, ::Type{UInt8}) = throw(EOFError()) +write(::DevNull, ::UInt8) = 1 +unsafe_write(::DevNull, ::Ptr{UInt8}, n::UInt)::Int = n +close(::DevNull) = nothing +flush(::DevNull) = nothing +wait_readnb(::DevNull) = wait() +wait_close(::DevNull) = wait() +eof(::DevNull) = true + +let CoreIO = Union{Core.CoreSTDOUT, Core.CoreSTDERR} + global write, unsafe_write + write(io::CoreIO, x::UInt8) = Core.write(io, x) + unsafe_write(io::CoreIO, x::Ptr{UInt8}, nb::UInt) = Core.unsafe_write(io, x, nb) +end + +stdin = devnull +stdout = Core.stdout +stderr = Core.stderr diff --git a/base/ctypes.jl b/base/ctypes.jl new file mode 100644 index 0000000..26640ed --- /dev/null +++ b/base/ctypes.jl @@ -0,0 +1,115 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# essential type definitions for interacting with C code +# (platform- or OS-dependent types are defined in c.jl) + +""" + Cuchar + +Equivalent to the native `unsigned char` c-type ([`UInt8`](@ref)). +""" +const Cuchar = UInt8 + + +""" + Cshort + +Equivalent to the native `signed short` c-type ([`Int16`](@ref)). +""" +const Cshort = Int16 + + +""" + Cushort + +Equivalent to the native `unsigned short` c-type ([`UInt16`](@ref)). +""" +const Cushort = UInt16 + + +""" + Cint + +Equivalent to the native `signed int` c-type ([`Int32`](@ref)). +""" +const Cint = Int32 + + +""" + Cuint + +Equivalent to the native `unsigned int` c-type ([`UInt32`](@ref)). +""" +const Cuint = UInt32 + + +""" + Cptrdiff_t + +Equivalent to the native `ptrdiff_t` c-type (`Int`). +""" +const Cptrdiff_t = Int + + +""" + Csize_t + +Equivalent to the native `size_t` c-type (`UInt`). +""" +const Csize_t = UInt + + +""" + Cssize_t + +Equivalent to the native `ssize_t` c-type. +""" +const Cssize_t = Int + + +""" + Cintmax_t + +Equivalent to the native `intmax_t` c-type ([`Int64`](@ref)). +""" +const Cintmax_t = Int64 + + +""" + Cuintmax_t + +Equivalent to the native `uintmax_t` c-type ([`UInt64`](@ref)). +""" +const Cuintmax_t = UInt64 + + +""" + Clonglong + +Equivalent to the native `signed long long` c-type ([`Int64`](@ref)). +""" +const Clonglong = Int64 + + +""" + Culonglong + +Equivalent to the native `unsigned long long` c-type ([`UInt64`](@ref)). +""" +const Culonglong = UInt64 + + +""" + Cfloat + +Equivalent to the native `float` c-type ([`Float32`](@ref)). +""" +const Cfloat = Float32 + + +""" + Cdouble + +Equivalent to the native `double` c-type ([`Float64`](@ref)). +""" +const Cdouble = Float64 diff --git a/base/deepcopy.jl b/base/deepcopy.jl new file mode 100644 index 0000000..5127b65 --- /dev/null +++ b/base/deepcopy.jl @@ -0,0 +1,116 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# deep copying + +# Note: deepcopy_internal(::Any, ::IdDict) is +# only exposed for specialization by libraries + +""" + deepcopy(x) + +Create a deep copy of `x`: everything is copied recursively, resulting in a fully +independent object. For example, deep-copying an array produces a new array whose elements +are deep copies of the original elements. Calling `deepcopy` on an object should generally +have the same effect as serializing and then deserializing it. + +As a special case, functions can only be actually deep-copied if they are anonymous, +otherwise they are just copied. The difference is only relevant in the case of closures, +i.e. functions which may contain hidden internal references. + +While it isn't normally necessary, user-defined types can override the default `deepcopy` +behavior by defining a specialized version of the function +`deepcopy_internal(x::T, dict::IdDict)` (which shouldn't otherwise be used), +where `T` is the type to be specialized for, and `dict` keeps track of objects copied +so far within the recursion. Within the definition, `deepcopy_internal` should be used +in place of `deepcopy`, and the `dict` variable should be +updated as appropriate before returning. +""" +function deepcopy(x) + isbitstype(typeof(x)) && return x + return deepcopy_internal(x, IdDict())::typeof(x) +end + +deepcopy_internal(x::Union{Symbol,Core.MethodInstance,Method,GlobalRef,DataType,Union,UnionAll,Task,Regex}, + stackdict::IdDict) = x +deepcopy_internal(x::Tuple, stackdict::IdDict) = + ntuple(i->deepcopy_internal(x[i], stackdict), length(x)) +deepcopy_internal(x::Module, stackdict::IdDict) = error("deepcopy of Modules not supported") + +function deepcopy_internal(x::SimpleVector, stackdict::IdDict) + if haskey(stackdict, x) + return stackdict[x] + end + y = Core.svec(Any[deepcopy_internal(x[i], stackdict) for i = 1:length(x)]...) + stackdict[x] = y + return y +end + +function deepcopy_internal(x::String, stackdict::IdDict) + if haskey(stackdict, x) + return stackdict[x] + end + y = GC.@preserve x unsafe_string(pointer(x), sizeof(x)) + stackdict[x] = y + return y +end + +function deepcopy_internal(@nospecialize(x), stackdict::IdDict) + T = typeof(x)::DataType + isbitstype(T) && return x + if haskey(stackdict, x) + return stackdict[x] + end + y = ccall(:jl_new_struct_uninit, Any, (Any,), T) + if T.mutable + stackdict[x] = y + end + for i in 1:nfields(x) + if isdefined(x,i) + ccall(:jl_set_nth_field, Cvoid, (Any, Csize_t, Any), y, i-1, + deepcopy_internal(getfield(x,i), stackdict)) + end + end + return y::T +end + +function deepcopy_internal(x::Array, stackdict::IdDict) + if haskey(stackdict, x) + return stackdict[x] + end + _deepcopy_array_t(x, eltype(x), stackdict) +end + +function _deepcopy_array_t(@nospecialize(x), T, stackdict::IdDict) + if isbitstype(T) + return (stackdict[x]=copy(x)) + end + dest = similar(x) + stackdict[x] = dest + for i = 1:(length(x)::Int) + if ccall(:jl_array_isassigned, Cint, (Any, Csize_t), x, i-1) != 0 + xi = ccall(:jl_arrayref, Any, (Any, Csize_t), x, i-1) + if !isbits(xi) + xi = deepcopy_internal(xi, stackdict) + end + ccall(:jl_arrayset, Cvoid, (Any, Any, Csize_t), dest, xi, i-1) + end + end + return dest +end + +function deepcopy_internal(x::Union{Dict,IdDict}, stackdict::IdDict) + if haskey(stackdict, x) + return stackdict[x]::typeof(x) + end + + if isbitstype(eltype(x)) + return (stackdict[x] = copy(x)) + end + + dest = empty(x) + stackdict[x] = dest + for (k, v) in x + dest[deepcopy_internal(k, stackdict)] = deepcopy_internal(v, stackdict) + end + dest +end diff --git a/base/deprecated.jl b/base/deprecated.jl new file mode 100644 index 0000000..69efdc7 --- /dev/null +++ b/base/deprecated.jl @@ -0,0 +1,199 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Deprecated functions and objects +# +# Please add new deprecations at the bottom of the file. +# A function deprecated in a release will be removed in the next one. +# Please also add a reference to the pull request which introduced the +# deprecation. For simple cases where a direct replacement is available, +# use @deprecate. @deprecate takes care of calling the replacement +# and of exporting the function. +# +# For more complex cases, move the body of the deprecated method in this file, +# and call depwarn() directly from inside it. The symbol depwarn() expects is +# the name of the function, which is used to ensure that the deprecation warning +# is only printed the first time for each call place. + +""" + @deprecate old new [ex=true] + +The first argument `old` is the signature of the deprecated method, the second one +`new` is the call which replaces it. `@deprecate` exports `old` unless the optional +third argument is `false`. + +# Examples +```jldoctest +julia> @deprecate old(x) new(x) +old (generic function with 1 method) + +julia> @deprecate old(x) new(x) false +old (generic function with 1 method) +``` +""" +macro deprecate(old, new, ex=true) + meta = Expr(:meta, :noinline) + if isa(old, Symbol) + oldname = Expr(:quote, old) + newname = Expr(:quote, new) + Expr(:toplevel, + ex ? Expr(:export, esc(old)) : nothing, + :(function $(esc(old))(args...) + $meta + depwarn($"`$old` is deprecated, use `$new` instead.", Core.Typeof($(esc(old))).name.mt.name) + $(esc(new))(args...) + end)) + elseif isa(old, Expr) && (old.head === :call || old.head === :where) + remove_linenums!(new) + oldcall = sprint(show_unquoted, old) + newcall = sprint(show_unquoted, new) + # if old.head is a :where, step down one level to the :call to avoid code duplication below + callexpr = old.head === :call ? old : old.args[1] + if callexpr.head === :call + if isa(callexpr.args[1], Symbol) + oldsym = callexpr.args[1]::Symbol + elseif isa(callexpr.args[1], Expr) && callexpr.args[1].head === :curly + oldsym = callexpr.args[1].args[1]::Symbol + else + error("invalid usage of @deprecate") + end + else + error("invalid usage of @deprecate") + end + Expr(:toplevel, + ex ? Expr(:export, esc(oldsym)) : nothing, + :($(esc(old)) = begin + $meta + depwarn($"`$oldcall` is deprecated, use `$newcall` instead.", Core.Typeof($(esc(oldsym))).name.mt.name) + $(esc(new)) + end)) + else + error("invalid usage of @deprecate") + end +end + +function depwarn(msg, funcsym) + opts = JLOptions() + if opts.depwarn == 2 + throw(ErrorException(msg)) + end + deplevel = opts.depwarn == 1 ? CoreLogging.Warn : CoreLogging.BelowMinLevel + @logmsg( + deplevel, + msg, + _module=begin + bt = backtrace() + frame, caller = firstcaller(bt, funcsym) + # TODO: Is it reasonable to attribute callers without linfo to Core? + caller.linfo isa Core.MethodInstance ? caller.linfo.def.module : Core + end, + _file=String(caller.file), + _line=caller.line, + _id=(frame,funcsym), + _group=:depwarn, + caller=caller, + maxlog=funcsym === nothing ? nothing : 1 + ) + nothing +end + +firstcaller(bt::Vector, ::Nothing) = Ptr{Cvoid}(0), StackTraces.UNKNOWN +firstcaller(bt::Vector, funcsym::Symbol) = firstcaller(bt, (funcsym,)) +function firstcaller(bt::Vector, funcsyms) + # Identify the calling line + found = false + lkup = StackTraces.UNKNOWN + found_frame = Ptr{Cvoid}(0) + for frame in bt + lkups = StackTraces.lookup(frame) + for outer lkup in lkups + if lkup == StackTraces.UNKNOWN || lkup.from_c + continue + end + if found + found_frame = frame + @goto found + end + found = lkup.func in funcsyms + # look for constructor type name + if !found && lkup.linfo isa Core.MethodInstance + li = lkup.linfo + ft = ccall(:jl_first_argument_datatype, Any, (Any,), li.def.sig) + if isa(ft,DataType) && ft.name === Type.body.name + ft = unwrap_unionall(ft.parameters[1]) + found = (isa(ft,DataType) && ft.name.name in funcsyms) + end + end + end + end + return found_frame, StackTraces.UNKNOWN + @label found + return found_frame, lkup +end + +deprecate(m::Module, s::Symbol, flag=1) = ccall(:jl_deprecate_binding, Cvoid, (Any, Any, Cint), m, s, flag) + +macro deprecate_binding(old, new, export_old=true, dep_message=:nothing, constant=true) + dep_message === :nothing && (dep_message = ", use $new instead.") + return Expr(:toplevel, + export_old ? Expr(:export, esc(old)) : nothing, + Expr(:const, Expr(:(=), esc(Symbol(string("_dep_message_",old))), esc(dep_message))), + constant ? Expr(:const, Expr(:(=), esc(old), esc(new))) : Expr(:(=), esc(old), esc(new)), + Expr(:call, :deprecate, __module__, Expr(:quote, old))) +end + +macro deprecate_stdlib(old, mod, export_old=true, newname=old) + rename = old === newname ? "" : " as `$newname`" + dep_message = """: it has been moved to the standard library package `$mod`$rename. + Add `using $mod` to your imports.""" + new = GlobalRef(Base.root_module(Base, mod), newname) + return Expr(:toplevel, + export_old ? Expr(:export, esc(old)) : nothing, + Expr(:const, Expr(:(=), esc(Symbol(string("_dep_message_",old))), esc(dep_message))), + Expr(:const, Expr(:(=), esc(old), esc(new))), + Expr(:call, :deprecate, __module__, Expr(:quote, old))) +end + +macro deprecate_moved(old, new, export_old=true) + eold = esc(old) + emsg = string(old, " has been moved to the package ", new, ".jl.\n", + "Run `Pkg.add(\"", new, "\")` to install it, restart Julia,\n", + "and then run `using ", new, "` to load it.") + return Expr(:toplevel, + :($eold(args...; kwargs...) = error($emsg)), + export_old ? Expr(:export, eold) : nothing, + Expr(:call, :deprecate, __module__, Expr(:quote, old), 2)) +end + +# BEGIN 0.7 deprecations + +function promote_eltype_op end + +# END 0.7 deprecations + +# BEGIN 1.0 deprecations + +# @deprecate one(i::CartesianIndex) oneunit(i) +# @deprecate one(::Type{I}) where I<:CartesianIndex oneunit(I) + +@deprecate reindex(V, idxs, subidxs) reindex(idxs, subidxs) false +@deprecate substrides(parent::AbstractArray, strds::Tuple, I::Tuple) substrides(strds, I) false + +# TODO: deprecate these +one(::CartesianIndex{N}) where {N} = one(CartesianIndex{N}) +one(::Type{CartesianIndex{N}}) where {N} = CartesianIndex(ntuple(x -> 1, Val(N))) + +MPFR.BigFloat(x, prec::Int) = BigFloat(x; precision=prec) +MPFR.BigFloat(x, prec::Int, rounding::RoundingMode) = BigFloat(x, rounding; precision=prec) +MPFR.BigFloat(x::Real, prec::Int) = BigFloat(x; precision=prec) +MPFR.BigFloat(x::Real, prec::Int, rounding::RoundingMode) = BigFloat(x, rounding; precision=prec) + +# END 1.0 deprecations + +# BEGIN 1.3 deprecations + +@eval Threads begin + Base.@deprecate_binding RecursiveSpinLock ReentrantLock + Base.@deprecate_binding Mutex ReentrantLock +end + +# END 1.3 deprecations diff --git a/base/dict.jl b/base/dict.jl new file mode 100644 index 0000000..4cff4ee --- /dev/null +++ b/base/dict.jl @@ -0,0 +1,794 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +function _truncate_at_width_or_chars(str, width, chars="", truncmark="…") + truncwidth = textwidth(truncmark) + (width <= 0 || width < truncwidth) && return "" + + wid = truncidx = lastidx = 0 + for (idx, c) in pairs(str) + lastidx = idx + wid += textwidth(c) + wid >= width - truncwidth && truncidx == 0 && (truncidx = lastidx) + (wid >= width || c in chars) && break + end + + lastidx != 0 && str[lastidx] in chars && (lastidx = prevind(str, lastidx)) + truncidx == 0 && (truncidx = lastidx) + if lastidx < lastindex(str) + return String(SubString(str, 1, truncidx) * truncmark) + else + return String(str) + end +end + +function show(io::IO, t::AbstractDict{K,V}) where V where K + recur_io = IOContext(io, :SHOWN_SET => t, + :typeinfo => eltype(t)) + + limit::Bool = get(io, :limit, false) + # show in a Julia-syntax-like form: Dict(k=>v, ...) + print(io, typeinfo_prefix(io, t)) + print(io, '(') + if !isempty(t) && !show_circular(io, t) + first = true + n = 0 + for pair in t + first || print(io, ',') + first = false + show(recur_io, pair) + n+=1 + limit && n >= 10 && (print(io, "…"); break) + end + end + print(io, ')') +end + +# Dict + +# These can be changed, to trade off better performance for space +const global maxallowedprobe = 16 +const global maxprobeshift = 6 + +""" + Dict([itr]) + +`Dict{K,V}()` constructs a hash table with keys of type `K` and values of type `V`. +Keys are compared with [`isequal`](@ref) and hashed with [`hash`](@ref). + +Given a single iterable argument, constructs a [`Dict`](@ref) whose key-value pairs +are taken from 2-tuples `(key,value)` generated by the argument. + +# Examples +```jldoctest +julia> Dict([("A", 1), ("B", 2)]) +Dict{String,Int64} with 2 entries: + "B" => 2 + "A" => 1 +``` + +Alternatively, a sequence of pair arguments may be passed. + +```jldoctest +julia> Dict("A"=>1, "B"=>2) +Dict{String,Int64} with 2 entries: + "B" => 2 + "A" => 1 +``` +""" +mutable struct Dict{K,V} <: AbstractDict{K,V} + slots::Array{UInt8,1} + keys::Array{K,1} + vals::Array{V,1} + ndel::Int + count::Int + age::UInt + idxfloor::Int # an index <= the indices of all used slots + maxprobe::Int + + function Dict{K,V}() where V where K + n = 16 + new(zeros(UInt8,n), Vector{K}(undef, n), Vector{V}(undef, n), 0, 0, 0, 1, 0) + end + function Dict{K,V}(d::Dict{K,V}) where V where K + new(copy(d.slots), copy(d.keys), copy(d.vals), d.ndel, d.count, d.age, + d.idxfloor, d.maxprobe) + end + function Dict{K, V}(slots, keys, vals, ndel, count, age, idxfloor, maxprobe) where {K, V} + new(slots, keys, vals, ndel, count, age, idxfloor, maxprobe) + end +end +function Dict{K,V}(kv) where V where K + h = Dict{K,V}() + for (k,v) in kv + h[k] = v + end + return h +end +Dict{K,V}(p::Pair) where {K,V} = setindex!(Dict{K,V}(), p.second, p.first) +function Dict{K,V}(ps::Pair...) where V where K + h = Dict{K,V}() + sizehint!(h, length(ps)) + for p in ps + h[p.first] = p.second + end + return h +end +# Note the constructors of WeakKeyDict mirror these here, keep in sync. +Dict() = Dict{Any,Any}() +Dict(kv::Tuple{}) = Dict() +copy(d::Dict) = Dict(d) + +const AnyDict = Dict{Any,Any} + +Dict(ps::Pair{K,V}...) where {K,V} = Dict{K,V}(ps) +Dict(ps::Pair...) = Dict(ps) + +function Dict(kv) + try + dict_with_eltype((K, V) -> Dict{K, V}, kv, eltype(kv)) + catch + if !isiterable(typeof(kv)) || !all(x->isa(x,Union{Tuple,Pair}),kv) + throw(ArgumentError("Dict(kv): kv needs to be an iterator of tuples or pairs")) + else + rethrow() + end + end +end + +function grow_to!(dest::AbstractDict{K, V}, itr) where V where K + y = iterate(itr) + y === nothing && return dest + ((k,v), st) = y + dest2 = empty(dest, typeof(k), typeof(v)) + dest2[k] = v + grow_to!(dest2, itr, st) +end + +# this is a special case due to (1) allowing both Pairs and Tuples as elements, +# and (2) Pair being invariant. a bit annoying. +function grow_to!(dest::AbstractDict{K,V}, itr, st) where V where K + y = iterate(itr, st) + while y !== nothing + (k,v), st = y + if isa(k,K) && isa(v,V) + dest[k] = v + else + new = empty(dest, promote_typejoin(K,typeof(k)), promote_typejoin(V,typeof(v))) + merge!(new, dest) + new[k] = v + return grow_to!(new, itr, st) + end + y = iterate(itr, st) + end + return dest +end + +empty(a::AbstractDict, ::Type{K}, ::Type{V}) where {K, V} = Dict{K, V}() + +hashindex(key, sz) = (((hash(key)%Int) & (sz-1)) + 1)::Int + +@propagate_inbounds isslotempty(h::Dict, i::Int) = h.slots[i] == 0x0 +@propagate_inbounds isslotfilled(h::Dict, i::Int) = h.slots[i] == 0x1 +@propagate_inbounds isslotmissing(h::Dict, i::Int) = h.slots[i] == 0x2 + +function rehash!(h::Dict{K,V}, newsz = length(h.keys)) where V where K + olds = h.slots + oldk = h.keys + oldv = h.vals + sz = length(olds) + newsz = _tablesz(newsz) + h.age += 1 + h.idxfloor = 1 + if h.count == 0 + resize!(h.slots, newsz) + fill!(h.slots, 0) + resize!(h.keys, newsz) + resize!(h.vals, newsz) + h.ndel = 0 + return h + end + + slots = zeros(UInt8,newsz) + keys = Vector{K}(undef, newsz) + vals = Vector{V}(undef, newsz) + age0 = h.age + count = 0 + maxprobe = 0 + + for i = 1:sz + @inbounds if olds[i] == 0x1 + k = oldk[i] + v = oldv[i] + index0 = index = hashindex(k, newsz) + while slots[index] != 0 + index = (index & (newsz-1)) + 1 + end + probe = (index - index0) & (newsz-1) + probe > maxprobe && (maxprobe = probe) + slots[index] = 0x1 + keys[index] = k + vals[index] = v + count += 1 + + if h.age != age0 + # if `h` is changed by a finalizer, retry + return rehash!(h, newsz) + end + end + end + + h.slots = slots + h.keys = keys + h.vals = vals + h.count = count + h.ndel = 0 + h.maxprobe = maxprobe + @assert h.age == age0 + + return h +end + +function sizehint!(d::Dict{T}, newsz) where T + oldsz = length(d.slots) + if newsz <= oldsz + # todo: shrink + # be careful: rehash!() assumes everything fits. it was only designed + # for growing. + return d + end + # grow at least 25% + newsz = min(max(newsz, (oldsz*5)>>2), + max_values(T)) + rehash!(d, newsz) +end + +""" + empty!(collection) -> collection + +Remove all elements from a `collection`. + +# Examples +```jldoctest +julia> A = Dict("a" => 1, "b" => 2) +Dict{String,Int64} with 2 entries: + "b" => 2 + "a" => 1 + +julia> empty!(A); + +julia> A +Dict{String,Int64} with 0 entries +``` +""" +function empty!(h::Dict{K,V}) where V where K + fill!(h.slots, 0x0) + sz = length(h.slots) + empty!(h.keys) + empty!(h.vals) + resize!(h.keys, sz) + resize!(h.vals, sz) + h.ndel = 0 + h.count = 0 + h.age += 1 + h.idxfloor = 1 + return h +end + +# get the index where a key is stored, or -1 if not present +function ht_keyindex(h::Dict{K,V}, key) where V where K + sz = length(h.keys) + iter = 0 + maxprobe = h.maxprobe + index = hashindex(key, sz) + keys = h.keys + + @inbounds while true + if isslotempty(h,index) + break + end + if !isslotmissing(h,index) && (key === keys[index] || isequal(key,keys[index])) + return index + end + + index = (index & (sz-1)) + 1 + iter += 1 + iter > maxprobe && break + end + return -1 +end + +# get the index where a key is stored, or -pos if not present +# and the key would be inserted at pos +# This version is for use by setindex! and get! +function ht_keyindex2!(h::Dict{K,V}, key) where V where K + age0 = h.age + sz = length(h.keys) + iter = 0 + maxprobe = h.maxprobe + index = hashindex(key, sz) + avail = 0 + keys = h.keys + + @inbounds while true + if isslotempty(h,index) + if avail < 0 + return avail + end + return -index + end + + if isslotmissing(h,index) + if avail == 0 + # found an available slot, but need to keep scanning + # in case "key" already exists in a later collided slot. + avail = -index + end + elseif key === keys[index] || isequal(key, keys[index]) + return index + end + + index = (index & (sz-1)) + 1 + iter += 1 + iter > maxprobe && break + end + + avail < 0 && return avail + + maxallowed = max(maxallowedprobe, sz>>maxprobeshift) + # Check if key is not present, may need to keep searching to find slot + @inbounds while iter < maxallowed + if !isslotfilled(h,index) + h.maxprobe = iter + return -index + end + index = (index & (sz-1)) + 1 + iter += 1 + end + + rehash!(h, h.count > 64000 ? sz*2 : sz*4) + + return ht_keyindex2!(h, key) +end + +@propagate_inbounds function _setindex!(h::Dict, v, key, index) + h.slots[index] = 0x1 + h.keys[index] = key + h.vals[index] = v + h.count += 1 + h.age += 1 + if index < h.idxfloor + h.idxfloor = index + end + + sz = length(h.keys) + # Rehash now if necessary + if h.ndel >= ((3*sz)>>2) || h.count*3 > sz*2 + # > 3/4 deleted or > 2/3 full + rehash!(h, h.count > 64000 ? h.count*2 : h.count*4) + end +end + +function setindex!(h::Dict{K,V}, v0, key0) where V where K + key = convert(K, key0) + if !isequal(key, key0) + throw(ArgumentError("$(limitrepr(key0)) is not a valid key for type $K")) + end + setindex!(h, v0, key) +end + +function setindex!(h::Dict{K,V}, v0, key::K) where V where K + v = convert(V, v0) + index = ht_keyindex2!(h, key) + + if index > 0 + h.age += 1 + @inbounds h.keys[index] = key + @inbounds h.vals[index] = v + else + @inbounds _setindex!(h, v, key, -index) + end + + return h +end + +""" + get!(collection, key, default) + +Return the value stored for the given key, or if no mapping for the key is present, store +`key => default`, and return `default`. + +# Examples +```jldoctest +julia> d = Dict("a"=>1, "b"=>2, "c"=>3); + +julia> get!(d, "a", 5) +1 + +julia> get!(d, "d", 4) +4 + +julia> d +Dict{String,Int64} with 4 entries: + "c" => 3 + "b" => 2 + "a" => 1 + "d" => 4 +``` +""" +get!(collection, key, default) + +get!(h::Dict{K,V}, key0, default) where {K,V} = get!(()->default, h, key0) + +""" + get!(f::Function, collection, key) + +Return the value stored for the given key, or if no mapping for the key is present, store +`key => f()`, and return `f()`. + +This is intended to be called using `do` block syntax: +```julia +get!(dict, key) do + # default value calculated here + time() +end +``` +""" +get!(f::Function, collection, key) + +function get!(default::Callable, h::Dict{K,V}, key0) where V where K + key = convert(K, key0) + if !isequal(key, key0) + throw(ArgumentError("$(limitrepr(key0)) is not a valid key for type $K")) + end + return get!(default, h, key) +end + +function get!(default::Callable, h::Dict{K,V}, key::K) where V where K + index = ht_keyindex2!(h, key) + + index > 0 && return h.vals[index] + + age0 = h.age + v = convert(V, default()) + if h.age != age0 + index = ht_keyindex2!(h, key) + end + if index > 0 + h.age += 1 + @inbounds h.keys[index] = key + @inbounds h.vals[index] = v + else + @inbounds _setindex!(h, v, key, -index) + end + return v +end + +# NOTE: this macro is trivial, and should +# therefore not be exported as-is: it's for internal use only. +macro get!(h, key0, default) + return quote + get!(()->$(esc(default)), $(esc(h)), $(esc(key0))) + end +end + + +function getindex(h::Dict{K,V}, key) where V where K + index = ht_keyindex(h, key) + @inbounds return (index < 0) ? throw(KeyError(key)) : h.vals[index]::V +end + +""" + get(collection, key, default) + +Return the value stored for the given key, or the given default value if no mapping for the +key is present. + +# Examples +```jldoctest +julia> d = Dict("a"=>1, "b"=>2); + +julia> get(d, "a", 3) +1 + +julia> get(d, "c", 3) +3 +``` +""" +get(collection, key, default) + +function get(h::Dict{K,V}, key, default) where V where K + index = ht_keyindex(h, key) + @inbounds return (index < 0) ? default : h.vals[index]::V +end + +""" + get(f::Function, collection, key) + +Return the value stored for the given key, or if no mapping for the key is present, return +`f()`. Use [`get!`](@ref) to also store the default value in the dictionary. + +This is intended to be called using `do` block syntax + +```julia +get(dict, key) do + # default value calculated here + time() +end +``` +""" +get(::Function, collection, key) + +function get(default::Callable, h::Dict{K,V}, key) where V where K + index = ht_keyindex(h, key) + @inbounds return (index < 0) ? default() : h.vals[index]::V +end + +""" + haskey(collection, key) -> Bool + +Determine whether a collection has a mapping for a given `key`. + +# Examples +```jldoctest +julia> D = Dict('a'=>2, 'b'=>3) +Dict{Char,Int64} with 2 entries: + 'a' => 2 + 'b' => 3 + +julia> haskey(D, 'a') +true + +julia> haskey(D, 'c') +false +``` +""" +haskey(h::Dict, key) = (ht_keyindex(h, key) >= 0) +in(key, v::KeySet{<:Any, <:Dict}) = (ht_keyindex(v.dict, key) >= 0) + +""" + getkey(collection, key, default) + +Return the key matching argument `key` if one exists in `collection`, otherwise return `default`. + +# Examples +```jldoctest +julia> D = Dict('a'=>2, 'b'=>3) +Dict{Char,Int64} with 2 entries: + 'a' => 2 + 'b' => 3 + +julia> getkey(D, 'a', 1) +'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase) + +julia> getkey(D, 'd', 'a') +'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase) +``` +""" +function getkey(h::Dict{K,V}, key, default) where V where K + index = ht_keyindex(h, key) + @inbounds return (index<0) ? default : h.keys[index]::K +end + +function _pop!(h::Dict, index) + @inbounds val = h.vals[index] + _delete!(h, index) + return val +end + +function pop!(h::Dict, key) + index = ht_keyindex(h, key) + return index > 0 ? _pop!(h, index) : throw(KeyError(key)) +end + +""" + pop!(collection, key[, default]) + +Delete and return the mapping for `key` if it exists in `collection`, otherwise return +`default`, or throw an error if `default` is not specified. + +# Examples +```jldoctest +julia> d = Dict("a"=>1, "b"=>2, "c"=>3); + +julia> pop!(d, "a") +1 + +julia> pop!(d, "d") +ERROR: KeyError: key "d" not found +Stacktrace: +[...] + +julia> pop!(d, "e", 4) +4 +``` +""" +pop!(collection, key, default) + +function pop!(h::Dict, key, default) + index = ht_keyindex(h, key) + return index > 0 ? _pop!(h, index) : default +end + +function pop!(h::Dict) + isempty(h) && throw(ArgumentError("dict must be non-empty")) + idx = skip_deleted_floor!(h) + @inbounds key = h.keys[idx] + @inbounds val = h.vals[idx] + _delete!(h, idx) + key => val +end + +function _delete!(h::Dict{K,V}, index) where {K,V} + @inbounds h.slots[index] = 0x2 + @inbounds _unsetindex!(h.keys, index) + @inbounds _unsetindex!(h.vals, index) + h.ndel += 1 + h.count -= 1 + h.age += 1 + return h +end + +""" + delete!(collection, key) + +Delete the mapping for the given key in a collection, and return the collection. + +# Examples +```jldoctest +julia> d = Dict("a"=>1, "b"=>2) +Dict{String,Int64} with 2 entries: + "b" => 2 + "a" => 1 + +julia> delete!(d, "b") +Dict{String,Int64} with 1 entry: + "a" => 1 +``` +""" +delete!(collection, key) + +function delete!(h::Dict, key) + index = ht_keyindex(h, key) + if index > 0 + _delete!(h, index) + end + return h +end + +function skip_deleted(h::Dict, i) + L = length(h.slots) + for i = i:L + @inbounds if isslotfilled(h,i) + return i + end + end + return 0 +end +function skip_deleted_floor!(h::Dict) + idx = skip_deleted(h, h.idxfloor) + if idx != 0 + h.idxfloor = idx + end + idx +end + +@propagate_inbounds _iterate(t::Dict{K,V}, i) where {K,V} = i == 0 ? nothing : (Pair{K,V}(t.keys[i],t.vals[i]), i == typemax(Int) ? 0 : i+1) +@propagate_inbounds function iterate(t::Dict) + _iterate(t, skip_deleted_floor!(t)) +end +@propagate_inbounds iterate(t::Dict, i) = _iterate(t, skip_deleted(t, i)) + +isempty(t::Dict) = (t.count == 0) +length(t::Dict) = t.count + +@propagate_inbounds function Base.iterate(v::T, i::Int = v.dict.idxfloor) where T <: Union{KeySet{<:Any, <:Dict}, ValueIterator{<:Dict}} + i == 0 && return nothing + i = skip_deleted(v.dict, i) + i == 0 && return nothing + vals = T <: KeySet ? v.dict.keys : v.dict.vals + (@inbounds vals[i], i == typemax(Int) ? 0 : i+1) +end + +function filter!(pred, h::Dict{K,V}) where {K,V} + h.count == 0 && return h + @inbounds for i=1:length(h.slots) + if h.slots[i] == 0x01 && !pred(Pair{K,V}(h.keys[i], h.vals[i])) + _delete!(h, i) + end + end + return h +end + +function reduce(::typeof(merge), items::Vector{<:Dict}) + K = mapreduce(keytype, promote_type, items) + V = mapreduce(valtype, promote_type, items) + return reduce(merge!, items; init=Dict{K,V}()) +end + +function map!(f, iter::ValueIterator{<:Dict}) + dict = iter.dict + vals = dict.vals + # @inbounds is here so the it gets propigated to isslotfiled + @inbounds for i = dict.idxfloor:lastindex(vals) + if isslotfilled(dict, i) + vals[i] = f(vals[i]) + end + end + return iter +end + +struct ImmutableDict{K,V} <: AbstractDict{K,V} + parent::ImmutableDict{K,V} + key::K + value::V + ImmutableDict{K,V}() where {K,V} = new() # represents an empty dictionary + ImmutableDict{K,V}(key, value) where {K,V} = (empty = new(); new(empty, key, value)) + ImmutableDict{K,V}(parent::ImmutableDict, key, value) where {K,V} = new(parent, key, value) +end + +""" + ImmutableDict + +ImmutableDict is a Dictionary implemented as an immutable linked list, +which is optimal for small dictionaries that are constructed over many individual insertions +Note that it is not possible to remove a value, although it can be partially overridden and hidden +by inserting a new value with the same key + + ImmutableDict(KV::Pair) + +Create a new entry in the Immutable Dictionary for the key => value pair + + - use `(key => value) in dict` to see if this particular combination is in the properties set + - use `get(dict, key, default)` to retrieve the most recent value for a particular key + +""" +ImmutableDict +ImmutableDict(KV::Pair{K,V}) where {K,V} = ImmutableDict{K,V}(KV[1], KV[2]) +ImmutableDict(t::ImmutableDict{K,V}, KV::Pair) where {K,V} = ImmutableDict{K,V}(t, KV[1], KV[2]) + +function in(key_value::Pair, dict::ImmutableDict, valcmp=(==)) + key, value = key_value + while isdefined(dict, :parent) + if dict.key == key + valcmp(value, dict.value) && return true + end + dict = dict.parent + end + return false +end + +function haskey(dict::ImmutableDict, key) + while isdefined(dict, :parent) + dict.key == key && return true + dict = dict.parent + end + return false +end + +function getindex(dict::ImmutableDict, key) + while isdefined(dict, :parent) + dict.key == key && return dict.value + dict = dict.parent + end + throw(KeyError(key)) +end +function get(dict::ImmutableDict, key, default) + while isdefined(dict, :parent) + dict.key == key && return dict.value + dict = dict.parent + end + return default +end + +# this actually defines reverse iteration (e.g. it should not be used for merge/copy/filter type operations) +function iterate(d::ImmutableDict{K,V}, t=d) where {K, V} + !isdefined(t, :parent) && return nothing + (Pair{K,V}(t.key, t.value), t.parent) +end +length(t::ImmutableDict) = count(x->true, t) +isempty(t::ImmutableDict) = !isdefined(t, :parent) +empty(::ImmutableDict, ::Type{K}, ::Type{V}) where {K, V} = ImmutableDict{K,V}() + +_similar_for(c::Dict, ::Type{Pair{K,V}}, itr, isz) where {K, V} = empty(c, K, V) +_similar_for(c::AbstractDict, ::Type{T}, itr, isz) where {T} = + throw(ArgumentError("for AbstractDicts, similar requires an element type of Pair;\n if calling map, consider a comprehension instead")) diff --git a/base/docs/Docs.jl b/base/docs/Docs.jl new file mode 100644 index 0000000..d409eea --- /dev/null +++ b/base/docs/Docs.jl @@ -0,0 +1,606 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" + Docs + +The `Docs` module provides the `@doc` macro which can be used to set and retrieve +documentation metadata for Julia objects. + +Please see the manual section on documentation for more +information. +""" +module Docs + +@nospecialize # don't specialize on any arguments of the methods declared herein + +""" +# Documentation + +Functions, methods and types can be documented by placing a string before the definition: + + \""" + # The Foo Function + `foo(x)`: Foo the living hell out of `x`. + \""" + foo(x) = ... + +The `@doc` macro can be used directly to both set and retrieve documentation / metadata. By +default, documentation is written as Markdown, but any object can be placed before the +arrow. For example: + + @doc "blah" -> + function foo() ... + +The `->` is not required if the object is on the same line, e.g. + + @doc "foo" foo + +## Documenting objects after they are defined +You can document an object after its definition by + + @doc "foo" function_to_doc + @doc "bar" TypeToDoc + +For macros, the syntax is `@doc "macro doc" :(@Module.macro)` or `@doc "macro doc" +:(string_macro"")` for string macros. Without the quote `:()` the expansion of the macro +will be documented. + +## Retrieving Documentation +You can retrieve docs for functions, macros and other objects as follows: + + @doc foo + @doc @time + @doc md"" + +## Functions & Methods +Placing documentation before a method definition (e.g. `function foo() ...` or `foo() = ...`) +will cause that specific method to be documented, as opposed to the whole function. Method +docs are concatenated together in the order they were defined to provide docs for the +function. +""" +:(Core.@doc) + +include("bindings.jl") + +import .Base.Meta: quot, isexpr +import .Base: Callable, with_output_color +using .Base: RefValue +import ..CoreDocs: lazy_iterpolate + +export doc + +# Basic API / Storage + +const modules = Module[] +const META = gensym(:meta) + +meta(m::Module) = isdefined(m, META) ? getfield(m, META) : IdDict() + +function initmeta(m::Module) + if !isdefined(m, META) + Core.eval(m, :(const $META = $(IdDict()))) + push!(modules, m) + end + nothing +end + +function signature!(tv, expr::Expr) + is_macrocall = isexpr(expr, :macrocall) + if is_macrocall || isexpr(expr, :call) + sig = :(Union{Tuple{}}) + first_arg = is_macrocall ? 3 : 2 # skip function arguments + for arg in expr.args[first_arg:end] + isexpr(arg, :parameters) && continue + if isexpr(arg, :kw) # optional arg + push!(sig.args, :(Tuple{$(sig.args[end].args[2:end]...)})) + end + push!(sig.args[end].args, argtype(arg)) + end + if isexpr(expr.args[1], :curly) && isempty(tv) + append!(tv, tvar.(expr.args[1].args[2:end])) + end + for i = length(tv):-1:1 + push!(sig.args, :(Tuple{$(tv[i].args[1])})) + end + for i = length(tv):-1:1 + sig = Expr(:where, sig, tv[i]) + end + return sig + elseif isexpr(expr, :where) + append!(tv, tvar.(expr.args[2:end])) + return signature!(tv, expr.args[1]) + else + return signature!(tv, expr.args[1]) + end +end +signature!(tv, @nospecialize(other)) = :(Union{}) +signature(expr::Expr) = signature!([], expr) +signature(@nospecialize other) = signature!([], other) + +function argtype(expr::Expr) + isexpr(expr, :(::)) && return expr.args[end] + isexpr(expr, :(...)) && return :(Vararg{$(argtype(expr.args[1]))}) + return argtype(expr.args[1]) +end +argtype(@nospecialize other) = :Any + +tvar(x::Expr) = x +tvar(s::Symbol) = :($s <: Any) + +# Docsystem types. +# ================ + +""" + Docs.DocStr + +Stores the contents of a single docstring as well as related metadata. + +Both the raw text, `.text`, and the parsed markdown, `.object`, are tracked by this type. +Parsing of the raw text is done lazily when a request is made to render the docstring, +which helps to reduce total precompiled image size. + +The `.data` fields stores several values related to the docstring, such as: path, +linenumber, source code, and fielddocs. +""" +mutable struct DocStr + text :: Core.SimpleVector + object :: Any + data :: Dict{Symbol, Any} +end + +function docstr(binding::Binding, typesig = Union{}) + @nospecialize typesig + for m in modules + dict = meta(m) + if haskey(dict, binding) + docs = dict[binding].docs + if haskey(docs, typesig) + return docs[typesig] + end + end + end + error("could not find matching docstring for '$binding :: $typesig'.") +end +docstr(object, data = Dict()) = _docstr(object, data) + +_docstr(vec::Core.SimpleVector, data) = DocStr(vec, nothing, data) +_docstr(str::AbstractString, data) = DocStr(Core.svec(str), nothing, data) +_docstr(object, data) = DocStr(Core.svec(), object, data) + +_docstr(doc::DocStr, data) = (doc.data = merge(data, doc.data); doc) + +macro ref(x) + binding = bindingexpr(namify(x)) + typesig = signature(x) + return esc(docexpr(__source__, __module__, binding, typesig)) +end + +docexpr(__source__, __module__, args...) = Expr(:call, docstr, args...) + +""" + MultiDoc + +Stores a collection of docstrings for related objects, ie. a `Function`/`DataType` and +associated `Method` objects. + +Each documented object in a `MultiDoc` is referred to by it's signature which is represented +by a `Union` of `Tuple` types. For example, the following `Method` definition + + f(x, y) = ... + +is stored as `Tuple{Any, Any}` in the `MultiDoc` while + + f(x::T, y = ?) where {T} = ... + +is stored as `Union{Tuple{T, Any}, Tuple{T}} where T`. + +Note: The `Function`/`DataType` object's signature is always `Union{}`. +""" +mutable struct MultiDoc + "Ordered (via definition order) vector of object signatures." + order::Vector{Type} + "Documentation for each object. Keys are signatures." + docs::IdDict{Any,Any} + + MultiDoc() = new(Type[], IdDict()) +end + +# Docstring registration. +# ======================= + +""" + Docs.doc!(__module__, binding, str, sig) + +Adds a new docstring `str` to the docsystem of `__module__` for `binding` and signature `sig`. +""" +function doc!(__module__::Module, b::Binding, str::DocStr, @nospecialize sig = Union{}) + initmeta(__module__) + m = get!(meta(__module__), b, MultiDoc()) + if haskey(m.docs, sig) + # We allow for docstrings to be updated, but print a warning since it is possible + # that over-writing a docstring *may* have been accidental. The warning + # is suppressed for symbols in Main, for interactive use (#23011). + __module__ == Main || @warn "Replacing docs for `$b :: $sig` in module `$(__module__)`" + else + # The ordering of docstrings for each Binding is defined by the order in which they + # are initially added. Replacing a specific docstring does not change it's ordering. + push!(m.order, sig) + end + m.docs[sig] = str + str.data[:binding] = b + str.data[:typesig] = sig + return b +end + +# Docstring lookup. +# ================= + +""" + getdoc(obj) + getdoc(obj, sig) + +Return a custom docstring object associated with the object `obj` and, optionally, the tuple +type signature `sig`. See `MultiDoc` docs for an explanation of the possible values of `sig`. + +The returned object can either be a markdown object generated by `Markdown.parse` or some +other custom type used to display non-markdown formatted documentation. + +A return value of `nothing` can be used to signify to the docsystem that no documentation +was found for `obj`, in which case the docsystem will fall back to searching for the +`Binding` associated with `obj` instead. +""" +function getdoc end + +getdoc(@nospecialize(x), @nospecialize(sig)) = getdoc(x) +getdoc(@nospecialize(x)) = nothing + +# Utilities. +# ========== + +""" +`catdoc(xs...)`: Combine the documentation metadata `xs` into a single meta object. +""" +catdoc() = nothing +catdoc(xs...) = vcat(xs...) + +const keywords = Dict{Symbol, DocStr}() + +function unblock(@nospecialize ex) + isexpr(ex, :block) || return ex + exs = filter(ex -> !(isa(ex, LineNumberNode) || isexpr(ex, :line)), ex.args) + length(exs) == 1 || return ex + return unblock(exs[1]) +end + +uncurly(@nospecialize ex) = isexpr(ex, :curly) ? ex.args[1] : ex + +namify(@nospecialize x) = astname(x, isexpr(x, :macro)) + +function astname(x::Expr, ismacro::Bool) + if isexpr(x, :.) + ismacro ? macroname(x) : x + # Call overloading, e.g. `(a::A)(b) = b` or `function (a::A)(b) b end` should document `A(b)` + elseif (isexpr(x, :function) || isexpr(x, :(=))) && isexpr(x.args[1], :call) && isexpr(x.args[1].args[1], :(::)) + return astname(x.args[1].args[1].args[end], ismacro) + else + n = isexpr(x, (:module, :struct)) ? 2 : 1 + astname(x.args[n], ismacro) + end +end +astname(q::QuoteNode, ismacro::Bool) = astname(q.value, ismacro) +astname(s::Symbol, ismacro::Bool) = ismacro ? macroname(s) : s +astname(@nospecialize(other), ismacro::Bool) = other + +macroname(s::Symbol) = Symbol('@', s) +macroname(x::Expr) = Expr(x.head, x.args[1], macroname(x.args[end].value)) + +isfield(@nospecialize x) = isexpr(x, :.) && + (isa(x.args[1], Symbol) || isfield(x.args[1])) && + (isa(x.args[2], QuoteNode) || isexpr(x.args[2], :quote)) + +# @doc expression builders. +# ========================= + +""" + Docs.metadata(source, module, expr, ismodule) + +Build a `Dict` expression containing metadata captured from the expression `expr`. + +Fields that may be included in the returned `Dict`: + +- `:path`: Symbol representing the file where `expr` is defined. +- `:linenumber`: Linenumber where `expr` is defined. +- `:module`: Module where the docstring is defined. +- `:fields`: `Dict` of all field docs found in `expr`. Only for concrete types. +""" +function metadata(__source__, __module__, expr, ismodule) + args = [] + # Filename and linenumber of the docstring. + __file__ = isa(__source__.file, Symbol) ? String(__source__.file) : "" + push!(args, Pair(:path, __file__)) + push!(args, Pair(:linenumber, __source__.line)) + # Module in which the docstring is defined. + if ismodule # Module docs go inside the module with name `expr` + push!(args, :($Pair(:module, $expr))) + else + push!(args, Pair(:module, __module__)) + end + if isexpr(expr, :struct) + # Field docs for concrete types. + fields = [] + last_docstr = nothing + for each in expr.args[3].args + if isa(each, Symbol) || isexpr(each, :(::)) + # a field declaration + if last_docstr !== nothing + push!(fields, (namify(each), last_docstr)) + last_docstr = nothing + end + elseif isexpr(each, :function) || isexpr(each, :(=)) + break + elseif isa(each, String) || isexpr(each, :string) || isexpr(each, :call) || + (isexpr(each, :macrocall) && each.args[1] === Symbol("@doc_str")) + # forms that might be doc strings + last_docstr = each + end + end + dict = :($(Dict)($([:($(Pair)($(quot(f)), $d)) for (f, d) in fields]...))) + push!(args, :($(Pair)(:fields, $dict))) + end + return :($(Dict)($(args...))) +end + +function keyworddoc(__source__, __module__, str, def::Base.BaseDocs.Keyword) + @nospecialize str + docstr = esc(docexpr(__source__, __module__, lazy_iterpolate(str), metadata(__source__, __module__, def, false))) + return :($setindex!($(keywords), $docstr, $(esc(quot(def.name)))); nothing) +end + +function objectdoc(__source__, __module__, str, def, expr, sig = :(Union{})) + @nospecialize str def expr sig + binding = esc(bindingexpr(namify(expr))) + docstr = esc(docexpr(__source__, __module__, lazy_iterpolate(str), metadata(__source__, __module__, expr, false))) + # Note: we want to avoid introducing line number nodes here (issue #24468) + return Expr(:block, esc(def), :($(doc!)($__module__, $binding, $docstr, $(esc(sig))))) +end + +function calldoc(__source__, __module__, str, def::Expr) + @nospecialize str + args = def.args[2:end] + if isempty(args) || all(validcall, args) + objectdoc(__source__, __module__, str, nothing, def, signature(def)) + else + docerror(def) + end +end +validcall(x) = isa(x, Symbol) || isexpr(x, (:(::), :..., :kw, :parameters)) + +function moduledoc(__source__, __module__, meta, def, def′::Expr) + @nospecialize meta def + name = namify(def′) + docex = Expr(:call, doc!, name, bindingexpr(name), + docexpr(__source__, name, lazy_iterpolate(meta), metadata(__source__, __module__, name, true))) + if def === nothing + esc(:(Core.eval($name, $(quot(docex))))) + else + def = unblock(def) + block = def.args[3].args + if !def.args[1] + isempty(block) && error("empty baremodules are not documentable.") + insert!(block, 2, :(import Base: @doc)) + end + push!(block, docex) + esc(Expr(:toplevel, def)) + end +end + +# Shares a single doc, `meta`, between several expressions from the tuple expression `ex`. +function multidoc(__source__, __module__, meta, ex::Expr, define::Bool) + @nospecialize meta + out = Expr(:block) + str = docexpr(__source__, __module__, lazy_iterpolate(meta), metadata(__source__, __module__, ex, false)) + ref = RefValue{DocStr}() + first = true + for arg in ex.args + # The first `arg` to be documented needs to also create the docstring for the group + # (after doing the action defined by the argument). + # Subsequent `arg`s just need `ref` to be able to find the docstring without having + # to create an entirely new one each. + if first + first = false + docstr = :($getindex($setindex!($(ref), $str))) + else + docstr = :($getindex($(ref))) + end + push!(out.args, docm(__source__, __module__, docstr, arg, define)) + end + return out +end + +""" + @__doc__(ex) + +Low-level macro used to mark expressions returned by a macro that should be documented. If +more than one expression is marked then the same docstring is applied to each expression. + + macro example(f) + quote + \$(f)() = 0 + @__doc__ \$(f)(x) = 1 + \$(f)(x, y) = 2 + end |> esc + end + +`@__doc__` has no effect when a macro that uses it is not documented. +""" +:(Core.@__doc__) + +function __doc__!(meta, def, define::Bool) + @nospecialize meta def + # Two cases must be handled here to avoid redefining all definitions contained in `def`: + if define + # `def` has not been defined yet (this is the common case, i.e. when not generating + # the Base image). We just need to convert each `@__doc__` marker to an `@doc`. + finddoc(def) do each + each.head = :macrocall + each.args = [Symbol("@doc"), nothing, meta, each.args[end], define] # TODO: forward line number info + end + else + # `def` has already been defined during Base image gen so we just need to find and + # document any subexpressions marked with `@__doc__`. + docs = [] + found = finddoc(def) do each + push!(docs, :(@doc($meta, $(each.args[end]), $define))) + end + # If any subexpressions have been documented then replace the entire expression with + # just those documented subexpressions to avoid redefining any definitions. + if found + def.head = :toplevel + def.args = docs + end + found + end +end +# Walk expression tree `def` and call `λ` when any `@__doc__` markers are found. Returns +# `true` to signify that at least one `@__doc__` has been found, and `false` otherwise. +function finddoc(λ, def::Expr) + if isexpr(def, :block, 2) && isexpr(def.args[1], :meta, 1) && def.args[1].args[1] === :doc + # Found the macroexpansion of an `@__doc__` expression. + λ(def) + true + else + found = false + for each in def.args + found |= finddoc(λ, each) + end + found + end +end +finddoc(λ, @nospecialize def) = false + +# Predicates and helpers for `docm` expression selection: + +const FUNC_HEADS = [:function, :macro, :(=)] +const BINDING_HEADS = [:const, :global, :(=)] +# For the special `:@mac` / `:(Base.@mac)` syntax for documenting a macro after definition. +isquotedmacrocall(@nospecialize x) = + isexpr(x, :copyast, 1) && + isa(x.args[1], QuoteNode) && + isexpr(x.args[1].value, :macrocall, 2) +# Simple expressions / atoms the may be documented. +isbasicdoc(@nospecialize x) = isexpr(x, :.) || isa(x, Union{QuoteNode, Symbol}) +is_signature(@nospecialize x) = isexpr(x, :call) || (isexpr(x, :(::), 2) && isexpr(x.args[1], :call)) || isexpr(x, :where) + +function docm(source::LineNumberNode, mod::Module, ex) + @nospecialize ex + if isexpr(ex, :->) && length(ex.args) > 1 + return docm(source, mod, ex.args...) + else + # TODO: this is a shim to continue to allow `@doc` for looking up docstrings + REPL = Base.REPL_MODULE_REF[] + return REPL.lookup_doc(ex) + end +end + +function docm(source::LineNumberNode, mod::Module, meta, ex, define::Bool = true) + @nospecialize meta ex + # Some documented expressions may be decorated with macro calls which obscure the actual + # expression. Expand the macro calls and remove extra blocks. + x = unblock(macroexpand(mod, ex)) + # Don't try to redefine expressions. This is only needed for `Base` img gen since + # otherwise calling `loaddocs` would redefine all documented functions and types. + def = define ? x : nothing + if isa(x, GlobalRef) && (x::GlobalRef).mod == mod + x = (x::GlobalRef).name + end + + # Keywords using the `@kw_str` macro in `base/docs/basedocs.jl`. + # + # "..." + # kw"if", kw"else" + # + doc = + isa(x, Base.BaseDocs.Keyword) ? keyworddoc(source, mod, meta, x) : + + # Method / macro definitions and "call" syntax. + # + # function f(...) ... end + # f(...) = ... + # macro m(...) end + # function f end + # f(...) + # + isexpr(x, FUNC_HEADS) && is_signature(x.args[1]) ? objectdoc(source, mod, meta, def, x, signature(x)) : + isexpr(x, [:function, :macro]) && !isexpr(x.args[1], :call) ? objectdoc(source, mod, meta, def, x) : + isexpr(x, :call) ? calldoc(source, mod, meta, x) : + + # Type definitions. + # + # struct T ... end + # abstract type T end + # primitive type T N end + # + isexpr(x, [:struct, :abstract, :primitive]) ? objectdoc(source, mod, meta, def, x) : + + # "Bindings". Names that resolve to objects with different names, ie. + # + # const T = S + # T = S + # global T = S + # + isexpr(x, BINDING_HEADS) && !isexpr(x.args[1], :call) ? objectdoc(source, mod, meta, def, x) : + + # Quoted macrocall syntax. `:@time` / `:(Base.@time)`. + isquotedmacrocall(x) ? objectdoc(source, mod, meta, def, x) : + # Modules and baremodules. + isexpr(x, :module) ? moduledoc(source, mod, meta, def, x) : + # Document several expressions with the same docstring. `a, b, c`. + isexpr(x, :tuple) ? multidoc(source, mod, meta, x, define) : + # Errors generated by calling `macroexpand` are passed back to the call site. + isexpr(x, :error) ? esc(x) : + # When documenting macro-generated code we look for embedded `@__doc__` calls. + __doc__!(meta, x, define) ? esc(x) : + # Any "basic" expression such as a bare function or module name or numeric literal. + isbasicdoc(x) ? objectdoc(source, mod, meta, nothing, x) : + + # All other expressions are undocumentable and should be handled on a case-by-case basis + # with `@__doc__`. Unbound string literals are also undocumentable since they cannot be + # retrieved from the module's metadata `IdDict` without a reference to the string. + docerror(ex) + + return doc +end + +function docerror(@nospecialize ex) + txt = """ + cannot document the following expression: + + $(isa(ex, AbstractString) ? repr(ex) : ex)""" + if isexpr(ex, :macrocall) + txt *= "\n\n'$(ex.args[1])' not documentable. See 'Base.@__doc__' docs for details." + end + return :($(error)($txt, "\n")) +end + +include("utils.jl") + +# Swap out the bootstrap macro with the real one. +Core.atdoc!(docm) + +function loaddocs(docs) + for (mod, ex, str, file, line) in docs + data = Dict(:path => string(file), :linenumber => line) + doc = docstr(str, data) + docstring = docm(LineNumberNode(line, file), mod, doc, ex, false) # expand the real @doc macro now + Core.eval(mod, Expr(Core.unescape, docstring, Docs)) + end + empty!(docs) + nothing +end + +function formatdoc end +function parsedoc end +function apropos end +function doc end + +end diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl new file mode 100644 index 0000000..e581c16 --- /dev/null +++ b/base/docs/basedocs.jl @@ -0,0 +1,2246 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +module BaseDocs + +@nospecialize # don't specialize on any arguments of the methods declared herein + +struct Keyword + name::Symbol +end +macro kw_str(text) + return Keyword(Symbol(text)) +end + +""" +**Welcome to Julia $(string(VERSION)).** The full manual is available at + + https://docs.julialang.org/ + +as well as many great tutorials and learning resources: + + https://julialang.org/learning/ + +For help on a specific function or macro, type `?` followed +by its name, e.g. `?cos`, or `?@time`, and press enter. +Type `;` to enter shell mode, `]` to enter package mode. +""" +kw"help", kw"?", kw"Julia", kw"julia", kw"" + +""" + using + +`using Foo` will load the module or package `Foo` and make its [`export`](@ref)ed names +available for direct use. Names can also be used via dot syntax (e.g. `Foo.foo` to access +the name `foo`), whether they are `export`ed or not. +See the [manual section about modules](@ref modules) for details. +""" +kw"using" + +""" + import + +`import Foo` will load the module or package `Foo`. +Names from the imported `Foo` module can be accessed with dot syntax +(e.g. `Foo.foo` to access the name `foo`). +See the [manual section about modules](@ref modules) for details. +""" +kw"import" + +""" + export + +`export` is used within modules to tell Julia which functions should be +made available to the user. For example: `export foo` makes the name +`foo` available when [`using`](@ref) the module. +See the [manual section about modules](@ref modules) for details. +""" +kw"export" + +""" + abstract type + +`abstract type` declares a type that cannot be instantiated, and serves only as a node in the +type graph, thereby describing sets of related concrete types: those concrete types +which are their descendants. Abstract types form the conceptual hierarchy which makes +Julia’s type system more than just a collection of object implementations. For example: + +```julia +abstract type Number end +abstract type Real <: Number end +``` +[`Number`](@ref) has no supertype, whereas [`Real`](@ref) is an abstract subtype of `Number`. +""" +kw"abstract type" + +""" + module + +`module` declares a [`Module`](@ref), which is a separate global variable workspace. Within a +module, you can control which names from other modules are visible (via importing), and +specify which of your names are intended to be public (via exporting). +Modules allow you to create top-level definitions without worrying about name conflicts +when your code is used together with somebody else’s. +See the [manual section about modules](@ref modules) for more details. + +# Examples +```julia +module Foo +import Base.show +export MyType, foo + +struct MyType + x +end + +bar(x) = 2x +foo(a::MyType) = bar(a.x) + 1 +show(io::IO, a::MyType) = print(io, "MyType \$(a.x)") +end +``` +""" +kw"module" + +""" + __init__ + +`__init__()` function in your module would executes immediately *after* the module is loaded at +runtime for the first time (i.e., it is only called once and only after all statements in the +module have been executed). Because it is called *after* fully importing the module, `__init__` +functions of submodules will be executed *first*. Two typical uses of __init__ are calling +runtime initialization functions of external C libraries and initializing global constants +that involve pointers returned by external libraries. +See the [manual section about modules](@ref modules) for more details. + +# Examples +```julia +const foo_data_ptr = Ref{Ptr{Cvoid}}(0) +function __init__() + ccall((:foo_init, :libfoo), Cvoid, ()) + foo_data_ptr[] = ccall((:foo_data, :libfoo), Ptr{Cvoid}, ()) + nothing +end +``` +""" +kw"__init__" + +""" + baremodule + +`baremodule` declares a module that does not contain `using Base` +or a definition of [`eval`](@ref Base.eval). It does still import `Core`. +""" +kw"baremodule" + +""" + primitive type + +`primitive type` declares a concrete type whose data consists only of a series of bits. Classic +examples of primitive types are integers and floating-point values. Some example built-in +primitive type declarations: + +```julia +primitive type Char 32 end +primitive type Bool <: Integer 8 end +``` +The number after the name indicates how many bits of storage the type requires. Currently, +only sizes that are multiples of 8 bits are supported. +The [`Bool`](@ref) declaration shows how a primitive type can be optionally +declared to be a subtype of some supertype. +""" +kw"primitive type" + +""" + macro + +`macro` defines a method for inserting generated code into a program. +A macro maps a sequence of argument expressions to a returned expression, and the +resulting expression is substituted directly into the program at the point where +the macro is invoked. +Macros are a way to run generated code without calling [`eval`](@ref Base.eval), since the generated +code instead simply becomes part of the surrounding program. +Macro arguments may include expressions, literal values, and symbols. + +# Examples +```jldoctest +julia> macro sayhello(name) + return :( println("Hello, ", \$name, "!") ) + end +@sayhello (macro with 1 method) + +julia> @sayhello "Charlie" +Hello, Charlie! +``` +""" +kw"macro" + +""" + local + +`local` introduces a new local variable. +See the [manual section on variable scoping](@ref scope-of-variables) for more information. + +# Examples +```jldoctest +julia> function foo(n) + x = 0 + for i = 1:n + local x # introduce a loop-local x + x = i + end + x + end +foo (generic function with 1 method) + +julia> foo(10) +0 +``` +""" +kw"local" + +""" + global + +`global x` makes `x` in the current scope and its inner scopes refer to the global +variable of that name. +See the [manual section on variable scoping](@ref scope-of-variables) for more information. + +# Examples +```jldoctest +julia> z = 3 +3 + +julia> function foo() + global z = 6 # use the z variable defined outside foo + end +foo (generic function with 1 method) + +julia> foo() +6 + +julia> z +6 +``` +""" +kw"global" + +""" + = + +`=` is the assignment operator. +* For variable `a` and expression `b`, `a = b` makes `a` refer to the value of `b`. +* For functions `f(x)`, `f(x) = x` defines a new function constant `f`, or adds a new method to `f` if `f` is already defined; this usage is equivalent to `function f(x); x; end`. +* `a[i] = v` calls [`setindex!`](@ref)`(a,v,i)`. +* `a.b = c` calls [`setproperty!`](@ref)`(a,:b,c)`. +* Inside a function call, `f(a=b)` passes `b` as the value of keyword argument `a`. +* Inside parentheses with commas, `(a=1,)` constructs a [`NamedTuple`](@ref). + +# Examples +Assigning `a` to `b` does not create a copy of `b`; instead use [`copy`](@ref) or [`deepcopy`](@ref). + +```jldoctest +julia> b = [1]; a = b; b[1] = 2; a +1-element Array{Int64,1}: + 2 + +julia> b = [1]; a = copy(b); b[1] = 2; a +1-element Array{Int64,1}: + 1 + +``` +Collections passed to functions are also not copied. Functions can modify (mutate) the contents of the objects their arguments refer to. (The names of functions which do this are conventionally suffixed with '!'.) +```jldoctest +julia> function f!(x); x[:] .+= 1; end +f! (generic function with 1 method) + +julia> a = [1]; f!(a); a +1-element Array{Int64,1}: + 2 + +``` +Assignment can operate on multiple variables in parallel, taking values from an iterable: +```jldoctest +julia> a, b = 4, 5 +(4, 5) + +julia> a, b = 1:3 +1:3 + +julia> a, b +(1, 2) + +``` +Assignment can operate on multiple variables in series, and will return the value of the right-hand-most expression: +```jldoctest +julia> a = [1]; b = [2]; c = [3]; a = b = c +1-element Array{Int64,1}: + 3 + +julia> b[1] = 2; a, b, c +([2], [2], [2]) + +``` +Assignment at out-of-bounds indices does not grow a collection. If the collection is a [`Vector`](@ref) it can instead be grown with [`push!`](@ref) or [`append!`](@ref). +```jldoctest +julia> a = [1, 1]; a[3] = 2 +ERROR: BoundsError: attempt to access 2-element Array{Int64,1} at index [3] +[...] + +julia> push!(a, 2, 3) +4-element Array{Int64,1}: + 1 + 1 + 2 + 3 + +``` +Assigning `[]` does not eliminate elements from a collection; instead use [`filter!`](@ref). +```jldoctest +julia> a = collect(1:3); a[a .<= 1] = [] +ERROR: DimensionMismatch("tried to assign 0 elements to 1 destinations") +[...] + +julia> filter!(x -> x > 1, a) # in-place & thus more efficient than a = a[a .> 1] +2-element Array{Int64,1}: + 2 + 3 + +``` +""" +kw"=" + +""" + let + +`let` statements allocate new variable bindings each time they run. Whereas an +assignment modifies an existing value location, `let` creates new locations. This +difference is only detectable in the case of variables that outlive their scope via +closures. The `let` syntax accepts a comma-separated series of assignments and variable +names: + +```julia +let var1 = value1, var2, var3 = value3 + code +end +``` +The assignments are evaluated in order, with each right-hand side evaluated in the scope +before the new variable on the left-hand side has been introduced. Therefore it makes +sense to write something like `let x = x`, since the two `x` variables are distinct and +have separate storage. +""" +kw"let" + +""" + quote + +`quote` creates multiple expression objects in a block without using the explicit +[`Expr`](@ref) constructor. For example: + +```julia +ex = quote + x = 1 + y = 2 + x + y +end +``` +Unlike the other means of quoting, `:( ... )`, this form introduces `QuoteNode` elements +to the expression tree, which must be considered when directly manipulating the tree. +For other purposes, `:( ... )` and `quote .. end` blocks are treated identically. +""" +kw"quote" + +""" + Expr(head::Symbol, args...) + +A type representing compound expressions in parsed julia code (ASTs). +Each expression consists of a `head` `Symbol` identifying which kind of +expression it is (e.g. a call, for loop, conditional statement, etc.), +and subexpressions (e.g. the arguments of a call). +The subexpressions are stored in a `Vector{Any}` field called `args`. + +See the manual chapter on [Metaprogramming](@ref) and the developer +documentation [Julia ASTs](@ref). + +# Examples +```jldoctest +julia> Expr(:call, :+, 1, 2) +:(1 + 2) + +julia> dump(:(a ? b : c)) +Expr + head: Symbol if + args: Array{Any}((3,)) + 1: Symbol a + 2: Symbol b + 3: Symbol c +``` +""" +Expr + +""" + ' + +The conjugate transposition operator, see [`adjoint`](@ref). + +# Examples +```jldoctest +julia> A = [1.0 -2.0im; 4.0im 2.0] +2×2 Array{Complex{Float64},2}: + 1.0+0.0im -0.0-2.0im + 0.0+4.0im 2.0+0.0im + +julia> A' +2×2 Array{Complex{Float64},2}: + 1.0-0.0im 0.0-4.0im + -0.0+2.0im 2.0-0.0im +``` +""" +kw"'" + +""" + const + +`const` is used to declare global variables whose values will not change. In almost all code +(and particularly performance sensitive code) global variables should be declared +constant in this way. + +```julia +const x = 5 +``` + +Multiple variables can be declared within a single `const`: +```julia +const y, z = 7, 11 +``` + +Note that `const` only applies to one `=` operation, therefore `const x = y = 1` +declares `x` to be constant but not `y`. On the other hand, `const x = const y = 1` +declares both `x` and `y` constant. + +Note that "constant-ness" does not extend into mutable containers; only the +association between a variable and its value is constant. +If `x` is an array or dictionary (for example) you can still modify, add, or remove elements. + +In some cases changing the value of a `const` variable gives a warning instead of +an error. +However, this can produce unpredictable behavior or corrupt the state of your program, +and so should be avoided. +This feature is intended only for convenience during interactive use. +""" +kw"const" + +""" + function + +Functions are defined with the `function` keyword: + +```julia +function add(a, b) + return a + b +end +``` +Or the short form notation: + +```julia +add(a, b) = a + b +``` + +The use of the [`return`](@ref) keyword is exactly the same as in other languages, +but is often optional. A function without an explicit `return` statement will return +the last expression in the function body. +""" +kw"function" + +""" + return + +`return x` causes the enclosing function to exit early, passing the given value `x` +back to its caller. `return` by itself with no value is equivalent to `return nothing` +(see [`nothing`](@ref)). + +```julia +function compare(a, b) + a == b && return "equal to" + a < b ? "less than" : "greater than" +end +``` +In general you can place a `return` statement anywhere within a function body, including +within deeply nested loops or conditionals, but be careful with `do` blocks. For +example: + +```julia +function test1(xs) + for x in xs + iseven(x) && return 2x + end +end + +function test2(xs) + map(xs) do x + iseven(x) && return 2x + x + end +end +``` +In the first example, the return breaks out of `test1` as soon as it hits +an even number, so `test1([5,6,7])` returns `12`. + +You might expect the second example to behave the same way, but in fact the `return` +there only breaks out of the *inner* function (inside the `do` block) and gives a value +back to `map`. `test2([5,6,7])` then returns `[5,12,7]`. + +When used in a top-level expression (i.e. outside any function), `return` causes +the entire current top-level expression to terminate early. +""" +kw"return" + +""" + if/elseif/else + +`if`/`elseif`/`else` performs conditional evaluation, which allows portions of code to +be evaluated or not evaluated depending on the value of a boolean expression. Here is +the anatomy of the `if`/`elseif`/`else` conditional syntax: + +```julia +if x < y + println("x is less than y") +elseif x > y + println("x is greater than y") +else + println("x is equal to y") +end +``` +If the condition expression `x < y` is true, then the corresponding block is evaluated; +otherwise the condition expression `x > y` is evaluated, and if it is true, the +corresponding block is evaluated; if neither expression is true, the `else` block is +evaluated. The `elseif` and `else` blocks are optional, and as many `elseif` blocks as +desired can be used. +""" +kw"if", kw"elseif", kw"else" + +""" + for + +`for` loops repeatedly evaluate a block of statements while +iterating over a sequence of values. + +# Examples +```jldoctest +julia> for i in [1, 4, 0] + println(i) + end +1 +4 +0 +``` +""" +kw"for" + +""" + while + +`while` loops repeatedly evaluate a conditional expression, and continue evaluating the +body of the while loop as long as the expression remains true. If the condition +expression is false when the while loop is first reached, the body is never evaluated. + +# Examples +```jldoctest +julia> i = 1 +1 + +julia> while i < 5 + println(i) + global i += 1 + end +1 +2 +3 +4 +``` +""" +kw"while" + +""" + end + +`end` marks the conclusion of a block of expressions, for example +[`module`](@ref), [`struct`](@ref), [`mutable struct`](@ref), +[`begin`](@ref), [`let`](@ref), [`for`](@ref) etc. +`end` may also be used when indexing into an array to represent +the last index of a dimension. + +# Examples +```jldoctest +julia> A = [1 2; 3 4] +2×2 Array{Int64,2}: + 1 2 + 3 4 + +julia> A[end, :] +2-element Array{Int64,1}: + 3 + 4 +``` +""" +kw"end" + +""" + try/catch + +A `try`/`catch` statement allows intercepting errors (exceptions) thrown +by [`throw`](@ref) so that program execution can continue. +For example, the following code attempts to write a file, but warns the user +and proceeds instead of terminating execution if the file cannot be written: + +```julia +try + open("/danger", "w") do f + println(f, "Hello") + end +catch + @warn "Could not write file." +end +``` + +or, when the file cannot be read into a variable: + +```julia +lines = try + open("/danger", "r") do f + readlines(f) + end +catch + @warn "File not found." +end +``` + +The syntax `catch e` (where `e` is any variable) assigns the thrown +exception object to the given variable within the `catch` block. + +The power of the `try`/`catch` construct lies in the ability to unwind a deeply +nested computation immediately to a much higher level in the stack of calling functions. +""" +kw"try", kw"catch" + +""" + finally + +Run some code when a given block of code exits, regardless +of how it exits. For example, here is how we can guarantee that an opened file is +closed: + +```julia +f = open("file") +try + operate_on_file(f) +finally + close(f) +end +``` + +When control leaves the [`try`](@ref) block (for example, due to a [`return`](@ref), or just finishing +normally), [`close(f)`](@ref) will be executed. If the `try` block exits due to an exception, +the exception will continue propagating. A `catch` block may be combined with `try` and +`finally` as well. In this case the `finally` block will run after `catch` has handled +the error. +""" +kw"finally" + +""" + break + +Break out of a loop immediately. + +# Examples +```jldoctest +julia> i = 0 +0 + +julia> while true + global i += 1 + i > 5 && break + println(i) + end +1 +2 +3 +4 +5 +``` +""" +kw"break" + +""" + continue + +Skip the rest of the current loop iteration. + +# Examples +```jldoctest +julia> for i = 1:6 + iseven(i) && continue + println(i) + end +1 +3 +5 +``` +""" +kw"continue" + +""" + do + +Create an anonymous function and pass it as the first argument to +a function call. +For example: + +```julia +map(1:10) do x + 2x +end +``` + +is equivalent to `map(x->2x, 1:10)`. + +Use multiple arguments like so: + +```julia +map(1:10, 11:20) do x, y + x + y +end +``` +""" +kw"do" + +""" + ... + +The "splat" operator, `...`, represents a sequence of arguments. +`...` can be used in function definitions, to indicate that the function +accepts an arbitrary number of arguments. +`...` can also be used to apply a function to a sequence of arguments. + +# Examples +```jldoctest +julia> add(xs...) = reduce(+, xs) +add (generic function with 1 method) + +julia> add(1, 2, 3, 4, 5) +15 + +julia> add([1, 2, 3]...) +6 + +julia> add(7, 1:100..., 1000:1100...) +111107 +``` +""" +kw"..." + +""" + ; + +`;` has a similar role in Julia as in many C-like languages, and is used to delimit the +end of the previous statement. `;` is not necessary after new lines, but can be used to +separate statements on a single line or to join statements into a single expression. +`;` is also used to suppress output printing in the REPL and similar interfaces. + +# Examples +```julia +julia> function foo() + x = "Hello, "; x *= "World!" + return x + end +foo (generic function with 1 method) + +julia> bar() = (x = "Hello, Mars!"; return x) +bar (generic function with 1 method) + +julia> foo(); + +julia> bar() +"Hello, Mars!" +``` +""" +kw";" + +""" + x && y + +Short-circuiting boolean AND. +""" +kw"&&" + +""" + x || y + +Short-circuiting boolean OR. +""" +kw"||" + +""" + ccall((function_name, library), returntype, (argtype1, ...), argvalue1, ...) + ccall(function_name, returntype, (argtype1, ...), argvalue1, ...) + ccall(function_pointer, returntype, (argtype1, ...), argvalue1, ...) + +Call a function in a C-exported shared library, specified by the tuple `(function_name, library)`, +where each component is either a string or symbol. Instead of specifying a library, +one can also use a `function_name` symbol or string, which is resolved in the current process. +Alternatively, `ccall` may also be used to call a function pointer `function_pointer`, such as one returned by `dlsym`. + +Note that the argument type tuple must be a literal tuple, and not a tuple-valued +variable or expression. + +Each `argvalue` to the `ccall` will be converted to the corresponding +`argtype`, by automatic insertion of calls to `unsafe_convert(argtype, +cconvert(argtype, argvalue))`. (See also the documentation for +[`unsafe_convert`](@ref Base.unsafe_convert) and [`cconvert`](@ref Base.cconvert) for further details.) +In most cases, this simply results in a call to `convert(argtype, argvalue)`. +""" +kw"ccall" + +""" + llvmcall(IR::String, ReturnType, (ArgumentType1, ...), ArgumentValue1, ...) + llvmcall((declarations::String, IR::String), ReturnType, (ArgumentType1, ...), ArgumentValue1, ...) + +Call LLVM IR string in the first argument. Similar to an LLVM function `define` block, +arguments are available as consecutive unnamed SSA variables (%0, %1, etc.). + +The optional declarations string contains external functions declarations that are +necessary for llvm to compile the IR string. Multiple declarations can be passed in by +separating them with line breaks. + +Note that the argument type tuple must be a literal tuple, and not a tuple-valued +variable or expression. + +Each `ArgumentValue` to `llvmcall` will be converted to the corresponding +`ArgumentType`, by automatic insertion of calls to `unsafe_convert(ArgumentType, +cconvert(ArgumentType, ArgumentValue))`. (See also the documentation for +[`unsafe_convert`](@ref Base.unsafe_convert) and [`cconvert`](@ref Base.cconvert) for further details.) +In most cases, this simply results in a call to `convert(ArgumentType, ArgumentValue)`. + +See `test/llvmcall.jl` for usage examples. +""" +Core.Intrinsics.llvmcall + +""" + begin + +`begin...end` denotes a block of code. + +```julia +begin + println("Hello, ") + println("World!") +end +``` + +Usually `begin` will not be necessary, since keywords such as [`function`](@ref) and [`let`](@ref) +implicitly begin blocks of code. See also [`;`](@ref). +""" +kw"begin" + +""" + struct + +The most commonly used kind of type in Julia is a struct, specified as a name and a +set of fields. + +```julia +struct Point + x + y +end +``` + +Fields can have type restrictions, which may be parameterized: + +```julia + struct Point{X} + x::X + y::Float64 + end +``` + +A struct can also declare an abstract super type via `<:` syntax: + +```julia +struct Point <: AbstractPoint + x + y +end +``` + +`struct`s are immutable by default; an instance of one of these types cannot +be modified after construction. Use [`mutable struct`](@ref) instead to declare a +type whose instances can be modified. + +See the manual section on [Composite Types](@ref) for more details, +such as how to define constructors. +""" +kw"struct" + +""" + mutable struct + +`mutable struct` is similar to [`struct`](@ref), but additionally allows the +fields of the type to be set after construction. See the manual section on +[Composite Types](@ref) for more information. +""" +kw"mutable struct" + +""" + new + +Special function available to inner constructors which created a new object +of the type. +See the manual section on [Inner Constructor Methods](@ref) for more information. +""" +kw"new" + +""" + where + +The `where` keyword creates a type that is an iterated union of other types, over all +values of some variable. For example `Vector{T} where T<:Real` includes all [`Vector`](@ref)s +where the element type is some kind of `Real` number. + +The variable bound defaults to [`Any`](@ref) if it is omitted: + +```julia +Vector{T} where T # short for `where T<:Any` +``` +Variables can also have lower bounds: + +```julia +Vector{T} where T>:Int +Vector{T} where Int<:T<:Real +``` +There is also a concise syntax for nested `where` expressions. For example, this: + +```julia +Pair{T, S} where S<:Array{T} where T<:Number +``` +can be shortened to: + +```julia +Pair{T, S} where {T<:Number, S<:Array{T}} +``` +This form is often found on method signatures. + +Note that in this form, the variables are listed outermost-first. This matches the +order in which variables are substituted when a type is "applied" to parameter values +using the syntax `T{p1, p2, ...}`. +""" +kw"where" + +""" + var + +The syntax `var"#example#"` refers to a variable named `Symbol("#example#")`, +even though `#example#` is not a valid Julia identifier name. + +This can be useful for interoperability with programming languages which have +different rules for the construction of valid identifiers. For example, to +refer to the `R` variable `draw.segments`, you can use `var"draw.segments"` in +your Julia code. + +It is also used to `show` julia source code which has gone through macro +hygiene or otherwise contains variable names which can't be parsed normally. + +Note that this syntax requires parser support so it is expanded directly by the +parser rather than being implemented as a normal string macro `@var_str`. + +!!! compat "Julia 1.3" + This syntax requires at least Julia 1.3. + +""" +kw"var\"name\"", kw"@var_str" + +""" + ans + +A variable referring to the last computed value, automatically set at the interactive prompt. +""" +kw"ans" + +""" + devnull + +Used in a stream redirect to discard all data written to it. Essentially equivalent to +`/dev/null` on Unix or `NUL` on Windows. Usage: + +```julia +run(pipeline(`cat test.txt`, devnull)) +``` +""" +devnull + +# doc strings for code in boot.jl and built-ins + +""" + Nothing + +A type with no fields that is the type of [`nothing`](@ref). +""" +Nothing + +""" + nothing + +The singleton instance of type [`Nothing`](@ref), used by convention when there is no value to return +(as in a C `void` function) or when a variable or field holds no value. +""" +nothing + +""" + Core.TypeofBottom + +The singleton type containing only the value `Union{}` (which represents the empty type). +""" +Core.TypeofBottom + +""" + Function + +Abstract type of all functions. + +# Examples +```jldoctest +julia> isa(+, Function) +true + +julia> typeof(sin) +typeof(sin) + +julia> ans <: Function +true +``` +""" +Function + +""" + ReadOnlyMemoryError() + +An operation tried to write to memory that is read-only. +""" +ReadOnlyMemoryError + +""" + ErrorException(msg) + +Generic error type. The error message, in the `.msg` field, may provide more specific details. + +# Examples +```jldoctest +julia> ex = ErrorException("I've done a bad thing"); + +julia> ex.msg +"I've done a bad thing" +``` +""" +ErrorException + +""" + WrappedException(msg) + +Generic type for `Exception`s wrapping another `Exception`, such as `LoadError` and +`InitError`. Those exceptions contain information about the root cause of an +exception. Subtypes define a field `error` containing the causing `Exception`. +""" +Core.WrappedException + +""" + UndefRefError() + +The item or field is not defined for the given object. + +# Examples +```jldoctest +julia> struct MyType + a::Vector{Int} + MyType() = new() + end + +julia> A = MyType() +MyType(#undef) + +julia> A.a +ERROR: UndefRefError: access to undefined reference +Stacktrace: +[...] +``` +""" +UndefRefError + +""" + Float32(x [, mode::RoundingMode]) + +Create a `Float32` from `x`. If `x` is not exactly representable then `mode` determines how +`x` is rounded. + +# Examples +```jldoctest +julia> Float32(1/3, RoundDown) +0.3333333f0 + +julia> Float32(1/3, RoundUp) +0.33333334f0 +``` + +See [`RoundingMode`](@ref) for available rounding modes. +""" +Float32(x) + +""" + Float64(x [, mode::RoundingMode]) + +Create a `Float64` from `x`. If `x` is not exactly representable then `mode` determines how +`x` is rounded. + +# Examples +```jldoctest +julia> Float64(pi, RoundDown) +3.141592653589793 + +julia> Float64(pi, RoundUp) +3.1415926535897936 +``` + +See [`RoundingMode`](@ref) for available rounding modes. +""" +Float64(x) + +""" + OutOfMemoryError() + +An operation allocated too much memory for either the system or the garbage collector to +handle properly. +""" +OutOfMemoryError + +""" + BoundsError([a],[i]) + +An indexing operation into an array, `a`, tried to access an out-of-bounds element at index `i`. + +# Examples +```jldoctest; filter = r"Stacktrace:(\\n \\[[0-9]+\\].*)*" +julia> A = fill(1.0, 7); + +julia> A[8] +ERROR: BoundsError: attempt to access 7-element Array{Float64,1} at index [8] +Stacktrace: + [1] getindex(::Array{Float64,1}, ::Int64) at ./array.jl:660 + [2] top-level scope + +julia> B = fill(1.0, (2,3)); + +julia> B[2, 4] +ERROR: BoundsError: attempt to access 2×3 Array{Float64,2} at index [2, 4] +Stacktrace: + [1] getindex(::Array{Float64,2}, ::Int64, ::Int64) at ./array.jl:661 + [2] top-level scope + +julia> B[9] +ERROR: BoundsError: attempt to access 2×3 Array{Float64,2} at index [9] +Stacktrace: + [1] getindex(::Array{Float64,2}, ::Int64) at ./array.jl:660 + [2] top-level scope +``` +""" +BoundsError + +""" + InexactError(name::Symbol, T, val) + +Cannot exactly convert `val` to type `T` in a method of function `name`. + +# Examples +```jldoctest +julia> convert(Float64, 1+2im) +ERROR: InexactError: Float64(1 + 2im) +Stacktrace: +[...] +``` +""" +InexactError + +""" + DomainError(val) + DomainError(val, msg) + +The argument `val` to a function or constructor is outside the valid domain. + +# Examples +```jldoctest +julia> sqrt(-1) +ERROR: DomainError with -1.0: +sqrt will only return a complex result if called with a complex argument. Try sqrt(Complex(x)). +Stacktrace: +[...] +``` +""" +DomainError + +""" + Task(func) + +Create a `Task` (i.e. coroutine) to execute the given function `func` (which must be +callable with no arguments). The task exits when this function returns. + +# Examples +```jldoctest +julia> a() = sum(i for i in 1:1000); + +julia> b = Task(a); +``` + +In this example, `b` is a runnable `Task` that hasn't started yet. +""" +Task + +""" + StackOverflowError() + +The function call grew beyond the size of the call stack. This usually happens when a call +recurses infinitely. +""" +StackOverflowError + +""" + nfields(x) -> Int + +Get the number of fields in the given object. + +# Examples +```jldoctest +julia> a = 1//2; + +julia> nfields(a) +2 + +julia> b = 1 +1 + +julia> nfields(b) +0 + +julia> ex = ErrorException("I've done a bad thing"); + +julia> nfields(ex) +1 +``` + +In these examples, `a` is a [`Rational`](@ref), which has two fields. +`b` is an `Int`, which is a primitive bitstype with no fields at all. +`ex` is an [`ErrorException`](@ref), which has one field. +""" +nfields + +""" + UndefVarError(var::Symbol) + +A symbol in the current scope is not defined. + +# Examples +```jldoctest +julia> a +ERROR: UndefVarError: a not defined + +julia> a = 1; + +julia> a +1 +``` +""" +UndefVarError + +""" + UndefKeywordError(var::Symbol) + +The required keyword argument `var` was not assigned in a function call. + +# Examples +```jldoctest; filter = r"Stacktrace:(\\n \\[[0-9]+\\].*)*" +julia> function my_func(;my_arg) + return my_arg + 1 + end +my_func (generic function with 1 method) + +julia> my_func() +ERROR: UndefKeywordError: keyword argument my_arg not assigned +Stacktrace: + [1] my_func() at ./REPL[1]:2 + [2] top-level scope at REPL[2]:1 +``` +""" +UndefKeywordError + +""" + OverflowError(msg) + +The result of an expression is too large for the specified type and will cause a wraparound. +""" +OverflowError + +""" + TypeError(func::Symbol, context::AbstractString, expected::Type, got) + +A type assertion failure, or calling an intrinsic function with an incorrect argument type. +""" +TypeError + +""" + InterruptException() + +The process was stopped by a terminal interrupt (CTRL+C). +""" +InterruptException + +""" + applicable(f, args...) -> Bool + +Determine whether the given generic function has a method applicable to the given arguments. + +See also [`hasmethod`](@ref). + +# Examples +```jldoctest +julia> function f(x, y) + x + y + end; + +julia> applicable(f, 1) +false + +julia> applicable(f, 1, 2) +true +``` +""" +applicable + +""" + invoke(f, argtypes::Type, args...; kwargs...) + +Invoke a method for the given generic function `f` matching the specified types `argtypes` on the +specified arguments `args` and passing the keyword arguments `kwargs`. The arguments `args` must +conform with the specified types in `argtypes`, i.e. conversion is not automatically performed. +This method allows invoking a method other than the most specific matching method, which is useful +when the behavior of a more general definition is explicitly needed (often as part of the +implementation of a more specific method of the same function). + +# Examples +```jldoctest +julia> f(x::Real) = x^2; + +julia> f(x::Integer) = 1 + invoke(f, Tuple{Real}, x); + +julia> f(2) +5 +``` +""" +invoke + +""" + isa(x, type) -> Bool + +Determine whether `x` is of the given `type`. Can also be used as an infix operator, e.g. +`x isa type`. + +# Examples +```jldoctest +julia> isa(1, Int) +true + +julia> isa(1, Matrix) +false + +julia> isa(1, Char) +false + +julia> isa(1, Number) +true + +julia> 1 isa Number +true +``` +""" +isa + +""" + DivideError() + +Integer division was attempted with a denominator value of 0. + +# Examples +```jldoctest +julia> 2/0 +Inf + +julia> div(2, 0) +ERROR: DivideError: integer division error +Stacktrace: +[...] +``` +""" +DivideError + +""" + Number + +Abstract supertype for all number types. +""" +Number + +""" + Real <: Number + +Abstract supertype for all real numbers. +""" +Real + +""" + AbstractFloat <: Real + +Abstract supertype for all floating point numbers. +""" +AbstractFloat + +""" + Integer <: Real + +Abstract supertype for all integers. +""" +Integer + +""" + Signed <: Integer + +Abstract supertype for all signed integers. +""" +Signed + +""" + Unsigned <: Integer + +Abstract supertype for all unsigned integers. +""" +Unsigned + +""" + Bool <: Integer + +Boolean type, containing the values `true` and `false`. +""" +Bool + +for bit in (16, 32, 64) + @eval begin + """ + Float$($bit) <: AbstractFloat + + $($bit)-bit floating point number type. + """ + $(Symbol("Float", bit)) + end +end + +for bit in (8, 16, 32, 64, 128) + @eval begin + """ + Int$($bit) <: Signed + + $($bit)-bit signed integer type. + """ + $(Symbol("Int", bit)) + + """ + UInt$($bit) <: Unsigned + + $($bit)-bit unsigned integer type. + """ + $(Symbol("UInt", bit)) + end +end + +""" + Symbol + +The type of object used to represent identifiers in parsed julia code (ASTs). +Also often used as a name or label to identify an entity (e.g. as a dictionary key). +`Symbol`s can be entered using the `:` quote operator: +```jldoctest +julia> :name +:name + +julia> typeof(:name) +Symbol + +julia> x = 42 +42 + +julia> eval(:x) +42 +``` +`Symbol`s can also be constructed from strings or other values by calling the +constructor `Symbol(x...)`. + +`Symbol`s are immutable and should be compared using `===`. +The implementation re-uses the same object for all `Symbol`s with the same name, +so comparison tends to be efficient (it can just compare pointers). + +Unlike strings, `Symbol`s are "atomic" or "scalar" entities that do not support +iteration over characters. +""" +Symbol + +""" + Symbol(x...) -> Symbol + +Create a [`Symbol`](@ref) by concatenating the string representations of the arguments together. + +# Examples +```jldoctest +julia> Symbol("my", "name") +:myname + +julia> Symbol("day", 4) +:day4 +``` +""" +Symbol(x...) + +""" + tuple(xs...) + +Construct a tuple of the given objects. + +# Examples +```jldoctest +julia> tuple(1, 'a', pi) +(1, 'a', π) +``` +""" +tuple + +""" + getfield(value, name::Symbol) + +Extract a named field from a `value` of composite type. +See also [`getproperty`](@ref Base.getproperty). + +# Examples +```jldoctest +julia> a = 1//2 +1//2 + +julia> getfield(a, :num) +1 + +julia> a.num +1 +``` +""" +getfield + +""" + setfield!(value, name::Symbol, x) + +Assign `x` to a named field in `value` of composite type. +The `value` must be mutable and `x` must be a subtype of `fieldtype(typeof(value), name)`. +See also [`setproperty!`](@ref Base.setproperty!). + +# Examples +```jldoctest +julia> mutable struct MyMutableStruct + field::Int + end + +julia> a = MyMutableStruct(1); + +julia> setfield!(a, :field, 2); + +julia> getfield(a, :field) +2 + +julia> a = 1//2 +1//2 + +julia> setfield!(a, :num, 3); +ERROR: setfield! immutable struct of type Rational cannot be changed +``` +""" +setfield! + +""" + typeof(x) + +Get the concrete type of `x`. + +# Examples +```jldoctest +julia> a = 1//2; + +julia> typeof(a) +Rational{Int64} + +julia> M = [1 2; 3.5 4]; + +julia> typeof(M) +Array{Float64,2} +``` +""" +typeof + +""" + isdefined(m::Module, s::Symbol) + isdefined(object, s::Symbol) + isdefined(object, index::Int) + +Tests whether a global variable or object field is defined. The arguments can be a module and a symbol +or a composite object and field name (as a symbol) or index. + +To test whether an array element is defined, use [`isassigned`](@ref) instead. + +See also [`@isdefined`](@ref). + +# Examples +```jldoctest +julia> isdefined(Base, :sum) +true + +julia> isdefined(Base, :NonExistentMethod) +false + +julia> a = 1//2; + +julia> isdefined(a, 2) +true + +julia> isdefined(a, 3) +false + +julia> isdefined(a, :num) +true + +julia> isdefined(a, :numerator) +false +``` +""" +isdefined + + +""" + Vector{T}(undef, n) + +Construct an uninitialized [`Vector{T}`](@ref) of length `n`. See [`undef`](@ref). + +# Examples +```julia-repl +julia> Vector{Float64}(undef, 3) +3-element Array{Float64,1}: + 6.90966e-310 + 6.90966e-310 + 6.90966e-310 +``` +""" +Vector{T}(::UndefInitializer, n) + +""" + Vector{T}(nothing, m) + +Construct a [`Vector{T}`](@ref) of length `m`, initialized with +[`nothing`](@ref) entries. Element type `T` must be able to hold +these values, i.e. `Nothing <: T`. + +# Examples +```jldoctest +julia> Vector{Union{Nothing, String}}(nothing, 2) +2-element Array{Union{Nothing, String},1}: + nothing + nothing +``` +""" +Vector{T}(::Nothing, n) + +""" + Vector{T}(missing, m) + +Construct a [`Vector{T}`](@ref) of length `m`, initialized with +[`missing`](@ref) entries. Element type `T` must be able to hold +these values, i.e. `Missing <: T`. + +# Examples +```jldoctest +julia> Vector{Union{Missing, String}}(missing, 2) +2-element Array{Union{Missing, String},1}: + missing + missing +``` +""" +Vector{T}(::Missing, n) + +""" + Matrix{T}(undef, m, n) + +Construct an uninitialized [`Matrix{T}`](@ref) of size `m`×`n`. See [`undef`](@ref). + +# Examples +```julia-repl +julia> Matrix{Float64}(undef, 2, 3) +2×3 Array{Float64,2}: + 6.93517e-310 6.93517e-310 6.93517e-310 + 6.93517e-310 6.93517e-310 1.29396e-320 +``` +""" +Matrix{T}(::UndefInitializer, m, n) + +""" + Matrix{T}(nothing, m, n) + +Construct a [`Matrix{T}`](@ref) of size `m`×`n`, initialized with +[`nothing`](@ref) entries. Element type `T` must be able to hold +these values, i.e. `Nothing <: T`. + +# Examples +```jldoctest +julia> Matrix{Union{Nothing, String}}(nothing, 2, 3) +2×3 Array{Union{Nothing, String},2}: + nothing nothing nothing + nothing nothing nothing +``` +""" +Matrix{T}(::Nothing, m, n) + +""" + Matrix{T}(missing, m, n) + +Construct a [`Matrix{T}`](@ref) of size `m`×`n`, initialized with +[`missing`](@ref) entries. Element type `T` must be able to hold +these values, i.e. `Missing <: T`. + +# Examples +```jldoctest +julia> Matrix{Union{Missing, String}}(missing, 2, 3) +2×3 Array{Union{Missing, String},2}: + missing missing missing + missing missing missing +``` +""" +Matrix{T}(::Missing, m, n) + +""" + Array{T}(undef, dims) + Array{T,N}(undef, dims) + +Construct an uninitialized `N`-dimensional [`Array`](@ref) +containing elements of type `T`. `N` can either be supplied explicitly, +as in `Array{T,N}(undef, dims)`, or be determined by the length or number of `dims`. +`dims` may be a tuple or a series of integer arguments corresponding to the lengths +in each dimension. If the rank `N` is supplied explicitly, then it must +match the length or number of `dims`. See [`undef`](@ref). + +# Examples +```julia-repl +julia> A = Array{Float64,2}(undef, 2, 3) # N given explicitly +2×3 Array{Float64,2}: + 6.90198e-310 6.90198e-310 6.90198e-310 + 6.90198e-310 6.90198e-310 0.0 + +julia> B = Array{Float64}(undef, 2) # N determined by the input +2-element Array{Float64,1}: + 1.87103e-320 + 0.0 +``` +""" +Array{T,N}(::UndefInitializer, dims) + +""" + Array{T}(nothing, dims) + Array{T,N}(nothing, dims) + +Construct an `N`-dimensional [`Array`](@ref) containing elements of type `T`, +initialized with [`nothing`](@ref) entries. Element type `T` must be able +to hold these values, i.e. `Nothing <: T`. + +# Examples +```jldoctest +julia> Array{Union{Nothing, String}}(nothing, 2) +2-element Array{Union{Nothing, String},1}: + nothing + nothing + +julia> Array{Union{Nothing, Int}}(nothing, 2, 3) +2×3 Array{Union{Nothing, Int64},2}: + nothing nothing nothing + nothing nothing nothing +``` +""" +Array{T,N}(::Nothing, dims) + + +""" + Array{T}(missing, dims) + Array{T,N}(missing, dims) + +Construct an `N`-dimensional [`Array`](@ref) containing elements of type `T`, +initialized with [`missing`](@ref) entries. Element type `T` must be able +to hold these values, i.e. `Missing <: T`. + +# Examples +```jldoctest +julia> Array{Union{Missing, String}}(missing, 2) +2-element Array{Union{Missing, String},1}: + missing + missing + +julia> Array{Union{Missing, Int}}(missing, 2, 3) +2×3 Array{Union{Missing, Int64},2}: + missing missing missing + missing missing missing +``` +""" +Array{T,N}(::Missing, dims) + +""" + UndefInitializer + +Singleton type used in array initialization, indicating the array-constructor-caller +would like an uninitialized array. See also [`undef`](@ref), +an alias for `UndefInitializer()`. + +# Examples +```julia-repl +julia> Array{Float64,1}(UndefInitializer(), 3) +3-element Array{Float64,1}: + 2.2752528595e-314 + 2.202942107e-314 + 2.275252907e-314 +``` +""" +UndefInitializer + +""" + undef + +Alias for `UndefInitializer()`, which constructs an instance of the singleton type +[`UndefInitializer`](@ref), used in array initialization to indicate the +array-constructor-caller would like an uninitialized array. + +# Examples +```julia-repl +julia> Array{Float64,1}(undef, 3) +3-element Array{Float64,1}: + 2.2752528595e-314 + 2.202942107e-314 + 2.275252907e-314 +``` +""" +undef + +""" + Ptr{T}() + +Creates a null pointer to type `T`. +""" +Ptr{T}() + +""" + +(x, y...) + +Addition operator. `x+y+z+...` calls this function with all arguments, i.e. `+(x, y, z, ...)`. + +# Examples +```jldoctest +julia> 1 + 20 + 4 +25 + +julia> +(1, 20, 4) +25 +``` +""" +(+)(x, y...) + +""" + -(x) + +Unary minus operator. + +# Examples +```jldoctest +julia> -1 +-1 + +julia> -(2) +-2 + +julia> -[1 2; 3 4] +2×2 Array{Int64,2}: + -1 -2 + -3 -4 +``` +""" +-(x) + +""" + -(x, y) + +Subtraction operator. + +# Examples +```jldoctest +julia> 2 - 3 +-1 + +julia> -(2, 4.5) +-2.5 +``` +""" +-(x, y) + +""" + *(x, y...) + +Multiplication operator. `x*y*z*...` calls this function with all arguments, i.e. `*(x, y, z, ...)`. + +# Examples +```jldoctest +julia> 2 * 7 * 8 +112 + +julia> *(2, 7, 8) +112 +``` +""" +(*)(x, y...) + +""" + /(x, y) + +Right division operator: multiplication of `x` by the inverse of `y` on the right. Gives +floating-point results for integer arguments. + +# Examples +```jldoctest +julia> 1/2 +0.5 + +julia> 4/2 +2.0 + +julia> 4.5/2 +2.25 +``` +""" +/(x, y) + +""" + ArgumentError(msg) + +The parameters to a function call do not match a valid signature. Argument `msg` is a +descriptive error string. +""" +ArgumentError + +""" + MethodError(f, args) + +A method with the required type signature does not exist in the given generic function. +Alternatively, there is no unique most-specific method. +""" +MethodError + +""" + AssertionError([msg]) + +The asserted condition did not evaluate to `true`. +Optional argument `msg` is a descriptive error string. + +# Examples +```jldoctest +julia> @assert false "this is not true" +ERROR: AssertionError: this is not true +``` + +`AssertionError` is usually thrown from [`@assert`](@ref). +""" +AssertionError + +""" + LoadError(file::AbstractString, line::Int, error) + +An error occurred while [`include`](@ref Base.include)ing, [`require`](@ref Base.require)ing, or [`using`](@ref) a file. The error specifics +should be available in the `.error` field. +""" +LoadError + +""" + InitError(mod::Symbol, error) + +An error occurred when running a module's `__init__` function. The actual error thrown is +available in the `.error` field. +""" +InitError + +""" + Any::DataType + +`Any` is the union of all types. It has the defining property `isa(x, Any) == true` for any `x`. `Any` therefore +describes the entire universe of possible values. For example `Integer` is a subset of `Any` that includes `Int`, +`Int8`, and other integer types. +""" +Any + +""" + Union{} + +`Union{}`, the empty [`Union`](@ref) of types, is the type that has no values. That is, it has the defining +property `isa(x, Union{}) == false` for any `x`. `Base.Bottom` is defined as its alias and the type of `Union{}` +is `Core.TypeofBottom`. + +# Examples +```jldoctest +julia> isa(nothing, Union{}) +false +``` +""" +kw"Union{}", Base.Bottom + +""" + Union{Types...} + +A type union is an abstract type which includes all instances of any of its argument types. The empty +union [`Union{}`](@ref) is the bottom type of Julia. + +# Examples +```jldoctest +julia> IntOrString = Union{Int,AbstractString} +Union{Int64, AbstractString} + +julia> 1 :: IntOrString +1 + +julia> "Hello!" :: IntOrString +"Hello!" + +julia> 1.0 :: IntOrString +ERROR: TypeError: in typeassert, expected Union{Int64, AbstractString}, got Float64 +``` +""" +Union + + +""" + UnionAll + +A union of types over all values of a type parameter. `UnionAll` is used to describe parametric types +where the values of some parameters are not known. + +# Examples +```jldoctest +julia> typeof(Vector) +UnionAll + +julia> typeof(Vector{Int}) +DataType +``` +""" +UnionAll + +""" + :: + +With the `::`-operator type annotations are attached to expressions and variables in programs. +See the manual section on [Type Declarations](@ref). + +Outside of declarations `::` is used to assert that expressions and variables in programs have a given type. + +# Examples +```jldoctest +julia> (1+2)::AbstractFloat +ERROR: TypeError: typeassert: expected AbstractFloat, got Int64 + +julia> (1+2)::Int +3 +``` +""" +kw"::" + +""" + Vararg{T,N} + +The last parameter of a tuple type [`Tuple`](@ref) can be the special type `Vararg`, which denotes any +number of trailing elements. The type `Vararg{T,N}` corresponds to exactly `N` elements of type `T`. +`Vararg{T}` corresponds to zero or more elements of type `T`. `Vararg` tuple types are used to represent the +arguments accepted by varargs methods (see the section on [Varargs Functions](@ref) in the manual.) + +# Examples +```jldoctest +julia> mytupletype = Tuple{AbstractString,Vararg{Int}} +Tuple{AbstractString,Vararg{Int64,N} where N} + +julia> isa(("1",), mytupletype) +true + +julia> isa(("1",1), mytupletype) +true + +julia> isa(("1",1,2), mytupletype) +true + +julia> isa(("1",1,2,3.0), mytupletype) +false +``` +""" +Vararg + +""" + Tuple{Types...} + +Tuples are an abstraction of the arguments of a function – without the function itself. The salient aspects of +a function's arguments are their order and their types. Therefore a tuple type is similar to a parameterized +immutable type where each parameter is the type of one field. Tuple types may have any number of parameters. + +Tuple types are covariant in their parameters: `Tuple{Int}` is a subtype of `Tuple{Any}`. Therefore `Tuple{Any}` +is considered an abstract type, and tuple types are only concrete if their parameters are. Tuples do not have +field names; fields are only accessed by index. + +See the manual section on [Tuple Types](@ref). +""" +Tuple + +""" + typeassert(x, type) + +Throw a [`TypeError`](@ref) unless `x isa type`. +The syntax `x::type` calls this function. + +# Examples +```jldoctest +julia> typeassert(2.5, Int) +ERROR: TypeError: in typeassert, expected Int64, got Float64 +Stacktrace: +[...] +``` +""" +typeassert + +""" + getproperty(value, name::Symbol) + +The syntax `a.b` calls `getproperty(a, :b)`. + +# Examples +```jldoctest +julia> struct MyType + x + end + +julia> function Base.getproperty(obj::MyType, sym::Symbol) + if sym === :special + return obj.x + 1 + else # fallback to getfield + return getfield(obj, sym) + end + end + +julia> obj = MyType(1); + +julia> obj.special +2 + +julia> obj.x +1 +``` + +See also [`propertynames`](@ref Base.propertynames) and +[`setproperty!`](@ref Base.setproperty!). +""" +Base.getproperty + +""" + setproperty!(value, name::Symbol, x) + +The syntax `a.b = c` calls `setproperty!(a, :b, c)`. + +See also [`propertynames`](@ref Base.propertynames) and +[`getproperty`](@ref Base.getproperty). +""" +Base.setproperty! + +""" + StridedArray{T, N} + +An `N` dimensional *strided* array with elements of type `T`. These arrays follow +the [strided array interface](@ref man-interface-strided-arrays). If `A` is a +`StridedArray`, then its elements are stored in memory with offsets, which may +vary between dimensions but are constant within a dimension. For example, `A` could +have stride 2 in dimension 1, and stride 3 in dimension 2. Incrementing `A` along +dimension `d` jumps in memory by [`strides(A, d)`] slots. Strided arrays are +particularly important and useful because they can sometimes be passed directly +as pointers to foreign language libraries like BLAS. +""" +StridedArray + +""" + StridedVector{T} + +One dimensional [`StridedArray`](@ref) with elements of type `T`. +""" +StridedVector + +""" + StridedMatrix{T} + +Two dimensional [`StridedArray`](@ref) with elements of type `T`. +""" +StridedMatrix + +""" + StridedVecOrMat{T} + +Union type of [`StridedVector`](@ref) and [`StridedMatrix`](@ref) with elements of type `T`. +""" +StridedVecOrMat + +""" + Module + +A `Module` is a separate global variable workspace. See [`module`](@ref) and the [manual section about modules](@ref modules) for details. +""" +Module + +""" + Core + +`Core` is the module that contains all identifiers considered "built in" to the language, i.e. part of the core language and not libraries. Every module implicitly specifies `using Core`, since you can't do anything without those definitions. +""" +Core.Core + +""" + Main + +`Main` is the top-level module, and Julia starts with `Main` set as the current module. Variables defined at the prompt go in `Main`, and `varinfo` lists variables in `Main`. +```jldoctest +julia> @__MODULE__ +Main +``` +""" +Main.Main + +""" + Base + +The base library of Julia. `Base` is a module that contains basic functionality (the contents of `base/`). All modules implicitly contain `using Base`, since this is needed in the vast majority of cases. +""" +Base.Base + +""" + QuoteNode + +A quoted piece of code, that does not support interpolation. See the [manual section about QuoteNodes](@ref man-quote-node) for details. +""" +QuoteNode + +end diff --git a/base/docs/bindings.jl b/base/docs/bindings.jl new file mode 100644 index 0000000..e007d79 --- /dev/null +++ b/base/docs/bindings.jl @@ -0,0 +1,46 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +export @var + +struct Binding + mod::Module + var::Symbol + + function Binding(m::Module, v::Symbol) + # Normalise the binding module for module symbols so that: + # Binding(Base, :Base) === Binding(Main, :Base) + m = nameof(m) === v ? parentmodule(m) : m + new(Base.binding_module(m, v), v) + end +end + +bindingexpr(x) = Expr(:call, Binding, splitexpr(x)...) + +defined(b::Binding) = isdefined(b.mod, b.var) +resolve(b::Binding) = getfield(b.mod, b.var) + +function splitexpr(x::Expr) + isexpr(x, :macrocall) ? splitexpr(x.args[1]) : + isexpr(x, :.) ? (x.args[1], x.args[2]) : + error("Invalid @var syntax `$x`.") +end +splitexpr(s::Symbol) = Expr(:macrocall, getfield(Base, Symbol("@__MODULE__")), nothing), quot(s) +splitexpr(other) = error("Invalid @var syntax `$other`.") + +macro var(x) + esc(bindingexpr(x)) +end + +function Base.show(io::IO, b::Binding) + if b.mod === Main + print(io, b.var) + else + print(io, b.mod, '.', Base.isoperator(b.var) ? ":" : "", b.var) + end +end + +aliasof(b::Binding) = defined(b) ? (a = aliasof(resolve(b), b); defined(a) ? a : b) : b +aliasof(d::DataType, b) = Binding(d.name.module, d.name.name) +aliasof(λ::Function, b) = (m = typeof(λ).name.mt; Binding(m.module, m.name)) +aliasof(m::Module, b) = Binding(m, nameof(m)) +aliasof(other, b) = b diff --git a/base/docs/core.jl b/base/docs/core.jl new file mode 100644 index 0000000..718e499 --- /dev/null +++ b/base/docs/core.jl @@ -0,0 +1,33 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +module CoreDocs + +import ..esc, ..push!, ..getindex, ..unsafe_load, ..Csize_t, ..@nospecialize + +@nospecialize # don't specialize on any arguments of the methods declared herein + +function doc!(source::LineNumberNode, mod::Module, str, ex) + push!(DOCS, Core.svec(mod, ex, str, source.file, source.line)) + nothing +end +const DOCS = Array{Core.SimpleVector,1}() + +isexpr(x, h::Symbol) = isa(x, Expr) && x.head === h + +lazy_iterpolate(s::AbstractString) = Expr(:call, Core.svec, s) +lazy_iterpolate(x) = isexpr(x, :string) ? Expr(:call, Core.svec, x.args...) : x + +function docm(source::LineNumberNode, mod::Module, str, x) + out = Expr(:call, doc!, QuoteNode(source), mod, lazy_iterpolate(str), QuoteNode(x)) + if isexpr(x, :module) + out = Expr(:toplevel, out, x) + elseif isexpr(x, :call) + else + out = Expr(:block, x, out) + end + return esc(out) +end +docm(source::LineNumberNode, mod::Module, x) = + isexpr(x, :->) ? docm(source, mod, x.args[1], x.args[2].args[2]) : error("invalid '@doc'.") + +end diff --git a/base/docs/utils.jl b/base/docs/utils.jl new file mode 100644 index 0000000..b1f3327 --- /dev/null +++ b/base/docs/utils.jl @@ -0,0 +1,93 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Text / HTML objects + +import .Base: print, show, ==, hash + +export HTML, @html_str + +export HTML, Text + +""" +`HTML(s)`: Create an object that renders `s` as html. + + HTML("
foo
") + +You can also use a stream for large amounts of data: + + HTML() do io + println(io, "
foo
") + end +""" +mutable struct HTML{T} + content::T +end + +function HTML(xs...) + HTML() do io + for x in xs + print(io, x) + end + end +end + +show(io::IO, ::MIME"text/html", h::HTML) = print(io, h.content) +show(io::IO, ::MIME"text/html", h::HTML{<:Function}) = h.content(io) + +""" + @html_str -> Docs.HTML + +Create an `HTML` object from a literal string. +""" +macro html_str(s) + :(HTML($s)) +end + +function catdoc(xs::HTML...) + HTML() do io + for x in xs + show(io, MIME"text/html"(), x) + end + end +end + +export Text, @text_str + +""" +`Text(s)`: Create an object that renders `s` as plain text. + + Text("foo") + +You can also use a stream for large amounts of data: + + Text() do io + println(io, "foo") + end +""" +mutable struct Text{T} + content::T +end + +print(io::IO, t::Text) = print(io, t.content) +print(io::IO, t::Text{<:Function}) = t.content(io) +show(io::IO, t::Text) = print(io, t) + +==(t1::T, t2::T) where {T<:Union{HTML,Text}} = t1.content == t2.content +hash(t::T, h::UInt) where {T<:Union{HTML,Text}} = hash(T, hash(t.content, h)) + +""" + @text_str -> Docs.Text + +Create a `Text` object from a literal string. +""" +macro text_str(s) + :(Text($s)) +end + +function catdoc(xs::Text...) + Text() do io + for x in xs + show(io, MIME"text/plain"(), x) + end + end +end diff --git a/base/download.jl b/base/download.jl new file mode 100644 index 0000000..fc16633 --- /dev/null +++ b/base/download.jl @@ -0,0 +1,97 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# file downloading + +if Sys.iswindows() + function download_powershell(url::AbstractString, filename::AbstractString) + ps = joinpath(get(ENV, "SYSTEMROOT", "C:\\Windows"), "System32\\WindowsPowerShell\\v1.0\\powershell.exe") + tls12 = "[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12" + client = "New-Object System.Net.Webclient" + # in the following we escape ' with '' (see https://ss64.com/ps/syntax-esc.html) + downloadfile = "($client).DownloadFile('$(replace(url, "'" => "''"))', '$(replace(filename, "'" => "''"))')" + # PowerShell v3 or later is required for Tls12 + proc = run(pipeline(`$ps -Version 3 -NoProfile -Command "$tls12; $downloadfile"`; stderr=stderr); wait=false) + if !success(proc) + if proc.exitcode % Int32 == -393216 + # appears to be "wrong version" exit code, based on + # https://docs.microsoft.com/en-us/azure/cloud-services/cloud-services-startup-tasks-common + @error "Downloading files requires Windows Management Framework 3.0 or later." + end + pipeline_error(proc) + end + return filename + end +end + +function find_curl() + if Sys.isapple() && Sys.isexecutable("/usr/bin/curl") + "/usr/bin/curl" + elseif Sys.iswindows() && Sys.isexecutable(joinpath(get(ENV, "SYSTEMROOT", "C:\\Windows"), "System32\\curl.exe")) + joinpath(get(ENV, "SYSTEMROOT", "C:\\Windows"), "System32\\curl.exe") + elseif !Sys.iswindows() && Sys.which("curl") !== nothing + "curl" + else + nothing + end +end + +function download_curl(curl_exe::AbstractString, url::AbstractString, filename::AbstractString) + err = PipeBuffer() + process = run(pipeline(`$curl_exe -s -S -g -L -f -o $filename $url`, stderr=err), wait=false) + if !success(process) + error_msg = readline(err) + @error "Download failed: $error_msg" + pipeline_error(process) + end + return filename +end + +const DOWNLOAD_HOOKS = Callable[] + +function download_url(url::String) + for hook in DOWNLOAD_HOOKS + url = String(hook(url)::AbstractString) + end + return url +end + +function download(url::AbstractString, filename::AbstractString) + url = download_url(url) + curl_exe = find_curl() + if curl_exe !== nothing + return download_curl(curl_exe, url, filename) + elseif Sys.iswindows() + return download_powershell(url, filename) + elseif Sys.which("wget") !== nothing + try + run(`wget -O $filename $url`) + catch + rm(filename, force=true) # wget always creates a file + rethrow() + end + elseif Sys.which("fetch") !== nothing + run(`fetch -f $filename $url`) + else + error("No download agent available; install curl, wget, or fetch.") + end + return filename +end + +function download(url::AbstractString) + filename = tempname() + download(url, filename) +end + +""" + download(url::AbstractString, [localfile::AbstractString]) + +Download a file from the given url, optionally renaming it to the given local file name. If +no filename is given this will download into a randomly-named file in your temp directory. +Note that this function relies on the availability of external tools such as `curl`, `wget` +or `fetch` to download the file and is provided for convenience. For production use or +situations in which more options are needed, please use a package that provides the desired +functionality instead. + +Returns the filename of the downloaded file. +""" +download(url, filename) diff --git a/base/env.jl b/base/env.jl new file mode 100644 index 0000000..8f5256f --- /dev/null +++ b/base/env.jl @@ -0,0 +1,168 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +if Sys.iswindows() + const ERROR_ENVVAR_NOT_FOUND = UInt32(203) + + _getenvlen(var::Vector{UInt16}) = ccall(:GetEnvironmentVariableW,stdcall,UInt32,(Ptr{UInt16},Ptr{UInt16},UInt32),var,C_NULL,0) + _hasenv(s::Vector{UInt16}) = _getenvlen(s) != 0 || Libc.GetLastError() != ERROR_ENVVAR_NOT_FOUND + _hasenv(s::AbstractString) = _hasenv(cwstring(s)) + + function access_env(onError::Function, str::AbstractString) + var = cwstring(str) + len = _getenvlen(var) + if len == 0 + return Libc.GetLastError() != ERROR_ENVVAR_NOT_FOUND ? "" : onError(str) + end + val = zeros(UInt16,len) + ret = ccall(:GetEnvironmentVariableW,stdcall,UInt32,(Ptr{UInt16},Ptr{UInt16},UInt32),var,val,len) + windowserror(:getenv, (ret == 0 && len != 1) || ret != len-1 || val[end] != 0) + pop!(val) # NUL + return transcode(String, val) + end + + function _setenv(svar::AbstractString, sval::AbstractString, overwrite::Bool=true) + var = cwstring(svar) + val = cwstring(sval) + if overwrite || !_hasenv(var) + ret = ccall(:SetEnvironmentVariableW,stdcall,Int32,(Ptr{UInt16},Ptr{UInt16}),var,val) + windowserror(:setenv, ret == 0) + end + end + + function _unsetenv(svar::AbstractString) + var = cwstring(svar) + ret = ccall(:SetEnvironmentVariableW,stdcall,Int32,(Ptr{UInt16},Ptr{UInt16}),var,C_NULL) + windowserror(:setenv, ret == 0) + end +else # !windows + _getenv(var::AbstractString) = ccall(:getenv, Cstring, (Cstring,), var) + _hasenv(s::AbstractString) = _getenv(s) != C_NULL + + function access_env(onError::Function, var::AbstractString) + val = _getenv(var) + val == C_NULL ? onError(var) : unsafe_string(val) + end + + function _setenv(var::AbstractString, val::AbstractString, overwrite::Bool=true) + ret = ccall(:setenv, Int32, (Cstring,Cstring,Int32), var, val, overwrite) + systemerror(:setenv, ret != 0) + end + + function _unsetenv(var::AbstractString) + ret = ccall(:unsetenv, Int32, (Cstring,), var) + systemerror(:unsetenv, ret != 0) + end +end # os test + +## ENV: hash interface ## + +""" + EnvDict() -> EnvDict + +A singleton of this type provides a hash table interface to environment variables. +""" +struct EnvDict <: AbstractDict{String,String}; end + +""" + ENV + +Reference to the singleton `EnvDict`, providing a dictionary interface to system environment +variables. + +(On Windows, system environment variables are case-insensitive, and `ENV` correspondingly converts +all keys to uppercase for display, iteration, and copying. Portable code should not rely on the +ability to distinguish variables by case, and should beware that setting an ostensibly lowercase +variable may result in an uppercase `ENV` key.) +""" +const ENV = EnvDict() + +getindex(::EnvDict, k::AbstractString) = access_env(k->throw(KeyError(k)), k) +get(::EnvDict, k::AbstractString, def) = access_env(k->def, k) +get(f::Callable, ::EnvDict, k::AbstractString) = access_env(k->f(), k) +in(k::AbstractString, ::KeySet{String, EnvDict}) = _hasenv(k) +pop!(::EnvDict, k::AbstractString) = (v = ENV[k]; _unsetenv(k); v) +pop!(::EnvDict, k::AbstractString, def) = haskey(ENV,k) ? pop!(ENV,k) : def +delete!(::EnvDict, k::AbstractString) = (_unsetenv(k); ENV) +setindex!(::EnvDict, v, k::AbstractString) = _setenv(k,string(v)) +push!(::EnvDict, kv::Pair{<:AbstractString}) = setindex!(ENV, kv.second, kv.first) + +if Sys.iswindows() + GESW() = (pos = ccall(:GetEnvironmentStringsW,stdcall,Ptr{UInt16},()); (pos,pos)) + function winuppercase(s::AbstractString) + isempty(s) && return s + LOCALE_INVARIANT = 0x0000007f + LCMAP_UPPERCASE = 0x00000200 + ws = transcode(UInt16, String(s)) + result = ccall(:LCMapStringW, stdcall, Cint, (UInt32, UInt32, Ptr{UInt16}, Cint, Ptr{UInt16}, Cint), + LOCALE_INVARIANT, LCMAP_UPPERCASE, ws, length(ws), ws, length(ws)) + systemerror(:LCMapStringW, iszero(result)) + return transcode(String, ws) + end + function iterate(hash::EnvDict, block::Tuple{Ptr{UInt16},Ptr{UInt16}} = GESW()) + if unsafe_load(block[1]) == 0 + ccall(:FreeEnvironmentStringsW, stdcall, Int32, (Ptr{UInt16},), block[2]) + return nothing + end + pos = block[1] + blk = block[2] + len = ccall(:wcslen, UInt, (Ptr{UInt16},), pos) + buf = Vector{UInt16}(undef, len) + GC.@preserve buf unsafe_copyto!(pointer(buf), pos, len) + env = transcode(String, buf) + m = match(r"^(=?[^=]+)=(.*)$"s, env) + if m === nothing + error("malformed environment entry: $env") + end + return (Pair{String,String}(winuppercase(m.captures[1]), m.captures[2]), (pos+(len+1)*2, blk)) + end +else # !windows + function iterate(::EnvDict, i=0) + env = ccall(:jl_environ, Any, (Int32,), i) + env === nothing && return nothing + env = env::String + m = match(r"^(.*?)=(.*)$"s, env) + if m === nothing + error("malformed environment entry: $env") + end + return (Pair{String,String}(m.captures[1], m.captures[2]), i+1) + end +end # os-test + +#TODO: Make these more efficient +function length(::EnvDict) + i = 0 + for (k,v) in ENV + i += 1 + end + return i +end + +function show(io::IO, ::EnvDict) + for (k,v) = ENV + println(io, "$k=$v") + end +end + +""" + withenv(f::Function, kv::Pair...) + +Execute `f` in an environment that is temporarily modified (not replaced as in `setenv`) +by zero or more `"var"=>val` arguments `kv`. `withenv` is generally used via the +`withenv(kv...) do ... end` syntax. A value of `nothing` can be used to temporarily unset an +environment variable (if it is set). When `withenv` returns, the original environment has +been restored. +""" +function withenv(f::Function, keyvals::Pair{T}...) where T<:AbstractString + old = Dict{T,Any}() + for (key,val) in keyvals + old[key] = get(ENV,key,nothing) + val !== nothing ? (ENV[key]=val) : delete!(ENV, key) + end + try f() + finally + for (key,val) in old + val !== nothing ? (ENV[key]=val) : delete!(ENV, key) + end + end +end +withenv(f::Function) = f() # handle empty keyvals case; see #10853 diff --git a/base/error.jl b/base/error.jl new file mode 100644 index 0000000..76964c6 --- /dev/null +++ b/base/error.jl @@ -0,0 +1,268 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# pseudo-definitions to show how everything behaves +# +# throw(label, val) = # throw a value to a dynamically enclosing block +# +# function rethrow(val) +# global current_exception = val +# throw(current_handler(), current_exception) +# end +# +# rethrow() = rethrow(current_exception) +# +# function throw(val) +# global catch_backtrace = backtrace() +# rethrow(val) +# end + +""" + throw(e) + +Throw an object as an exception. +""" +throw + +## native julia error handling ## + +""" + error(message::AbstractString) + +Raise an `ErrorException` with the given message. +""" +error(s::AbstractString) = throw(ErrorException(s)) + +""" + error(msg...) + +Raise an `ErrorException` with the given message. +""" +function error(s::Vararg{Any,N}) where {N} + @_noinline_meta + throw(ErrorException(Main.Base.string(s...))) +end + +""" + rethrow([e]) + +Throw an object without changing the current exception backtrace. The default argument is +the current exception (if called within a `catch` block). +""" +rethrow() = ccall(:jl_rethrow, Bottom, ()) +rethrow(e) = ccall(:jl_rethrow_other, Bottom, (Any,), e) + +struct InterpreterIP + code::Union{CodeInfo,Core.MethodInstance,Nothing} + stmt::Csize_t +end + +# convert dual arrays (ips, interpreter_frames) to a single array of locations +function _reformat_bt(bt, bt2) + ret = Vector{Union{InterpreterIP,Ptr{Cvoid}}}() + i, j = 1, 1 + while i <= length(bt) + ip = bt[i]::Ptr{Cvoid} + if UInt(ip) == (-1 % UInt) + # The next one is really a CodeInfo + push!(ret, InterpreterIP( + bt2[j], + bt[i+2])) + j += 1 + i += 3 + else + push!(ret, Ptr{Cvoid}(ip)) + i += 1 + end + end + ret +end + +function backtrace end + +""" + catch_backtrace() + +Get the backtrace of the current exception, for use within `catch` blocks. +""" +function catch_backtrace() + bt = Ref{Any}(nothing) + bt2 = Ref{Any}(nothing) + ccall(:jl_get_backtrace, Cvoid, (Ref{Any}, Ref{Any}), bt, bt2) + return _reformat_bt(bt[], bt2[]) +end + +""" + catch_stack(task=current_task(); [inclue_bt=true]) + +Get the stack of exceptions currently being handled. For nested catch blocks +there may be more than one current exception in which case the most recently +thrown exception is last in the stack. The stack is returned as a Vector of +`(exception,backtrace)` pairs, or a Vector of exceptions if `include_bt` is +false. + +Explicitly passing `task` will return the current exception stack on an +arbitrary task. This is useful for inspecting tasks which have failed due to +uncaught exceptions. + +!!! compat "Julia 1.1" + This function is experimental in Julia 1.1 and will likely be renamed in a + future release (see https://github.com/JuliaLang/julia/pull/29901). +""" +function catch_stack(task=current_task(); include_bt=true) + raw = ccall(:jl_get_excstack, Any, (Any,Cint,Cint), task, include_bt, typemax(Cint)) + formatted = Any[] + stride = include_bt ? 3 : 1 + for i = reverse(1:stride:length(raw)) + e = raw[i] + push!(formatted, include_bt ? (e,Base._reformat_bt(raw[i+1],raw[i+2])) : e) + end + formatted +end + +## keyword arg lowering generates calls to this ## +function kwerr(kw, args::Vararg{Any,N}) where {N} + @_noinline_meta + throw(MethodError(typeof(args[1]).name.mt.kwsorter, (kw,args...))) +end + +## system error handling ## +""" + systemerror(sysfunc, iftrue) + +Raises a `SystemError` for `errno` with the descriptive string `sysfunc` if `iftrue` is `true` +""" +systemerror(p, b::Bool; extrainfo=nothing) = b ? throw(Main.Base.SystemError(string(p), Libc.errno(), extrainfo)) : nothing + + +## system errors from Windows API functions +struct WindowsErrorInfo + errnum::UInt32 + extrainfo +end +""" + windowserror(sysfunc, iftrue) + +Like [`systemerror`](@ref), but for Windows API functions that use [`GetLastError`](@ref) instead +of setting [`errno`](@ref). +""" +windowserror(p, b::Bool; extrainfo=nothing) = b ? throw(Main.Base.SystemError(string(p), Libc.errno(), WindowsErrorInfo(Libc.GetLastError(), extrainfo))) : nothing + + +## assertion macro ## + + +""" + @assert cond [text] + +Throw an [`AssertionError`](@ref) if `cond` is `false`. Preferred syntax for writing assertions. +Message `text` is optionally displayed upon assertion failure. + +!!! warning + An assert might be disabled at various optimization levels. + Assert should therefore only be used as a debugging tool + and not used for authentication verification (e.g., verifying passwords), + nor should side effects needed for the function to work correctly + be used inside of asserts. + +# Examples +```jldoctest +julia> @assert iseven(3) "3 is an odd number!" +ERROR: AssertionError: 3 is an odd number! + +julia> @assert isodd(3) "What even are numbers?" +``` +""" +macro assert(ex, msgs...) + msg = isempty(msgs) ? ex : msgs[1] + if isa(msg, AbstractString) + msg = msg # pass-through + elseif !isempty(msgs) && (isa(msg, Expr) || isa(msg, Symbol)) + # message is an expression needing evaluating + msg = :(Main.Base.string($(esc(msg)))) + elseif isdefined(Main, :Base) && isdefined(Main.Base, :string) && applicable(Main.Base.string, msg) + msg = Main.Base.string(msg) + else + # string() might not be defined during bootstrap + msg = quote + msg = $(Expr(:quote,msg)) + isdefined(Main, :Base) ? Main.Base.string(msg) : + (Core.println(msg); "Error during bootstrap. See stdout.") + end + end + return :($(esc(ex)) ? $(nothing) : throw(AssertionError($msg))) +end + +struct ExponentialBackOff + n::Int + first_delay::Float64 + max_delay::Float64 + factor::Float64 + jitter::Float64 + + function ExponentialBackOff(n, first_delay, max_delay, factor, jitter) + all(x->x>=0, (n, first_delay, max_delay, factor, jitter)) || error("all inputs must be non-negative") + new(n, first_delay, max_delay, factor, jitter) + end +end + +""" + ExponentialBackOff(; n=1, first_delay=0.05, max_delay=10.0, factor=5.0, jitter=0.1) + +A [`Float64`](@ref) iterator of length `n` whose elements exponentially increase at a +rate in the interval `factor` * (1 ± `jitter`). The first element is +`first_delay` and all elements are clamped to `max_delay`. +""" +ExponentialBackOff(; n=1, first_delay=0.05, max_delay=10.0, factor=5.0, jitter=0.1) = + ExponentialBackOff(n, first_delay, max_delay, factor, jitter) +function iterate(ebo::ExponentialBackOff, state= (ebo.n, min(ebo.first_delay, ebo.max_delay))) + state[1] < 1 && return nothing + next_n = state[1]-1 + curr_delay = state[2] + next_delay = min(ebo.max_delay, state[2] * ebo.factor * (1.0 - ebo.jitter + (rand(Float64) * 2.0 * ebo.jitter))) + (curr_delay, (next_n, next_delay)) +end +length(ebo::ExponentialBackOff) = ebo.n +eltype(::Type{ExponentialBackOff}) = Float64 + +""" + retry(f; delays=ExponentialBackOff(), check=nothing) -> Function + +Return an anonymous function that calls function `f`. If an exception arises, +`f` is repeatedly called again, each time `check` returns `true`, after waiting the +number of seconds specified in `delays`. `check` should input `delays`'s +current state and the `Exception`. + +!!! compat "Julia 1.2" + Before Julia 1.2 this signature was restricted to `f::Function`. + +# Examples +```julia +retry(f, delays=fill(5.0, 3)) +retry(f, delays=rand(5:10, 2)) +retry(f, delays=Base.ExponentialBackOff(n=3, first_delay=5, max_delay=1000)) +retry(http_get, check=(s,e)->e.status == "503")(url) +retry(read, check=(s,e)->isa(e, IOError))(io, 128; all=false) +``` +""" +function retry(f; delays=ExponentialBackOff(), check=nothing) + (args...; kwargs...) -> begin + y = iterate(delays) + while y !== nothing + (delay, state) = y + try + return f(args...; kwargs...) + catch e + y === nothing && rethrow() + if check !== nothing + result = check(state, e) + state, retry_or_not = length(result) == 2 ? result : (state, result) + retry_or_not || rethrow() + end + end + sleep(delay) + y = iterate(delays, state) + end + # When delays is out, just run the function without try/catch + return f(args...; kwargs...) + end +end diff --git a/base/errorshow.jl b/base/errorshow.jl new file mode 100644 index 0000000..51b3d11 --- /dev/null +++ b/base/errorshow.jl @@ -0,0 +1,652 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" + showerror(io, e) + +Show a descriptive representation of an exception object `e`. +This method is used to display the exception after a call to [`throw`](@ref). + +# Examples +```jldoctest +julia> struct MyException <: Exception + msg::AbstractString + end + +julia> function Base.showerror(io::IO, err::MyException) + print(io, "MyException: ") + print(io, err.msg) + end + +julia> err = MyException("test exception") +MyException("test exception") + +julia> sprint(showerror, err) +"MyException: test exception" + +julia> throw(MyException("test exception")) +ERROR: MyException: test exception +``` +""" +showerror(io::IO, ex) = show(io, ex) + +function showerror(io::IO, ex::BoundsError) + print(io, "BoundsError") + if isdefined(ex, :a) + print(io, ": attempt to access ") + summary(io, ex.a) + if isdefined(ex, :i) + !isa(ex.a, AbstractArray) && print(io, "\n ") + print(io, " at index [") + if isa(ex.i, AbstractRange) + print(io, ex.i) + else + join(io, ex.i, ", ") + end + print(io, ']') + end + end +end + +function showerror(io::IO, ex::TypeError) + print(io, "TypeError: ") + if ex.expected === Bool + print(io, "non-boolean (", typeof(ex.got), ") used in boolean context") + else + if isvarargtype(ex.got) + targs = (ex.got,) + elseif isa(ex.got, Type) + targs = ("Type{", ex.got, "}") + else + targs = (typeof(ex.got),) + end + if ex.context == "" + ctx = "in $(ex.func)" + elseif ex.func === Symbol("keyword argument") + ctx = "in keyword argument $(ex.context)" + else + ctx = "in $(ex.func), in $(ex.context)" + end + print(io, ctx, ", expected ", ex.expected, ", got ", targs...) + end +end + +function showerror(io::IO, ex, bt; backtrace=true) + try + with_output_color(get(io, :color, false) ? error_color() : :nothing, io) do io + showerror(io, ex) + end + finally + backtrace && show_backtrace(io, bt) + end +end + +function showerror(io::IO, ex::LoadError, bt; backtrace=true) + print(io, "LoadError: ") + showerror(io, ex.error, bt, backtrace=backtrace) + print(io, "\nin expression starting at $(ex.file):$(ex.line)") +end +showerror(io::IO, ex::LoadError) = showerror(io, ex, []) + +function showerror(io::IO, ex::InitError, bt; backtrace=true) + print(io, "InitError: ") + showerror(io, ex.error, bt, backtrace=backtrace) + print(io, "\nduring initialization of module ", ex.mod) +end +showerror(io::IO, ex::InitError) = showerror(io, ex, []) + +function showerror(io::IO, ex::DomainError) + if isa(ex.val, AbstractArray) + compact = get(io, :compact, true) + limit = get(io, :limit, true) + print(IOContext(io, :compact => compact, :limit => limit), + "DomainError with ", ex.val) + else + print(io, "DomainError with ", ex.val) + end + if isdefined(ex, :msg) + print(io, ":\n", ex.msg) + end + nothing +end + +function showerror(io::IO, ex::SystemError) + if @static(Sys.iswindows() ? ex.extrainfo isa WindowsErrorInfo : false) + errstring = Libc.FormatMessage(ex.extrainfo.errnum) + extrainfo = ex.extrainfo.extrainfo + else + errstring = Libc.strerror(ex.errnum) + extrainfo = ex.extrainfo + end + if extrainfo === nothing + print(io, "SystemError: $(ex.prefix): ", errstring) + else + print(io, "SystemError (with $extrainfo): $(ex.prefix): ", errstring) + end +end + +showerror(io::IO, ::DivideError) = print(io, "DivideError: integer division error") +showerror(io::IO, ::StackOverflowError) = print(io, "StackOverflowError:") +showerror(io::IO, ::UndefRefError) = print(io, "UndefRefError: access to undefined reference") +showerror(io::IO, ::EOFError) = print(io, "EOFError: read end of file") +function showerror(io::IO, ex::ErrorException) + print(io, ex.msg) + if ex.msg == "type String has no field data" + println(io) + print(io, "Use `codeunits(str)` instead.") + end +end +showerror(io::IO, ex::KeyError) = (print(io, "KeyError: key "); + show(io, ex.key); + print(io, " not found")) +showerror(io::IO, ex::InterruptException) = print(io, "InterruptException:") +showerror(io::IO, ex::ArgumentError) = print(io, "ArgumentError: ", ex.msg) +showerror(io::IO, ex::AssertionError) = print(io, "AssertionError: ", ex.msg) +showerror(io::IO, ex::OverflowError) = print(io, "OverflowError: ", ex.msg) + +showerror(io::IO, ex::UndefKeywordError) = + print(io, "UndefKeywordError: keyword argument $(ex.var) not assigned") + +function showerror(io::IO, ex::UndefVarError) + if ex.var in [:UTF16String, :UTF32String, :WString, :utf16, :utf32, :wstring, :RepString] + return showerror(io, ErrorException(""" + `$(ex.var)` has been moved to the package LegacyStrings.jl: + Run Pkg.add("LegacyStrings") to install LegacyStrings on Julia v0.5-; + Then do `using LegacyStrings` to get `$(ex.var)`. + """)) + end + print(io, "UndefVarError: $(ex.var) not defined") +end + +function showerror(io::IO, ex::InexactError) + print(io, "InexactError: ", ex.func, '(') + nameof(ex.T) === ex.func || print(io, ex.T, ", ") + print(io, ex.val, ')') +end + +typesof(args...) = Tuple{Any[ Core.Typeof(a) for a in args ]...} + +function showerror(io::IO, ex::MethodError) + # ex.args is a tuple type if it was thrown from `invoke` and is + # a tuple of the arguments otherwise. + is_arg_types = isa(ex.args, DataType) + arg_types = is_arg_types ? ex.args : typesof(ex.args...) + f = ex.f + meth = methods_including_ambiguous(f, arg_types) + if length(meth) > 1 + return showerror_ambiguous(io, meth, f, arg_types) + end + arg_types_param::SimpleVector = arg_types.parameters + print(io, "MethodError: ") + ft = typeof(f) + name = ft.name.mt.name + f_is_function = false + kwargs = () + if startswith(string(ft.name.name), "#kw#") + f = ex.args[2] + ft = typeof(f) + name = ft.name.mt.name + arg_types_param = arg_types_param[3:end] + kwargs = pairs(ex.args[1]) + ex = MethodError(f, ex.args[3:end]) + end + if f == Base.convert && length(arg_types_param) == 2 && !is_arg_types + f_is_function = true + # See #13033 + T = striptype(ex.args[1]) + if T === nothing + print(io, "First argument to `convert` must be a Type, got ", ex.args[1]) + else + print(io, "Cannot `convert` an object of type ", arg_types_param[2], " to an object of type ", T) + end + elseif isempty(methods(f)) && isa(f, DataType) && f.abstract + print(io, "no constructors have been defined for ", f) + elseif isempty(methods(f)) && !isa(f, Function) && !isa(f, Type) + print(io, "objects of type ", ft, " are not callable") + else + if ft <: Function && isempty(ft.parameters) && + isdefined(ft.name.module, name) && + ft == typeof(getfield(ft.name.module, name)) + f_is_function = true + print(io, "no method matching ", name) + elseif isa(f, Type) + print(io, "no method matching ", f) + else + print(io, "no method matching (::", ft, ")") + end + print(io, "(") + for (i, typ) in enumerate(arg_types_param) + print(io, "::", typ) + i == length(arg_types_param) || print(io, ", ") + end + if !isempty(kwargs) + print(io, "; ") + for (i, (k, v)) in enumerate(kwargs) + print(io, k, "=") + show(IOContext(io, :limit => true), v) + i == length(kwargs) || print(io, ", ") + end + end + print(io, ")") + end + if ft <: AbstractArray + print(io, "\nUse square brackets [] for indexing an Array.") + end + # Check for local functions that shadow methods in Base + if f_is_function && isdefined(Base, name) + basef = getfield(Base, name) + if basef !== ex.f && hasmethod(basef, arg_types) + println(io) + print(io, "You may have intended to import Base.", name) + end + end + if (ex.world != typemax(UInt) && hasmethod(ex.f, arg_types) && + !hasmethod(ex.f, arg_types, world = ex.world)) + curworld = get_world_counter() + println(io) + print(io, "The applicable method may be too new: running in world age $(ex.world), while current world is $(curworld).") + end + if !is_arg_types + # Check for row vectors used where a column vector is intended. + vec_args = [] + hasrows = false + for arg in ex.args + isrow = isa(arg,Array) && ndims(arg)==2 && size(arg,1)==1 + hasrows |= isrow + push!(vec_args, isrow ? vec(arg) : arg) + end + if hasrows && applicable(f, vec_args...) + print(io, "\n\nYou might have used a 2d row vector where a 1d column vector was required.", + "\nNote the difference between 1d column vector [1,2,3] and 2d row vector [1 2 3].", + "\nYou can convert to a column vector with the vec() function.") + end + end + try + show_method_candidates(io, ex, kwargs) + catch ex + @error "Error showing method candidates, aborted" exception=ex,catch_backtrace() + end +end + +striptype(::Type{T}) where {T} = T +striptype(::Any) = nothing + +function showerror_ambiguous(io::IO, meth, f, args) + print(io, "MethodError: ", f, "(") + p = args.parameters + for (i,a) in enumerate(p) + print(io, "::", a) + i < length(p) && print(io, ", ") + end + print(io, ") is ambiguous. Candidates:") + sigfix = Any + for m in meth + print(io, "\n ", m) + sigfix = typeintersect(m.sig, sigfix) + end + if isa(unwrap_unionall(sigfix), DataType) && sigfix <: Tuple + print(io, "\nPossible fix, define\n ") + Base.show_tuple_as_call(io, :function, sigfix) + end + nothing +end + +#Show an error by directly calling jl_printf. +#Useful in Base submodule __init__ functions where stderr isn't defined yet. +function showerror_nostdio(err, msg::AbstractString) + stderr_stream = ccall(:jl_stderr_stream, Ptr{Cvoid}, ()) + ccall(:jl_printf, Cint, (Ptr{Cvoid},Cstring), stderr_stream, msg) + ccall(:jl_printf, Cint, (Ptr{Cvoid},Cstring), stderr_stream, ":\n") + ccall(:jl_static_show, Csize_t, (Ptr{Cvoid},Any), stderr_stream, err) + ccall(:jl_printf, Cint, (Ptr{Cvoid},Cstring), stderr_stream, "\n") +end + +function show_method_candidates(io::IO, ex::MethodError, @nospecialize kwargs=()) + is_arg_types = isa(ex.args, DataType) + arg_types = is_arg_types ? ex.args : typesof(ex.args...) + arg_types_param = Any[arg_types.parameters...] + # Displays the closest candidates of the given function by looping over the + # functions methods and counting the number of matching arguments. + f = ex.f + ft = typeof(f) + lines = [] + # These functions are special cased to only show if first argument is matched. + special = f in [convert, getindex, setindex!] + funcs = Any[(f, arg_types_param)] + + # An incorrect call method produces a MethodError for convert. + # It also happens that users type convert when they mean call. So + # pool MethodErrors for these two functions. + if f === convert && !isempty(arg_types_param) + at1 = arg_types_param[1] + if isa(at1,DataType) && (at1::DataType).name === Type.body.name && !Core.Compiler.has_free_typevars(at1) + push!(funcs, (at1.parameters[1], arg_types_param[2:end])) + end + end + + for (func, arg_types_param) in funcs + for method in methods(func) + buf = IOBuffer() + iob0 = iob = IOContext(buf, io) + tv = Any[] + sig0 = method.sig + while isa(sig0, UnionAll) + push!(tv, sig0.var) + iob = IOContext(iob, :unionall_env => sig0.var) + sig0 = sig0.body + end + s1 = sig0.parameters[1] + sig = sig0.parameters[2:end] + print(iob, " ") + if !isa(func, rewrap_unionall(s1, method.sig)) + # function itself doesn't match + continue + else + # TODO: use the methodshow logic here + use_constructor_syntax = isa(func, Type) + print(iob, use_constructor_syntax ? func : typeof(func).name.mt.name) + end + print(iob, "(") + t_i = copy(arg_types_param) + right_matches = 0 + for i = 1 : min(length(t_i), length(sig)) + i > 1 && print(iob, ", ") + # If isvarargtype then it checks whether the rest of the input arguments matches + # the varargtype + if Base.isvarargtype(sig[i]) + sigstr = (unwrap_unionall(sig[i]).parameters[1], "...") + j = length(t_i) + else + sigstr = (sig[i],) + j = i + end + # Checks if the type of arg 1:i of the input intersects with the current method + t_in = typeintersect(rewrap_unionall(Tuple{sig[1:i]...}, method.sig), + rewrap_unionall(Tuple{t_i[1:j]...}, method.sig)) + # If the function is one of the special cased then it should break the loop if + # the type of the first argument is not matched. + t_in === Union{} && special && i == 1 && break + if t_in === Union{} + if get(io, :color, false) + Base.with_output_color(Base.error_color(), iob) do iob + print(iob, "::", sigstr...) + end + else + print(iob, "!Matched::", sigstr...) + end + # If there is no typeintersect then the type signature from the method is + # inserted in t_i this ensures if the type at the next i matches the type + # signature then there will be a type intersect + t_i[i] = sig[i] + else + right_matches += j==i ? 1 : 0 + print(iob, "::", sigstr...) + end + end + special && right_matches == 0 && continue + + if length(t_i) > length(sig) && !isempty(sig) && Base.isvarargtype(sig[end]) + # It ensures that methods like f(a::AbstractString...) gets the correct + # number of right_matches + for t in arg_types_param[length(sig):end] + if t <: rewrap_unionall(unwrap_unionall(sig[end]).parameters[1], method.sig) + right_matches += 1 + end + end + end + + if right_matches > 0 || length(arg_types_param) < 2 + if length(t_i) < length(sig) + # If the methods args is longer than input then the method + # arguments is printed as not a match + for (k, sigtype) in enumerate(sig[length(t_i)+1:end]) + sigtype = isvarargtype(sigtype) ? unwrap_unionall(sigtype) : sigtype + if Base.isvarargtype(sigtype) + sigstr = (sigtype.parameters[1], "...") + else + sigstr = (sigtype,) + end + if !((min(length(t_i), length(sig)) == 0) && k==1) + print(iob, ", ") + end + if get(io, :color, false) + Base.with_output_color(Base.error_color(), iob) do iob + print(iob, "::", sigstr...) + end + else + print(iob, "!Matched::", sigstr...) + end + end + end + kwords = Symbol[] + if isdefined(ft.name.mt, :kwsorter) + kwsorter_t = typeof(ft.name.mt.kwsorter) + kwords = kwarg_decl(method, kwsorter_t) + length(kwords) > 0 && print(iob, "; ", join(kwords, ", ")) + end + print(iob, ")") + show_method_params(iob0, tv) + print(iob, " at ", method.file, ":", method.line) + if !isempty(kwargs) + unexpected = Symbol[] + if isempty(kwords) || !(any(endswith(string(kword), "...") for kword in kwords)) + for (k, v) in kwargs + if !(k in kwords) + push!(unexpected, k) + end + end + end + if !isempty(unexpected) + Base.with_output_color(Base.error_color(), iob) do iob + plur = length(unexpected) > 1 ? "s" : "" + print(iob, " got unsupported keyword argument$plur \"", join(unexpected, "\", \""), "\"") + end + end + end + if ex.world < reinterpret(UInt, method.primary_world) + print(iob, " (method too new to be called from this world context.)") + elseif ex.world > reinterpret(UInt, method.deleted_world) + print(iob, " (method deleted before this world age.)") + end + # TODO: indicate if it's in the wrong world + push!(lines, (buf, right_matches)) + end + end + end + + if !isempty(lines) # Display up to three closest candidates + Base.with_output_color(:normal, io) do io + println(io) + print(io, "Closest candidates are:") + sort!(lines, by = x -> -x[2]) + i = 0 + for line in lines + println(io) + if i >= 3 + print(io, " ...") + break + end + i += 1 + print(io, String(take!(line[1]))) + end + end + end +end + +# Contains file name and file number. Gets set when a backtrace +# or methodlist is shown. Used by the REPL to make it possible to open +# the location of a stackframe/method in the editor. +global LAST_SHOWN_LINE_INFOS = Tuple{String, Int}[] + +function show_trace_entry(io, frame, n; prefix = "") + push!(LAST_SHOWN_LINE_INFOS, (string(frame.file), frame.line)) + print(io, "\n", prefix) + show(io, frame, full_path=true) + n > 1 && print(io, " (repeats ", n, " times)") +end + +# In case the line numbers in the source code have changed since the code was compiled, +# allow packages to set a callback function that corrects them. +# (Used by Revise and perhaps other packages.) +# +# Set this with +# Base.update_stackframes_callback[] = my_updater! +# where my_updater! takes a single argument and works in-place. The argument will be a +# Vector{Any} storing tuples (sf::StackFrame, nrepetitions::Int), and the updater should +# replace `sf` as needed. +const update_stackframes_callback = Ref{Function}(identity) + +const BIG_STACKTRACE_SIZE = 50 # Arbitrary constant chosen here + +function show_reduced_backtrace(io::IO, t::Vector, with_prefix::Bool) + recorded_positions = IdDict{UInt, Vector{Int}}() + #= For each frame of hash h, recorded_positions[h] is the list of indices i + such that hash(t[i-1]) == h, ie the list of positions in which the + frame appears just before. =# + + displayed_stackframes = [] + repeated_cycle = Tuple{Int,Int,Int}[] + # First: line to introuce the "cycle repetition" message + # Second: length of the cycle + # Third: number of repetitions + frame_counter = 1 + while frame_counter < length(t) + (last_frame, n) = t[frame_counter] + frame_counter += 1 # Indicating the next frame + + current_hash = hash(last_frame) + positions = get(recorded_positions, current_hash, Int[]) + recorded_positions[current_hash] = push!(positions, frame_counter) + + repetitions = 0 + for index_p in length(positions)-1:-1:1 # More recent is more likely + p = positions[index_p] + cycle_length = frame_counter - p + i = frame_counter + j = p + while i < length(t) && t[i] == t[j] + i+=1 ; j+=1 + end + if j >= frame_counter-1 + #= At least one cycle repeated =# + repetitions = div(i - frame_counter + 1, cycle_length) + push!(repeated_cycle, (length(displayed_stackframes), cycle_length, repetitions)) + frame_counter += cycle_length * repetitions - 1 + break + end + end + + if repetitions==0 + push!(displayed_stackframes, (last_frame, n)) + end + end + + try invokelatest(update_stackframes_callback[], displayed_stackframes) catch end + + push!(repeated_cycle, (0,0,0)) # repeated_cycle is never empty + frame_counter = 1 + for i in 1:length(displayed_stackframes) + (frame, n) = displayed_stackframes[i] + if with_prefix + show_trace_entry(io, frame, n, prefix = string(" [", frame_counter, "] ")) + else + show_trace_entry(io, frame, n) + end + while repeated_cycle[1][1] == i # never empty because of the initial (0,0,0) + cycle_length = repeated_cycle[1][2] + repetitions = repeated_cycle[1][3] + popfirst!(repeated_cycle) + print(io, "\n ... (the last ", cycle_length, " lines are repeated ", + repetitions, " more time", repetitions>1 ? "s)" : ")") + frame_counter += cycle_length * repetitions + end + frame_counter += 1 + end +end + +function show_backtrace(io::IO, t::Vector) + resize!(LAST_SHOWN_LINE_INFOS, 0) + filtered = process_backtrace(t) + isempty(filtered) && return + + if length(filtered) == 1 && StackTraces.is_top_level_frame(filtered[1][1]) + f = filtered[1][1] + if f.line == 0 && f.file == Symbol("") + # don't show a single top-level frame with no location info + return + end + end + + print(io, "\nStacktrace:") + if length(filtered) < BIG_STACKTRACE_SIZE + # Fast track: no duplicate stack frame detection. + try invokelatest(update_stackframes_callback[], filtered) catch end + frame_counter = 0 + for (last_frame, n) in filtered + frame_counter += 1 + show_trace_entry(IOContext(io, :backtrace => true), last_frame, n, prefix = string(" [", frame_counter, "] ")) + end + return + end + + show_reduced_backtrace(IOContext(io, :backtrace => true), filtered, true) +end + +function show_backtrace(io::IO, t::Vector{Any}) + if length(t) < BIG_STACKTRACE_SIZE + try invokelatest(update_stackframes_callback[], t) catch end + for entry in t + show_trace_entry(io, entry...) + end + else + show_reduced_backtrace(io, t, false) + end +end + +function process_backtrace(t::Vector, limit::Int=typemax(Int); skipC = true) + n = 0 + last_frame = StackTraces.UNKNOWN + count = 0 + ret = Any[] + for i = eachindex(t) + lkups = StackTraces.lookup(t[i]) + for lkup in lkups + if lkup === StackTraces.UNKNOWN + continue + end + + if lkup.from_c && skipC; continue; end + if i == 1 && lkup.func === :error; continue; end + count += 1 + if count > limit; break; end + + if lkup.file != last_frame.file || lkup.line != last_frame.line || lkup.func != last_frame.func || lkup.linfo !== lkup.linfo + if n > 0 + push!(ret, (last_frame,n)) + end + n = 1 + last_frame = lkup + else + n += 1 + end + end + end + if n > 0 + push!(ret, (last_frame,n)) + end + return ret +end + +function show_exception_stack(io::IO, stack::Vector) + # Display exception stack with the top of the stack first. This ordering + # means that the user doesn't have to scroll up in the REPL to discover the + # root cause. + nexc = length(stack) + for i = nexc:-1:1 + if nexc != i + printstyled(io, "caused by [exception ", i, "]\n", color=:light_black) + end + exc, bt = stack[i] + showerror(io, exc, bt, backtrace = bt!==nothing) + println(io) + end +end diff --git a/base/essentials.jl b/base/essentials.jl new file mode 100644 index 0000000..4598471 --- /dev/null +++ b/base/essentials.jl @@ -0,0 +1,837 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +using Core: CodeInfo, SimpleVector + +const Callable = Union{Function,Type} + +const Bottom = Union{} + +""" + AbstractSet{T} + +Supertype for set-like types whose elements are of type `T`. +[`Set`](@ref), [`BitSet`](@ref) and other types are subtypes of this. +""" +abstract type AbstractSet{T} end + +""" + AbstractDict{K, V} + +Supertype for dictionary-like types with keys of type `K` and values of type `V`. +[`Dict`](@ref), [`IdDict`](@ref) and other types are subtypes of this. +""" +abstract type AbstractDict{K,V} end + +# The real @inline macro is not available until after array.jl, so this +# internal macro splices the meta Expr directly into the function body. +macro _inline_meta() + Expr(:meta, :inline) +end +macro _noinline_meta() + Expr(:meta, :noinline) +end + +macro _gc_preserve_begin(arg1) + Expr(:gc_preserve_begin, esc(arg1)) +end + +macro _gc_preserve_end(token) + Expr(:gc_preserve_end, esc(token)) +end + +""" + @nospecialize + +Applied to a function argument name, hints to the compiler that the method +should not be specialized for different types of that argument, +but instead to use precisely the declared type for each argument. +This is only a hint for avoiding excess code generation. +Can be applied to an argument within a formal argument list, +or in the function body. +When applied to an argument, the macro must wrap the entire argument expression. +When used in a function body, the macro must occur in statement position and +before any code. + +When used without arguments, it applies to all arguments of the parent scope. +In local scope, this means all arguments of the containing function. +In global (top-level) scope, this means all methods subsequently defined in the current module. + +Specialization can reset back to the default by using [`@specialize`](@ref). + +```julia +function example_function(@nospecialize x) + ... +end + +function example_function(@nospecialize(x = 1), y) + ... +end + +function example_function(x, y, z) + @nospecialize x y + ... +end + +@nospecialize +f(y) = [x for x in y] +@specialize +``` +""" +macro nospecialize(vars...) + if nfields(vars) === 1 + # in argument position, need to fix `@nospecialize x=v` to `@nospecialize (kw x v)` + var = getfield(vars, 1) + if isa(var, Expr) && var.head === :(=) + var.head = :kw + end + end + return Expr(:meta, :nospecialize, vars...) +end + +""" + @specialize + +Reset the specialization hint for an argument back to the default. +For details, see [`@nospecialize`](@ref). +""" +macro specialize(vars...) + if nfields(vars) === 1 + # in argument position, need to fix `@specialize x=v` to `@specialize (kw x v)` + var = getfield(vars, 1) + if isa(var, Expr) && var.head === :(=) + var.head = :kw + end + end + return Expr(:meta, :specialize, vars...) +end + +macro _pure_meta() + return Expr(:meta, :pure) +end +# another version of inlining that propagates an inbounds context +macro _propagate_inbounds_meta() + return Expr(:meta, :inline, :propagate_inbounds) +end + +""" + convert(T, x) + +Convert `x` to a value of type `T`. + +If `T` is an [`Integer`](@ref) type, an [`InexactError`](@ref) will be raised if `x` +is not representable by `T`, for example if `x` is not integer-valued, or is outside the +range supported by `T`. + +# Examples +```jldoctest +julia> convert(Int, 3.0) +3 + +julia> convert(Int, 3.5) +ERROR: InexactError: Int64(3.5) +Stacktrace: +[...] +``` + +If `T` is a [`AbstractFloat`](@ref) or [`Rational`](@ref) type, +then it will return the closest value to `x` representable by `T`. + +```jldoctest +julia> x = 1/3 +0.3333333333333333 + +julia> convert(Float32, x) +0.33333334f0 + +julia> convert(Rational{Int32}, x) +1//3 + +julia> convert(Rational{Int64}, x) +6004799503160661//18014398509481984 +``` + +If `T` is a collection type and `x` a collection, the result of +`convert(T, x)` may alias all or part of `x`. +```jldoctest +julia> x = Int[1, 2, 3]; + +julia> y = convert(Vector{Int}, x); + +julia> y === x +true +``` +""" +function convert end + +convert(::Type{Union{}}, x) = throw(MethodError(convert, (Union{}, x))) +convert(::Type{Any}, x) = x +convert(::Type{T}, x::T) where {T} = x +convert(::Type{Type}, x::Type) = x # the ssair optimizer is strongly dependent on this method existing to avoid over-specialization + # in the absence of inlining-enabled + # (due to fields typed as `Type`, which is generally a bad idea) + +""" + @eval [mod,] ex + +Evaluate an expression with values interpolated into it using `eval`. +If two arguments are provided, the first is the module to evaluate in. +""" +macro eval(ex) + :(Core.eval($__module__, $(Expr(:quote,ex)))) +end +macro eval(mod, ex) + :(Core.eval($(esc(mod)), $(Expr(:quote,ex)))) +end + +argtail(x, rest...) = rest + +""" + tail(x::Tuple)::Tuple + +Return a `Tuple` consisting of all but the first component of `x`. + +# Examples +```jldoctest +julia> Base.tail((1,2,3)) +(2, 3) + +julia> Base.tail(()) +ERROR: ArgumentError: Cannot call tail on an empty tuple. +``` +""" +tail(x::Tuple) = argtail(x...) +tail(::Tuple{}) = throw(ArgumentError("Cannot call tail on an empty tuple.")) + +tuple_type_head(T::Type) = (@_pure_meta; fieldtype(T::Type{<:Tuple}, 1)) + +function tuple_type_tail(T::Type) + @_pure_meta + if isa(T, UnionAll) + return UnionAll(T.var, tuple_type_tail(T.body)) + elseif isa(T, Union) + return Union{tuple_type_tail(T.a), tuple_type_tail(T.b)} + else + T.name === Tuple.name || throw(MethodError(tuple_type_tail, (T,))) + if isvatuple(T) && length(T.parameters) == 1 + va = T.parameters[1] + (isa(va, DataType) && isa(va.parameters[2], Int)) || return T + return Tuple{Vararg{va.parameters[1], va.parameters[2]-1}} + end + return Tuple{argtail(T.parameters...)...} + end +end + +tuple_type_cons(::Type, ::Type{Union{}}) = Union{} +function tuple_type_cons(::Type{S}, ::Type{T}) where T<:Tuple where S + @_pure_meta + Tuple{S, T.parameters...} +end + +function unwrap_unionall(@nospecialize(a)) + while isa(a,UnionAll) + a = a.body + end + return a +end + +function rewrap_unionall(@nospecialize(t), @nospecialize(u)) + if !isa(u, UnionAll) + return t + end + return UnionAll(u.var, rewrap_unionall(t, u.body)) +end + +# replace TypeVars in all enclosing UnionAlls with fresh TypeVars +function rename_unionall(@nospecialize(u)) + if !isa(u,UnionAll) + return u + end + body = rename_unionall(u.body) + if body === u.body + body = u + else + body = UnionAll(u.var, body) + end + var = u.var::TypeVar + nv = TypeVar(var.name, var.lb, var.ub) + return UnionAll(nv, body{nv}) +end + +const _va_typename = Vararg.body.body.name +function isvarargtype(@nospecialize(t)) + t = unwrap_unionall(t) + return isa(t, DataType) && (t::DataType).name === _va_typename +end + +function isvatuple(@nospecialize(t)) + t = unwrap_unionall(t) + if isa(t, DataType) + n = length(t.parameters) + return n > 0 && isvarargtype(t.parameters[n]) + end + return false +end + +function unwrapva(@nospecialize(t)) + # NOTE: this returns a related type, but it's NOT a subtype of the original tuple + t2 = unwrap_unionall(t) + return isvarargtype(t2) ? rewrap_unionall(t2.parameters[1], t) : t +end + +function unconstrain_vararg_length(@nospecialize(va)) + # construct a new Vararg type where its length is unconstrained, + # but its element type still captures any dependencies the input + # element type may have had on the input length + T = unwrap_unionall(va).parameters[1] + return rewrap_unionall(Vararg{T}, va) +end + +typename(a) = error("typename does not apply to this type") +typename(a::DataType) = a.name +function typename(a::Union) + ta = typename(a.a) + tb = typename(a.b) + ta === tb || error("typename does not apply to unions whose components have different typenames") + return tb +end +typename(union::UnionAll) = typename(union.body) + +const AtLeast1 = Tuple{Any, Vararg{Any}} + +# converting to empty tuple type +convert(::Type{Tuple{}}, ::Tuple{}) = () +convert(::Type{Tuple{}}, x::AtLeast1) = throw(MethodError(convert, (Tuple{}, x))) + +# converting to tuple types with at least one element +convert(::Type{T}, x::T) where {T<:AtLeast1} = x +convert(::Type{T}, x::AtLeast1) where {T<:AtLeast1} = + (convert(tuple_type_head(T), x[1]), convert(tuple_type_tail(T), tail(x))...) + +# converting to Vararg tuple types +convert(::Type{Tuple{Vararg{V}}}, x::Tuple{Vararg{V}}) where {V} = x +convert(T::Type{Tuple{Vararg{V}}}, x::Tuple) where {V} = + (convert(tuple_type_head(T), x[1]), convert(T, tail(x))...) + +# TODO: the following definitions are equivalent (behaviorally) to the above method +# I think they may be faster / more efficient for inference, +# if we could enable them, but are they? +# TODO: These currently can't be used (#21026, #23017) since with +# z(::Type{<:Tuple{Vararg{T}}}) where {T} = T +# calling +# z(Tuple{Val{T}} where T) +# fails, even though `Type{Tuple{Val}} == Type{Tuple{Val{S}} where S}` +# and so T should be `Val` (aka `Val{S} where S`) +#convert(_::Type{Tuple{S}}, x::Tuple{S}) where {S} = x +#convert(_::Type{Tuple{S}}, x::Tuple{Any}) where {S} = (convert(S, x[1]),) +#convert(_::Type{T}, x::T) where {S, N, T<:Tuple{S, Vararg{S, N}}} = x +#convert(_::Type{Tuple{S, Vararg{S, N}}}, +# x::Tuple{Any, Vararg{Any, N}}) where +# {S, N} = cnvt_all(S, x...) +#convert(_::Type{Tuple{Vararg{S, N}}}, +# x::Tuple{Vararg{Any, N}}) where +# {S, N} = cnvt_all(S, x...) +# TODO: These currently can't be used since +# Type{NTuple} <: (Type{Tuple{Vararg{S}}} where S) is true +# even though the value S doesn't exist +#convert(_::Type{Tuple{Vararg{S}}}, +# x::Tuple{Any, Vararg{Any}}) where +# {S} = cnvt_all(S, x...) +#convert(_::Type{Tuple{Vararg{S}}}, +# x::Tuple{Vararg{Any}}) where +# {S} = cnvt_all(S, x...) +#cnvt_all(T) = () +#cnvt_all(T, x, rest...) = (convert(T, x), cnvt_all(T, rest...)...) +# TODO: These may be necessary if the above are enabled +#convert(::Type{Tuple{}}, ::Tuple{}) = () +#convert(::Type{Tuple{Vararg{S}}} where S, ::Tuple{}) = () + +""" + oftype(x, y) + +Convert `y` to the type of `x` (`convert(typeof(x), y)`). + +# Examples +```jldoctest +julia> x = 4; + +julia> y = 3.; + +julia> oftype(x, y) +3 + +julia> oftype(y, x) +4.0 +``` +""" +oftype(x, y) = convert(typeof(x), y) + +unsigned(x::Int) = reinterpret(UInt, x) +signed(x::UInt) = reinterpret(Int, x) + +""" + cconvert(T,x) + +Convert `x` to a value to be passed to C code as type `T`, typically by calling `convert(T, x)`. + +In cases where `x` cannot be safely converted to `T`, unlike [`convert`](@ref), `cconvert` may +return an object of a type different from `T`, which however is suitable for +[`unsafe_convert`](@ref) to handle. The result of this function should be kept valid (for the GC) +until the result of [`unsafe_convert`](@ref) is not needed anymore. +This can be used to allocate memory that will be accessed by the `ccall`. +If multiple objects need to be allocated, a tuple of the objects can be used as return value. + +Neither `convert` nor `cconvert` should take a Julia object and turn it into a `Ptr`. +""" +function cconvert end + +cconvert(T::Type, x) = convert(T, x) # do the conversion eagerly in most cases +cconvert(::Type{<:Ptr}, x) = x # but defer the conversion to Ptr to unsafe_convert +unsafe_convert(::Type{T}, x::T) where {T} = x # unsafe_convert (like convert) defaults to assuming the convert occurred +unsafe_convert(::Type{T}, x::T) where {T<:Ptr} = x # to resolve ambiguity with the next method +unsafe_convert(::Type{P}, x::Ptr) where {P<:Ptr} = convert(P, x) + +""" + reinterpret(type, A) + +Change the type-interpretation of a block of memory. +For arrays, this constructs a view of the array with the same binary data as the given +array, but with the specified element type. +For example, +`reinterpret(Float32, UInt32(7))` interprets the 4 bytes corresponding to `UInt32(7)` as a +[`Float32`](@ref). + +# Examples +```jldoctest +julia> reinterpret(Float32, UInt32(7)) +1.0f-44 + +julia> reinterpret(Float32, UInt32[1 2 3 4 5]) +1×5 reinterpret(Float32, ::Array{UInt32,2}): + 1.4013e-45 2.8026e-45 4.2039e-45 5.60519e-45 7.00649e-45 +``` +""" +reinterpret(::Type{T}, x) where {T} = bitcast(T, x) +reinterpret(::Type{Unsigned}, x::Float16) = reinterpret(UInt16,x) +reinterpret(::Type{Signed}, x::Float16) = reinterpret(Int16,x) + +""" + sizeof(T::DataType) + sizeof(obj) + +Size, in bytes, of the canonical binary representation of the given `DataType` `T`, if any. +Size, in bytes, of object `obj` if it is not `DataType`. + +# Examples +```jldoctest +julia> sizeof(Float32) +4 + +julia> sizeof(ComplexF64) +16 + +julia> sizeof(1.0) +8 + +julia> sizeof([1.0:10.0;]) +80 +``` + +If `DataType` `T` does not have a specific size, an error is thrown. + +```jldoctest +julia> sizeof(AbstractArray) +ERROR: Abstract type AbstractArray does not have a definite size. +Stacktrace: +[...] +``` +""" +sizeof(x) = Core.sizeof(x) + +# simple Array{Any} operations needed for bootstrap +@eval setindex!(A::Array{Any}, @nospecialize(x), i::Int) = arrayset($(Expr(:boundscheck)), A, x, i) + +""" + precompile(f, args::Tuple{Vararg{Any}}) + +Compile the given function `f` for the argument tuple (of types) `args`, but do not execute it. +""" +function precompile(@nospecialize(f), args::Tuple) + ccall(:jl_compile_hint, Int32, (Any,), Tuple{Core.Typeof(f), args...}) != 0 +end + +function precompile(argt::Type) + ccall(:jl_compile_hint, Int32, (Any,), argt) != 0 +end + +""" + esc(e) + +Only valid in the context of an [`Expr`](@ref) returned from a macro. Prevents the macro hygiene +pass from turning embedded variables into gensym variables. See the [Macros](@ref man-macros) +section of the Metaprogramming chapter of the manual for more details and examples. +""" +esc(@nospecialize(e)) = Expr(:escape, e) + +""" + @boundscheck(blk) + +Annotates the expression `blk` as a bounds checking block, allowing it to be elided by [`@inbounds`](@ref). + +!!! note + The function in which `@boundscheck` is written must be inlined into + its caller in order for `@inbounds` to have effect. + +# Examples +```jldoctest; filter = r"Stacktrace:(\\n \\[[0-9]+\\].*)*" +julia> @inline function g(A, i) + @boundscheck checkbounds(A, i) + return "accessing (\$A)[\$i]" + end; + +julia> f1() = return g(1:2, -1); + +julia> f2() = @inbounds return g(1:2, -1); + +julia> f1() +ERROR: BoundsError: attempt to access 2-element UnitRange{Int64} at index [-1] +Stacktrace: + [1] throw_boundserror(::UnitRange{Int64}, ::Tuple{Int64}) at ./abstractarray.jl:455 + [2] checkbounds at ./abstractarray.jl:420 [inlined] + [3] g at ./none:2 [inlined] + [4] f1() at ./none:1 + [5] top-level scope + +julia> f2() +"accessing (1:2)[-1]" +``` + +!!! warning + + The `@boundscheck` annotation allows you, as a library writer, to opt-in to + allowing *other code* to remove your bounds checks with [`@inbounds`](@ref). + As noted there, the caller must verify—using information they can access—that + their accesses are valid before using `@inbounds`. For indexing into your + [`AbstractArray`](@ref) subclasses, for example, this involves checking the + indices against its [`size`](@ref). Therefore, `@boundscheck` annotations + should only be added to a [`getindex`](@ref) or [`setindex!`](@ref) + implementation after you are certain its behavior is correct. +""" +macro boundscheck(blk) + return Expr(:if, Expr(:boundscheck), esc(blk)) +end + +""" + @inbounds(blk) + +Eliminates array bounds checking within expressions. + +In the example below the in-range check for referencing +element `i` of array `A` is skipped to improve performance. + +```julia +function sum(A::AbstractArray) + r = zero(eltype(A)) + for i = 1:length(A) + @inbounds r += A[i] + end + return r +end +``` + +!!! warning + + Using `@inbounds` may return incorrect results/crashes/corruption + for out-of-bounds indices. The user is responsible for checking it manually. + Only use `@inbounds` when it is certain from the information locally available + that all accesses are in bounds. +""" +macro inbounds(blk) + return Expr(:block, + Expr(:inbounds, true), + Expr(:local, Expr(:(=), :val, esc(blk))), + Expr(:inbounds, :pop), + :val) +end + +""" + @label name + +Labels a statement with the symbolic label `name`. The label marks the end-point +of an unconditional jump with [`@goto name`](@ref). +""" +macro label(name::Symbol) + return esc(Expr(:symboliclabel, name)) +end + +""" + @goto name + +`@goto name` unconditionally jumps to the statement at the location [`@label name`](@ref). + +`@label` and `@goto` cannot create jumps to different top-level statements. Attempts cause an +error. To still use `@goto`, enclose the `@label` and `@goto` in a block. +""" +macro goto(name::Symbol) + return esc(Expr(:symbolicgoto, name)) +end + +# SimpleVector + +function getindex(v::SimpleVector, i::Int) + @boundscheck if !(1 <= i <= length(v)) + throw(BoundsError(v,i)) + end + t = @_gc_preserve_begin v + x = unsafe_load(convert(Ptr{Ptr{Cvoid}},pointer_from_objref(v)) + i*sizeof(Ptr)) + x == C_NULL && throw(UndefRefError()) + o = unsafe_pointer_to_objref(x) + @_gc_preserve_end t + return o +end + +function length(v::SimpleVector) + t = @_gc_preserve_begin v + l = unsafe_load(convert(Ptr{Int},pointer_from_objref(v))) + @_gc_preserve_end t + return l +end +firstindex(v::SimpleVector) = 1 +lastindex(v::SimpleVector) = length(v) +iterate(v::SimpleVector, i=1) = (length(v) < i ? nothing : (v[i], i + 1)) +eltype(::Type{SimpleVector}) = Any +keys(v::SimpleVector) = OneTo(length(v)) +isempty(v::SimpleVector) = (length(v) == 0) +axes(v::SimpleVector) = (OneTo(length(v)),) +axes(v::SimpleVector, d::Integer) = d <= 1 ? axes(v)[d] : OneTo(1) + +function ==(v1::SimpleVector, v2::SimpleVector) + length(v1)==length(v2) || return false + for i = 1:length(v1) + v1[i] == v2[i] || return false + end + return true +end + +map(f, v::SimpleVector) = Any[ f(v[i]) for i = 1:length(v) ] + +getindex(v::SimpleVector, I::AbstractArray) = Core.svec(Any[ v[i] for i in I ]...) + +""" + isassigned(array, i) -> Bool + +Test whether the given array has a value associated with index `i`. Return `false` +if the index is out of bounds, or has an undefined reference. + +# Examples +```jldoctest +julia> isassigned(rand(3, 3), 5) +true + +julia> isassigned(rand(3, 3), 3 * 3 + 1) +false + +julia> mutable struct Foo end + +julia> v = similar(rand(3), Foo) +3-element Array{Foo,1}: + #undef + #undef + #undef + +julia> isassigned(v, 1) +false +``` +""" +function isassigned end + +function isassigned(v::SimpleVector, i::Int) + @boundscheck 1 <= i <= length(v) || return false + t = @_gc_preserve_begin v + x = unsafe_load(convert(Ptr{Ptr{Cvoid}},pointer_from_objref(v)) + i*sizeof(Ptr)) + @_gc_preserve_end t + return x != C_NULL +end + + +""" + Colon() + +Colons (:) are used to signify indexing entire objects or dimensions at once. + +Very few operations are defined on Colons directly; instead they are converted +by [`to_indices`](@ref) to an internal vector type (`Base.Slice`) to represent the +collection of indices they span before being used. + +The singleton instance of `Colon` is also a function used to construct ranges; +see [`:`](@ref). +""" +struct Colon <: Function +end +const (:) = Colon() + +""" + Val(c) + +Return `Val{c}()`, which contains no run-time data. Types like this can be used to +pass the information between functions through the value `c`, which must be an `isbits` +value. The intent of this construct is to be able to dispatch on constants directly (at +compile time) without having to test the value of the constant at run time. + +# Examples +```jldoctest +julia> f(::Val{true}) = "Good" +f (generic function with 1 method) + +julia> f(::Val{false}) = "Bad" +f (generic function with 2 methods) + +julia> f(Val(true)) +"Good" +``` +""" +struct Val{x} +end + +Val(x) = (@_pure_meta; Val{x}()) + +""" + invokelatest(f, args...; kwargs...) + +Calls `f(args...; kwargs...)`, but guarantees that the most recent method of `f` +will be executed. This is useful in specialized circumstances, +e.g. long-running event loops or callback functions that may +call obsolete versions of a function `f`. +(The drawback is that `invokelatest` is somewhat slower than calling +`f` directly, and the type of the result cannot be inferred by the compiler.) +""" +function invokelatest(@nospecialize(f), @nospecialize args...; kwargs...) + if isempty(kwargs) + return Core._apply_latest(f, args) + end + # We use a closure (`inner`) to handle kwargs. + inner() = f(args...; kwargs...) + Core._apply_latest(inner) +end + +# TODO: possibly make this an intrinsic +inferencebarrier(@nospecialize(x)) = Ref{Any}(x)[] + +""" + isempty(collection) -> Bool + +Determine whether a collection is empty (has no elements). + +# Examples +```jldoctest +julia> isempty([]) +true + +julia> isempty([1 2 3]) +false +``` +""" +function isempty(itr) + d = isdone(itr) + d !== missing && return d + iterate(itr) === nothing +end + +""" + values(iterator) + +For an iterator or collection that has keys and values, return an iterator +over the values. +This function simply returns its argument by default, since the elements +of a general iterator are normally considered its "values". + +# Examples +```jldoctest +julia> d = Dict("a"=>1, "b"=>2); + +julia> values(d) +Base.ValueIterator for a Dict{String,Int64} with 2 entries. Values: + 2 + 1 + +julia> values([2]) +1-element Array{Int64,1}: + 2 +``` +""" +values(itr) = itr + +""" + Missing + +A type with no fields whose singleton instance [`missing`](@ref) is used +to represent missing values. +""" +struct Missing end + +""" + missing + +The singleton instance of type [`Missing`](@ref) representing a missing value. +""" +const missing = Missing() + +""" + ismissing(x) + +Indicate whether `x` is [`missing`](@ref). +""" +ismissing(::Any) = false +ismissing(::Missing) = true + +function popfirst! end +function peek end + +""" + @__LINE__ -> Int + +Expand to the line number of the location of the macrocall. +Return `0` if the line number could not be determined. +""" +macro __LINE__() + return __source__.line +end + +# Just for bootstrapping purposes below +macro __FILE_SYMBOL__() + return Expr(:quote, __source__.file) +end + +# Iteration +""" + isdone(itr, state...) -> Union{Bool, Missing} + +This function provides a fast-path hint for iterator completion. +This is useful for mutable iterators that want to avoid having elements +consumed, if they are not going to be exposed to the user (e.g. to check +for done-ness in `isempty` or `zip`). Mutable iterators that want to +opt into this feature should define an isdone method that returns +true/false depending on whether the iterator is done or not. Stateless +iterators need not implement this function. If the result is `missing`, +callers may go ahead and compute `iterate(x, state...) === nothing` to +compute a definite answer. +""" +isdone(itr, state...) = missing + +""" + iterate(iter [, state]) -> Union{Nothing, Tuple{Any, Any}} + +Advance the iterator to obtain the next element. If no elements +remain, `nothing` should be returned. Otherwise, a 2-tuple of the +next element and the new iteration state should be returned. +""" +function iterate end + +""" + isiterable(T) -> Bool + +Test if type `T` is an iterable collection type or not, +that is whether it has an `iterate` method or not. +""" +function isiterable(T)::Bool + return hasmethod(iterate, Tuple{T}) +end diff --git a/base/experimental.jl b/base/experimental.jl new file mode 100644 index 0000000..3034660 --- /dev/null +++ b/base/experimental.jl @@ -0,0 +1,52 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" + Experimental + +!!! warning + Types, methods, or macros defined in this module are experimental and subject + to change and will not have deprecations. Caveat emptor. +""" +module Experimental + +""" + Const(A::Array) + +Mark an Array as constant/read-only. The invariant guaranteed is that you will not +modify an Array (through another reference) within an `@aliasscope` scope. + +!!! warning + Experimental API. Subject to change without deprecation. +""" +struct Const{T,N} <: DenseArray{T,N} + a::Array{T,N} +end + +Base.IndexStyle(::Type{<:Const}) = IndexLinear() +Base.size(C::Const) = size(C.a) +Base.axes(C::Const) = axes(C.a) +@eval Base.getindex(A::Const, i1::Int) = + (Base.@_inline_meta; Core.const_arrayref($(Expr(:boundscheck)), A.a, i1)) +@eval Base.getindex(A::Const, i1::Int, i2::Int, I::Int...) = + (Base.@_inline_meta; Core.const_arrayref($(Expr(:boundscheck)), A.a, i1, i2, I...)) + +""" + @aliasscope expr + +Allows the compiler to assume that all `Const`s are not being modified through stores +within this scope, even if the compiler can't prove this to be the case. + +!!! warning + Experimental API. Subject to change without deprecation. +""" +macro aliasscope(body) + sym = gensym() + quote + $(Expr(:aliasscope)) + $sym = $(esc(body)) + $(Expr(:popaliasscope)) + $sym + end +end + +end diff --git a/base/exports.jl b/base/exports.jl new file mode 100644 index 0000000..ebd7463 --- /dev/null +++ b/base/exports.jl @@ -0,0 +1,985 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +export +# Modules + Meta, + StackTraces, + Sys, + Libc, + Docs, + Threads, + Iterators, + Broadcast, + MathConstants, + +# Types + AbstractChannel, + AbstractIrrational, + AbstractMatrix, + AbstractRange, + AbstractSet, + AbstractUnitRange, + AbstractVector, + AbstractVecOrMat, + Array, + AbstractDict, + BigFloat, + BigInt, + BitArray, + BitMatrix, + BitVector, + CartesianIndex, + CartesianIndices, + LinearIndices, + Channel, + Cmd, + Colon, + Complex, + ComplexF64, + ComplexF32, + ComplexF16, + DenseMatrix, + DenseVecOrMat, + DenseVector, + Dict, + Dims, + Enum, + ExponentialBackOff, + IndexCartesian, + IndexLinear, + IndexStyle, + InsertionSort, + BitSet, + IOBuffer, + IOStream, + LinRange, + Irrational, + Matrix, + MergeSort, + Missing, + NTuple, + IdDict, + OrdinalRange, + Pair, + PartialQuickSort, + PermutedDimsArray, + QuickSort, + Rational, + Regex, + RegexMatch, + RoundFromZero, + RoundDown, + RoundingMode, + RoundNearest, + RoundNearestTiesAway, + RoundNearestTiesUp, + RoundToZero, + RoundUp, + Set, + Some, + StepRange, + StepRangeLen, + StridedArray, + StridedMatrix, + StridedVecOrMat, + StridedVector, + SubArray, + SubString, + SubstitutionString, + Timer, + UnitRange, + Val, + VecOrMat, + Vector, + VersionNumber, + WeakKeyDict, + +# Ccall types + Cchar, + Cdouble, + Cfloat, + Cint, + Cintmax_t, + Clong, + Clonglong, + Cptrdiff_t, + Cshort, + Csize_t, + Cssize_t, + Cuchar, + Cuint, + Cuintmax_t, + Culong, + Culonglong, + Cushort, + Cwchar_t, + Cstring, + Cwstring, + +# Exceptions + CapturedException, + CompositeException, + DimensionMismatch, + EOFError, + InvalidStateException, + KeyError, + MissingException, + ProcessFailedException, + TaskFailedException, + SystemError, + StringIndexError, + +# Global constants and variables + ARGS, + C_NULL, + DEPOT_PATH, + ENDIAN_BOM, + ENV, + LOAD_PATH, + PROGRAM_FILE, + stderr, + stdin, + stdout, + VERSION, + devnull, + +# Mathematical constants + Inf, + Inf16, + Inf32, + Inf64, + NaN, + NaN16, + NaN32, + NaN64, + im, + π, pi, + ℯ, + +# Operators + !, + !=, + ≠, + !==, + ≡, + ≢, + xor, + ⊻, + %, + ÷, + &, + *, + +, + -, + /, + //, + <, + <:, + <<, + <=, + ≤, + ==, + >, + >:, + >=, + ≥, + >>, + >>>, + \, + ^, + |, + |>, + ~, + :, + =>, + ∘, + +# scalar math + @evalpoly, + abs, + abs2, + acos, + acosd, + acosh, + acot, + acotd, + acoth, + acsc, + acscd, + acsch, + angle, + asec, + asecd, + asech, + asin, + asind, + asinh, + atan, + atand, + atanh, + big, + binomial, + bswap, + cbrt, + ceil, + cis, + clamp, + cld, + cmp, + complex, + conj, + copysign, + cos, + cosc, + cosd, + cosh, + cospi, + cot, + cotd, + coth, + count_ones, + count_zeros, + csc, + cscd, + csch, + deg2rad, + denominator, + div, + divrem, + eps, + exp, + exp10, + exp2, + expm1, + exponent, + factorial, + fld, + fld1, + fldmod, + fldmod1, + flipsign, + float, + tryparse, + floor, + fma, + frexp, + gcd, + gcdx, + hypot, + imag, + inv, + invmod, + isapprox, + iseven, + isfinite, + isinf, + isinteger, + isnan, + isodd, + ispow2, + isqrt, + isreal, + issubnormal, + iszero, + isone, + lcm, + ldexp, + leading_ones, + leading_zeros, + log, + log10, + log1p, + log2, + maxintfloat, + mod, + mod1, + modf, + mod2pi, + muladd, + nextfloat, + nextpow, + nextprod, + numerator, + one, + oneunit, + powermod, + prevfloat, + prevpow, + rad2deg, + rationalize, + real, + floatmax, + floatmin, + reim, + reinterpret, + rem, + rem2pi, + round, + sec, + secd, + sech, + sign, + signbit, + signed, + significand, + sin, + sinc, + sincos, + sincosd, + sind, + sinh, + sinpi, + sqrt, + tan, + tand, + tanh, + trailing_ones, + trailing_zeros, + trunc, + unsafe_trunc, + typemax, + typemin, + unsigned, + widemul, + zero, + √, + ∛, + ≈, + ≉, + +# arrays + axes, + broadcast!, + broadcast, + cat, + checkbounds, + checkindex, + circcopy!, + circshift, + circshift!, + clamp!, + conj!, + copy!, + copyto!, + diff, + cumprod, + cumprod!, + cumsum, + cumsum!, + accumulate, + accumulate!, + eachcol, + eachindex, + eachrow, + eachslice, + extrema, + fill!, + fill, + first, + hcat, + hvcat, + indexin, + argmax, + argmin, + invperm, + invpermute!, + isassigned, + isperm, + issorted, + last, + mapslices, + max, + maximum!, + maximum, + min, + minimum!, + minimum, + minmax, + ndims, + ones, + parent, + parentindices, + partialsort, + partialsort!, + partialsortperm, + partialsortperm!, + permute!, + permutedims, + permutedims!, + prod!, + prod, + promote_shape, + range, + reshape, + reverse!, + reverse, + rot180, + rotl90, + rotr90, + size, + selectdim, + sort!, + sort, + sortperm, + sortperm!, + sortslices, + dropdims, + step, + stride, + strides, + sum!, + sum, + to_indices, + vcat, + vec, + view, + zeros, + +# search, find, match and related functions + eachmatch, + endswith, + findall, + findfirst, + findlast, + findmax, + findmin, + findmin!, + findmax!, + findnext, + findprev, + match, + occursin, + searchsorted, + searchsortedfirst, + searchsortedlast, + startswith, + +# linear algebra + adjoint, + transpose, + kron, + +# bitarrays + falses, + trues, + +# dequeues + append!, + insert!, + pop!, + prepend!, + push!, + resize!, + popfirst!, + pushfirst!, + +# collections + all!, + all, + allunique, + any!, + any, + firstindex, + collect, + count, + delete!, + deleteat!, + eltype, + empty!, + empty, + lastindex, + filter!, + filter, + foldl, + foldr, + foreach, + get, + get!, + getindex, + getkey, + haskey, + in, + intersect!, + intersect, + isempty, + issubset, + issetequal, + keys, + keytype, + length, + map!, + map, + mapfoldl, + mapfoldr, + mapreduce, + merge!, + merge, + pairs, + reduce, + setdiff!, + setdiff, + setindex!, + similar, + sizehint!, + splice!, + symdiff!, + symdiff, + union!, + union, + unique!, + unique, + values, + valtype, + ∈, + ∉, + ∋, + ∌, + ⊆, + ⊈, + ⊊, + ⊇, + ⊉, + ⊋, + ∩, + ∪, + +# strings + ascii, + bitstring, + bytes2hex, + chomp, + chop, + codepoint, + codeunit, + codeunits, + digits, + digits!, + escape_string, + hex2bytes, + hex2bytes!, + isascii, + iscntrl, + isdigit, + isletter, + islowercase, + isnumeric, + isprint, + ispunct, + isspace, + isuppercase, + isxdigit, + lowercase, + lowercasefirst, + isvalid, + join, + lpad, + lstrip, + ncodeunits, + ndigits, + nextind, + prevind, + repeat, + replace, + replace!, + repr, + reverseind, + rpad, + rsplit, + rstrip, + split, + string, + strip, + textwidth, + thisind, + titlecase, + transcode, + unescape_string, + uppercase, + uppercasefirst, + +# text output + IOContext, + displaysize, + dump, + print, + println, + printstyled, + show, + showerror, + sprint, + summary, + +# logging + @debug, + @info, + @warn, + @error, + +# bigfloat & precision + precision, + rounding, + setprecision, + setrounding, + get_zero_subnormals, + set_zero_subnormals, + +# iteration + iterate, + + enumerate, # re-exported from Iterators + zip, + +# object identity and equality + copy, + deepcopy, + hash, + identity, + isbits, + isequal, + isimmutable, + isless, + ifelse, + objectid, + sizeof, + +# tasks and conditions + Condition, + current_task, + islocked, + istaskdone, + istaskstarted, + istaskfailed, + lock, + notify, + ReentrantLock, + schedule, + task_local_storage, + trylock, + unlock, + yield, + yieldto, + wait, + timedwait, + asyncmap, + asyncmap!, + +# channels + take!, + put!, + isready, + fetch, + bind, + +# missing values + coalesce, + ismissing, + missing, + skipmissing, + something, + isnothing, + nonmissingtype, + +# time + sleep, + time, + time_ns, + +# errors + backtrace, + catch_backtrace, + error, + rethrow, + retry, + systemerror, + +# stack traces + stacktrace, + +# types + convert, + getproperty, + setproperty!, + fieldoffset, + fieldname, + fieldnames, + fieldcount, + fieldtypes, + hasfield, + propertynames, + hasproperty, + isabstracttype, + isbitstype, + isprimitivetype, + isstructtype, + isconcretetype, + isdispatchtuple, + oftype, + promote, + promote_rule, + promote_type, + instances, + supertype, + typeintersect, + typejoin, + widen, + +# syntax + esc, + gensym, + macroexpand, + @macroexpand1, + @macroexpand, + parse, + +# help and reflection + code_typed, + code_lowered, + fullname, + functionloc, + isconst, + isinteractive, + hasmethod, + methods, + nameof, + parentmodule, + pathof, + names, + which, + @isdefined, + +# loading source files + __precompile__, + evalfile, + include_string, + include_dependency, + +# RTS internals + GC, + finalizer, + finalize, + precompile, + +# misc + atexit, + atreplinit, + exit, + ntuple, + +# I/O and events + close, + countlines, + eachline, + eof, + fd, + fdio, + flush, + gethostname, + htol, + hton, + ismarked, + isopen, + isreadonly, + ltoh, + mark, + bytesavailable, + ntoh, + open, + pipeline, + Pipe, + PipeBuffer, + position, + RawFD, + read, + read!, + readavailable, + readbytes!, + readchomp, + readdir, + readline, + readlines, + readuntil, + redirect_stderr, + redirect_stdin, + redirect_stdout, + reset, + seek, + seekend, + seekstart, + skip, + skipchars, + take!, + truncate, + unmark, + unsafe_read, + unsafe_write, + write, + +# multimedia I/O + AbstractDisplay, + display, + displayable, + TextDisplay, + istextmime, + MIME, + @MIME_str, + popdisplay, + pushdisplay, + redisplay, + showable, + HTML, + Text, + +# paths and file names + abspath, + basename, + dirname, + expanduser, + homedir, + isabspath, + isdirpath, + joinpath, + normpath, + realpath, + relpath, + splitdir, + splitdrive, + splitext, + splitpath, + +# filesystem operations + cd, + chmod, + chown, + cp, + ctime, + download, + filemode, + filesize, + gperm, + isblockdev, + ischardev, + isdir, + isfifo, + isfile, + islink, + ismount, + ispath, + isreadable, + issetgid, + issetuid, + issocket, + issticky, + iswritable, + lstat, + mkdir, + mkpath, + mktemp, + mktempdir, + mtime, + mv, + operm, + pwd, + readlink, + rm, + stat, + symlink, + tempdir, + tempname, + touch, + uperm, + walkdir, + +# external processes ## TODO: whittle down these exports. + detach, + getpid, + ignorestatus, + kill, + process_exited, + process_running, + run, + setenv, + success, + withenv, + +# C interface + @cfunction, + cglobal, + disable_sigint, + pointer, + pointer_from_objref, + unsafe_wrap, + unsafe_string, + reenable_sigint, + unsafe_copyto!, + unsafe_load, + unsafe_pointer_to_objref, + unsafe_store!, + +# implemented in Random module + rand, + randn, + +# Macros + # parser internal + @__FILE__, + @__DIR__, + @__LINE__, + @__MODULE__, + @int128_str, + @uint128_str, + @big_str, + @cmd, # `commands` + + # notation for certain types + @b_str, # byte vector + @r_str, # regex + @s_str, # regex substitution string + @v_str, # version number + @raw_str, # raw string with no interpolation/unescaping + + # documentation + @text_str, + @html_str, + @doc, + + # output + @show, + + # profiling + @time, + @timed, + @timev, + @elapsed, + @allocated, + + # tasks + @sync, + @async, + @task, + @threadcall, + + # metaprogramming utilities + @generated, + @gensym, + @eval, + @deprecate, + + # performance annotations + @boundscheck, + @inbounds, + @fastmath, + @simd, + @inline, + @noinline, + @nospecialize, + @specialize, + @polly, + + @assert, + @__dot__, + @enum, + @label, + @goto, + @view, + @views, + @static diff --git a/base/expr.jl b/base/expr.jl new file mode 100644 index 0000000..c6a02e4 --- /dev/null +++ b/base/expr.jl @@ -0,0 +1,429 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## symbols ## + +""" + gensym([tag]) + +Generates a symbol which will not conflict with other variable names. +""" +gensym() = ccall(:jl_gensym, Ref{Symbol}, ()) + +gensym(s::String) = ccall(:jl_tagged_gensym, Ref{Symbol}, (Ptr{UInt8}, Int32), s, sizeof(s)) + +gensym(ss::String...) = map(gensym, ss) +gensym(s::Symbol) = + ccall(:jl_tagged_gensym, Ref{Symbol}, (Ptr{UInt8}, Int32), s, ccall(:strlen, Csize_t, (Ptr{UInt8},), s)) + +""" + @gensym + +Generates a gensym symbol for a variable. For example, `@gensym x y` is transformed into +`x = gensym("x"); y = gensym("y")`. +""" +macro gensym(names...) + blk = Expr(:block) + for name in names + push!(blk.args, :($(esc(name)) = gensym($(string(name))))) + end + push!(blk.args, :nothing) + return blk +end + +## expressions ## + +function copy(e::Expr) + n = Expr(e.head) + n.args = copy_exprargs(e.args) + return n +end + +# copy parts of an AST that the compiler mutates +copy_exprs(@nospecialize(x)) = x +copy_exprs(x::Expr) = copy(x) +function copy_exprs(x::PhiNode) + new_values = Vector{Any}(undef, length(x.values)) + for i = 1:length(x.values) + isassigned(x.values, i) || continue + new_values[i] = copy_exprs(x.values[i]) + end + return PhiNode(copy(x.edges), new_values) +end +function copy_exprs(x::PhiCNode) + new_values = Vector{Any}(undef, length(x.values)) + for i = 1:length(x.values) + isassigned(x.values, i) || continue + new_values[i] = copy_exprs(x.values[i]) + end + return PhiCNode(new_values) +end +copy_exprargs(x::Array{Any,1}) = Any[copy_exprs(x[i]) for i in 1:length(x)] + +# create copies of the CodeInfo definition, and any mutable fields +function copy(c::CodeInfo) + cnew = ccall(:jl_copy_code_info, Ref{CodeInfo}, (Any,), c) + cnew.code = copy_exprargs(cnew.code) + cnew.slotnames = copy(cnew.slotnames) + cnew.slotflags = copy(cnew.slotflags) + cnew.codelocs = copy(cnew.codelocs) + cnew.linetable = copy(cnew.linetable) + cnew.ssaflags = copy(cnew.ssaflags) + ssavaluetypes = cnew.ssavaluetypes + ssavaluetypes isa Vector{Any} && (cnew.ssavaluetypes = copy(ssavaluetypes)) + return cnew +end + + +==(x::Expr, y::Expr) = x.head === y.head && isequal(x.args, y.args) +==(x::QuoteNode, y::QuoteNode) = isequal(x.value, y.value) + +""" + macroexpand(m::Module, x; recursive=true) + +Take the expression `x` and return an equivalent expression with all macros removed (expanded) +for executing in module `m`. +The `recursive` keyword controls whether deeper levels of nested macros are also expanded. +This is demonstrated in the example below: +```julia-repl +julia> module M + macro m1() + 42 + end + macro m2() + :(@m1()) + end + end +M + +julia> macroexpand(M, :(@m2()), recursive=true) +42 + +julia> macroexpand(M, :(@m2()), recursive=false) +:(#= REPL[16]:6 =# M.@m1) +``` +""" +function macroexpand(m::Module, @nospecialize(x); recursive=true) + if recursive + ccall(:jl_macroexpand, Any, (Any, Any), x, m) + else + ccall(:jl_macroexpand1, Any, (Any, Any), x, m) + end +end + +""" + @macroexpand + +Return equivalent expression with all macros removed (expanded). + +There are differences between `@macroexpand` and [`macroexpand`](@ref). + +* While [`macroexpand`](@ref) takes a keyword argument `recursive`, `@macroexpand` +is always recursive. For a non recursive macro version, see [`@macroexpand1`](@ref). + +* While [`macroexpand`](@ref) has an explicit `module` argument, `@macroexpand` always +expands with respect to the module in which it is called. +This is best seen in the following example: +```julia-repl +julia> module M + macro m() + 1 + end + function f() + (@macroexpand(@m), + macroexpand(M, :(@m)), + macroexpand(Main, :(@m)) + ) + end + end +M + +julia> macro m() + 2 + end +@m (macro with 1 method) + +julia> M.f() +(1, 1, 2) +``` +With `@macroexpand` the expression expands where `@macroexpand` appears in the code (module `M` in the example). +With `macroexpand` the expression expands in the module given as the first argument. +""" +macro macroexpand(code) + return :(macroexpand($__module__, $(QuoteNode(code)), recursive=true)) +end + + +""" + @macroexpand1 + +Non recursive version of [`@macroexpand`](@ref). +""" +macro macroexpand1(code) + return :(macroexpand($__module__, $(QuoteNode(code)), recursive=false)) +end + +## misc syntax ## + +""" + Core.eval(m::Module, expr) + +Evaluate an expression in the given module and return the result. +""" +Core.eval + +""" + @inline + +Give a hint to the compiler that this function is worth inlining. + +Small functions typically do not need the `@inline` annotation, +as the compiler does it automatically. By using `@inline` on bigger functions, +an extra nudge can be given to the compiler to inline it. +This is shown in the following example: + +```julia +@inline function bigfunction(x) + #= + Function Definition + =# +end +``` +""" +macro inline(ex) + esc(isa(ex, Expr) ? pushmeta!(ex, :inline) : ex) +end + +""" + @noinline + +Prevents the compiler from inlining a function. + +Small functions are typically inlined automatically. +By using `@noinline` on small functions, auto-inlining can be +prevented. This is shown in the following example: + +```julia +@noinline function smallfunction(x) + #= + Function Definition + =# +end +``` +""" +macro noinline(ex) + esc(isa(ex, Expr) ? pushmeta!(ex, :noinline) : ex) +end + +""" + @pure ex + @pure(ex) + +`@pure` gives the compiler a hint for the definition of a pure function, +helping for type inference. + +A pure function can only depend on immutable information. +This also means a `@pure` function cannot use any global mutable state, including +generic functions. Calls to generic functions depend on method tables which are +mutable global state. +Use with caution, incorrect `@pure` annotation of a function may introduce +hard to identify bugs. Double check for calls to generic functions. +""" +macro pure(ex) + esc(isa(ex, Expr) ? pushmeta!(ex, :pure) : ex) +end + +""" + @propagate_inbounds + +Tells the compiler to inline a function while retaining the caller's inbounds context. +""" +macro propagate_inbounds(ex) + if isa(ex, Expr) + pushmeta!(ex, :inline) + pushmeta!(ex, :propagate_inbounds) + esc(ex) + else + esc(ex) + end +end + +""" + @polly + +Tells the compiler to apply the polyhedral optimizer Polly to a function. +""" +macro polly(ex) + esc(isa(ex, Expr) ? pushmeta!(ex, :polly) : ex) +end + +## some macro utilities ## + +function pushmeta!(ex::Expr, sym::Symbol, args::Any...) + if isempty(args) + tag = sym + else + tag = Expr(sym, args...) + end + + inner = ex + while inner.head === :macrocall + inner = inner.args[end]::Expr + end + + idx, exargs = findmeta(inner) + if idx != 0 + push!(exargs[idx].args, tag) + else + body::Expr = inner.args[2] + pushfirst!(body.args, Expr(:meta, tag)) + end + ex +end + +popmeta!(body, sym) = _getmeta(body, sym, true) +peekmeta(body, sym) = _getmeta(body, sym, false) + +function _getmeta(body::Expr, sym::Symbol, delete::Bool) + body.head === :block || return false, [] + _getmeta(body.args, sym, delete) +end +_getmeta(arg, sym, delete::Bool) = (false, []) +function _getmeta(body::Array{Any,1}, sym::Symbol, delete::Bool) + idx, blockargs = findmeta_block(body, args -> findmetaarg(args,sym)!=0) + if idx == 0 + return false, [] + end + metaargs = blockargs[idx].args + i = findmetaarg(blockargs[idx].args, sym) + if i == 0 + return false, [] + end + ret = isa(metaargs[i], Expr) ? (metaargs[i]::Expr).args : [] + if delete + deleteat!(metaargs, i) + isempty(metaargs) && deleteat!(blockargs, idx) + end + true, ret +end + +# Find index of `sym` in a meta expression argument list, or 0. +function findmetaarg(metaargs, sym) + for i = 1:length(metaargs) + arg = metaargs[i] + if (isa(arg, Symbol) && (arg::Symbol) == sym) || + (isa(arg, Expr) && (arg::Expr).head == sym) + return i + end + end + return 0 +end + +function is_short_function_def(ex) + ex.head === :(=) || return false + while length(ex.args) >= 1 && isa(ex.args[1], Expr) + (ex.args[1].head === :call) && return true + (ex.args[1].head === :where || ex.args[1].head === :(::)) || return false + ex = ex.args[1] + end + return false +end + +function findmeta(ex::Expr) + if ex.head === :function || is_short_function_def(ex) + body::Expr = ex.args[2] + body.head === :block || error(body, " is not a block expression") + return findmeta_block(ex.args) + end + error(ex, " is not a function expression") +end + +findmeta(ex::Array{Any,1}) = findmeta_block(ex) + +function findmeta_block(exargs, argsmatch=args->true) + for i = 1:length(exargs) + a = exargs[i] + if isa(a, Expr) + if (a::Expr).head === :meta && argsmatch((a::Expr).args) + return i, exargs + elseif (a::Expr).head === :block + idx, exa = findmeta_block(a.args, argsmatch) + if idx != 0 + return idx, exa + end + end + end + end + return 0, [] +end + +remove_linenums!(ex) = ex +function remove_linenums!(ex::Expr) + if ex.head === :block || ex.head === :quote + # remove line number expressions from metadata (not argument literal or inert) position + filter!(ex.args) do x + isa(x, Expr) && x.head === :line && return false + isa(x, LineNumberNode) && return false + return true + end + end + for subex in ex.args + subex isa Expr && remove_linenums!(subex) + end + return ex +end +function remove_linenums!(src::CodeInfo) + src.codelocs .= 0 + length(src.linetable) > 1 && resize!(src.linetable, 1) + return src +end + +macro generated() + return Expr(:generated) +end + +""" + @generated f + @generated(f) +`@generated` is used to annotate a function which will be generated. +In the body of the generated function, only types of arguments can be read +(not the values). The function returns a quoted expression evaluated when the +function is called. The `@generated` macro should not be used on functions mutating +the global scope or depending on mutable elements. + +See [Metaprogramming](@ref) for further details. + +## Example: +```julia +julia> @generated function bar(x) + if x <: Integer + return :(x ^ 2) + else + return :(x) + end + end +bar (generic function with 1 method) + +julia> bar(4) +16 + +julia> bar("baz") +"baz" +``` +""" +macro generated(f) + if isa(f, Expr) && (f.head === :function || is_short_function_def(f)) + body = f.args[2] + lno = body.args[1] + return Expr(:escape, + Expr(f.head, f.args[1], + Expr(:block, + lno, + Expr(:if, Expr(:generated), + body, + Expr(:block, + Expr(:meta, :generated_only), + Expr(:return, nothing)))))) + else + error("invalid syntax; @generated must be used with a function definition") + end +end diff --git a/base/fastmath.jl b/base/fastmath.jl new file mode 100644 index 0000000..d8159ba --- /dev/null +++ b/base/fastmath.jl @@ -0,0 +1,380 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Support for @fastmath + +# This module provides versions of math functions that may violate +# strict IEEE semantics. + +# This allows the following transformations. For more information see +# http://llvm.org/docs/LangRef.html#fast-math-flags: +# nnan: No NaNs - Allow optimizations to assume the arguments and +# result are not NaN. Such optimizations are required to retain +# defined behavior over NaNs, but the value of the result is +# undefined. +# ninf: No Infs - Allow optimizations to assume the arguments and +# result are not +/-Inf. Such optimizations are required to +# retain defined behavior over +/-Inf, but the value of the +# result is undefined. +# nsz: No Signed Zeros - Allow optimizations to treat the sign of a +# zero argument or result as insignificant. +# arcp: Allow Reciprocal - Allow optimizations to use the reciprocal +# of an argument rather than perform division. + +module FastMath + +export @fastmath + +import Core.Intrinsics: sqrt_llvm, neg_float_fast, + add_float_fast, sub_float_fast, mul_float_fast, div_float_fast, rem_float_fast, + eq_float_fast, ne_float_fast, lt_float_fast, le_float_fast + +const fast_op = + Dict(# basic arithmetic + :+ => :add_fast, + :- => :sub_fast, + :* => :mul_fast, + :/ => :div_fast, + :(==) => :eq_fast, + :!= => :ne_fast, + :< => :lt_fast, + :<= => :le_fast, + :abs => :abs_fast, + :abs2 => :abs2_fast, + :cmp => :cmp_fast, + :conj => :conj_fast, + :inv => :inv_fast, + :rem => :rem_fast, + :sign => :sign_fast, + :isfinite => :isfinite_fast, + :isinf => :isinf_fast, + :isnan => :isnan_fast, + :issubnormal => :issubnormal_fast, + # math functions + :^ => :pow_fast, + :acos => :acos_fast, + :acosh => :acosh_fast, + :angle => :angle_fast, + :asin => :asin_fast, + :asinh => :asinh_fast, + :atan => :atan_fast, + :atanh => :atanh_fast, + :cbrt => :cbrt_fast, + :cis => :cis_fast, + :cos => :cos_fast, + :cosh => :cosh_fast, + :exp10 => :exp10_fast, + :exp2 => :exp2_fast, + :exp => :exp_fast, + :expm1 => :expm1_fast, + :hypot => :hypot_fast, + :log10 => :log10_fast, + :log1p => :log1p_fast, + :log2 => :log2_fast, + :log => :log_fast, + :max => :max_fast, + :min => :min_fast, + :minmax => :minmax_fast, + :sin => :sin_fast, + :sincos => :sincos_fast, + :sinh => :sinh_fast, + :sqrt => :sqrt_fast, + :tan => :tan_fast, + :tanh => :tanh_fast) + +const rewrite_op = + Dict(:+= => :+, + :-= => :-, + :*= => :*, + :/= => :/, + :^= => :^) + +function make_fastmath(expr::Expr) + if expr.head === :quote + return expr + elseif expr.head === :call && expr.args[1] === :^ && expr.args[3] isa Integer + # mimic Julia's literal_pow lowering of literal integer powers + return Expr(:call, :(Base.FastMath.pow_fast), make_fastmath(expr.args[2]), Val{expr.args[3]}()) + end + op = get(rewrite_op, expr.head, :nothing) + if op !== :nothing + var = expr.args[1] + rhs = expr.args[2] + if isa(var, Symbol) + # simple assignment + expr = :($var = $op($var, $rhs)) + elseif isa(var, Expr) && var.head === :ref + # array reference + arr = var.args[1] + inds = var.args[2:end] + arrvar = gensym() + indvars = Any[gensym() for i in inds] + expr = quote + $(Expr(:(=), arrvar, arr)) + $(Expr(:(=), Expr(:tuple, indvars...), Expr(:tuple, inds...))) + $arrvar[$(indvars...)] = $op($arrvar[$(indvars...)], $rhs) + end + end + end + Expr(make_fastmath(expr.head), map(make_fastmath, expr.args)...) +end +function make_fastmath(symb::Symbol) + fast_symb = get(fast_op, symb, :nothing) + if fast_symb === :nothing + return symb + end + :(Base.FastMath.$fast_symb) +end +make_fastmath(expr) = expr + +""" + @fastmath expr + +Execute a transformed version of the expression, which calls functions that +may violate strict IEEE semantics. This allows the fastest possible operation, +but results are undefined -- be careful when doing this, as it may change numerical +results. + +This sets the [LLVM Fast-Math flags](http://llvm.org/docs/LangRef.html#fast-math-flags), +and corresponds to the `-ffast-math` option in clang. See [the notes on performance +annotations](@ref man-performance-annotations) for more details. + +# Examples +```jldoctest +julia> @fastmath 1+2 +3 + +julia> @fastmath(sin(3)) +0.1411200080598672 +``` +""" +macro fastmath(expr) + make_fastmath(esc(expr)) +end + + +# Basic arithmetic + +const FloatTypes = Union{Float32,Float64} + +sub_fast(x::FloatTypes) = neg_float_fast(x) + +add_fast(x::T, y::T) where {T<:FloatTypes} = add_float_fast(x, y) +sub_fast(x::T, y::T) where {T<:FloatTypes} = sub_float_fast(x, y) +mul_fast(x::T, y::T) where {T<:FloatTypes} = mul_float_fast(x, y) +div_fast(x::T, y::T) where {T<:FloatTypes} = div_float_fast(x, y) +rem_fast(x::T, y::T) where {T<:FloatTypes} = rem_float_fast(x, y) + +add_fast(x::T, y::T, zs::T...) where {T<:FloatTypes} = + add_fast(add_fast(x, y), zs...) +mul_fast(x::T, y::T, zs::T...) where {T<:FloatTypes} = + mul_fast(mul_fast(x, y), zs...) + +@fastmath begin + cmp_fast(x::T, y::T) where {T<:FloatTypes} = ifelse(x==y, 0, ifelse(x x, y, x) + min_fast(x::T, y::T) where {T<:FloatTypes} = ifelse(y > x, x, y) + minmax_fast(x::T, y::T) where {T<:FloatTypes} = ifelse(y > x, (x,y), (y,x)) + + max_fast(x::T, y::T, z::T...) where {T<:FloatTypes} = max_fast(max_fast(x, y), z...) + min_fast(x::T, y::T, z::T...) where {T<:FloatTypes} = min_fast(min_fast(x, y), z...) +end + +# fall-back implementations and type promotion + +for op in (:abs, :abs2, :conj, :inv, :sign) + op_fast = fast_op[op] + @eval begin + # fall-back implementation for non-numeric types + $op_fast(xs...) = $op(xs...) + end +end + +for op in (:+, :-, :*, :/, :(==), :!=, :<, :<=, :cmp, :rem, :min, :max, :minmax) + op_fast = fast_op[op] + @eval begin + # fall-back implementation for non-numeric types + $op_fast(xs...) = $op(xs...) + # type promotion + $op_fast(x::Number, y::Number, zs::Number...) = + $op_fast(promote(x,y,zs...)...) + # fall-back implementation that applies after promotion + $op_fast(x::T,ys::T...) where {T<:Number} = $op(x,ys...) + end +end + + +# Math functions + +# builtins + +pow_fast(x::Float32, y::Integer) = ccall("llvm.powi.f32", llvmcall, Float32, (Float32, Int32), x, y) +pow_fast(x::Float64, y::Integer) = ccall("llvm.powi.f64", llvmcall, Float64, (Float64, Int32), x, y) +pow_fast(x::FloatTypes, ::Val{p}) where {p} = pow_fast(x, p) # inlines already via llvm.powi +@inline pow_fast(x, v::Val) = Base.literal_pow(^, x, v) + +sqrt_fast(x::FloatTypes) = sqrt_llvm(x) + +# libm + +const libm = Base.libm_name + +for f in (:acosh, :asinh, :atanh, :cbrt, + :cosh, :exp2, :expm1, :log10, :log1p, :log2, + :log, :sinh, :tanh) + f_fast = fast_op[f] + @eval begin + $f_fast(x::Float32) = + ccall(($(string(f,"f")),libm), Float32, (Float32,), x) + $f_fast(x::Float64) = + ccall(($(string(f)),libm), Float64, (Float64,), x) + end +end + +pow_fast(x::Float32, y::Float32) = + ccall(("powf",libm), Float32, (Float32,Float32), x, y) +pow_fast(x::Float64, y::Float64) = + ccall(("pow",libm), Float64, (Float64,Float64), x, y) + +sincos_fast(v::FloatTypes) = sincos(v) + +@inline function sincos_fast(v::Float16) + s, c = sincos_fast(Float32(v)) + return Float16(s), Float16(c) +end +sincos_fast(v::AbstractFloat) = (sin_fast(v), cos_fast(v)) +sincos_fast(v::Real) = sincos_fast(float(v)::AbstractFloat) +sincos_fast(v) = (sin_fast(v), cos_fast(v)) + +@fastmath begin + hypot_fast(x::T, y::T) where {T<:FloatTypes} = sqrt(x*x + y*y) + + # complex numbers + + function cis_fast(x::T) where {T<:FloatTypes} + s, c = sincos_fast(x) + Complex{T}(c, s) + end + + # See + pow_fast(x::T, y::T) where {T<:ComplexTypes} = exp(y*log(x)) + pow_fast(x::T, y::Complex{T}) where {T<:FloatTypes} = exp(y*log(x)) + pow_fast(x::Complex{T}, y::T) where {T<:FloatTypes} = exp(y*log(x)) + acos_fast(x::T) where {T<:ComplexTypes} = + convert(T,π)/2 + im*log(im*x + sqrt(1-x*x)) + acosh_fast(x::ComplexTypes) = log(x + sqrt(x+1) * sqrt(x-1)) + angle_fast(x::ComplexTypes) = atan(imag(x), real(x)) + asin_fast(x::ComplexTypes) = -im*asinh(im*x) + asinh_fast(x::ComplexTypes) = log(x + sqrt(1+x*x)) + atan_fast(x::ComplexTypes) = -im*atanh(im*x) + atanh_fast(x::T) where {T<:ComplexTypes} = convert(T,1)/2*(log(1+x) - log(1-x)) + cis_fast(x::ComplexTypes) = exp(-imag(x)) * cis(real(x)) + cos_fast(x::ComplexTypes) = cosh(im*x) + cosh_fast(x::T) where {T<:ComplexTypes} = convert(T,1)/2*(exp(x) + exp(-x)) + exp10_fast(x::T) where {T<:ComplexTypes} = + exp10(real(x)) * cis(imag(x)*log(convert(T,10))) + exp2_fast(x::T) where {T<:ComplexTypes} = + exp2(real(x)) * cis(imag(x)*log(convert(T,2))) + exp_fast(x::ComplexTypes) = exp(real(x)) * cis(imag(x)) + expm1_fast(x::ComplexTypes) = exp(x)-1 + log10_fast(x::T) where {T<:ComplexTypes} = log(x) / log(convert(T,10)) + log1p_fast(x::ComplexTypes) = log(1+x) + log2_fast(x::T) where {T<:ComplexTypes} = log(x) / log(convert(T,2)) + log_fast(x::T) where {T<:ComplexTypes} = T(log(abs2(x))/2, angle(x)) + log_fast(b::T, x::T) where {T<:ComplexTypes} = T(log(x)/log(b)) + sin_fast(x::ComplexTypes) = -im*sinh(im*x) + sinh_fast(x::T) where {T<:ComplexTypes} = convert(T,1)/2*(exp(x) - exp(-x)) + sqrt_fast(x::ComplexTypes) = sqrt(abs(x)) * cis(angle(x)/2) + tan_fast(x::ComplexTypes) = -im*tanh(im*x) + tanh_fast(x::ComplexTypes) = (a=exp(x); b=exp(-x); (a-b)/(a+b)) +end + +# fall-back implementations and type promotion + +for f in (:acos, :acosh, :angle, :asin, :asinh, :atan, :atanh, :cbrt, + :cis, :cos, :cosh, :exp10, :exp2, :exp, :expm1, + :log10, :log1p, :log2, :log, :sin, :sinh, :sqrt, :tan, + :tanh) + f_fast = fast_op[f] + @eval begin + $f_fast(x) = $f(x) + end +end + +for f in (:^, :atan, :hypot, :log) + f_fast = fast_op[f] + @eval begin + # fall-back implementation for non-numeric types + $f_fast(x, y) = $f(x, y) + # type promotion + $f_fast(x::Number, y::Number) = $f_fast(promote(x, y)...) + # fall-back implementation that applies after promotion + $f_fast(x::T, y::T) where {T<:Number} = $f(x, y) + end +end + +end diff --git a/base/file.jl b/base/file.jl new file mode 100644 index 0000000..8436e62 --- /dev/null +++ b/base/file.jl @@ -0,0 +1,910 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Operations with the file system (paths) ## + +export + cd, + chmod, + chown, + cp, + cptree, + mkdir, + mkpath, + mktemp, + mktempdir, + mv, + pwd, + rename, + readlink, + readdir, + rm, + samefile, + sendfile, + symlink, + tempdir, + tempname, + touch, + unlink, + walkdir + +# get and set current directory + +""" + pwd() -> AbstractString + +Get the current working directory. + +# Examples +```julia-repl +julia> pwd() +"/home/JuliaUser" + +julia> cd("/home/JuliaUser/Projects/julia") + +julia> pwd() +"/home/JuliaUser/Projects/julia" +``` +""" +function pwd() + buf = Base.StringVector(AVG_PATH - 1) # space for null-terminator implied by StringVector + sz = RefValue{Csize_t}(length(buf) + 1) # total buffer size including null + while true + rc = ccall(:uv_cwd, Cint, (Ptr{UInt8}, Ptr{Csize_t}), buf, sz) + if rc == 0 + resize!(buf, sz[]) + return String(buf) + elseif rc == Base.UV_ENOBUFS + resize!(buf, sz[] - 1) # space for null-terminator implied by StringVector + else + uv_error(:cwd, rc) + end + end +end + + +""" + cd(dir::AbstractString=homedir()) + +Set the current working directory. + +# Examples +```julia-repl +julia> cd("/home/JuliaUser/Projects/julia") + +julia> pwd() +"/home/JuliaUser/Projects/julia" + +julia> cd() + +julia> pwd() +"/home/JuliaUser" +``` +""" +function cd(dir::AbstractString) + uv_error("chdir $dir", ccall(:uv_chdir, Cint, (Cstring,), dir)) +end +cd() = cd(homedir()) + +if Sys.iswindows() + function cd(f::Function, dir::AbstractString) + old = pwd() + try + cd(dir) + f() + finally + cd(old) + end + end +else + function cd(f::Function, dir::AbstractString) + fd = ccall(:open, Int32, (Cstring, Int32), :., 0) + systemerror(:open, fd == -1) + try + cd(dir) + f() + finally + systemerror(:fchdir, ccall(:fchdir, Int32, (Int32,), fd) != 0) + systemerror(:close, ccall(:close, Int32, (Int32,), fd) != 0) + end + end +end +""" + cd(f::Function, dir::AbstractString=homedir()) + +Temporarily change the current working directory to `dir`, apply function `f` and +finally return to the original directory. + +# Examples +```julia-repl +julia> pwd() +"/home/JuliaUser" + +julia> cd(readdir, "/home/JuliaUser/Projects/julia") +34-element Array{String,1}: + ".circleci" + ".freebsdci.sh" + ".git" + ".gitattributes" + ".github" + ⋮ + "test" + "ui" + "usr" + "usr-staging" + +julia> pwd() +"/home/JuliaUser" +``` +""" +cd(f::Function) = cd(f, homedir()) + +function checkmode(mode::Integer) + if !(0 <= mode <= 511) + throw(ArgumentError("Mode must be between 0 and 511 = 0o777")) + end + mode +end + +""" + mkdir(path::AbstractString; mode::Unsigned = 0o777) + +Make a new directory with name `path` and permissions `mode`. `mode` defaults to `0o777`, +modified by the current file creation mask. This function never creates more than one +directory. If the directory already exists, or some intermediate directories do not exist, +this function throws an error. See [`mkpath`](@ref) for a function which creates all +required intermediate directories. +Return `path`. + +# Examples +```julia-repl +julia> mkdir("testingdir") +"testingdir" + +julia> cd("testingdir") + +julia> pwd() +"/home/JuliaUser/testingdir" +``` +""" +function mkdir(path::AbstractString; mode::Integer = 0o777) + @static if Sys.iswindows() + ret = ccall(:_wmkdir, Int32, (Cwstring,), path) + else + ret = ccall(:mkdir, Int32, (Cstring, UInt32), path, checkmode(mode)) + end + systemerror(:mkdir, ret != 0; extrainfo=path) + path +end + +""" + mkpath(path::AbstractString; mode::Unsigned = 0o777) + +Create all directories in the given `path`, with permissions `mode`. `mode` defaults to +`0o777`, modified by the current file creation mask. +Return `path`. + +# Examples +```julia-repl +julia> mkdir("testingdir") +"testingdir" + +julia> cd("testingdir") + +julia> pwd() +"/home/JuliaUser/testingdir" + +julia> mkpath("my/test/dir") +"my/test/dir" + +julia> readdir() +1-element Array{String,1}: + "my" + +julia> cd("my") + +julia> readdir() +1-element Array{String,1}: + "test" + +julia> readdir("test") +1-element Array{String,1}: + "dir" +``` +""" +function mkpath(path::AbstractString; mode::Integer = 0o777) + isdirpath(path) && (path = dirname(path)) + dir = dirname(path) + (path == dir || isdir(path)) && return path + mkpath(dir, mode = checkmode(mode)) + try + mkdir(path, mode = mode) + catch err + # If there is a problem with making the directory, but the directory + # does in fact exist, then ignore the error. Else re-throw it. + if !isa(err, SystemError) || !isdir(path) + rethrow() + end + end + path +end + +""" + rm(path::AbstractString; force::Bool=false, recursive::Bool=false) + +Delete the file, link, or empty directory at the given path. If `force=true` is passed, a +non-existing path is not treated as error. If `recursive=true` is passed and the path is a +directory, then all contents are removed recursively. + +# Examples +```jldoctest +julia> mkpath("my/test/dir"); + +julia> rm("my", recursive=true) + +julia> rm("this_file_does_not_exist", force=true) + +julia> rm("this_file_does_not_exist") +ERROR: IOError: unlink: no such file or directory (ENOENT) +Stacktrace: +[...] +``` +""" +function rm(path::AbstractString; force::Bool=false, recursive::Bool=false) + if islink(path) || !isdir(path) + try + @static if Sys.iswindows() + # is writable on windows actually means "is deletable" + if (filemode(path) & 0o222) == 0 + chmod(path, 0o777) + end + end + unlink(path) + catch err + if force && isa(err, IOError) && err.code==Base.UV_ENOENT + return + end + rethrow() + end + else + if recursive + for p in readdir(path) + rm(joinpath(path, p), force=force, recursive=true) + end + end + @static if Sys.iswindows() + ret = ccall(:_wrmdir, Int32, (Cwstring,), path) + else + ret = ccall(:rmdir, Int32, (Cstring,), path) + end + systemerror(:rmdir, ret != 0, extrainfo=path) + end +end + + +# The following use Unix command line facilities +function checkfor_mv_cp_cptree(src::AbstractString, dst::AbstractString, txt::AbstractString; + force::Bool=false) + if ispath(dst) + if force + # Check for issue when: (src == dst) or when one is a link to the other + # https://github.com/JuliaLang/julia/pull/11172#issuecomment-100391076 + if Base.samefile(src, dst) + abs_src = islink(src) ? abspath(readlink(src)) : abspath(src) + abs_dst = islink(dst) ? abspath(readlink(dst)) : abspath(dst) + throw(ArgumentError(string("'src' and 'dst' refer to the same file/dir.", + "This is not supported.\n ", + "`src` refers to: $(abs_src)\n ", + "`dst` refers to: $(abs_dst)\n"))) + end + rm(dst; recursive=true) + else + throw(ArgumentError(string("'$dst' exists. `force=true` ", + "is required to remove '$dst' before $(txt)."))) + end + end +end + +function cptree(src::AbstractString, dst::AbstractString; force::Bool=false, + follow_symlinks::Bool=false) + isdir(src) || throw(ArgumentError("'$src' is not a directory. Use `cp(src, dst)`")) + checkfor_mv_cp_cptree(src, dst, "copying"; force=force) + mkdir(dst) + for name in readdir(src) + srcname = joinpath(src, name) + if !follow_symlinks && islink(srcname) + symlink(readlink(srcname), joinpath(dst, name)) + elseif isdir(srcname) + cptree(srcname, joinpath(dst, name); force=force, + follow_symlinks=follow_symlinks) + else + sendfile(srcname, joinpath(dst, name)) + end + end +end + +""" + cp(src::AbstractString, dst::AbstractString; force::Bool=false, follow_symlinks::Bool=false) + +Copy the file, link, or directory from `src` to `dst`. +`force=true` will first remove an existing `dst`. + +If `follow_symlinks=false`, and `src` is a symbolic link, `dst` will be created as a +symbolic link. If `follow_symlinks=true` and `src` is a symbolic link, `dst` will be a copy +of the file or directory `src` refers to. +Return `dst`. +""" +function cp(src::AbstractString, dst::AbstractString; force::Bool=false, + follow_symlinks::Bool=false) + checkfor_mv_cp_cptree(src, dst, "copying"; force=force) + if !follow_symlinks && islink(src) + symlink(readlink(src), dst) + elseif isdir(src) + cptree(src, dst; force=force, follow_symlinks=follow_symlinks) + else + sendfile(src, dst) + end + dst +end + +""" + mv(src::AbstractString, dst::AbstractString; force::Bool=false) + +Move the file, link, or directory from `src` to `dst`. +`force=true` will first remove an existing `dst`. +Return `dst`. + +# Examples +```jldoctest; filter = r"Stacktrace:(\\n \\[[0-9]+\\].*)*" +julia> write("hello.txt", "world"); + +julia> mv("hello.txt", "goodbye.txt") +"goodbye.txt" + +julia> "hello.txt" in readdir() +false + +julia> readline("goodbye.txt") +"world" + +julia> write("hello.txt", "world2"); + +julia> mv("hello.txt", "goodbye.txt") +ERROR: ArgumentError: 'goodbye.txt' exists. `force=true` is required to remove 'goodbye.txt' before moving. +Stacktrace: + [1] #checkfor_mv_cp_cptree#10(::Bool, ::Function, ::String, ::String, ::String) at ./file.jl:293 +[...] + +julia> mv("hello.txt", "goodbye.txt", force=true) +"goodbye.txt" + +julia> rm("goodbye.txt"); + +``` +""" +function mv(src::AbstractString, dst::AbstractString; force::Bool=false) + checkfor_mv_cp_cptree(src, dst, "moving"; force=force) + rename(src, dst) + dst +end + +""" + touch(path::AbstractString) + +Update the last-modified timestamp on a file to the current time. +Return `path`. + +# Examples +```julia-repl +julia> write("my_little_file", 2); + +julia> mtime("my_little_file") +1.5273815391135583e9 + +julia> touch("my_little_file"); + +julia> mtime("my_little_file") +1.527381559163435e9 +``` + +We can see the [`mtime`](@ref) has been modified by `touch`. +""" +function touch(path::AbstractString) + f = open(path, JL_O_WRONLY | JL_O_CREAT, 0o0666) + try + if Sys.isunix() + ret = ccall(:futimes, Cint, (Cint, Ptr{Cvoid}), fd(f), C_NULL) + systemerror(:futimes, ret != 0, extrainfo=path) + else + t = time() + futime(f,t,t) + end + finally + close(f) + end + path +end + +""" + tempdir() + +Gets the path of the temporary directory. On Windows, `tempdir()` uses the first environment +variable found in the ordered list `TMP`, `TEMP`, `USERPROFILE`. On all other operating +systems, `tempdir()` uses the first environment variable found in the ordered list `TMPDIR`, +`TMP`, `TEMP`, and `TEMPDIR`. If none of these are found, the path `"/tmp"` is used. +""" +function tempdir() + buf = Base.StringVector(AVG_PATH - 1) # space for null-terminator implied by StringVector + sz = RefValue{Csize_t}(length(buf) + 1) # total buffer size including null + while true + rc = ccall(:uv_os_tmpdir, Cint, (Ptr{UInt8}, Ptr{Csize_t}), buf, sz) + if rc == 0 + resize!(buf, sz[]) + return String(buf) + elseif rc == Base.UV_ENOBUFS + resize!(buf, sz[] - 1) # space for null-terminator implied by StringVector + else + uv_error(:tmpdir, rc) + end + end +end + +const TEMP_CLEANUP_MIN = Ref(1024) +const TEMP_CLEANUP_MAX = Ref(1024) +const TEMP_CLEANUP = Dict{String,Bool}() +const TEMP_CLEANUP_LOCK = ReentrantLock() + +function temp_cleanup_later(path::AbstractString; asap::Bool=false) + lock(TEMP_CLEANUP_LOCK) + TEMP_CLEANUP[path] = asap + if length(TEMP_CLEANUP) > TEMP_CLEANUP_MAX[] + temp_cleanup_purge(false) + TEMP_CLEANUP_MAX[] = max(TEMP_CLEANUP_MIN[], 2*length(TEMP_CLEANUP)) + end + unlock(TEMP_CLEANUP_LOCK) + return nothing +end + +function temp_cleanup_purge(all::Bool=true) + need_gc = Sys.iswindows() + for (path, asap) in TEMP_CLEANUP + if (all || asap) && ispath(path) + need_gc && GC.gc(true) + need_gc = false + rm(path, recursive=true, force=true) + end + !ispath(path) && delete!(TEMP_CLEANUP, path) + end +end + +const temp_prefix = "jl_" + +if Sys.iswindows() + +function _win_tempname(temppath::AbstractString, uunique::UInt32) + tempp = cwstring(temppath) + temppfx = cwstring(temp_prefix) + tname = Vector{UInt16}(undef, 32767) + uunique = ccall(:GetTempFileNameW, stdcall, UInt32, + (Ptr{UInt16}, Ptr{UInt16}, UInt32, Ptr{UInt16}), + tempp, temppfx, uunique, tname) + windowserror("GetTempFileName", uunique == 0) + lentname = something(findfirst(iszero, tname)) + @assert lentname > 0 + resize!(tname, lentname - 1) + return transcode(String, tname) +end + +function mktemp(parent::AbstractString=tempdir(); cleanup::Bool=true) + filename = _win_tempname(parent, UInt32(0)) + cleanup && temp_cleanup_later(filename) + return (filename, Base.open(filename, "r+")) +end + +function tempname() + parent = tempdir() + seed::UInt32 = rand(UInt32) + while true + if (seed & typemax(UInt16)) == 0 + seed += 1 + end + filename = _win_tempname(parent, seed) + if !ispath(filename) + return filename + end + seed += 1 + end +end + +else # !windows +# Obtain a temporary filename. +function tempname() + d = tempdir() # tempnam ignores TMPDIR on darwin + p = ccall(:tempnam, Cstring, (Cstring, Cstring), d, temp_prefix) + systemerror(:tempnam, p == C_NULL) + s = unsafe_string(p) + Libc.free(p) + return s +end + +# Create and return the name of a temporary file along with an IOStream +function mktemp(parent::AbstractString=tempdir(); cleanup::Bool=true) + b = joinpath(parent, temp_prefix * "XXXXXX") + p = ccall(:mkstemp, Int32, (Cstring,), b) # modifies b + systemerror(:mktemp, p == -1) + cleanup && temp_cleanup_later(b) + return (b, fdio(p, true)) +end + + +end # os-test + + +""" + tempname() + +Generate a temporary file path. This function only returns a path; no file is +created. The path is likely to be unique, but this cannot be guaranteed. + +!!! warning + + This can lead to security holes if another process obtains the same + file name and creates the file before you are able to. Open the file with + `JL_O_EXCL` if this is a concern. Using [`mktemp()`](@ref) is also recommended instead. +""" +tempname() + +""" + mktemp(parent=tempdir(); cleanup=true) -> (path, io) + +Return `(path, io)`, where `path` is the path of a new temporary file in `parent` +and `io` is an open file object for this path. The `cleanup` option controls whether +the temporary file is automatically deleted when the process exits. +""" +mktemp(parent) + +""" + mktempdir(parent=tempdir(); prefix=$(repr(temp_prefix)), cleanup=true) -> path + +Create a temporary directory in the `parent` directory with a name +constructed from the given prefix and a random suffix, and return its path. +Additionally, any trailing `X` characters may be replaced with random characters. +If `parent` does not exist, throw an error. The `cleanup` option controls whether +the temporary directory is automatically deleted when the process exits. +""" +function mktempdir(parent::AbstractString=tempdir(); + prefix::AbstractString=temp_prefix, cleanup::Bool=true) + if isempty(parent) || occursin(path_separator_re, parent[end:end]) + # append a path_separator only if parent didn't already have one + tpath = "$(parent)$(prefix)XXXXXX" + else + tpath = "$(parent)$(path_separator)$(prefix)XXXXXX" + end + + req = Libc.malloc(_sizeof_uv_fs) + try + ret = ccall(:uv_fs_mkdtemp, Cint, + (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Ptr{Cvoid}), + C_NULL, req, tpath, C_NULL) + if ret < 0 + ccall(:uv_fs_req_cleanup, Cvoid, (Ptr{Cvoid},), req) + uv_error("mktempdir", ret) + end + path = unsafe_string(ccall(:jl_uv_fs_t_path, Cstring, (Ptr{Cvoid},), req)) + ccall(:uv_fs_req_cleanup, Cvoid, (Ptr{Cvoid},), req) + cleanup && temp_cleanup_later(path) + return path + finally + Libc.free(req) + end +end + + +""" + mktemp(f::Function, parent=tempdir()) + +Apply the function `f` to the result of [`mktemp(parent)`](@ref) and remove the +temporary file upon completion. +""" +function mktemp(fn::Function, parent::AbstractString=tempdir()) + (tmp_path, tmp_io) = mktemp(parent, cleanup=false) + try + fn(tmp_path, tmp_io) + finally + try + close(tmp_io) + rm(tmp_path) + catch ex + @error "mktemp cleanup" _group=:file exception=(ex, catch_backtrace()) + # might be possible to remove later + temp_cleanup_later(tmp_path, asap=true) + end + end +end + +""" + mktempdir(f::Function, parent=tempdir(); prefix=$(repr(temp_prefix))) + +Apply the function `f` to the result of [`mktempdir(parent; prefix)`](@ref) and remove the +temporary directory all of its contents upon completion. +""" +function mktempdir(fn::Function, parent::AbstractString=tempdir(); + prefix::AbstractString=temp_prefix) + tmpdir = mktempdir(parent; prefix=prefix, cleanup=false) + try + fn(tmpdir) + finally + try + rm(tmpdir, recursive=true) + catch ex + @error "mktempdir cleanup" _group=:file exception=(ex, catch_backtrace()) + # might be possible to remove later + temp_cleanup_later(tmpdir, asap=true) + end + end +end + +struct uv_dirent_t + name::Ptr{UInt8} + typ::Cint +end + +""" + readdir(dir::AbstractString=".") -> Vector{String} + +Return the files and directories in the directory `dir` (or the current working directory if not given). + +# Examples +```julia-repl +julia> readdir("/home/JuliaUser/Projects/julia") +34-element Array{String,1}: + ".circleci" + ".freebsdci.sh" + ".git" + ".gitattributes" + ".github" + ⋮ + "test" + "ui" + "usr" + "usr-staging" +``` +""" +function readdir(path::AbstractString) + # Allocate space for uv_fs_t struct + uv_readdir_req = zeros(UInt8, ccall(:jl_sizeof_uv_fs_t, Int32, ())) + + # defined in sys.c, to call uv_fs_readdir, which sets errno on error. + err = ccall(:uv_fs_scandir, Int32, (Ptr{Cvoid}, Ptr{UInt8}, Cstring, Cint, Ptr{Cvoid}), + C_NULL, uv_readdir_req, path, 0, C_NULL) + err < 0 && throw(SystemError("unable to read directory $path", -err)) + #uv_error("unable to read directory $path", err) + + # iterate the listing into entries + entries = String[] + ent = Ref{uv_dirent_t}() + while Base.UV_EOF != ccall(:uv_fs_scandir_next, Cint, (Ptr{Cvoid}, Ptr{uv_dirent_t}), uv_readdir_req, ent) + push!(entries, unsafe_string(ent[].name)) + end + + # Clean up the request string + ccall(:uv_fs_req_cleanup, Cvoid, (Ptr{UInt8},), uv_readdir_req) + + return entries +end + +readdir() = readdir(".") + +""" + walkdir(dir; topdown=true, follow_symlinks=false, onerror=throw) + +Return an iterator that walks the directory tree of a directory. +The iterator returns a tuple containing `(rootpath, dirs, files)`. +The directory tree can be traversed top-down or bottom-up. +If `walkdir` encounters a [`SystemError`](@ref) +it will rethrow the error by default. +A custom error handling function can be provided through `onerror` keyword argument. +`onerror` is called with a `SystemError` as argument. + +# Examples +```julia +for (root, dirs, files) in walkdir(".") + println("Directories in \$root") + for dir in dirs + println(joinpath(root, dir)) # path to directories + end + println("Files in \$root") + for file in files + println(joinpath(root, file)) # path to files + end +end +``` + +```julia-repl +julia> mkpath("my/test/dir"); + +julia> itr = walkdir("my"); + +julia> (root, dirs, files) = first(itr) +("my", ["test"], String[]) + +julia> (root, dirs, files) = first(itr) +("my/test", ["dir"], String[]) + +julia> (root, dirs, files) = first(itr) +("my/test/dir", String[], String[]) +``` +""" +function walkdir(root; topdown=true, follow_symlinks=false, onerror=throw) + content = nothing + try + content = readdir(root) + catch err + isa(err, SystemError) || throw(err) + onerror(err) + # Need to return an empty closed channel to skip the current root folder + chnl = Channel(0) + close(chnl) + return chnl + end + dirs = Vector{eltype(content)}() + files = Vector{eltype(content)}() + for name in content + if isdir(joinpath(root, name)) + push!(dirs, name) + else + push!(files, name) + end + end + + function _it(chnl) + if topdown + put!(chnl, (root, dirs, files)) + end + for dir in dirs + path = joinpath(root,dir) + if follow_symlinks || !islink(path) + for (root_l, dirs_l, files_l) in walkdir(path, topdown=topdown, follow_symlinks=follow_symlinks, onerror=onerror) + put!(chnl, (root_l, dirs_l, files_l)) + end + end + end + if !topdown + put!(chnl, (root, dirs, files)) + end + end + + return Channel(_it) +end + +function unlink(p::AbstractString) + err = ccall(:jl_fs_unlink, Int32, (Cstring,), p) + uv_error("unlink", err) + nothing +end + +# For move command +function rename(src::AbstractString, dst::AbstractString) + err = ccall(:jl_fs_rename, Int32, (Cstring, Cstring), src, dst) + # on error, default to cp && rm + if err < 0 + # force: is already done in the mv function + cp(src, dst; force=false, follow_symlinks=false) + rm(src; recursive=true) + end + nothing +end + +function sendfile(src::AbstractString, dst::AbstractString) + src_open = false + dst_open = false + local src_file, dst_file + try + src_file = open(src, JL_O_RDONLY) + src_open = true + dst_file = open(dst, JL_O_CREAT | JL_O_TRUNC | JL_O_WRONLY, filemode(src_file)) + dst_open = true + + bytes = filesize(stat(src_file)) + sendfile(dst_file, src_file, Int64(0), Int(bytes)) + finally + if src_open && isopen(src_file) + close(src_file) + end + if dst_open && isopen(dst_file) + close(dst_file) + end + end +end + +if Sys.iswindows() + const UV_FS_SYMLINK_JUNCTION = 0x0002 +end + +""" + symlink(target::AbstractString, link::AbstractString) + +Creates a symbolic link to `target` with the name `link`. + +!!! note + This function raises an error under operating systems that do not support + soft symbolic links, such as Windows XP. +""" +function symlink(p::AbstractString, np::AbstractString) + @static if Sys.iswindows() + if Sys.windows_version() < Sys.WINDOWS_VISTA_VER + error("Windows XP does not support soft symlinks") + end + end + flags = 0 + @static if Sys.iswindows() + if isdir(p) + flags |= UV_FS_SYMLINK_JUNCTION + p = abspath(p) + end + end + err = ccall(:jl_fs_symlink, Int32, (Cstring, Cstring, Cint), p, np, flags) + @static if Sys.iswindows() + if err < 0 && !isdir(p) + @warn "On Windows, creating file symlinks requires Administrator privileges" maxlog=1 _group=:file + end + end + uv_error("symlink",err) +end + +""" + readlink(path::AbstractString) -> AbstractString + +Return the target location a symbolic link `path` points to. +""" +function readlink(path::AbstractString) + req = Libc.malloc(_sizeof_uv_fs) + try + ret = ccall(:uv_fs_readlink, Int32, + (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Ptr{Cvoid}), + C_NULL, req, path, C_NULL) + if ret < 0 + ccall(:uv_fs_req_cleanup, Cvoid, (Ptr{Cvoid},), req) + uv_error("readlink", ret) + @assert false + end + tgt = unsafe_string(ccall(:jl_uv_fs_t_ptr, Cstring, (Ptr{Cvoid},), req)) + ccall(:uv_fs_req_cleanup, Cvoid, (Ptr{Cvoid},), req) + return tgt + finally + Libc.free(req) + end +end + +""" + chmod(path::AbstractString, mode::Integer; recursive::Bool=false) + +Change the permissions mode of `path` to `mode`. Only integer `mode`s (e.g. `0o777`) are +currently supported. If `recursive=true` and the path is a directory all permissions in +that directory will be recursively changed. +Return `path`. +""" +function chmod(path::AbstractString, mode::Integer; recursive::Bool=false) + err = ccall(:jl_fs_chmod, Int32, (Cstring, Cint), path, mode) + uv_error("chmod", err) + if recursive && isdir(path) + for p in readdir(path) + if !islink(joinpath(path, p)) + chmod(joinpath(path, p), mode, recursive=true) + end + end + end + path +end + +""" + chown(path::AbstractString, owner::Integer, group::Integer=-1) + +Change the owner and/or group of `path` to `owner` and/or `group`. If the value entered for `owner` or `group` +is `-1` the corresponding ID will not change. Only integer `owner`s and `group`s are currently supported. +Return `path`. +""" +function chown(path::AbstractString, owner::Integer, group::Integer=-1) + err = ccall(:jl_fs_chown, Int32, (Cstring, Cint, Cint), path, owner, group) + uv_error("chown",err) + path +end diff --git a/base/filesystem.jl b/base/filesystem.jl new file mode 100644 index 0000000..29b77a4 --- /dev/null +++ b/base/filesystem.jl @@ -0,0 +1,248 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## File Operations (Libuv-based) ## + +module Filesystem + +const S_IRUSR = 0o400 +const S_IWUSR = 0o200 +const S_IXUSR = 0o100 +const S_IRWXU = 0o700 +const S_IRGRP = 0o040 +const S_IWGRP = 0o020 +const S_IXGRP = 0o010 +const S_IRWXG = 0o070 +const S_IROTH = 0o004 +const S_IWOTH = 0o002 +const S_IXOTH = 0o001 +const S_IRWXO = 0o007 + +export File, + StatStruct, + # open, + futime, + write, + JL_O_WRONLY, + JL_O_RDONLY, + JL_O_RDWR, + JL_O_APPEND, + JL_O_CREAT, + JL_O_EXCL, + JL_O_TRUNC, + JL_O_TEMPORARY, + JL_O_SHORT_LIVED, + JL_O_SEQUENTIAL, + JL_O_RANDOM, + JL_O_NOCTTY, + S_IRUSR, S_IWUSR, S_IXUSR, S_IRWXU, + S_IRGRP, S_IWGRP, S_IXGRP, S_IRWXG, + S_IROTH, S_IWOTH, S_IXOTH, S_IRWXO + +import .Base: + IOError, _UVError, _sizeof_uv_fs, check_open, close, eof, eventloop, fd, isopen, + bytesavailable, position, read, read!, readavailable, seek, seekend, show, + skip, stat, unsafe_read, unsafe_write, write, transcode, uv_error, + rawhandle, OS_HANDLE, INVALID_OS_HANDLE, windowserror + +import .Base.RefValue + +if Sys.iswindows() + import .Base: cwstring +end + +# Average buffer size including null terminator for several filesystem operations. +# On Windows we use the MAX_PATH = 260 value on Win32. +const AVG_PATH = Sys.iswindows() ? 260 : 512 + +include("path.jl") +include("stat.jl") +include("file.jl") +include(string(length(Core.ARGS) >= 2 ? Core.ARGS[2] : "", "file_constants.jl")) # include($BUILDROOT/base/file_constants.jl) + +## Operations with File (fd) objects ## + +abstract type AbstractFile <: IO end + +mutable struct File <: AbstractFile + open::Bool + handle::OS_HANDLE + File(fd::OS_HANDLE) = new(true, fd) +end +if OS_HANDLE !== RawFD + File(fd::RawFD) = File(Libc._get_osfhandle(fd)) # TODO: calling close would now destroy the wrong handle +end + +rawhandle(file::File) = file.handle + +# Filesystem.open, not Base.open +function open(path::AbstractString, flags::Integer, mode::Integer=0) + req = Libc.malloc(_sizeof_uv_fs) + local handle + try + ret = ccall(:uv_fs_open, Int32, + (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Int32, Int32, Ptr{Cvoid}), + C_NULL, req, path, flags, mode, C_NULL) + handle = ccall(:uv_fs_get_result, Cssize_t, (Ptr{Cvoid},), req) + ccall(:uv_fs_req_cleanup, Cvoid, (Ptr{Cvoid},), req) + uv_error("open", ret) + finally # conversion to Cstring could cause an exception + Libc.free(req) + end + return File(OS_HANDLE(@static Sys.iswindows() ? Ptr{Cvoid}(handle) : Cint(handle))) +end + +isopen(f::File) = f.open + +function check_open(f::File) + if !isopen(f) + throw(ArgumentError("file is closed")) + end +end + +function close(f::File) + if isopen(f) + f.open = false + err = ccall(:jl_fs_close, Int32, (OS_HANDLE,), f.handle) + f.handle = INVALID_OS_HANDLE + uv_error("close", err) + end + nothing +end + +# sendfile is the most efficient way to copy from a file descriptor +function sendfile(dst::File, src::File, src_offset::Int64, bytes::Int) + check_open(dst) + check_open(src) + while true + result = ccall(:jl_fs_sendfile, Int32, (OS_HANDLE, OS_HANDLE, Int64, Csize_t), + src.handle, dst.handle, src_offset, bytes) + uv_error("sendfile", result) + nsent = result + bytes -= nsent + src_offset += nsent + bytes <= 0 && break + end + nothing +end + +function unsafe_write(f::File, buf::Ptr{UInt8}, len::UInt, offset::Int64=Int64(-1)) + check_open(f) + err = ccall(:jl_fs_write, Int32, (OS_HANDLE, Ptr{UInt8}, Csize_t, Int64), + f.handle, buf, len, offset) + uv_error("write", err) + return len +end + +write(f::File, c::UInt8) = write(f, Ref{UInt8}(c)) + +function truncate(f::File, n::Integer) + check_open(f) + req = Libc.malloc(_sizeof_uv_fs) + err = ccall(:uv_fs_ftruncate, Int32, + (Ptr{Cvoid}, Ptr{Cvoid}, OS_HANDLE, Int64, Ptr{Cvoid}), + C_NULL, req, f.handle, n, C_NULL) + Libc.free(req) + uv_error("ftruncate", err) + return f +end + +function futime(f::File, atime::Float64, mtime::Float64) + check_open(f) + req = Libc.malloc(_sizeof_uv_fs) + err = ccall(:uv_fs_futime, Int32, + (Ptr{Cvoid}, Ptr{Cvoid}, OS_HANDLE, Float64, Float64, Ptr{Cvoid}), + C_NULL, req, f.handle, atime, mtime, C_NULL) + Libc.free(req) + uv_error("futime", err) + return f +end + +function read(f::File, ::Type{UInt8}) + check_open(f) + ret = ccall(:jl_fs_read_byte, Int32, (OS_HANDLE,), f.handle) + uv_error("read", ret) + return ret % UInt8 +end + +function read(f::File, ::Type{Char}) + b0 = read(f, UInt8) + l = 8 * (4 - leading_ones(b0)) + c = UInt32(b0) << 24 + if l < 24 + s = 16 + while s ≥ l && !eof(f) + p = position(f) + b = read(f, UInt8) + if b & 0xc0 != 0x80 + seek(f, p) + break + end + c |= UInt32(b) << s + s -= 8 + end + end + return reinterpret(Char, c) +end + +read(f::File, ::Type{T}) where {T<:AbstractChar} = T(read(f, Char)) # fallback + +function unsafe_read(f::File, p::Ptr{UInt8}, nel::UInt) + check_open(f) + ret = ccall(:jl_fs_read, Int32, (OS_HANDLE, Ptr{Cvoid}, Csize_t), + f.handle, p, nel) + uv_error("read", ret) + ret == nel || throw(EOFError()) + nothing +end + +bytesavailable(f::File) = max(0, filesize(f) - position(f)) # position can be > filesize + +eof(f::File) = bytesavailable(f) == 0 + +function readbytes!(f::File, b::Array{UInt8}, nb=length(b)) + nr = min(nb, bytesavailable(f)) + if length(b) < nr + resize!(b, nr) + end + ret = ccall(:jl_fs_read, Int32, (OS_HANDLE, Ptr{Cvoid}, Csize_t), + f.handle, b, nr) + uv_error("read", ret) + return ret +end +read(io::File) = read!(io, Base.StringVector(bytesavailable(io))) +readavailable(io::File) = read(io) +read(io::File, nb::Integer) = read!(io, Base.StringVector(min(nb, bytesavailable(io)))) + +const SEEK_SET = Int32(0) +const SEEK_CUR = Int32(1) +const SEEK_END = Int32(2) + +function seek(f::File, n::Integer) + ret = ccall(:jl_lseek, Int64, (OS_HANDLE, Int64, Int32), f.handle, n, SEEK_SET) + systemerror("seek", ret == -1) + return f +end + +function seekend(f::File) + ret = ccall(:jl_lseek, Int64, (OS_HANDLE, Int64, Int32), f.handle, 0, SEEK_END) + systemerror("seekend", ret == -1) + return f +end + +function skip(f::File, n::Integer) + ret = ccall(:jl_lseek, Int64, (OS_HANDLE, Int64, Int32), f.handle, n, SEEK_CUR) + systemerror("skip", ret == -1) + return f +end + +function position(f::File) + check_open(f) + ret = ccall(:jl_lseek, Int64, (OS_HANDLE, Int64, Int32), f.handle, 0, SEEK_CUR) + systemerror("lseek", ret == -1) + return ret +end + +fd(f::File) = f.handle +stat(f::File) = stat(f.handle) + +end diff --git a/base/float.jl b/base/float.jl new file mode 100644 index 0000000..25102f6 --- /dev/null +++ b/base/float.jl @@ -0,0 +1,905 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +const IEEEFloat = Union{Float16, Float32, Float64} + +## floating point traits ## + +""" + Inf16 + +Positive infinity of type [`Float16`](@ref). +""" +const Inf16 = bitcast(Float16, 0x7c00) +""" + NaN16 + +A not-a-number value of type [`Float16`](@ref). +""" +const NaN16 = bitcast(Float16, 0x7e00) +""" + Inf32 + +Positive infinity of type [`Float32`](@ref). +""" +const Inf32 = bitcast(Float32, 0x7f800000) +""" + NaN32 + +A not-a-number value of type [`Float32`](@ref). +""" +const NaN32 = bitcast(Float32, 0x7fc00000) +const Inf64 = bitcast(Float64, 0x7ff0000000000000) +const NaN64 = bitcast(Float64, 0x7ff8000000000000) + +const Inf = Inf64 +""" + Inf, Inf64 + +Positive infinity of type [`Float64`](@ref). +""" +Inf, Inf64 + +const NaN = NaN64 +""" + NaN, NaN64 + +A not-a-number value of type [`Float64`](@ref). +""" +NaN, NaN64 + +## conversions to floating-point ## +Float16(x::Integer) = convert(Float16, convert(Float32, x)) +for t in (Int8, Int16, Int32, Int64, Int128, UInt8, UInt16, UInt32, UInt64, UInt128) + @eval promote_rule(::Type{Float16}, ::Type{$t}) = Float16 +end +promote_rule(::Type{Float16}, ::Type{Bool}) = Float16 + +for t1 in (Float32, Float64) + for st in (Int8, Int16, Int32, Int64) + @eval begin + (::Type{$t1})(x::($st)) = sitofp($t1, x) + promote_rule(::Type{$t1}, ::Type{$st}) = $t1 + end + end + for ut in (Bool, UInt8, UInt16, UInt32, UInt64) + @eval begin + (::Type{$t1})(x::($ut)) = uitofp($t1, x) + promote_rule(::Type{$t1}, ::Type{$ut}) = $t1 + end + end +end +(::Type{T})(x::Float16) where {T<:Integer} = T(Float32(x)) + +Bool(x::Real) = x==0 ? false : x==1 ? true : throw(InexactError(:Bool, Bool, x)) + +promote_rule(::Type{Float64}, ::Type{UInt128}) = Float64 +promote_rule(::Type{Float64}, ::Type{Int128}) = Float64 +promote_rule(::Type{Float32}, ::Type{UInt128}) = Float32 +promote_rule(::Type{Float32}, ::Type{Int128}) = Float32 + +function Float64(x::UInt128) + x == 0 && return 0.0 + n = 128-leading_zeros(x) # ndigits0z(x,2) + if n <= 53 + y = ((x % UInt64) << (53-n)) & 0x000f_ffff_ffff_ffff + else + y = ((x >> (n-54)) % UInt64) & 0x001f_ffff_ffff_ffff # keep 1 extra bit + y = (y+1)>>1 # round, ties up (extra leading bit in case of next exponent) + y &= ~UInt64(trailing_zeros(x) == (n-54)) # fix last bit to round to even + end + d = ((n+1022) % UInt64) << 52 + reinterpret(Float64, d + y) +end + +function Float64(x::Int128) + x == 0 && return 0.0 + s = ((x >>> 64) % UInt64) & 0x8000_0000_0000_0000 # sign bit + x = abs(x) % UInt128 + n = 128-leading_zeros(x) # ndigits0z(x,2) + if n <= 53 + y = ((x % UInt64) << (53-n)) & 0x000f_ffff_ffff_ffff + else + y = ((x >> (n-54)) % UInt64) & 0x001f_ffff_ffff_ffff # keep 1 extra bit + y = (y+1)>>1 # round, ties up (extra leading bit in case of next exponent) + y &= ~UInt64(trailing_zeros(x) == (n-54)) # fix last bit to round to even + end + d = ((n+1022) % UInt64) << 52 + reinterpret(Float64, s | d + y) +end + +function Float32(x::UInt128) + x == 0 && return 0f0 + n = 128-leading_zeros(x) # ndigits0z(x,2) + if n <= 24 + y = ((x % UInt32) << (24-n)) & 0x007f_ffff + else + y = ((x >> (n-25)) % UInt32) & 0x00ff_ffff # keep 1 extra bit + y = (y+one(UInt32))>>1 # round, ties up (extra leading bit in case of next exponent) + y &= ~UInt32(trailing_zeros(x) == (n-25)) # fix last bit to round to even + end + d = ((n+126) % UInt32) << 23 + reinterpret(Float32, d + y) +end + +function Float32(x::Int128) + x == 0 && return 0f0 + s = ((x >>> 96) % UInt32) & 0x8000_0000 # sign bit + x = abs(x) % UInt128 + n = 128-leading_zeros(x) # ndigits0z(x,2) + if n <= 24 + y = ((x % UInt32) << (24-n)) & 0x007f_ffff + else + y = ((x >> (n-25)) % UInt32) & 0x00ff_ffff # keep 1 extra bit + y = (y+one(UInt32))>>1 # round, ties up (extra leading bit in case of next exponent) + y &= ~UInt32(trailing_zeros(x) == (n-25)) # fix last bit to round to even + end + d = ((n+126) % UInt32) << 23 + reinterpret(Float32, s | d + y) +end + +# Float32 -> Float16 algorithm from: +# "Fast Half Float Conversion" by Jeroen van der Zijp +# ftp://ftp.fox-toolkit.org/pub/fasthalffloatconversion.pdf +# +# With adjustments for round-to-nearest, ties to even. +# +let _basetable = Vector{UInt16}(undef, 512), + _shifttable = Vector{UInt8}(undef, 512) + for i = 0:255 + e = i - 127 + if e < -25 # Very small numbers map to zero + _basetable[i|0x000+1] = 0x0000 + _basetable[i|0x100+1] = 0x8000 + _shifttable[i|0x000+1] = 25 + _shifttable[i|0x100+1] = 25 + elseif e < -14 # Small numbers map to denorms + _basetable[i|0x000+1] = 0x0000 + _basetable[i|0x100+1] = 0x8000 + _shifttable[i|0x000+1] = -e-1 + _shifttable[i|0x100+1] = -e-1 + elseif e <= 15 # Normal numbers just lose precision + _basetable[i|0x000+1] = ((e+15)<<10) + _basetable[i|0x100+1] = ((e+15)<<10) | 0x8000 + _shifttable[i|0x000+1] = 13 + _shifttable[i|0x100+1] = 13 + elseif e < 128 # Large numbers map to Infinity + _basetable[i|0x000+1] = 0x7C00 + _basetable[i|0x100+1] = 0xFC00 + _shifttable[i|0x000+1] = 24 + _shifttable[i|0x100+1] = 24 + else # Infinity and NaN's stay Infinity and NaN's + _basetable[i|0x000+1] = 0x7C00 + _basetable[i|0x100+1] = 0xFC00 + _shifttable[i|0x000+1] = 13 + _shifttable[i|0x100+1] = 13 + end + end + global const shifttable = (_shifttable...,) + global const basetable = (_basetable...,) +end + +function Float16(val::Float32) + f = reinterpret(UInt32, val) + if isnan(val) + t = 0x8000 ⊻ (0x8000 & ((f >> 0x10) % UInt16)) + return reinterpret(Float16, t ⊻ ((f >> 0xd) % UInt16)) + end + i = ((f & ~significand_mask(Float32)) >> significand_bits(Float32)) + 1 + @inbounds sh = shifttable[i] + f &= significand_mask(Float32) + # If `val` is subnormal, the tables are set up to force the + # result to 0, so the significand has an implicit `1` in the + # cases we care about. + f |= significand_mask(Float32) + 0x1 + @inbounds h = (basetable[i] + (f >> sh) & significand_mask(Float16)) % UInt16 + # round + # NOTE: we maybe should ignore NaNs here, but the payload is + # getting truncated anyway so "rounding" it might not matter + nextbit = (f >> (sh-1)) & 1 + if nextbit != 0 && (h & 0x7C00) != 0x7C00 + # Round halfway to even or check lower bits + if h&1 == 1 || (f & ((1<<(sh-1))-1)) != 0 + h += UInt16(1) + end + end + reinterpret(Float16, h) +end + +function Float32(val::Float16) + local ival::UInt32 = reinterpret(UInt16, val) + local sign::UInt32 = (ival & 0x8000) >> 15 + local exp::UInt32 = (ival & 0x7c00) >> 10 + local sig::UInt32 = (ival & 0x3ff) >> 0 + local ret::UInt32 + + if exp == 0 + if sig == 0 + sign = sign << 31 + ret = sign | exp | sig + else + n_bit = 1 + bit = 0x0200 + while (bit & sig) == 0 + n_bit = n_bit + 1 + bit = bit >> 1 + end + sign = sign << 31 + exp = ((-14 - n_bit + 127) << 23) % UInt32 + sig = ((sig & (~bit)) << n_bit) << (23 - 10) + ret = sign | exp | sig + end + elseif exp == 0x1f + if sig == 0 # Inf + if sign == 0 + ret = 0x7f800000 + else + ret = 0xff800000 + end + else # NaN + ret = 0x7fc00000 | (sign<<31) | (sig<<(23-10)) + end + else + sign = sign << 31 + exp = ((exp - 15 + 127) << 23) % UInt32 + sig = sig << (23 - 10) + ret = sign | exp | sig + end + return reinterpret(Float32, ret) +end + +#convert(::Type{Float16}, x::Float32) = fptrunc(Float16, x) +Float32(x::Float64) = fptrunc(Float32, x) +Float16(x::Float64) = Float16(Float32(x)) + +#convert(::Type{Float32}, x::Float16) = fpext(Float32, x) +Float64(x::Float32) = fpext(Float64, x) +Float64(x::Float16) = Float64(Float32(x)) + +AbstractFloat(x::Bool) = Float64(x) +AbstractFloat(x::Int8) = Float64(x) +AbstractFloat(x::Int16) = Float64(x) +AbstractFloat(x::Int32) = Float64(x) +AbstractFloat(x::Int64) = Float64(x) # LOSSY +AbstractFloat(x::Int128) = Float64(x) # LOSSY +AbstractFloat(x::UInt8) = Float64(x) +AbstractFloat(x::UInt16) = Float64(x) +AbstractFloat(x::UInt32) = Float64(x) +AbstractFloat(x::UInt64) = Float64(x) # LOSSY +AbstractFloat(x::UInt128) = Float64(x) # LOSSY + +Bool(x::Float16) = x==0 ? false : x==1 ? true : throw(InexactError(:Bool, Bool, x)) + +""" + float(x) + +Convert a number or array to a floating point data type. +""" +float(x) = AbstractFloat(x) + +""" + float(T::Type) + +Return an appropriate type to represent a value of type `T` as a floating point value. +Equivalent to `typeof(float(zero(T)))`. + +# Examples +```jldoctest +julia> float(Complex{Int}) +Complex{Float64} + +julia> float(Int) +Float64 +``` +""" +float(::Type{T}) where {T<:Number} = typeof(float(zero(T))) +float(::Type{T}) where {T<:AbstractFloat} = T + +""" + unsafe_trunc(T, x) + +Return the nearest integral value of type `T` whose absolute value is +less than or equal to `x`. If the value is not representable by `T`, an arbitrary value will +be returned. +""" +function unsafe_trunc end + +for Ti in (Int8, Int16, Int32, Int64) + @eval begin + unsafe_trunc(::Type{$Ti}, x::Float16) = unsafe_trunc($Ti, Float32(x)) + unsafe_trunc(::Type{$Ti}, x::Float32) = fptosi($Ti, x) + unsafe_trunc(::Type{$Ti}, x::Float64) = fptosi($Ti, x) + end +end +for Ti in (UInt8, UInt16, UInt32, UInt64) + @eval begin + unsafe_trunc(::Type{$Ti}, x::Float16) = unsafe_trunc($Ti, Float32(x)) + unsafe_trunc(::Type{$Ti}, x::Float32) = fptoui($Ti, x) + unsafe_trunc(::Type{$Ti}, x::Float64) = fptoui($Ti, x) + end +end + +function unsafe_trunc(::Type{UInt128}, x::Float64) + xu = reinterpret(UInt64,x) + k = Int(xu >> 52) & 0x07ff - 1075 + xu = (xu & 0x000f_ffff_ffff_ffff) | 0x0010_0000_0000_0000 + if k <= 0 + UInt128(xu >> -k) + else + UInt128(xu) << k + end +end +function unsafe_trunc(::Type{Int128}, x::Float64) + copysign(unsafe_trunc(UInt128,x) % Int128, x) +end + +function unsafe_trunc(::Type{UInt128}, x::Float32) + xu = reinterpret(UInt32,x) + k = Int(xu >> 23) & 0x00ff - 150 + xu = (xu & 0x007f_ffff) | 0x0080_0000 + if k <= 0 + UInt128(xu >> -k) + else + UInt128(xu) << k + end +end +function unsafe_trunc(::Type{Int128}, x::Float32) + copysign(unsafe_trunc(UInt128,x) % Int128, x) +end + +unsafe_trunc(::Type{UInt128}, x::Float16) = unsafe_trunc(UInt128, Float32(x)) +unsafe_trunc(::Type{Int128}, x::Float16) = unsafe_trunc(Int128, Float32(x)) + +# matches convert methods +# also determines floor, ceil, round +trunc(::Type{Signed}, x::Float32) = trunc(Int,x) +trunc(::Type{Signed}, x::Float64) = trunc(Int,x) +trunc(::Type{Unsigned}, x::Float32) = trunc(UInt,x) +trunc(::Type{Unsigned}, x::Float64) = trunc(UInt,x) +trunc(::Type{Integer}, x::Float32) = trunc(Int,x) +trunc(::Type{Integer}, x::Float64) = trunc(Int,x) +trunc(::Type{T}, x::Float16) where {T<:Integer} = trunc(T, Float32(x)) + +# fallbacks +floor(::Type{T}, x::AbstractFloat) where {T<:Integer} = trunc(T,round(x, RoundDown)) +floor(::Type{T}, x::Float16) where {T<:Integer} = floor(T, Float32(x)) +ceil(::Type{T}, x::AbstractFloat) where {T<:Integer} = trunc(T,round(x, RoundUp)) +ceil(::Type{T}, x::Float16) where {T<:Integer} = ceil(T, Float32(x)) +round(::Type{T}, x::AbstractFloat) where {T<:Integer} = trunc(T,round(x, RoundNearest)) +round(::Type{T}, x::Float16) where {T<:Integer} = round(T, Float32(x)) + +round(x::Float64, r::RoundingMode{:ToZero}) = trunc_llvm(x) +round(x::Float32, r::RoundingMode{:ToZero}) = trunc_llvm(x) +round(x::Float64, r::RoundingMode{:Down}) = floor_llvm(x) +round(x::Float32, r::RoundingMode{:Down}) = floor_llvm(x) +round(x::Float64, r::RoundingMode{:Up}) = ceil_llvm(x) +round(x::Float32, r::RoundingMode{:Up}) = ceil_llvm(x) +round(x::Float64, r::RoundingMode{:Nearest}) = rint_llvm(x) +round(x::Float32, r::RoundingMode{:Nearest}) = rint_llvm(x) + +round(x::Float16, r::RoundingMode{:ToZero}) = Float16(round(Float32(x), r)) +round(x::Float16, r::RoundingMode{:Down}) = Float16(round(Float32(x), r)) +round(x::Float16, r::RoundingMode{:Up}) = Float16(round(Float32(x), r)) +round(x::Float16, r::RoundingMode{:Nearest}) = Float16(round(Float32(x), r)) + +## floating point promotions ## +promote_rule(::Type{Float32}, ::Type{Float16}) = Float32 +promote_rule(::Type{Float64}, ::Type{Float16}) = Float64 +promote_rule(::Type{Float64}, ::Type{Float32}) = Float64 + +widen(::Type{Float16}) = Float32 +widen(::Type{Float32}) = Float64 + +## floating point arithmetic ## +-(x::Float64) = neg_float(x) +-(x::Float32) = neg_float(x) +-(x::Float16) = reinterpret(Float16, reinterpret(UInt16, x) ⊻ 0x8000) + +for op in (:+, :-, :*, :/, :\, :^) + @eval ($op)(a::Float16, b::Float16) = Float16(($op)(Float32(a), Float32(b))) +end ++(x::Float32, y::Float32) = add_float(x, y) ++(x::Float64, y::Float64) = add_float(x, y) +-(x::Float32, y::Float32) = sub_float(x, y) +-(x::Float64, y::Float64) = sub_float(x, y) +*(x::Float32, y::Float32) = mul_float(x, y) +*(x::Float64, y::Float64) = mul_float(x, y) +/(x::Float32, y::Float32) = div_float(x, y) +/(x::Float64, y::Float64) = div_float(x, y) + +muladd(x::Float32, y::Float32, z::Float32) = muladd_float(x, y, z) +muladd(x::Float64, y::Float64, z::Float64) = muladd_float(x, y, z) +function muladd(a::Float16, b::Float16, c::Float16) + Float16(muladd(Float32(a), Float32(b), Float32(c))) +end + +# TODO: faster floating point div? +# TODO: faster floating point fld? +# TODO: faster floating point mod? + +for func in (:div,:fld,:cld,:rem,:mod) + @eval begin + $func(a::Float16,b::Float16) = Float16($func(Float32(a),Float32(b))) + end +end + +rem(x::Float32, y::Float32) = rem_float(x, y) +rem(x::Float64, y::Float64) = rem_float(x, y) + +cld(x::T, y::T) where {T<:AbstractFloat} = -fld(-x,y) + +function mod(x::T, y::T) where T<:AbstractFloat + r = rem(x,y) + if r == 0 + copysign(r,y) + elseif (r > 0) ⊻ (y > 0) + r+y + else + r + end +end + +## floating point comparisons ## +function ==(x::Float16, y::Float16) + ix = reinterpret(UInt16,x) + iy = reinterpret(UInt16,y) + if (ix|iy)&0x7fff > 0x7c00 #isnan(x) || isnan(y) + return false + end + if (ix|iy)&0x7fff == 0x0000 + return true + end + return ix == iy +end +==(x::Float32, y::Float32) = eq_float(x, y) +==(x::Float64, y::Float64) = eq_float(x, y) +!=(x::Float32, y::Float32) = ne_float(x, y) +!=(x::Float64, y::Float64) = ne_float(x, y) +<( x::Float32, y::Float32) = lt_float(x, y) +<( x::Float64, y::Float64) = lt_float(x, y) +<=(x::Float32, y::Float32) = le_float(x, y) +<=(x::Float64, y::Float64) = le_float(x, y) + +isequal(x::Float32, y::Float32) = fpiseq(x, y) +isequal(x::Float64, y::Float64) = fpiseq(x, y) +isless( x::Float32, y::Float32) = fpislt(x, y) +isless( x::Float64, y::Float64) = fpislt(x, y) +for op in (:<, :<=, :isless) + @eval ($op)(a::Float16, b::Float16) = ($op)(Float32(a), Float32(b)) +end + +# Exact Float (Tf) vs Integer (Ti) comparisons +# Assumes: +# - typemax(Ti) == 2^n-1 +# - typemax(Ti) can't be exactly represented by Tf: +# => Tf(typemax(Ti)) == 2^n or Inf +# - typemin(Ti) can be exactly represented by Tf +# +# 1. convert y::Ti to float fy::Tf +# 2. perform Tf comparison x vs fy +# 3. if x == fy, check if (1) resulted in rounding: +# a. convert fy back to Ti and compare with original y +# b. unsafe_convert undefined behaviour if fy == Tf(typemax(Ti)) +# (but consequently x == fy > y) +for Ti in (Int64,UInt64,Int128,UInt128) + for Tf in (Float32,Float64) + @eval begin + function ==(x::$Tf, y::$Ti) + fy = ($Tf)(y) + (x == fy) & (fy != $(Tf(typemax(Ti)))) & (y == unsafe_trunc($Ti,fy)) + end + ==(y::$Ti, x::$Tf) = x==y + + function <(x::$Ti, y::$Tf) + fx = ($Tf)(x) + (fx < y) | ((fx == y) & ((fx == $(Tf(typemax(Ti)))) | (x < unsafe_trunc($Ti,fx)) )) + end + function <=(x::$Ti, y::$Tf) + fx = ($Tf)(x) + (fx < y) | ((fx == y) & ((fx == $(Tf(typemax(Ti)))) | (x <= unsafe_trunc($Ti,fx)) )) + end + + function <(x::$Tf, y::$Ti) + fy = ($Tf)(y) + (x < fy) | ((x == fy) & (fy < $(Tf(typemax(Ti)))) & (unsafe_trunc($Ti,fy) < y)) + end + function <=(x::$Tf, y::$Ti) + fy = ($Tf)(y) + (x < fy) | ((x == fy) & (fy < $(Tf(typemax(Ti)))) & (unsafe_trunc($Ti,fy) <= y)) + end + end + end +end +for op in (:(==), :<, :<=) + @eval begin + ($op)(x::Float16, y::Union{Int128,UInt128,Int64,UInt64}) = ($op)(Float64(x), Float64(y)) + ($op)(x::Union{Int128,UInt128,Int64,UInt64}, y::Float16) = ($op)(Float64(x), Float64(y)) + + ($op)(x::Union{Float16,Float32}, y::Union{Int32,UInt32}) = ($op)(Float64(x), Float64(y)) + ($op)(x::Union{Int32,UInt32}, y::Union{Float16,Float32}) = ($op)(Float64(x), Float64(y)) + + ($op)(x::Float16, y::Union{Int16,UInt16}) = ($op)(Float32(x), Float32(y)) + ($op)(x::Union{Int16,UInt16}, y::Float16) = ($op)(Float32(x), Float32(y)) + end +end + + +abs(x::Float16) = reinterpret(Float16, reinterpret(UInt16, x) & 0x7fff) +abs(x::Float32) = abs_float(x) +abs(x::Float64) = abs_float(x) + +""" + isnan(f) -> Bool + +Test whether a floating point number is not a number (NaN). +""" +isnan(x::AbstractFloat) = x != x +isnan(x::Float16) = reinterpret(UInt16,x)&0x7fff > 0x7c00 +isnan(x::Real) = false + +""" + isfinite(f) -> Bool + +Test whether a number is finite. + +# Examples +```jldoctest +julia> isfinite(5) +true + +julia> isfinite(NaN32) +false +``` +""" +isfinite(x::AbstractFloat) = x - x == 0 +isfinite(x::Float16) = reinterpret(UInt16,x)&0x7c00 != 0x7c00 +isfinite(x::Real) = decompose(x)[3] != 0 +isfinite(x::Integer) = true + +""" + isinf(f) -> Bool + +Test whether a number is infinite. +""" +isinf(x::Real) = !isnan(x) & !isfinite(x) + +## hashing small, built-in numeric types ## + +hx(a::UInt64, b::Float64, h::UInt) = hash_uint64((3a + reinterpret(UInt64,b)) - h) +const hx_NaN = hx(UInt64(0), NaN, UInt(0 )) + +hash(x::UInt64, h::UInt) = hx(x, Float64(x), h) +hash(x::Int64, h::UInt) = hx(reinterpret(UInt64, abs(x)), Float64(x), h) +hash(x::Float64, h::UInt) = isnan(x) ? (hx_NaN ⊻ h) : hx(fptoui(UInt64, abs(x)), x, h) + +hash(x::Union{Bool,Int8,UInt8,Int16,UInt16,Int32,UInt32}, h::UInt) = hash(Int64(x), h) +hash(x::Float32, h::UInt) = hash(Float64(x), h) + +""" + precision(num::AbstractFloat) + +Get the precision of a floating point number, as defined by the effective number of bits in +the mantissa. +""" +function precision end + +precision(::Type{Float16}) = 11 +precision(::Type{Float32}) = 24 +precision(::Type{Float64}) = 53 +precision(::T) where {T<:AbstractFloat} = precision(T) + +""" + uabs(x::Integer) + +Return the absolute value of `x`, possibly returning a different type should the +operation be susceptible to overflow. This typically arises when `x` is a two's complement +signed integer, so that `abs(typemin(x)) == typemin(x) < 0`, in which case the result of +`uabs(x)` will be an unsigned integer of the same size. +""" +uabs(x::Integer) = abs(x) +uabs(x::BitSigned) = unsigned(abs(x)) + + +""" + nextfloat(x::AbstractFloat, n::Integer) + +The result of `n` iterative applications of `nextfloat` to `x` if `n >= 0`, or `-n` +applications of `prevfloat` if `n < 0`. +""" +function nextfloat(f::IEEEFloat, d::Integer) + F = typeof(f) + fumax = reinterpret(Unsigned, F(Inf)) + U = typeof(fumax) + + isnan(f) && return f + fi = reinterpret(Signed, f) + fneg = fi < 0 + fu = unsigned(fi & typemax(fi)) + + dneg = d < 0 + da = uabs(d) + if da > typemax(U) + fneg = dneg + fu = fumax + else + du = da % U + if fneg ⊻ dneg + if du > fu + fu = min(fumax, du - fu) + fneg = !fneg + else + fu = fu - du + end + else + if fumax - fu < du + fu = fumax + else + fu = fu + du + end + end + end + if fneg + fu |= sign_mask(F) + end + reinterpret(F, fu) +end + +""" + nextfloat(x::AbstractFloat) + +Return the smallest floating point number `y` of the same type as `x` such `x < y`. If no +such `y` exists (e.g. if `x` is `Inf` or `NaN`), then return `x`. +""" +nextfloat(x::AbstractFloat) = nextfloat(x,1) + +""" + prevfloat(x::AbstractFloat, n::Integer) + +The result of `n` iterative applications of `prevfloat` to `x` if `n >= 0`, or `-n` +applications of `nextfloat` if `n < 0`. +""" +prevfloat(x::AbstractFloat, d::Integer) = nextfloat(x, -d) + +""" + prevfloat(x::AbstractFloat) + +Return the largest floating point number `y` of the same type as `x` such `y < x`. If no +such `y` exists (e.g. if `x` is `-Inf` or `NaN`), then return `x`. +""" +prevfloat(x::AbstractFloat) = nextfloat(x,-1) + +for Ti in (Int8, Int16, Int32, Int64, Int128, UInt8, UInt16, UInt32, UInt64, UInt128) + for Tf in (Float32, Float64) + if Ti <: Unsigned || sizeof(Ti) < sizeof(Tf) + # Here `Tf(typemin(Ti))-1` is exact, so we can compare the lower-bound + # directly. `Tf(typemax(Ti))+1` is either always exactly representable, or + # rounded to `Inf` (e.g. when `Ti==UInt128 && Tf==Float32`). + @eval begin + function trunc(::Type{$Ti},x::$Tf) + if $(Tf(typemin(Ti))-one(Tf)) < x < $(Tf(typemax(Ti))+one(Tf)) + return unsafe_trunc($Ti,x) + else + throw(InexactError(:trunc, $Ti, x)) + end + end + function (::Type{$Ti})(x::$Tf) + if ($(Tf(typemin(Ti))) <= x <= $(Tf(typemax(Ti)))) && (round(x, RoundToZero) == x) + return unsafe_trunc($Ti,x) + else + throw(InexactError($(Expr(:quote,Ti.name.name)), $Ti, x)) + end + end + end + else + # Here `eps(Tf(typemin(Ti))) > 1`, so the only value which can be truncated to + # `Tf(typemin(Ti)` is itself. Similarly, `Tf(typemax(Ti))` is inexact and will + # be rounded up. This assumes that `Tf(typemin(Ti)) > -Inf`, which is true for + # these types, but not for `Float16` or larger integer types. + @eval begin + function trunc(::Type{$Ti},x::$Tf) + if $(Tf(typemin(Ti))) <= x < $(Tf(typemax(Ti))) + return unsafe_trunc($Ti,x) + else + throw(InexactError(:trunc, $Ti, x)) + end + end + function (::Type{$Ti})(x::$Tf) + if ($(Tf(typemin(Ti))) <= x < $(Tf(typemax(Ti)))) && (round(x, RoundToZero) == x) + return unsafe_trunc($Ti,x) + else + throw(InexactError($(Expr(:quote,Ti.name.name)), $Ti, x)) + end + end + end + end + end +end + +""" + issubnormal(f) -> Bool + +Test whether a floating point number is subnormal. +""" +function issubnormal(x::T) where {T<:IEEEFloat} + y = reinterpret(Unsigned, x) + (y & exponent_mask(T) == 0) & (y & significand_mask(T) != 0) +end + +@eval begin + typemin(::Type{Float16}) = $(bitcast(Float16, 0xfc00)) + typemax(::Type{Float16}) = $(Inf16) + typemin(::Type{Float32}) = $(-Inf32) + typemax(::Type{Float32}) = $(Inf32) + typemin(::Type{Float64}) = $(-Inf64) + typemax(::Type{Float64}) = $(Inf64) + typemin(x::T) where {T<:Real} = typemin(T) + typemax(x::T) where {T<:Real} = typemax(T) + + floatmin(::Type{Float16}) = $(bitcast(Float16, 0x0400)) + floatmin(::Type{Float32}) = $(bitcast(Float32, 0x00800000)) + floatmin(::Type{Float64}) = $(bitcast(Float64, 0x0010000000000000)) + floatmax(::Type{Float16}) = $(bitcast(Float16, 0x7bff)) + floatmax(::Type{Float32}) = $(bitcast(Float32, 0x7f7fffff)) + floatmax(::Type{Float64}) = $(bitcast(Float64, 0x7fefffffffffffff)) + + eps(x::AbstractFloat) = isfinite(x) ? abs(x) >= floatmin(x) ? ldexp(eps(typeof(x)), exponent(x)) : nextfloat(zero(x)) : oftype(x, NaN) + eps(::Type{Float16}) = $(bitcast(Float16, 0x1400)) + eps(::Type{Float32}) = $(bitcast(Float32, 0x34000000)) + eps(::Type{Float64}) = $(bitcast(Float64, 0x3cb0000000000000)) + eps() = eps(Float64) +end + +""" + floatmin(T) + +The smallest in absolute value non-subnormal value representable by the given +floating-point DataType `T`. +""" +floatmin(x::T) where {T<:AbstractFloat} = floatmin(T) + +""" + floatmax(T) + +The highest finite value representable by the given floating-point DataType `T`. + +# Examples +```jldoctest +julia> floatmax(Float16) +Float16(6.55e4) + +julia> floatmax(Float32) +3.4028235f38 +``` +""" +floatmax(x::T) where {T<:AbstractFloat} = floatmax(T) + +floatmin() = floatmin(Float64) +floatmax() = floatmax(Float64) + +""" + eps(::Type{T}) where T<:AbstractFloat + eps() + +Return the *machine epsilon* of the floating point type `T` (`T = Float64` by +default). This is defined as the gap between 1 and the next largest value representable by +`typeof(one(T))`, and is equivalent to `eps(one(T))`. (Since `eps(T)` is a +bound on the *relative error* of `T`, it is a "dimensionless" quantity like [`one`](@ref).) + +# Examples +```jldoctest +julia> eps() +2.220446049250313e-16 + +julia> eps(Float32) +1.1920929f-7 + +julia> 1.0 + eps() +1.0000000000000002 + +julia> 1.0 + eps()/2 +1.0 +``` +""" +eps(::Type{<:AbstractFloat}) + +""" + eps(x::AbstractFloat) + +Return the *unit in last place* (ulp) of `x`. This is the distance between consecutive +representable floating point values at `x`. In most cases, if the distance on either side +of `x` is different, then the larger of the two is taken, that is + + eps(x) == max(x-prevfloat(x), nextfloat(x)-x) + +The exceptions to this rule are the smallest and largest finite values +(e.g. `nextfloat(-Inf)` and `prevfloat(Inf)` for [`Float64`](@ref)), which round to the +smaller of the values. + +The rationale for this behavior is that `eps` bounds the floating point rounding +error. Under the default `RoundNearest` rounding mode, if ``y`` is a real number and ``x`` +is the nearest floating point number to ``y``, then + +```math +|y-x| \\leq \\operatorname{eps}(x)/2. +``` + +# Examples +```jldoctest +julia> eps(1.0) +2.220446049250313e-16 + +julia> eps(prevfloat(2.0)) +2.220446049250313e-16 + +julia> eps(2.0) +4.440892098500626e-16 + +julia> x = prevfloat(Inf) # largest finite Float64 +1.7976931348623157e308 + +julia> x + eps(x)/2 # rounds up +Inf + +julia> x + prevfloat(eps(x)/2) # rounds down +1.7976931348623157e308 +``` +""" +eps(::AbstractFloat) + + +## byte order swaps for arbitrary-endianness serialization/deserialization ## +bswap(x::IEEEFloat) = bswap_int(x) + +# bit patterns +reinterpret(::Type{Unsigned}, x::Float64) = reinterpret(UInt64, x) +reinterpret(::Type{Unsigned}, x::Float32) = reinterpret(UInt32, x) +reinterpret(::Type{Signed}, x::Float64) = reinterpret(Int64, x) +reinterpret(::Type{Signed}, x::Float32) = reinterpret(Int32, x) + +sign_mask(::Type{Float64}) = 0x8000_0000_0000_0000 +exponent_mask(::Type{Float64}) = 0x7ff0_0000_0000_0000 +exponent_one(::Type{Float64}) = 0x3ff0_0000_0000_0000 +exponent_half(::Type{Float64}) = 0x3fe0_0000_0000_0000 +significand_mask(::Type{Float64}) = 0x000f_ffff_ffff_ffff + +sign_mask(::Type{Float32}) = 0x8000_0000 +exponent_mask(::Type{Float32}) = 0x7f80_0000 +exponent_one(::Type{Float32}) = 0x3f80_0000 +exponent_half(::Type{Float32}) = 0x3f00_0000 +significand_mask(::Type{Float32}) = 0x007f_ffff + +sign_mask(::Type{Float16}) = 0x8000 +exponent_mask(::Type{Float16}) = 0x7c00 +exponent_one(::Type{Float16}) = 0x3c00 +exponent_half(::Type{Float16}) = 0x3800 +significand_mask(::Type{Float16}) = 0x03ff + +for T in (Float16, Float32, Float64) + @eval significand_bits(::Type{$T}) = $(trailing_ones(significand_mask(T))) + @eval exponent_bits(::Type{$T}) = $(sizeof(T)*8 - significand_bits(T) - 1) +end + +# integer size of float +uinttype(::Type{Float64}) = UInt64 +uinttype(::Type{Float32}) = UInt32 +uinttype(::Type{Float16}) = UInt16 + +Base.iszero(x::Float16) = reinterpret(UInt16, x) & ~sign_mask(Float16) == 0x0000 + +## Array operations on floating point numbers ## + +float(A::AbstractArray{<:AbstractFloat}) = A + +function float(A::AbstractArray{T}) where T + if !isconcretetype(T) + error("`float` not defined on abstractly-typed arrays; please convert to a more specific type") + end + convert(AbstractArray{typeof(float(zero(T)))}, A) +end + +float(r::StepRange) = float(r.start):float(r.step):float(last(r)) +float(r::UnitRange) = float(r.start):float(last(r)) +float(r::StepRangeLen{T}) where {T} = + StepRangeLen{typeof(float(T(r.ref)))}(float(r.ref), float(r.step), length(r), r.offset) +function float(r::LinRange) + LinRange(float(r.start), float(r.stop), length(r)) +end diff --git a/base/floatfuncs.jl b/base/floatfuncs.jl new file mode 100644 index 0000000..8c93428 --- /dev/null +++ b/base/floatfuncs.jl @@ -0,0 +1,333 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## floating-point functions ## + +copysign(x::Float64, y::Float64) = copysign_float(x, y) +copysign(x::Float32, y::Float32) = copysign_float(x, y) +copysign(x::Float32, y::Real) = copysign(x, Float32(y)) +copysign(x::Float64, y::Real) = copysign(x, Float64(y)) + +flipsign(x::Float64, y::Float64) = bitcast(Float64, xor_int(bitcast(UInt64, x), and_int(bitcast(UInt64, y), 0x8000000000000000))) +flipsign(x::Float32, y::Float32) = bitcast(Float32, xor_int(bitcast(UInt32, x), and_int(bitcast(UInt32, y), 0x80000000))) +flipsign(x::Float32, y::Real) = flipsign(x, Float32(y)) +flipsign(x::Float64, y::Real) = flipsign(x, Float64(y)) + +signbit(x::Float64) = signbit(bitcast(Int64, x)) +signbit(x::Float32) = signbit(bitcast(Int32, x)) +signbit(x::Float16) = signbit(bitcast(Int16, x)) + +""" + maxintfloat(T=Float64) + +The largest consecutive integer-valued floating-point number that is exactly represented in +the given floating-point type `T` (which defaults to `Float64`). + +That is, `maxintfloat` returns the smallest positive integer-valued floating-point number +`n` such that `n+1` is *not* exactly representable in the type `T`. + +When an `Integer`-type value is needed, use `Integer(maxintfloat(T))`. +""" +maxintfloat(::Type{Float64}) = 9007199254740992. +maxintfloat(::Type{Float32}) = Float32(16777216.) +maxintfloat(::Type{Float16}) = Float16(2048f0) +maxintfloat(x::T) where {T<:AbstractFloat} = maxintfloat(T) + +""" + maxintfloat(T, S) + +The largest consecutive integer representable in the given floating-point type `T` that +also does not exceed the maximum integer representable by the integer type `S`. Equivalently, +it is the minimum of `maxintfloat(T)` and [`typemax(S)`](@ref). +""" +maxintfloat(::Type{S}, ::Type{T}) where {S<:AbstractFloat, T<:Integer} = min(maxintfloat(S), S(typemax(T))) +maxintfloat() = maxintfloat(Float64) + +isinteger(x::AbstractFloat) = (x - trunc(x) == 0) + +""" + round([T,] x, [r::RoundingMode]) + round(x, [r::RoundingMode]; digits::Integer=0, base = 10) + round(x, [r::RoundingMode]; sigdigits::Integer, base = 10) + +Rounds the number `x`. + +Without keyword arguments, `x` is rounded to an integer value, returning a value of type +`T`, or of the same type of `x` if no `T` is provided. An [`InexactError`](@ref) will be +thrown if the value is not representable by `T`, similar to [`convert`](@ref). + +If the `digits` keyword argument is provided, it rounds to the specified number of digits +after the decimal place (or before if negative), in base `base`. + +If the `sigdigits` keyword argument is provided, it rounds to the specified number of +significant digits, in base `base`. + +The [`RoundingMode`](@ref) `r` controls the direction of the rounding; the default is +[`RoundNearest`](@ref), which rounds to the nearest integer, with ties (fractional values +of 0.5) being rounded to the nearest even integer. Note that `round` may give incorrect +results if the global rounding mode is changed (see [`rounding`](@ref)). + +# Examples +```jldoctest +julia> round(1.7) +2.0 + +julia> round(Int, 1.7) +2 + +julia> round(1.5) +2.0 + +julia> round(2.5) +2.0 + +julia> round(pi; digits=2) +3.14 + +julia> round(pi; digits=3, base=2) +3.125 + +julia> round(123.456; sigdigits=2) +120.0 + +julia> round(357.913; sigdigits=4, base=2) +352.0 +``` + +!!! note + Rounding to specified digits in bases other than 2 can be inexact when + operating on binary floating point numbers. For example, the [`Float64`](@ref) + value represented by `1.15` is actually *less* than 1.15, yet will be + rounded to 1.2. + + # Examples + ```jldoctest; setup = :(using Printf) + julia> x = 1.15 + 1.15 + + julia> @sprintf "%.20f" x + "1.14999999999999991118" + + julia> x < 115//100 + true + + julia> round(x, digits=1) + 1.2 + ``` + +# Extensions + +To extend `round` to new numeric types, it is typically sufficient to define `Base.round(x::NewType, r::RoundingMode)`. +""" +round(T::Type, x) + +round(::Type{T}, x::AbstractFloat, r::RoundingMode{:ToZero}) where {T<:Integer} = trunc(T, x) +round(::Type{T}, x::AbstractFloat, r::RoundingMode) where {T<:Integer} = trunc(T, round(x,r)) + +# NOTE: this relies on the current keyword dispatch behaviour (#9498). +function round(x::Real, r::RoundingMode=RoundNearest; + digits::Union{Nothing,Integer}=nothing, sigdigits::Union{Nothing,Integer}=nothing, base::Union{Nothing,Integer}=nothing) + isfinite(x) || return x + if digits === nothing + if sigdigits === nothing + if base === nothing + # avoid recursive calls + throw(MethodError(round, (x,r))) + else + round(x,r) + # or throw(ArgumentError("`round` cannot use `base` argument without `digits` or `sigdigits` arguments.")) + end + else + _round_sigdigits(x, r, sigdigits, base === nothing ? 10 : base) + end + else + if sigdigits === nothing + _round_digits(x, r, digits, base === nothing ? 10 : base) + else + throw(ArgumentError("`round` cannot use both `digits` and `sigdigits` arguments.")) + end + end +end + +trunc(x::Real; kwargs...) = round(x, RoundToZero; kwargs...) +floor(x::Real; kwargs...) = round(x, RoundDown; kwargs...) +ceil(x::Real; kwargs...) = round(x, RoundUp; kwargs...) + +round(x::Integer, r::RoundingMode) = x + +# round x to multiples of 1/invstep +function _round_invstep(x, invstep, r::RoundingMode) + y = round(x * invstep, r) / invstep + if !isfinite(y) + return x + end + return y +end + +# round x to multiples of step +function _round_step(x, step, r::RoundingMode) + # TODO: use div with rounding mode + y = round(x / step, r) * step + if !isfinite(y) + if x > 0 + return (r == RoundUp ? oftype(x, Inf) : zero(x)) + elseif x < 0 + return (r == RoundDown ? -oftype(x, Inf) : -zero(x)) + else + return x + end + end + return y +end + +function _round_digits(x, r::RoundingMode, digits::Integer, base) + fx = float(x) + if digits >= 0 + invstep = oftype(fx, base)^digits + _round_invstep(fx, invstep, r) + else + step = oftype(fx, base)^-digits + _round_step(fx, step, r) + end +end + +hidigit(x::Integer, base) = ndigits0z(x, base) +function hidigit(x::AbstractFloat, base) + iszero(x) && return 0 + if base == 10 + return 1 + floor(Int, log10(abs(x))) + elseif base == 2 + return 1 + exponent(x) + else + return 1 + floor(Int, log(base, abs(x))) + end +end +hidigit(x::Real, base) = hidigit(float(x), base) + +function _round_sigdigits(x, r::RoundingMode, sigdigits::Integer, base) + h = hidigit(x, base) + _round_digits(x, r, sigdigits-h, base) +end + +# C-style round +function round(x::AbstractFloat, ::RoundingMode{:NearestTiesAway}) + y = trunc(x) + ifelse(x==y,y,trunc(2*x-y)) +end +# Java-style round +function round(x::T, ::RoundingMode{:NearestTiesUp}) where {T <: AbstractFloat} + copysign(floor((x + (T(0.25) - eps(T(0.5)))) + (T(0.25) + eps(T(0.5)))), x) +end + +# isapprox: approximate equality of numbers +""" + isapprox(x, y; rtol::Real=atol>0 ? 0 : √eps, atol::Real=0, nans::Bool=false, norm::Function) + +Inexact equality comparison: `true` if `norm(x-y) <= max(atol, rtol*max(norm(x), norm(y)))`. The +default `atol` is zero and the default `rtol` depends on the types of `x` and `y`. The keyword +argument `nans` determines whether or not NaN values are considered equal (defaults to false). + +For real or complex floating-point values, if an `atol > 0` is not specified, `rtol` defaults to +the square root of [`eps`](@ref) of the type of `x` or `y`, whichever is bigger (least precise). +This corresponds to requiring equality of about half of the significand digits. Otherwise, +e.g. for integer arguments or if an `atol > 0` is supplied, `rtol` defaults to zero. + +`x` and `y` may also be arrays of numbers, in which case `norm` defaults to the usual +`norm` function in LinearAlgebra, but +may be changed by passing a `norm::Function` keyword argument. (For numbers, `norm` is the +same thing as `abs`.) When `x` and `y` are arrays, if `norm(x-y)` is not finite (i.e. `±Inf` +or `NaN`), the comparison falls back to checking whether all elements of `x` and `y` are +approximately equal component-wise. + +The binary operator `≈` is equivalent to `isapprox` with the default arguments, and `x ≉ y` +is equivalent to `!isapprox(x,y)`. + +Note that `x ≈ 0` (i.e., comparing to zero with the default tolerances) is +equivalent to `x == 0` since the default `atol` is `0`. In such cases, you should either +supply an appropriate `atol` (or use `norm(x) ≤ atol`) or rearrange your code (e.g. +use `x ≈ y` rather than `x - y ≈ 0`). It is not possible to pick a nonzero `atol` +automatically because it depends on the overall scaling (the "units") of your problem: +for example, in `x - y ≈ 0`, `atol=1e-9` is an absurdly small tolerance if `x` is the +[radius of the Earth](https://en.wikipedia.org/wiki/Earth_radius) in meters, +but an absurdly large tolerance if `x` is the +[radius of a Hydrogen atom](https://en.wikipedia.org/wiki/Bohr_radius) in meters. + + +# Examples +```jldoctest +julia> 0.1 ≈ (0.1 - 1e-10) +true + +julia> isapprox(10, 11; atol = 2) +true + +julia> isapprox([10.0^9, 1.0], [10.0^9, 2.0]) +true + +julia> 1e-10 ≈ 0 +false + +julia> isapprox(1e-10, 0, atol=1e-8) +true +``` +""" +function isapprox(x::Number, y::Number; atol::Real=0, rtol::Real=rtoldefault(x,y,atol), nans::Bool=false) + x == y || (isfinite(x) && isfinite(y) && abs(x-y) <= max(atol, rtol*max(abs(x), abs(y)))) || (nans && isnan(x) && isnan(y)) +end + +const ≈ = isapprox +""" + x ≉ y + +This is equivalent to `!isapprox(x,y)` (see [`isapprox`](@ref)). +""" +≉(args...; kws...) = !≈(args...; kws...) + +# default tolerance arguments +rtoldefault(::Type{T}) where {T<:AbstractFloat} = sqrt(eps(T)) +rtoldefault(::Type{<:Real}) = 0 +function rtoldefault(x::Union{T,Type{T}}, y::Union{S,Type{S}}, atol::Real) where {T<:Number,S<:Number} + rtol = max(rtoldefault(real(T)),rtoldefault(real(S))) + return atol > 0 ? zero(rtol) : rtol +end + +# fused multiply-add + +""" + fma(x, y, z) + +Computes `x*y+z` without rounding the intermediate result `x*y`. On some systems this is +significantly more expensive than `x*y+z`. `fma` is used to improve accuracy in certain +algorithms. See [`muladd`](@ref). +""" +function fma end + +fma_libm(x::Float32, y::Float32, z::Float32) = + ccall(("fmaf", libm_name), Float32, (Float32,Float32,Float32), x, y, z) +fma_libm(x::Float64, y::Float64, z::Float64) = + ccall(("fma", libm_name), Float64, (Float64,Float64,Float64), x, y, z) +fma_llvm(x::Float32, y::Float32, z::Float32) = fma_float(x, y, z) +fma_llvm(x::Float64, y::Float64, z::Float64) = fma_float(x, y, z) +# Disable LLVM's fma if it is incorrect, e.g. because LLVM falls back +# onto a broken system libm; if so, use openlibm's fma instead +# 1.0000305f0 = 1 + 1/2^15 +# 1.0000000009313226 = 1 + 1/2^30 +# If fma_llvm() clobbers the rounding mode, the result of 0.1 + 0.2 will be 0.3 +# instead of the properly-rounded 0.30000000000000004; check after calling fma +if (Sys.ARCH !== :i686 && fma_llvm(1.0000305f0, 1.0000305f0, -1.0f0) == 6.103609f-5 && + (fma_llvm(1.0000000009313226, 1.0000000009313226, -1.0) == + 1.8626451500983188e-9) && 0.1 + 0.2 == 0.30000000000000004) + fma(x::Float32, y::Float32, z::Float32) = fma_llvm(x,y,z) + fma(x::Float64, y::Float64, z::Float64) = fma_llvm(x,y,z) +else + fma(x::Float32, y::Float32, z::Float32) = fma_libm(x,y,z) + fma(x::Float64, y::Float64, z::Float64) = fma_libm(x,y,z) +end +function fma(a::Float16, b::Float16, c::Float16) + Float16(fma(Float32(a), Float32(b), Float32(c))) +end + +# This is necessary at least on 32-bit Intel Linux, since fma_llvm may +# have called glibc, and some broken glibc fma implementations don't +# properly restore the rounding mode +Rounding.setrounding_raw(Float32, Rounding.JL_FE_TONEAREST) +Rounding.setrounding_raw(Float64, Rounding.JL_FE_TONEAREST) diff --git a/base/gcutils.jl b/base/gcutils.jl new file mode 100644 index 0000000..e710a0d --- /dev/null +++ b/base/gcutils.jl @@ -0,0 +1,97 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +==(w::WeakRef, v::WeakRef) = isequal(w.value, v.value) +==(w::WeakRef, v) = isequal(w.value, v) +==(w, v::WeakRef) = isequal(w, v.value) + +""" + finalizer(f, x) + +Register a function `f(x)` to be called when there are no program-accessible references to +`x`, and return `x`. The type of `x` must be a `mutable struct`, otherwise the behavior of +this function is unpredictable. + +`f` must not cause a task switch, which excludes most I/O operations such as `println`. +`@schedule println("message")` or `ccall(:jl_, Void, (Any,), "message")` may be helpful for +debugging purposes. +""" +function finalizer(@nospecialize(f), @nospecialize(o)) + if isimmutable(o) + error("objects of type ", typeof(o), " cannot be finalized") + end + ccall(:jl_gc_add_finalizer_th, Cvoid, (Ptr{Cvoid}, Any, Any), + Core.getptls(), o, f) + return o +end + +function finalizer(f::Ptr{Cvoid}, o::T) where T + @_inline_meta + if isimmutable(o) + error("objects of type ", typeof(o), " cannot be finalized") + end + ccall(:jl_gc_add_ptr_finalizer, Cvoid, (Ptr{Cvoid}, Any, Ptr{Cvoid}), + Core.getptls(), o, f) + return o +end + +""" + finalize(x) + +Immediately run finalizers registered for object `x`. +""" +finalize(@nospecialize(o)) = ccall(:jl_finalize_th, Cvoid, (Ptr{Cvoid}, Any,), + Core.getptls(), o) + +""" + Base.GC + +Module with garbage collection utilities. +""" +module GC + +""" + GC.gc() + +Perform garbage collection. + +!!! warning + Excessive use will likely lead to poor performance. +""" +gc(full::Bool=true) = ccall(:jl_gc_collect, Cvoid, (Int32,), full) + +""" + GC.enable(on::Bool) + +Control whether garbage collection is enabled using a boolean argument (`true` for enabled, +`false` for disabled). Return previous GC state. + +!!! warning + Disabling garbage collection should be used only with caution, as it can cause memory + use to grow without bound. +""" +enable(on::Bool) = ccall(:jl_gc_enable, Int32, (Int32,), on) != 0 + +""" + GC.@preserve x1 x2 ... xn expr + +Temporarily protect the given objects from being garbage collected, even if they would +otherwise be unreferenced. + +The last argument is the expression during which the object(s) will be preserved. +The previous arguments are the objects to preserve. +""" +macro preserve(args...) + syms = args[1:end-1] + for x in syms + isa(x, Symbol) || error("Preserved variable must be a symbol") + end + s, r = gensym(), gensym() + esc(quote + $s = $(Expr(:gc_preserve_begin, syms...)) + $r = $(args[end]) + $(Expr(:gc_preserve_end, s)) + $r + end) +end + +end # module GC diff --git a/base/generator.jl b/base/generator.jl new file mode 100644 index 0000000..b0f7e32 --- /dev/null +++ b/base/generator.jl @@ -0,0 +1,128 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" + Generator(f, iter) + +Given a function `f` and an iterator `iter`, construct an iterator that yields +the values of `f` applied to the elements of `iter`. +The syntax `f(x) for x in iter [if cond(x)::Bool]` is syntax for constructing an instance of this +type. The `[if cond(x)::Bool]` expression is optional and acts as a "guard", effectively +filtering out values where the condition is false. + +```jldoctest +julia> g = (abs2(x) for x in 1:5 if x != 3); + +julia> for x in g + println(x) + end +1 +4 +16 +25 + +julia> collect(g) +4-element Array{Int64,1}: + 1 + 4 + 16 + 25 +``` +""" +struct Generator{I,F} + f::F + iter::I +end + +Generator(f, I1, I2, Is...) = Generator(a->f(a...), zip(I1, I2, Is...)) + +Generator(::Type{T}, iter::I) where {T,I} = Generator{I,Type{T}}(T, iter) + +Generator(::Type{T}, I1, I2, Is...) where {T} = Generator(a->T(a...), zip(I1, I2, Is...)) + +function iterate(g::Generator, s...) + @_inline_meta + y = iterate(g.iter, s...) + y === nothing && return nothing + y = y::Tuple{Any, Any} # try to give inference some idea of what to expect about the behavior of the next line + return (g.f(y[1]), y[2]) +end + +length(g::Generator) = length(g.iter) +size(g::Generator) = size(g.iter) +axes(g::Generator) = axes(g.iter) +ndims(g::Generator) = ndims(g.iter) + + +## iterator traits + +abstract type IteratorSize end +struct SizeUnknown <: IteratorSize end +struct HasLength <: IteratorSize end +struct HasShape{N} <: IteratorSize end +struct IsInfinite <: IteratorSize end + +""" + IteratorSize(itertype::Type) -> IteratorSize + +Given the type of an iterator, return one of the following values: + +* `SizeUnknown()` if the length (number of elements) cannot be determined in advance. +* `HasLength()` if there is a fixed, finite length. +* `HasShape{N}()` if there is a known length plus a notion of multidimensional shape (as for an array). + In this case `N` should give the number of dimensions, and the [`axes`](@ref) function is valid + for the iterator. +* `IsInfinite()` if the iterator yields values forever. + +The default value (for iterators that do not define this function) is `HasLength()`. +This means that most iterators are assumed to implement [`length`](@ref). + +This trait is generally used to select between algorithms that pre-allocate space for their +result, and algorithms that resize their result incrementally. + +```jldoctest +julia> Base.IteratorSize(1:5) +Base.HasShape{1}() + +julia> Base.IteratorSize((2,3)) +Base.HasLength() +``` +""" +IteratorSize(x) = IteratorSize(typeof(x)) +IteratorSize(::Type) = HasLength() # HasLength is the default + +IteratorSize(::Type{<:AbstractArray{<:Any,N}}) where {N} = HasShape{N}() +IteratorSize(::Type{Generator{I,F}}) where {I,F} = IteratorSize(I) + +IteratorSize(::Type{Any}) = SizeUnknown() + +haslength(iter) = IteratorSize(iter) isa Union{HasShape, HasLength} + +abstract type IteratorEltype end +struct EltypeUnknown <: IteratorEltype end +struct HasEltype <: IteratorEltype end + +""" + IteratorEltype(itertype::Type) -> IteratorEltype + +Given the type of an iterator, return one of the following values: + +* `EltypeUnknown()` if the type of elements yielded by the iterator is not known in advance. +* `HasEltype()` if the element type is known, and [`eltype`](@ref) would return a meaningful value. + +`HasEltype()` is the default, since iterators are assumed to implement [`eltype`](@ref). + +This trait is generally used to select between algorithms that pre-allocate a specific +type of result, and algorithms that pick a result type based on the types of yielded +values. + +```jldoctest +julia> Base.IteratorEltype(1:5) +Base.HasEltype() +``` +""" +IteratorEltype(x) = IteratorEltype(typeof(x)) +IteratorEltype(::Type) = HasEltype() # HasEltype is the default + +IteratorEltype(::Type{Generator{I,T}}) where {I,T} = EltypeUnknown() + +IteratorEltype(::Type{Any}) = EltypeUnknown() diff --git a/base/gmp.jl b/base/gmp.jl new file mode 100644 index 0000000..b816695 --- /dev/null +++ b/base/gmp.jl @@ -0,0 +1,726 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +module GMP + +export BigInt + +import .Base: *, +, -, /, <, <<, >>, >>>, <=, ==, >, >=, ^, (~), (&), (|), xor, + binomial, cmp, convert, div, divrem, factorial, fld, gcd, gcdx, lcm, mod, + ndigits, promote_rule, rem, show, isqrt, string, powermod, + sum, trailing_zeros, trailing_ones, count_ones, tryparse_internal, + bin, oct, dec, hex, isequal, invmod, _prevpow2, _nextpow2, ndigits0zpb, + widen, signed, unsafe_trunc, trunc, iszero, isone, big, flipsign, signbit, + hastypemax + +if Clong == Int32 + const ClongMax = Union{Int8, Int16, Int32} + const CulongMax = Union{UInt8, UInt16, UInt32} +else + const ClongMax = Union{Int8, Int16, Int32, Int64} + const CulongMax = Union{UInt8, UInt16, UInt32, UInt64} +end +const CdoubleMax = Union{Float16, Float32, Float64} + +version() = VersionNumber(unsafe_string(unsafe_load(cglobal((:__gmp_version, :libgmp), Ptr{Cchar})))) +bits_per_limb() = Int(unsafe_load(cglobal((:__gmp_bits_per_limb, :libgmp), Cint))) + +const VERSION = version() +const BITS_PER_LIMB = bits_per_limb() + +# GMP's mp_limb_t is by default a typedef of `unsigned long`, but can also be configured to be either +# `unsigned int` or `unsigned long long int`. The correct unsigned type is here named Limb, and must +# be used whenever mp_limb_t is in the signature of ccall'ed GMP functions. +if BITS_PER_LIMB == 32 + const Limb = UInt32 + const SLimbMax = Union{Int8, Int16, Int32} + const ULimbMax = Union{UInt8, UInt16, UInt32} +elseif BITS_PER_LIMB == 64 + const Limb = UInt64 + const SLimbMax = Union{Int8, Int16, Int32, Int64} + const ULimbMax = Union{UInt8, UInt16, UInt32, UInt64} +else + error("GMP: cannot determine the type mp_limb_t (__gmp_bits_per_limb == $BITS_PER_LIMB)") +end + +""" + BigInt <: Signed + +Arbitrary precision integer type. +""" +mutable struct BigInt <: Signed + alloc::Cint + size::Cint + d::Ptr{Limb} + + function BigInt(; nbits::Integer=0) + b = MPZ.init2!(new(), nbits) + finalizer(cglobal((:__gmpz_clear, :libgmp)), b) + return b + end +end + +""" + BigInt(x) + +Create an arbitrary precision integer. `x` may be an `Int` (or anything that can be +converted to an `Int`). The usual mathematical operators are defined for this type, and +results are promoted to a [`BigInt`](@ref). + +Instances can be constructed from strings via [`parse`](@ref), or using the `big` +string literal. + +# Examples +```jldoctest +julia> parse(BigInt, "42") +42 + +julia> big"313" +313 +``` +""" +BigInt(x) + +""" + ALLOC_OVERFLOW_FUNCTION + +A reference that holds a boolean, if true, indicating julia is linked with a patched GMP that +does not abort on huge allocation and throws OutOfMemoryError instead. +""" +const ALLOC_OVERFLOW_FUNCTION = Ref(false) + +function __init__() + try + if version().major != VERSION.major || bits_per_limb() != BITS_PER_LIMB + msg = bits_per_limb() != BITS_PER_LIMB ? error : warn + msg("The dynamically loaded GMP library (v\"$(version())\" with __gmp_bits_per_limb == $(bits_per_limb()))\n", + "does not correspond to the compile time version (v\"$VERSION\" with __gmp_bits_per_limb == $BITS_PER_LIMB).\n", + "Please rebuild Julia.") + end + + ccall((:__gmp_set_memory_functions, :libgmp), Cvoid, + (Ptr{Cvoid},Ptr{Cvoid},Ptr{Cvoid}), + cglobal(:jl_gc_counted_malloc), + cglobal(:jl_gc_counted_realloc_with_old_size), + cglobal(:jl_gc_counted_free_with_size)) + ZERO.alloc, ZERO.size, ZERO.d = 0, 0, C_NULL + ONE.alloc, ONE.size, ONE.d = 1, 1, pointer(_ONE) + catch ex + Base.showerror_nostdio(ex, "WARNING: Error during initialization of module GMP") + end + # This only works with a patched version of GMP, ignore otherwise + try + ccall((:__gmp_set_alloc_overflow_function, :libgmp), Cvoid, + (Ptr{Cvoid},), + cglobal(:jl_throw_out_of_memory_error)) + ALLOC_OVERFLOW_FUNCTION[] = true + catch ex + # ErrorException("ccall: could not find function...") + if typeof(ex) != ErrorException + rethrow() + end + end +end + + +module MPZ +# wrapping of libgmp functions +# - "output parameters" are labeled x, y, z, and are returned when appropriate +# - constant input parameters are labeled a, b, c +# - a method modifying its input has a "!" appendend to its name, according to Julia's conventions +# - some convenient methods are added (in addition to the pure MPZ ones), e.g. `add(a, b) = add!(BigInt(), a, b)` +# and `add!(x, a) = add!(x, x, a)`. +using .Base.GMP: BigInt, Limb, BITS_PER_LIMB + +const mpz_t = Ref{BigInt} +const bitcnt_t = Culong + +gmpz(op::Symbol) = (Symbol(:__gmpz_, op), :libgmp) + +init!(x::BigInt) = (ccall((:__gmpz_init, :libgmp), Cvoid, (mpz_t,), x); x) +init2!(x::BigInt, a) = (ccall((:__gmpz_init2, :libgmp), Cvoid, (mpz_t, bitcnt_t), x, a); x) + +realloc2!(x, a) = (ccall((:__gmpz_realloc2, :libgmp), Cvoid, (mpz_t, bitcnt_t), x, a); x) +realloc2(a) = realloc2!(BigInt(), a) + +sizeinbase(a::BigInt, b) = Int(ccall((:__gmpz_sizeinbase, :libgmp), Csize_t, (mpz_t, Cint), a, b)) + +for (op, nbits) in (:add => :(BITS_PER_LIMB*(1 + max(abs(a.size), abs(b.size)))), + :sub => :(BITS_PER_LIMB*(1 + max(abs(a.size), abs(b.size)))), + :mul => 0, :fdiv_q => 0, :tdiv_q => 0, :fdiv_r => 0, :tdiv_r => 0, + :gcd => 0, :lcm => 0, :and => 0, :ior => 0, :xor => 0) + op! = Symbol(op, :!) + @eval begin + $op!(x::BigInt, a::BigInt, b::BigInt) = (ccall($(gmpz(op)), Cvoid, (mpz_t, mpz_t, mpz_t), x, a, b); x) + $op(a::BigInt, b::BigInt) = $op!(BigInt(nbits=$nbits), a, b) + $op!(x::BigInt, b::BigInt) = $op!(x, x, b) + end +end + +invert!(x::BigInt, a::BigInt, b::BigInt) = + ccall((:__gmpz_invert, :libgmp), Cint, (mpz_t, mpz_t, mpz_t), x, a, b) +invert(a::BigInt, b::BigInt) = invert!(BigInt(), a, b) +invert!(x::BigInt, b::BigInt) = invert!(x, x, b) + +for op in (:add_ui, :sub_ui, :mul_ui, :mul_2exp, :fdiv_q_2exp, :pow_ui, :bin_ui) + op! = Symbol(op, :!) + @eval begin + $op!(x::BigInt, a::BigInt, b) = (ccall($(gmpz(op)), Cvoid, (mpz_t, mpz_t, Culong), x, a, b); x) + $op(a::BigInt, b) = $op!(BigInt(), a, b) + $op!(x::BigInt, b) = $op!(x, x, b) + end +end + +ui_sub!(x::BigInt, a, b::BigInt) = (ccall((:__gmpz_ui_sub, :libgmp), Cvoid, (mpz_t, Culong, mpz_t), x, a, b); x) +ui_sub(a, b::BigInt) = ui_sub!(BigInt(), a, b) + +for op in (:scan1, :scan0) + @eval $op(a::BigInt, b) = Int(ccall($(gmpz(op)), Culong, (mpz_t, Culong), a, b)) +end + +mul_si!(x::BigInt, a::BigInt, b) = (ccall((:__gmpz_mul_si, :libgmp), Cvoid, (mpz_t, mpz_t, Clong), x, a, b); x) +mul_si(a::BigInt, b) = mul_si!(BigInt(), a, b) +mul_si!(x::BigInt, b) = mul_si!(x, x, b) + +for op in (:neg, :com, :sqrt, :set) + op! = Symbol(op, :!) + @eval begin + $op!(x::BigInt, a::BigInt) = (ccall($(gmpz(op)), Cvoid, (mpz_t, mpz_t), x, a); x) + $op(a::BigInt) = $op!(BigInt(), a) + end + op === :set && continue # MPZ.set!(x) would make no sense + @eval $op!(x::BigInt) = $op!(x, x) +end + +for (op, T) in ((:fac_ui, Culong), (:set_ui, Culong), (:set_si, Clong), (:set_d, Cdouble)) + op! = Symbol(op, :!) + @eval begin + $op!(x::BigInt, a) = (ccall($(gmpz(op)), Cvoid, (mpz_t, $T), x, a); x) + $op(a) = $op!(BigInt(), a) + end +end + +popcount(a::BigInt) = Int(ccall((:__gmpz_popcount, :libgmp), Culong, (mpz_t,), a)) + +mpn_popcount(d::Ptr{Limb}, s::Integer) = Int(ccall((:__gmpn_popcount, :libgmp), Culong, (Ptr{Limb}, Csize_t), d, s)) +mpn_popcount(a::BigInt) = mpn_popcount(a.d, abs(a.size)) + +function tdiv_qr!(x::BigInt, y::BigInt, a::BigInt, b::BigInt) + ccall((:__gmpz_tdiv_qr, :libgmp), Cvoid, (mpz_t, mpz_t, mpz_t, mpz_t), x, y, a, b) + x, y +end +tdiv_qr(a::BigInt, b::BigInt) = tdiv_qr!(BigInt(), BigInt(), a, b) + +powm!(x::BigInt, a::BigInt, b::BigInt, c::BigInt) = + (ccall((:__gmpz_powm, :libgmp), Cvoid, (mpz_t, mpz_t, mpz_t, mpz_t), x, a, b, c); x) +powm(a::BigInt, b::BigInt, c::BigInt) = powm!(BigInt(), a, b, c) +powm!(x::BigInt, b::BigInt, c::BigInt) = powm!(x, x, b, c) + +function gcdext!(x::BigInt, y::BigInt, z::BigInt, a::BigInt, b::BigInt) + ccall((:__gmpz_gcdext, :libgmp), Cvoid, (mpz_t, mpz_t, mpz_t, mpz_t, mpz_t), x, y, z, a, b) + x, y, z +end +gcdext(a::BigInt, b::BigInt) = gcdext!(BigInt(), BigInt(), BigInt(), a, b) + +cmp(a::BigInt, b::BigInt) = Int(ccall((:__gmpz_cmp, :libgmp), Cint, (mpz_t, mpz_t), a, b)) +cmp_si(a::BigInt, b) = Int(ccall((:__gmpz_cmp_si, :libgmp), Cint, (mpz_t, Clong), a, b)) +cmp_ui(a::BigInt, b) = Int(ccall((:__gmpz_cmp_ui, :libgmp), Cint, (mpz_t, Culong), a, b)) +cmp_d(a::BigInt, b) = Int(ccall((:__gmpz_cmp_d, :libgmp), Cint, (mpz_t, Cdouble), a, b)) + +mpn_cmp(a::Ptr{Limb}, b::Ptr{Limb}, c) = ccall((:__gmpn_cmp, :libgmp), Cint, (Ptr{Limb}, Ptr{Limb}, Clong), a, b, c) +mpn_cmp(a::BigInt, b::BigInt, c) = mpn_cmp(a.d, b.d, c) + +get_str!(x, a, b::BigInt) = (ccall((:__gmpz_get_str,:libgmp), Ptr{Cchar}, (Ptr{Cchar}, Cint, mpz_t), x, a, b); x) +set_str!(x::BigInt, a, b) = Int(ccall((:__gmpz_set_str, :libgmp), Cint, (mpz_t, Ptr{UInt8}, Cint), x, a, b)) +get_d(a::BigInt) = ccall((:__gmpz_get_d, :libgmp), Cdouble, (mpz_t,), a) + +limbs_write!(x::BigInt, a) = ccall((:__gmpz_limbs_write, :libgmp), Ptr{Limb}, (mpz_t, Clong), x, a) +limbs_finish!(x::BigInt, a) = ccall((:__gmpz_limbs_finish, :libgmp), Cvoid, (mpz_t, Clong), x, a) +import!(x::BigInt, a, b, c, d, e, f) = ccall((:__gmpz_import, :libgmp), Cvoid, + (mpz_t, Csize_t, Cint, Csize_t, Cint, Csize_t, Ptr{Cvoid}), x, a, b, c, d, e, f) + +setbit!(x, a) = (ccall((:__gmpz_setbit, :libgmp), Cvoid, (mpz_t, bitcnt_t), x, a); x) +tstbit(a::BigInt, b) = ccall((:__gmpz_tstbit, :libgmp), Cint, (mpz_t, bitcnt_t), a, b) % Bool + +end # module MPZ + +const ZERO = BigInt() +const ONE = BigInt() +const _ONE = Limb[1] + +widen(::Type{Int128}) = BigInt +widen(::Type{UInt128}) = BigInt +widen(::Type{BigInt}) = BigInt + +signed(x::BigInt) = x + +BigInt(x::BigInt) = x +Signed(x::BigInt) = x + +hastypemax(::Type{BigInt}) = false + +function tryparse_internal(::Type{BigInt}, s::AbstractString, startpos::Int, endpos::Int, base_::Integer, raise::Bool) + # don't make a copy in the common case where we are parsing a whole String + bstr = startpos == firstindex(s) && endpos == lastindex(s) ? String(s) : String(SubString(s,startpos,endpos)) + + sgn, base, i = Base.parseint_preamble(true,Int(base_),bstr,firstindex(bstr),lastindex(bstr)) + if !(2 <= base <= 62) + raise && throw(ArgumentError("invalid base: base must be 2 ≤ base ≤ 62, got $base")) + return nothing + end + if i == 0 + raise && throw(ArgumentError("premature end of integer: $(repr(bstr))")) + return nothing + end + z = BigInt() + if Base.containsnul(bstr) + err = -1 # embedded NUL char (not handled correctly by GMP) + else + err = GC.@preserve bstr MPZ.set_str!(z, pointer(bstr)+(i-firstindex(bstr)), base) + end + if err != 0 + raise && throw(ArgumentError("invalid BigInt: $(repr(bstr))")) + return nothing + end + flipsign!(z, sgn) +end + +BigInt(x::Union{Clong,Int32}) = MPZ.set_si(x) +BigInt(x::Union{Culong,UInt32}) = MPZ.set_ui(x) +BigInt(x::Bool) = BigInt(UInt(x)) + +unsafe_trunc(::Type{BigInt}, x::Union{Float32,Float64}) = MPZ.set_d(x) + +function BigInt(x::Union{Float32,Float64}) + isinteger(x) || throw(InexactError(:BigInt, BigInt, x)) + unsafe_trunc(BigInt,x) +end + +function trunc(::Type{BigInt}, x::Union{Float32,Float64}) + isfinite(x) || throw(InexactError(:trunc, BigInt, x)) + unsafe_trunc(BigInt,x) +end + +BigInt(x::Float16) = BigInt(Float64(x)) +BigInt(x::Float32) = BigInt(Float64(x)) + +function BigInt(x::Integer) + x == 0 && return BigInt(Culong(0)) + nd = ndigits(x, base=2) + z = MPZ.realloc2(nd) + s = sign(x) + s == -1 && (x = -x) + x = unsigned(x) + size = 0 + limbnbits = sizeof(Limb) << 3 + while nd > 0 + size += 1 + unsafe_store!(z.d, x % Limb, size) + x >>>= limbnbits + nd -= limbnbits + end + z.size = s*size + z +end + + +rem(x::BigInt, ::Type{Bool}) = !iszero(x) & unsafe_load(x.d) % Bool # never unsafe here + +rem(x::BigInt, ::Type{T}) where T<:Union{SLimbMax,ULimbMax} = + iszero(x) ? zero(T) : flipsign(unsafe_load(x.d) % T, x.size) + +function rem(x::BigInt, ::Type{T}) where T<:Union{Base.BitUnsigned,Base.BitSigned} + u = zero(T) + for l = 1:min(abs(x.size), cld(sizeof(T), sizeof(Limb))) + u += (unsafe_load(x.d, l) % T) << ((sizeof(Limb)<<3)*(l-1)) + end + flipsign(u, x.size) +end + +rem(x::Integer, ::Type{BigInt}) = BigInt(x) + +function (::Type{T})(x::BigInt) where T<:Base.BitUnsigned + if sizeof(T) < sizeof(Limb) + convert(T, convert(Limb,x)) + else + 0 <= x.size <= cld(sizeof(T),sizeof(Limb)) || throw(InexactError(nameof(T), T, x)) + x % T + end +end + +function (::Type{T})(x::BigInt) where T<:Base.BitSigned + n = abs(x.size) + if sizeof(T) < sizeof(Limb) + SLimb = typeof(Signed(one(Limb))) + convert(T, convert(SLimb, x)) + else + 0 <= n <= cld(sizeof(T),sizeof(Limb)) || throw(InexactError(nameof(T), T, x)) + y = x % T + ispos(x) ⊻ (y > 0) && throw(InexactError(nameof(T), T, x)) # catch overflow + y + end +end + + +Float64(n::BigInt, ::RoundingMode{:ToZero}) = MPZ.get_d(n) + +function (::Type{T})(n::BigInt, ::RoundingMode{:ToZero}) where T<:Union{Float16,Float32} + T(Float64(n,RoundToZero),RoundToZero) +end + +function (::Type{T})(n::BigInt, ::RoundingMode{:Down}) where T<:CdoubleMax + x = T(n,RoundToZero) + x > n ? prevfloat(x) : x +end +function (::Type{T})(n::BigInt, ::RoundingMode{:Up}) where T<:CdoubleMax + x = T(n,RoundToZero) + x < n ? nextfloat(x) : x +end + +function Float64(x::BigInt, ::RoundingMode{:Nearest}) + x == 0 && return 0.0 + xsize = abs(x.size) + if xsize*BITS_PER_LIMB > 1024 + z = Inf64 + elseif xsize == 1 + z = Float64(unsafe_load(x.d)) + elseif Limb == UInt32 && xsize == 2 + z = Float64((unsafe_load(x.d, 2) % UInt64) << BITS_PER_LIMB + unsafe_load(x.d)) + else + y1 = unsafe_load(x.d, xsize) % UInt64 + n = 64 - leading_zeros(y1) + # load first 54(1 + 52 bits of fraction + 1 for rounding) + y = y1 >> (n - (precision(Float64)+1)) + if Limb == UInt64 + y += n > precision(Float64) ? 0 : (unsafe_load(x.d, xsize-1) >> (10+n)) + else + y += (unsafe_load(x.d, xsize-1) % UInt64) >> (n-22) + y += n > (precision(Float64) - 32) ? 0 : (unsafe_load(x.d, xsize-2) >> (10+n)) + end + y = (y + 1) >> 1 # round, ties up + y &= ~UInt64(trailing_zeros(x) == (n-54 + (xsize-1)*BITS_PER_LIMB)) # fix last bit to round to even + d = ((n+1021) % UInt64) << 52 + z = reinterpret(Float64, d+y) + z = ldexp(z, (xsize-1)*BITS_PER_LIMB) + end + return flipsign(z, x.size) +end + +function Float32(x::BigInt, ::RoundingMode{:Nearest}) + x == 0 && return 0f0 + xsize = abs(x.size) + if xsize*BITS_PER_LIMB > 128 + z = Inf32 + elseif xsize == 1 + z = Float32(unsafe_load(x.d)) + else + y1 = unsafe_load(x.d, xsize) + n = BITS_PER_LIMB - leading_zeros(y1) + # load first 25(1 + 23 bits of fraction + 1 for rounding) + y = (y1 >> (n - (precision(Float32)+1))) % UInt32 + y += (n > precision(Float32) ? 0 : unsafe_load(x.d, xsize-1) >> (BITS_PER_LIMB - (25-n))) % UInt32 + y = (y + one(UInt32)) >> 1 # round, ties up + y &= ~UInt32(trailing_zeros(x) == (n-25 + (xsize-1)*BITS_PER_LIMB)) # fix last bit to round to even + d = ((n+125) % UInt32) << 23 + z = reinterpret(Float32, d+y) + z = ldexp(z, (xsize-1)*BITS_PER_LIMB) + end + return flipsign(z, x.size) +end + +function Float16(x::BigInt, ::RoundingMode{:Nearest}) + x == 0 && return Float16(0.0) + y1 = unsafe_load(x.d) + n = BITS_PER_LIMB - leading_zeros(y1) + if n > 16 || abs(x.size) > 1 + z = Inf16 + else + # load first 12(1 + 10 bits for fraction + 1 for rounding) + y = (y1 >> (n - (precision(Float16)+1))) % UInt16 + y = (y + one(UInt16)) >> 1 # round, ties up + y &= ~UInt16(trailing_zeros(x) == (n-12)) # fix last bit to round to even + d = ((n+13) % UInt16) << 10 + z = reinterpret(Float16, d+y) + end + return flipsign(z, x.size) +end + +Float64(n::BigInt) = Float64(n, RoundNearest) +Float32(n::BigInt) = Float32(n, RoundNearest) +Float16(n::BigInt) = Float16(n, RoundNearest) + +promote_rule(::Type{BigInt}, ::Type{<:Integer}) = BigInt + +""" + big(x) + +Convert a number to a maximum precision representation (typically [`BigInt`](@ref) or +`BigFloat`). See [`BigFloat`](@ref) for information about some pitfalls with floating-point numbers. +""" +function big end + +big(::Type{<:Integer}) = BigInt +big(::Type{<:Rational}) = Rational{BigInt} + +big(n::Integer) = convert(BigInt, n) + +# Binary ops +for (fJ, fC) in ((:+, :add), (:-,:sub), (:*, :mul), + (:fld, :fdiv_q), (:div, :tdiv_q), (:mod, :fdiv_r), (:rem, :tdiv_r), + (:gcd, :gcd), (:lcm, :lcm), + (:&, :and), (:|, :ior), (:xor, :xor)) + @eval begin + ($fJ)(x::BigInt, y::BigInt) = MPZ.$fC(x, y) + end +end + +/(x::BigInt, y::BigInt) = float(x)/float(y) + +function invmod(x::BigInt, y::BigInt) + z = zero(BigInt) + ya = abs(y) + if ya == 1 + return z + end + if (y==0 || MPZ.invert!(z, x, ya) == 0) + throw(DomainError(y)) + end + # GMP always returns a positive inverse; we instead want to + # normalize such that div(z, y) == 0, i.e. we want a negative z + # when y is negative. + if y < 0 + MPZ.add!(z, y) + end + # The postcondition is: mod(z * x, y) == mod(big(1), m) && div(z, y) == 0 + return z +end + +# More efficient commutative operations +for (fJ, fC) in ((:+, :add), (:*, :mul), (:&, :and), (:|, :ior), (:xor, :xor)) + fC! = Symbol(fC, :!) + @eval begin + ($fJ)(a::BigInt, b::BigInt, c::BigInt) = MPZ.$fC!(MPZ.$fC(a, b), c) + ($fJ)(a::BigInt, b::BigInt, c::BigInt, d::BigInt) = MPZ.$fC!(MPZ.$fC!(MPZ.$fC(a, b), c), d) + ($fJ)(a::BigInt, b::BigInt, c::BigInt, d::BigInt, e::BigInt) = + MPZ.$fC!(MPZ.$fC!(MPZ.$fC!(MPZ.$fC(a, b), c), d), e) + end +end + +# Basic arithmetic without promotion ++(x::BigInt, c::CulongMax) = MPZ.add_ui(x, c) ++(c::CulongMax, x::BigInt) = x + c + +-(x::BigInt, c::CulongMax) = MPZ.sub_ui(x, c) +-(c::CulongMax, x::BigInt) = MPZ.ui_sub(c, x) + ++(x::BigInt, c::ClongMax) = c < 0 ? -(x, -(c % Culong)) : x + convert(Culong, c) ++(c::ClongMax, x::BigInt) = c < 0 ? -(x, -(c % Culong)) : x + convert(Culong, c) +-(x::BigInt, c::ClongMax) = c < 0 ? +(x, -(c % Culong)) : -(x, convert(Culong, c)) +-(c::ClongMax, x::BigInt) = c < 0 ? -(x + -(c % Culong)) : -(convert(Culong, c), x) + +*(x::BigInt, c::CulongMax) = MPZ.mul_ui(x, c) +*(c::CulongMax, x::BigInt) = x * c +*(x::BigInt, c::ClongMax) = MPZ.mul_si(x, c) +*(c::ClongMax, x::BigInt) = x * c + +/(x::BigInt, y::Union{ClongMax,CulongMax}) = float(x)/y +/(x::Union{ClongMax,CulongMax}, y::BigInt) = x/float(y) + +# unary ops +(-)(x::BigInt) = MPZ.neg(x) +(~)(x::BigInt) = MPZ.com(x) + +<<(x::BigInt, c::UInt) = c == 0 ? x : MPZ.mul_2exp(x, c) +>>(x::BigInt, c::UInt) = c == 0 ? x : MPZ.fdiv_q_2exp(x, c) +>>>(x::BigInt, c::UInt) = x >> c + +trailing_zeros(x::BigInt) = MPZ.scan1(x, 0) +trailing_ones(x::BigInt) = MPZ.scan0(x, 0) + +count_ones(x::BigInt) = MPZ.popcount(x) + +""" + count_ones_abs(x::BigInt) + +Number of ones in the binary representation of abs(x). +""" +count_ones_abs(x::BigInt) = iszero(x) ? 0 : MPZ.mpn_popcount(x) + +divrem(x::BigInt, y::BigInt) = MPZ.tdiv_qr(x, y) + +cmp(x::BigInt, y::BigInt) = sign(MPZ.cmp(x, y)) +cmp(x::BigInt, y::ClongMax) = sign(MPZ.cmp_si(x, y)) +cmp(x::BigInt, y::CulongMax) = sign(MPZ.cmp_ui(x, y)) +cmp(x::BigInt, y::Integer) = cmp(x, big(y)) +cmp(x::Integer, y::BigInt) = -cmp(y, x) + +cmp(x::BigInt, y::CdoubleMax) = isnan(y) ? -1 : sign(MPZ.cmp_d(x, y)) +cmp(x::CdoubleMax, y::BigInt) = -cmp(y, x) + +isqrt(x::BigInt) = MPZ.sqrt(x) + +^(x::BigInt, y::Culong) = MPZ.pow_ui(x, y) + +function bigint_pow(x::BigInt, y::Integer) + if y<0; throw(DomainError(y, "`y` cannot be negative.")); end + @noinline throw1(y) = + throw(OverflowError("exponent $y is too large and computation will overflow")) + if x== 1; return x; end + if x==-1; return isodd(y) ? x : -x; end + if y>typemax(Culong) + x==0 && return x + + #At this point, x is not 1, 0 or -1 and it is not possible to use + #gmpz_pow_ui to compute the answer. Note that the magnitude of the + #answer is: + #- at least 2^(2^32-1) ≈ 10^(1.3e9) (if Culong === UInt32). + #- at least 2^(2^64-1) ≈ 10^(5.5e18) (if Culong === UInt64). + # + #Assume that the answer will definitely overflow. + + throw1(y) + end + return x^convert(Culong, y) +end + +^(x::BigInt , y::BigInt ) = bigint_pow(x, y) +^(x::BigInt , y::Bool ) = y ? x : one(x) +^(x::BigInt , y::Integer) = bigint_pow(x, y) +^(x::Integer, y::BigInt ) = bigint_pow(BigInt(x), y) +^(x::Bool , y::BigInt ) = Base.power_by_squaring(x, y) + +function powermod(x::BigInt, p::BigInt, m::BigInt) + r = MPZ.powm(x, p, m) + return m < 0 && r > 0 ? MPZ.add!(r, m) : r # choose sign consistent with mod(x^p, m) +end + +powermod(x::Integer, p::Integer, m::BigInt) = powermod(big(x), big(p), m) + +function gcdx(a::BigInt, b::BigInt) + if iszero(b) # shortcut this to ensure consistent results with gcdx(a,b) + return a < 0 ? (-a,-ONE,b) : (a,one(BigInt),b) + # we don't return the globals ONE and ZERO in case the user wants to + # mutate the result + end + g, s, t = MPZ.gcdext(a, b) + if t == 0 + # work around a difference in some versions of GMP + if a == b + return g, t, s + elseif abs(a)==abs(b) + return g, t, -s + end + end + g, s, t +end + +sum(arr::AbstractArray{BigInt}) = foldl(MPZ.add!, arr; init=BigInt(0)) +# Note: a similar implementation for `prod` won't be efficient: +# 1) the time complexity of the allocations is negligible compared to the multiplications +# 2) assuming arr contains similarly sized BigInts, the multiplications are much more +# performant when doing e.g. ((a1*a2)*(a3*a4))*(...) rather than a1*(a2*(a3*(...))), +# which is exactly what the default implementation of `prod` does, via `mapreduce` +# (which maybe could be slightly optimized for BigInt). + +factorial(x::BigInt) = isneg(x) ? BigInt(0) : MPZ.fac_ui(x) + +binomial(n::BigInt, k::UInt) = MPZ.bin_ui(n, k) +binomial(n::BigInt, k::Integer) = k < 0 ? BigInt(0) : binomial(n, UInt(k)) + +==(x::BigInt, y::BigInt) = cmp(x,y) == 0 +==(x::BigInt, i::Integer) = cmp(x,i) == 0 +==(i::Integer, x::BigInt) = cmp(x,i) == 0 +==(x::BigInt, f::CdoubleMax) = isnan(f) ? false : cmp(x,f) == 0 +==(f::CdoubleMax, x::BigInt) = isnan(f) ? false : cmp(x,f) == 0 +iszero(x::BigInt) = x.size == 0 +isone(x::BigInt) = x == Culong(1) + +<=(x::BigInt, y::BigInt) = cmp(x,y) <= 0 +<=(x::BigInt, i::Integer) = cmp(x,i) <= 0 +<=(i::Integer, x::BigInt) = cmp(x,i) >= 0 +<=(x::BigInt, f::CdoubleMax) = isnan(f) ? false : cmp(x,f) <= 0 +<=(f::CdoubleMax, x::BigInt) = isnan(f) ? false : cmp(x,f) >= 0 + +<(x::BigInt, y::BigInt) = cmp(x,y) < 0 +<(x::BigInt, i::Integer) = cmp(x,i) < 0 +<(i::Integer, x::BigInt) = cmp(x,i) > 0 +<(x::BigInt, f::CdoubleMax) = isnan(f) ? false : cmp(x,f) < 0 +<(f::CdoubleMax, x::BigInt) = isnan(f) ? false : cmp(x,f) > 0 +isneg(x::BigInt) = x.size < 0 +ispos(x::BigInt) = x.size > 0 + +signbit(x::BigInt) = isneg(x) +flipsign!(x::BigInt, y::Integer) = (signbit(y) && (x.size = -x.size); x) +flipsign( x::BigInt, y::Integer) = signbit(y) ? -x : x +flipsign( x::BigInt, y::BigInt) = signbit(y) ? -x : x +# above method to resolving ambiguities with flipsign(::T, ::T) where T<:Signed + +show(io::IO, x::BigInt) = print(io, string(x)) + +function string(n::BigInt; base::Integer = 10, pad::Integer = 1) + base < 0 && return Base._base(Int(base), n, pad, (base>0) & (n.size<0)) + 2 <= base <= 62 || throw(ArgumentError("base must be 2 ≤ base ≤ 62, got $base")) + iszero(n) && pad < 1 && return "" + nd1 = ndigits(n, base=base) + nd = max(nd1, pad) + sv = Base.StringVector(nd + isneg(n)) + GC.@preserve sv MPZ.get_str!(pointer(sv) + nd - nd1, base, n) + @inbounds for i = (1:nd-nd1) .+ isneg(n) + sv[i] = '0' % UInt8 + end + isneg(n) && (sv[1] = '-' % UInt8) + String(sv) +end + +function ndigits0zpb(x::BigInt, b::Integer) + b < 2 && throw(DomainError(b, "`b` cannot be less than 2.")) + x.size == 0 && return 0 # for consistency with other ndigits0z methods + if ispow2(b) && 2 <= b <= 62 # GMP assumes b is in this range + MPZ.sizeinbase(x, b) + else + # non-base 2 mpz_sizeinbase might return an answer 1 too big + # use property that log(b, x) < ndigits(x, base=b) <= log(b, x) + 1 + n = MPZ.sizeinbase(x, 2) + lb = log2(b) # assumed accurate to <1ulp (true for openlibm) + q,r = divrem(n,lb) + iq = Int(q) + maxerr = q*eps(lb) # maximum error in remainder + if r-1.0 < maxerr + abs(x) >= big(b)^iq ? iq+1 : iq + elseif lb-r < maxerr + abs(x) >= big(b)^(iq+1) ? iq+2 : iq+1 + else + iq+1 + end + end +end + +# Fast paths for nextpow(2, x::BigInt) +# below, ONE is always left-shifted by at least one digit, so a new BigInt is +# allocated, which can be safely mutated +_prevpow2(x::BigInt) = -2 <= x <= 2 ? x : flipsign!(ONE << (ndigits(x, base=2) - 1), x) +_nextpow2(x::BigInt) = count_ones_abs(x) <= 1 ? x : flipsign!(ONE << ndigits(x, base=2), x) + +Base.checked_abs(x::BigInt) = abs(x) +Base.checked_neg(x::BigInt) = -x +Base.checked_add(a::BigInt, b::BigInt) = a + b +Base.checked_sub(a::BigInt, b::BigInt) = a - b +Base.checked_mul(a::BigInt, b::BigInt) = a * b +Base.checked_div(a::BigInt, b::BigInt) = div(a, b) +Base.checked_rem(a::BigInt, b::BigInt) = rem(a, b) +Base.checked_fld(a::BigInt, b::BigInt) = fld(a, b) +Base.checked_mod(a::BigInt, b::BigInt) = mod(a, b) +Base.checked_cld(a::BigInt, b::BigInt) = cld(a, b) +Base.add_with_overflow(a::BigInt, b::BigInt) = a + b, false +Base.sub_with_overflow(a::BigInt, b::BigInt) = a - b, false +Base.mul_with_overflow(a::BigInt, b::BigInt) = a * b, false + +function Base.deepcopy_internal(x::BigInt, stackdict::IdDict) + if haskey(stackdict, x) + return stackdict[x] + end + y = MPZ.set(x) + stackdict[x] = y + return y +end + +end # module diff --git a/base/grisu/bignum.jl b/base/grisu/bignum.jl new file mode 100644 index 0000000..2f1d67c --- /dev/null +++ b/base/grisu/bignum.jl @@ -0,0 +1,256 @@ +# This file is a part of Julia, but is derived from +# https://github.com/google/double-conversion which has the following license +# +# Copyright 2006-2014, the V8 project authors. All rights reserved. +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT +# OWNER 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. + +function normalizedexponent(significand, exponent::Int32) + significand = UInt64(significand) + while (significand & HiddenBit(Float64)) == 0 + significand <<= UInt64(1) + exponent -= Int32(1) + end + return exponent +end + +function bignumdtoa(v,mode,requested_digits::Int,buffer,bignums) + significand = _significand(v) + exponent = _exponent(v) + lower_boundary_is_closer = lowerboundaryiscloser(v) + need_boundary_deltas = mode == SHORTEST + + is_even = (significand & 1) == 0 + normalized_exponent = normalizedexponent(significand, exponent) + estimated_power = estimatepower(Int(normalized_exponent)) + + if mode == FIXED && -estimated_power - 1 > requested_digits + buffer[1] = 0 + len = 1 + decimal_point = -requested_digits + return true, len, decimal_point + end + num, den, minus, plus = bignums[1], bignums[2], bignums[3], bignums[4] + initialscaledstartvalues!(significand,exponent,lower_boundary_is_closer, + estimated_power,need_boundary_deltas, + num,den,minus,plus) + decimal_point = fixupmultiply10!(estimated_power,is_even,num,den,minus,plus) + if mode == SHORTEST + len = generateshortestdigits!(num,den,minus,plus,is_even,buffer) + elseif mode == FIXED + len, decimal_point = bignumtofixed!(requested_digits,num,den,buffer,decimal_point) + elseif mode == PRECISION + len, decimal_point = generatecounteddigits!(requested_digits,num,den,buffer,decimal_point) + end + buffer[len] = 0 + return true, len, decimal_point +end + +function generateshortestdigits!(num,den,minus,plus,is_even,buffer) + minus == plus && (plus = minus) + len = 1 + while true + digit = Bignums.dividemodulointbignum!(num,den) + buffer[len] = 0x30 + (digit % UInt8) + len += 1 + in_delta_room_minus = is_even ? + Bignums.lessequal(num,minus) : Bignums.less(num,minus) + in_delta_room_plus = is_even ? + Bignums.pluscompare(num,plus,den) >= 0 : Bignums.pluscompare(num,plus,den) > 0 + if !in_delta_room_minus && !in_delta_room_plus + Bignums.times10!(num) + Bignums.times10!(minus) + minus != plus && Bignums.times10!(plus) + elseif in_delta_room_minus && in_delta_room_plus + compare = Bignums.pluscompare(num,num,den) + if compare < 0 + elseif compare > 0 + buffer[len - 1] += 1 + else + if (buffer[len - 1] - 0x30) % 2 == 0 + else + buffer[len - 1] += 1 + end + end + return len + elseif in_delta_room_minus + return len + else + buffer[len - 1] += 1 + return len + end + end +end + +function generatecounteddigits!(count,num,den,buffer,decimal_point) + for i = 1:(count-1) + digit = Bignums.dividemodulointbignum!(num,den) + buffer[i] = 0x30 + (digit % UInt8) + Bignums.times10!(num) + end + digit = Bignums.dividemodulointbignum!(num,den) + if Bignums.pluscompare(num,num,den) >= 0 + digit += 1 + end + buffer[count] = 0x30 + (digit % UInt8) + for i = count:-1:2 + buffer[i] != 0x30 + 10 && break + buffer[i] = 0x30 + buffer[i - 1] += 1 + end + if buffer[1] == 0x30 + 10 + buffer[1] = 0x31 + decimal_point += 1 + end + len = count+1 + return len, decimal_point +end + +function bignumtofixed!(requested_digits,num,den,buffer,decimal_point) + if -decimal_point > requested_digits + decimal_point = -requested_digits + len = 1 + return len, decimal_point + elseif -decimal_point == requested_digits + Bignums.times10!(den) + if Bignums.pluscompare(num,num,den) >= 0 + buffer[1] = 0x31 + len = 2 + decimal_point += 1 + else + len = 1 + end + return len, decimal_point + else + needed_digits = decimal_point + requested_digits + len, decimal_point = generatecounteddigits!( + needed_digits,num,den,buffer,decimal_point) + end + return len, decimal_point +end + + +const k1Log10 = 0.30102999566398114 +const kSignificandSize = SignificandSize(Float64) +estimatepower(exponent::Int) = ceil(Int,(exponent + kSignificandSize - 1) * k1Log10 - 1e-10) + +function init3!( + significand,exponent,estimated_power,need_boundary_deltas, + num,den,minus,plus) + Bignums.assignuint64!(num,UInt64(significand)) + Bignums.shiftleft!(num,exponent) + Bignums.assignpoweruint16!(den,UInt16(10),estimated_power) + if need_boundary_deltas + Bignums.shiftleft!(den,1) + Bignums.shiftleft!(num,1) + Bignums.assignuint16!(plus,UInt16(1)) + Bignums.shiftleft!(plus,exponent) + Bignums.assignuint16!(minus,UInt16(1)) + Bignums.shiftleft!(minus,exponent) + else + Bignums.zero!(plus) + Bignums.zero!(minus) + end + return +end + + +function init1!( + significand,exponent,estimated_power,need_boundary_deltas, + num,den,minus,plus) + Bignums.assignuint64!(num,UInt64(significand)) + Bignums.assignpoweruint16!(den,UInt16(10),estimated_power) + Bignums.shiftleft!(den,-exponent) + if need_boundary_deltas + Bignums.shiftleft!(den,1) + Bignums.shiftleft!(num,1) + Bignums.assignuint16!(plus,UInt16(1)) + Bignums.assignuint16!(minus,UInt16(1)) + else + Bignums.zero!(plus) + Bignums.zero!(minus) + end + return +end + +function init2!( + significand,exponent,estimated_power,need_boundary_deltas, + num,den,minus,plus) + power_ten = num + Bignums.assignpoweruint16!(power_ten,UInt16(10),-estimated_power) + if need_boundary_deltas + Bignums.assignbignum!(plus,power_ten) + Bignums.assignbignum!(minus,power_ten) + else + Bignums.zero!(plus) + Bignums.zero!(minus) + end + Bignums.multiplybyuint64!(num,UInt64(significand)) + Bignums.assignuint16!(den,UInt16(1)) + Bignums.shiftleft!(den,-exponent) + if need_boundary_deltas + Bignums.shiftleft!(num,1) + Bignums.shiftleft!(den,1) + end + return +end + +function initialscaledstartvalues!(significand, + exponent,lower_boundary_is_closer,estimated_power, + need_boundary_deltas,num,den,minus,plus) + if exponent >= 0 + init3!(significand, exponent, estimated_power, need_boundary_deltas,num,den,minus,plus) + elseif estimated_power >= 0 + init1!(significand, exponent, estimated_power, need_boundary_deltas,num,den,minus,plus) + else + init2!(significand, exponent, estimated_power, need_boundary_deltas,num,den,minus,plus) + end + if need_boundary_deltas && lower_boundary_is_closer + Bignums.shiftleft!(den,1) + Bignums.shiftleft!(num,1) + Bignums.shiftleft!(plus,1) + end + return +end + +function fixupmultiply10!(estimated_power,is_even,num,den,minus,plus) + in_range = is_even ? Bignums.pluscompare(num,plus,den) >= 0 : + Bignums.pluscompare(num,plus,den) > 0 + if in_range + decimal_point = estimated_power + 1 + else + decimal_point = estimated_power + Bignums.times10!(num) + if minus == plus + Bignums.times10!(minus) + Bignums.assignbignum!(plus,minus) + else + Bignums.times10!(minus) + Bignums.times10!(plus) + end + end + return decimal_point +end diff --git a/base/grisu/bignums.jl b/base/grisu/bignums.jl new file mode 100644 index 0000000..8898549 --- /dev/null +++ b/base/grisu/bignums.jl @@ -0,0 +1,495 @@ +# This file is a part of Julia, but is derived from +# https://github.com/google/double-conversion which has the following license +# +# Copyright 2006-2014, the V8 project authors. All rights reserved. +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT +# OWNER 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 Bignums + +import Base: ==, < + +export Bignum + +const kMaxSignificantBits = 3584 + +const Chunk = UInt32 +const DoubleChunk = UInt64 + +const kChunkSize = sizeof(Chunk) * 8 +const kDoubleChunkSize = sizeof(DoubleChunk) * 8 +# With bigit size of 28 we loose some bits, but a double still fits easily +# into two chunks, and more importantly we can use the Comba multiplication. +const kBigitSize = 28 +const kBigitMask = Chunk((1 << kBigitSize) - 1) +# Every instance allocates kBigitLength chunks on the stack. Bignums cannot +# grow. There are no checks if the stack-allocated space is sufficient. +const kBigitCapacity = div(kMaxSignificantBits, kBigitSize) + +mutable struct Bignum + bigits::Vector{UInt32} + used_digits::Int32 + exponent::Int32 + function Bignum() + bigits = Vector{UInt32}(undef, kBigitCapacity) + @inbounds for i = 1:kBigitCapacity + bigits[i] = 0 + end + new(bigits,0,0) + end +end + +==(a::Bignum,b::Bignum) = compare(a,b) == 0 +<(a::Bignum,b::Bignum) = compare(a,b) < 0 + +times10!(x::Bignum) = multiplybyuint32!(x,UInt32(10)) + +plusequal(a,b,c) = pluscompare(a,b,c) == 0 +pluslessequal(a,b,c) = pluscompare(a,b,c) <= 0 +plusless(a,b,c) = pluscompare(a,b,c) < 0 +lessequal(a::Bignum,b::Bignum) = compare(a,b) <= 0 +less(a::Bignum,b::Bignum) = compare(a,b) < 0 + +bigitlength(x::Bignum) = x.used_digits + x.exponent + +bitsize(value) = 8 * sizeof(value) + +function zero!(x::Bignum) + for i = 1:x.used_digits + @inbounds x.bigits[i] = 0 + end + x.used_digits = 0 + x.exponent = 0 + return +end + +function clamp!(x::Bignum) + @inbounds while (x.used_digits > 0 && x.bigits[x.used_digits] == 0) + x.used_digits -= 1 + end + x.used_digits == 0 && (x.exponent = 0) + return +end + +isclamped(x::Bignum) = x.used_digits == 0 || x.bigits[x.used_digits] != 0 + +function align!(x::Bignum,other::Bignum) + @inbounds if x.exponent > other.exponent + zero_digits = x.exponent - other.exponent + for i = x.used_digits:-1:1 + x.bigits[i + zero_digits] = x.bigits[i] + end + for i = 1:zero_digits + x.bigits[i] = 0 + end + x.used_digits += zero_digits + x.exponent -= zero_digits + end + return +end + +function bigitshiftleft!(x::Bignum,shift_amount) + carry::UInt32 = 0 + @inbounds begin + for i = 1:x.used_digits + new_carry::Chunk = x.bigits[i] >> (kBigitSize - shift_amount) + x.bigits[i] = ((x.bigits[i] << shift_amount) + carry) & kBigitMask + carry = new_carry + end + if carry != 0 + x.bigits[x.used_digits+1] = carry + x.used_digits += 1 + end + end + return +end + +function subtracttimes!(x::Bignum,other::Bignum,factor) + if factor < 3 + for i = 1:factor + subtractbignum!(x,other) + end + return + end + borrow::Chunk = 0 + exponent_diff = other.exponent - x.exponent + @inbounds begin + for i = 1:other.used_digits + product::DoubleChunk = DoubleChunk(factor) * other.bigits[i] + remove::DoubleChunk = borrow + product + difference::Chunk = (x.bigits[i+exponent_diff] - (remove & kBigitMask)) % Chunk + x.bigits[i+exponent_diff] = difference & kBigitMask + borrow = ((difference >> (kChunkSize - 1)) + (remove >> kBigitSize)) % Chunk + end + for i = (other.used_digits + exponent_diff + 1):x.used_digits + borrow == 0 && return + difference::Chunk = x.bigits[i] - borrow + x.bigits[i] = difference & kBigitMask + borrow = difference >> (kChunkSize - 1) + end + end + clamp!(x) +end + +function assignuint16!(x::Bignum,value::UInt16) + zero!(x) + value == 0 && return + x.bigits[1] = value + x.used_digits = 1 + return +end + +const kUInt64Size = 64 +function assignuint64!(x::Bignum,value::UInt64) + zero!(x) + value == 0 && return + needed_bigits = div(kUInt64Size,kBigitSize) + 1 + @inbounds for i = 1:needed_bigits + x.bigits[i] = value & kBigitMask + value >>= kBigitSize + end + x.used_digits = needed_bigits + clamp!(x) +end + +function assignbignum!(x::Bignum,other::Bignum) + x.exponent = other.exponent + @inbounds begin + for i = 1:other.used_digits + x.bigits[i] = other.bigits[i] + end + for i = (other.used_digits+1):x.used_digits + x.bigits[i] = 0 + end + end + x.used_digits = other.used_digits + return +end + +function adduint64!(x::Bignum,operand::UInt64) + operand == 0 && return + other = Bignum() + assignuint64!(other,operand) + addbignum!(x,other) +end + +function addbignum!(x::Bignum,other::Bignum) + align!(x,other) + carry::Chunk = 0 + bigit_pos = other.exponent - x.exponent + @inbounds for i = 1:other.used_digits + sum::Chunk = x.bigits[bigit_pos+1] + other.bigits[i] + carry + x.bigits[bigit_pos+1] = sum & kBigitMask + carry = sum >> kBigitSize + bigit_pos += 1 + end + @inbounds while carry != 0 + sum = x.bigits[bigit_pos+1] + carry + x.bigits[bigit_pos+1] = sum & kBigitMask + carry = sum >> kBigitSize + bigit_pos += 1 + end + x.used_digits = max(bigit_pos,x.used_digits) + return +end + +function subtractbignum!(x::Bignum,other::Bignum) + align!(x,other) + offset = other.exponent - x.exponent + borrow = Chunk(0) + @inbounds begin + for i = 1:other.used_digits + difference = x.bigits[i+offset] - other.bigits[i] - borrow + x.bigits[i+offset] = difference & kBigitMask + borrow = difference >> (kChunkSize - 1) + end + i = other.used_digits+1 + while borrow != 0 + difference = x.bigits[i+offset] - borrow + x.bigits[i+offset] = difference & kBigitMask + borrow = difference >> (kChunkSize - 1) + i += 1 + end + end + clamp!(x) +end + +function shiftleft!(x::Bignum,shift_amount) + x.used_digits == 0 && return + x.exponent += div(shift_amount,kBigitSize) + local_shift = shift_amount % kBigitSize + bigitshiftleft!(x,local_shift) +end + +function multiplybyuint32!(x::Bignum,factor::UInt32) + factor == 1 && return + if factor == 0 + zero!(x) + return + end + x.used_digits == 0 && return + carry::DoubleChunk = 0 + @inbounds begin + for i = 1:x.used_digits + product::DoubleChunk = (factor % DoubleChunk) * x.bigits[i] + carry + x.bigits[i] = (product & kBigitMask) % Chunk + carry = product >> kBigitSize + end + while carry != 0 + x.bigits[x.used_digits+1] = carry & kBigitMask + x.used_digits += 1 + carry >>= kBigitSize + end + end + return +end + +function multiplybyuint64!(x::Bignum,factor::UInt64) + factor == 1 && return + if factor == 0 + zero!(x) + return + end + carry::UInt64 = 0 + low::UInt64 = factor & 0xFFFFFFFF + high::UInt64 = factor >> 32 + @inbounds begin + for i = 1:x.used_digits + product_low::UInt64 = low * x.bigits[i] + product_high::UInt64 = high * x.bigits[i] + tmp::UInt64 = (carry & kBigitMask) + product_low + x.bigits[i] = tmp & kBigitMask + carry = (carry >> kBigitSize) + (tmp >> kBigitSize) + + (product_high << (32 - kBigitSize)) + end + while carry != 0 + x.bigits[x.used_digits+1] = carry & kBigitMask + x.used_digits += 1 + carry >>= kBigitSize + end + end + return +end + +const kFive27 = UInt64(0x6765c793fa10079d) +const kFive1 = UInt16(5) +const kFive2 = UInt16(kFive1 * 5) +const kFive3 = UInt16(kFive2 * 5) +const kFive4 = UInt16(kFive3 * 5) +const kFive5 = UInt16(kFive4 * 5) +const kFive6 = UInt16(kFive5 * 5) +const kFive7 = UInt32(kFive6 * 5) +const kFive8 = UInt32(kFive7 * 5) +const kFive9 = UInt32(kFive8 * 5) +const kFive10 = UInt32(kFive9 * 5) +const kFive11 = UInt32(kFive10 * 5) +const kFive12 = UInt32(kFive11 * 5) +const kFive13 = UInt32(kFive12 * 5) +const kFive1_to_12 = UInt32[kFive1, kFive2, kFive3, kFive4, kFive5, kFive6, + kFive7, kFive8, kFive9, kFive10, kFive11, kFive12] +function multiplybypoweroften!(x::Bignum,exponent) + exponent == 0 && return + x.used_digits == 0 && return + remaining_exponent = exponent + while remaining_exponent >= 27 + multiplybyuint64!(x,kFive27) + remaining_exponent -= 27 + end + while remaining_exponent >= 13 + multiplybyuint32!(x,kFive13) + remaining_exponent -= 13 + end + remaining_exponent > 0 && multiplybyuint32!(x, + kFive1_to_12[remaining_exponent]) + shiftleft!(x,exponent) +end + +function square!(x::Bignum) + product_length = 2 * x.used_digits + (1 << (2 * (kChunkSize - kBigitSize))) <= x.used_digits && error("unimplemented") + accumulator::DoubleChunk = 0 + copy_offset = x.used_digits + @inbounds begin + for i = 1:x.used_digits + x.bigits[copy_offset + i] = x.bigits[i] + end + for i = 1:x.used_digits + bigit_index1 = i-1 + bigit_index2 = 0 + while bigit_index1 >= 0 + chunk1::Chunk = x.bigits[copy_offset + bigit_index1 + 1] + chunk2::Chunk = x.bigits[copy_offset + bigit_index2 + 1] + accumulator += (chunk1 % DoubleChunk) * chunk2 + bigit_index1 -= 1 + bigit_index2 += 1 + end + x.bigits[i] = (accumulator % Chunk) & kBigitMask + accumulator >>= kBigitSize + end + for i = x.used_digits+1:product_length + bigit_index1 = x.used_digits - 1 + bigit_index2 = i - bigit_index1 - 1 + while bigit_index2 < x.used_digits + chunk1::Chunk = x.bigits[copy_offset + bigit_index1 + 1] + chunk2::Chunk = x.bigits[copy_offset + bigit_index2 + 1] + accumulator += (chunk1 % DoubleChunk) * chunk2 + bigit_index1 -= 1 + bigit_index2 += 1 + end + x.bigits[i] = (accumulator % Chunk) & kBigitMask + accumulator >>= kBigitSize + end + end + x.used_digits = product_length + x.exponent *= 2 + clamp!(x) +end + +function assignpoweruint16!(x::Bignum,base::UInt16,power_exponent::Int) + if power_exponent == 0 + assignuint16!(x,UInt16(1)) + return + end + zero!(x) + shifts::Int = 0 + while base & UInt16(1) == UInt16(0) + base >>= UInt16(1) + shifts += 1 + end + bit_size::Int = 0 + tmp_base::Int= base + while tmp_base != 0 + tmp_base >>= 1 + bit_size += 1 + end + final_size = bit_size * power_exponent + mask::Int = 1 + while power_exponent >= mask + mask <<= 1 + end + mask >>= 2 + this_value::UInt64 = base + delayed_multiplication = false + max_32bits::UInt64 = 0xFFFFFFFF + while mask != 0 && this_value <= max_32bits + this_value *= this_value + if (power_exponent & mask) != 0 + base_bits_mask::UInt64 = ~(UInt64(1) << (64 - bit_size) - 1) + high_bits_zero = (this_value & base_bits_mask) == 0 + if high_bits_zero + this_value *= base + else + delayed_multiplication = true + end + end + mask >>= 1 + end + assignuint64!(x,this_value) + delayed_multiplication && multiplybyuint32!(x,UInt32(base)) + while mask != 0 + square!(x) + (power_exponent & mask) != 0 && multiplybyuint32!(x,UInt32(base)) + mask >>= 1 + end + shiftleft!(x,shifts * power_exponent) +end + +function dividemodulointbignum!(x::Bignum,other::Bignum) + bigitlength(x) < bigitlength(other) && return UInt16(0) + align!(x,other) + result::UInt16 = 0 + @inbounds begin + while bigitlength(x) > bigitlength(other) + result += x.bigits[x.used_digits] % UInt16 + subtracttimes!(x,other,x.bigits[x.used_digits]) + end + this_bigit::Chunk = x.bigits[x.used_digits] + other_bigit::Chunk = other.bigits[other.used_digits] + if other.used_digits == 1 + quotient = reinterpret(Int32,div(this_bigit,other_bigit)) + x.bigits[x.used_digits] = this_bigit - other_bigit * reinterpret(UInt32,quotient) + result += quotient % UInt16 + clamp!(x) + return result + end + end + division_estimate = reinterpret(Int32,div(this_bigit,other_bigit+Chunk(1))) + result += division_estimate % UInt16 + subtracttimes!(x,other,division_estimate) + other_bigit * (division_estimate+1) > this_bigit && return result + while lessequal(other, x) + subtractbignum!(x,other) + result += UInt16(1) + end + return result +end + +function pluscompare(a::Bignum,b::Bignum,c::Bignum) + bigitlength(a) < bigitlength(b) && return pluscompare(b,a,c) + bigitlength(a) + 1 < bigitlength(c) && return -1 + bigitlength(a) > bigitlength(c) && return 1 + a.exponent >= bigitlength(b) && bigitlength(a) < bigitlength(c) && return -1 + borrow::Chunk = 0 + min_exponent = min(a.exponent,b.exponent,c.exponent) + for i = (bigitlength(c)-1):-1:min_exponent + chunk_a::Chunk = bigitat(a,i) + chunk_b::Chunk = bigitat(b,i) + chunk_c::Chunk = bigitat(c,i) + sum::Chunk = chunk_a + chunk_b + if sum > chunk_c + borrow + return 1 + else + borrow = chunk_c + borrow - sum + borrow > 1 && return -1 + borrow <<= kBigitSize + end + end + borrow == 0 && return 0 + return -1 +end + +function compare(a::Bignum,b::Bignum) + bigit_length_a = bigitlength(a) + bigit_length_b = bigitlength(b) + bigit_length_a < bigit_length_b && return -1 + bigit_length_a > bigit_length_b && return 1 + for i = (bigit_length_a-1):-1:min(a.exponent,b.exponent) + bigit_a::Chunk = bigitat(a,i) + bigit_b::Chunk = bigitat(b,i) + bigit_a < bigit_b && return -1 + bigit_a > bigit_b && return 1 + end + return 0 +end + +function bigitat(x::Bignum,index) + index >= bigitlength(x) && return Chunk(0) + index < x.exponent && return Chunk(0) + @inbounds ret = x.bigits[index - x.exponent+1]::Chunk + return ret +end + +end # module diff --git a/base/grisu/fastfixed.jl b/base/grisu/fastfixed.jl new file mode 100644 index 0000000..014806b --- /dev/null +++ b/base/grisu/fastfixed.jl @@ -0,0 +1,252 @@ +# This file is a part of Julia, but is derived from +# https://github.com/google/double-conversion which has the following license +# +# Copyright 2006-2014, the V8 project authors. All rights reserved. +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT +# OWNER 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. + +const kDoubleSignificandSize = 53 + +function filldigits32fixedlength(n1,requested_len,buffer,len) + for i = (requested_len-1):-1:0 + buffer[len+i] = 0x30 + n1 % 10 + n1 = div(n1,10) + end + return len + requested_len +end + +function filldigits32(n,buffer,len) + n_len = 0 + while n != 0 + digit = n % 10 + n = div(n,10) + buffer[len+n_len] = 0x30 + digit + n_len += 1 + end + i,j = len, len + n_len - 1 + while i < j + buffer[i], buffer[j] = buffer[j], buffer[i] + i += 1 + j -= 1 + end + return len + n_len +end + +function filldigits64fixedlength(n2,buffer,len) + kTen7 = 10000000 + part2 = n2 % kTen7 + n2 = div(n2,kTen7) + part0, part1 = divrem(n2,kTen7) + len = filldigits32fixedlength(part0, 3, buffer, len) + len = filldigits32fixedlength(part1, 7, buffer, len) + len = filldigits32fixedlength(part2, 7, buffer, len) + return len +end + +function filldigits64(n3,buffer,len) + kTen7 = 10000000 + part2 = n3 % kTen7 + n3 = div(n3,kTen7) + part0, part1 = divrem(n3,kTen7) + if part0 != 0 + len = filldigits32(part0, buffer, len) + len = filldigits32fixedlength(part1, 7, buffer, len) + len = filldigits32fixedlength(part2, 7, buffer, len) + elseif part1 != 0 + len = filldigits32(part1, buffer, len) + len = filldigits32fixedlength(part2, 7, buffer, len) + else + len = filldigits32(part2, buffer, len) + end + return len +end + +function roundup(buffer, len, decimal_point) + if len == 1 + buffer[1] = 0x31 + decimal_point = 1 + len = 2 + return len, decimal_point + end + buffer[len - 1] += 1 + for i = (len-1):-1:2 + buffer[i] != 0x30 + 10 && return len, decimal_point + buffer[i] = 0x30 + buffer[i - 1] += 1 + end + if buffer[1] == 0x30 + 10 + buffer[1] = 0x31 + decimal_point += 1 + end + return len, decimal_point +end + +function fillfractionals(fractionals, exponent, + fractional_count, buffer, + len, decimal_point) + if -exponent <= 64 + point = -exponent + for i = 1:fractional_count + fractionals == 0 && break + fractionals *= 5 + point -= 1 + digit = fractionals >> point + buffer[len] = 0x30 + digit + len += 1 + fractionals -= UInt64(digit) << point + end + if ((fractionals >> (point - 1)) & 1) == 1 + len, decimal_point = roundup(buffer, len, decimal_point) + end + else + fract128 = UInt128(fractionals) << 64 + fract128 = shift(fract128,-exponent - 64) + point = 128 + for i = 1:fractional_count + fract128 == 0 && break + fract128 *= 5 + point -= 1 + digit, fract128 = divrem2(fract128,point) + buffer[len] = 0x30 + digit + len += 1 + end + if bitat(fract128,point - 1) == 1 + len, decimal_point = roundup(buffer, len, decimal_point) + end + end + return len, decimal_point +end + +low(x) = UInt64(x&0xffffffffffffffff) +high(x) = UInt64(x >>> 64) +bitat(x::UInt128,y) = y >= 64 ? (Int32(high(x) >> (y-64)) & 1) : (Int32(low(x) >> y) & 1) +function divrem2(x,power) + h = high(x) + l = low(x) + if power >= 64 + result = Int32(h >> (power - 64)) + h -= UInt64(result) << (power - 64) + return result, (UInt128(h) << 64) + l + else + part_low::UInt64 = l >> power + part_high::UInt64 = h << (64 - power) + result = Int32(part_low + part_high) + return result, UInt128(l - (part_low << power)) + end +end +function shift(x::UInt128,amt) + if amt == 0 + return x + elseif amt == -64 + return x << 64 + elseif amt == 64 + return x >> 64 + elseif amt <= 0 + h = high(x); l = low(x) + h <<= -amt + h += l >> (64 + amt) + l <<= -amt + return (UInt128(h) << 64) + l + else + h = high(x); l = low(x) + l >>= amt + l += h << (64 - amt) + h >>= amt + return (UInt128(h) << 64) + l + end +end + +function trimzeros(buffer, len, decimal_point) + while len > 1 && buffer[len - 1] == 0x30 + len -= 1 + end + first_non_zero::Int32 = 1 + while first_non_zero < len && buffer[first_non_zero] == 0x30 + first_non_zero += 1 + end + if first_non_zero != 1 + for i = first_non_zero:(len-1) + buffer[i - first_non_zero + 1] = buffer[i] + end + len -= first_non_zero-1 + decimal_point -= first_non_zero-1 + end + return len, decimal_point +end + +function fastfixedtoa(v,mode,fractional_count,buffer) + v = Float64(v) + significand::UInt64 = _significand(v) + exponent = _exponent(v) + exponent > 20 && return false, 0, 0 + fractional_count > 20 && return false, 0, 0 + len = 1 + if exponent + kDoubleSignificandSize > 64 + kFive17 = divisor = Int64(5)^17 + divisor_power = 17 + dividend = significand + if exponent > divisor_power + dividend <<= exponent - divisor_power + quotient = div(dividend,divisor) + remainder = (dividend % divisor) << divisor_power + else + divisor <<= divisor_power - exponent + quotient = div(dividend,divisor) + remainder = (dividend % divisor) << exponent + end + len = filldigits32(quotient, buffer, len) + len = filldigits64fixedlength(remainder, buffer, len) + decimal_point = len-1 + elseif exponent >= 0 + significand <<= exponent + len = filldigits64(significand, buffer, len) + decimal_point = len-1 + elseif exponent > -kDoubleSignificandSize + integrals = significand >> -exponent + fractionals = significand - (integrals << -exponent) + if integrals > 0xFFFFFFFF + len = filldigits64(integrals,buffer,len) + else + len = filldigits32(integrals%UInt32,buffer,len) + end + decimal_point = len-1 + len, decimal_point = fillfractionals(fractionals,exponent,fractional_count, + buffer,len, decimal_point) + elseif exponent < -128 + len = 1 + decimal_point = -fractional_count + else + decimal_point = 0 + len, decimal_point = fillfractionals(significand,exponent,fractional_count, + buffer,len, decimal_point) + end + len, decimal_point = trimzeros(buffer,len,decimal_point) + buffer[len] = 0 + if (len-1) == 0 + decimal_point = -fractional_count + end + return true, len, decimal_point +end diff --git a/base/grisu/fastprecision.jl b/base/grisu/fastprecision.jl new file mode 100644 index 0000000..dfb7a0c --- /dev/null +++ b/base/grisu/fastprecision.jl @@ -0,0 +1,99 @@ +# This file is a part of Julia, but is derived from +# https://github.com/google/double-conversion which has the following license +# +# Copyright 2006-2014, the V8 project authors. All rights reserved. +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT +# OWNER 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. + +function roundweed(buffer,len,rest,tk,unit,kappa) + unit >= tk && return false, kappa + tk - unit <= unit && return false, kappa + tk - rest > rest && (tk - 2 * rest >= 2 * unit) && return true, kappa + if rest > unit && (tk - (rest - unit) <= (rest - unit)) + buffer[len-1] += 1 + for i = (len-1):-1:2 + buffer[i] != 0x30 + 10 && break + buffer[i] = 0x30 + buffer[i-1] += 1 + end + if buffer[1] == 0x30 + 10 + buffer[1] = 0x31 + kappa += 1 + end + return true, kappa + end + return false, kappa +end + +function digitgen(w,buffer,requested_digits=1000) + unit::UInt64 = 1 + one = Float(unit << -w.e, w.e) + integrals = w.s >> -one.e + fractionals = w.s & (one.s-1) + divisor, kappa = bigpowten(integrals, 64 + one.e) + len = 1 + rest = 0 + while kappa > 0 + digit = div(integrals,divisor) + buffer[len] = 0x30 + digit + len += 1 + requested_digits -= 1 + integrals %= divisor + kappa -= 1 + if requested_digits == 0 + rest = (UInt64(integrals) << -one.e) + fractionals + r, kappa = roundweed(buffer, len, rest, UInt64(divisor) << -one.e, + unit,kappa) + return r, kappa, len + end + divisor = div(divisor,10) + end + while requested_digits > 0 && fractionals > unit + fractionals *= 10 + unit *= 10 + digit = fractionals >> -one.e + buffer[len] = 0x30 + digit + len += 1 + requested_digits -= 1 + fractionals &= one.s - 1 + kappa -= 1 + end + requested_digits != 0 && return false, kappa, len + r, kappa = roundweed(buffer,len,fractionals,one.s, + unit,kappa) + return r, kappa, len +end + +function fastprecision(v, requested_digits, buffer = Vector{UInt8}(undef, 100)) + f = normalize(Float64(v)) + ten_mk_min_exp = kMinExp - (f.e + FloatSignificandSize) + ten_mk_max_exp = kMaxExp - (f.e + FloatSignificandSize) + cp = binexp_cache(ten_mk_min_exp,ten_mk_max_exp) + scaled_w = f * cp + r, kappa, len = digitgen(scaled_w,buffer,requested_digits) + decimal_exponent = -cp.de + kappa + return r, len, decimal_exponent+len-1 +end diff --git a/base/grisu/fastshortest.jl b/base/grisu/fastshortest.jl new file mode 100644 index 0000000..acd810e --- /dev/null +++ b/base/grisu/fastshortest.jl @@ -0,0 +1,118 @@ +# This file is a part of Julia, but is derived from +# https://github.com/google/double-conversion which has the following license +# +# Copyright 2006-2014, the V8 project authors. All rights reserved. +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT +# OWNER 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. + +const kMinExp = -60 +const kMaxExp = -32 + +function roundweed(buffer,len,rest,tk,unit,kappa,too_high::UInt64,unsafe_interval::UInt64) + small = too_high - unit + big = too_high + unit + while rest < small && + unsafe_interval - rest >= tk && + (rest + tk < small || + small - rest >= rest + tk - small) + buffer[len-1] -= 1 + rest += tk + end + if rest < big && + unsafe_interval - rest >= tk && + (rest + tk < big || + big - rest > rest + tk - big) + return false, kappa + end + return (2 * unit <= rest) && (rest <= unsafe_interval - 4 * unit), kappa +end + +const SmallPowersOfTen = [ + 0, 1, 10, 100, 1000, 10000, 100000, + 1000000, 10000000, 100000000, 1000000000] + +function bigpowten(n,n_bits) + guess = ((n_bits + 1) * 1233) >> 12 + guess += 1 + i = SmallPowersOfTen[guess+1] + return n < i ? (SmallPowersOfTen[guess], guess-1) : (i,guess) +end + +function digitgen(low,w,high,buffer) + unit::UInt64 = 1 + one = Float(unit << -w.e, w.e) + too_high = Float(high.s+unit,high.e) + unsafe_interval = too_high - Float(low.s-unit,low.e) + integrals = too_high.s >> -one.e + fractionals = too_high.s & (one.s-1) + divisor, kappa = bigpowten(integrals, 64 + one.e) + len = 1 + rest = UInt64(0) + while kappa > 0 + digit = div(integrals,divisor) + buffer[len] = 0x30 + digit + len += 1 + integrals %= divisor + kappa -= 1 + rest = (UInt64(integrals) << -one.e) + fractionals + if rest < unsafe_interval.s + r, kappa = roundweed(buffer, len, rest, UInt64(divisor) << -one.e, + unit,kappa,(too_high - w).s,unsafe_interval.s) + return r, kappa, len + end + divisor = div(divisor,10) + end + while true + fractionals *= 10 + unit *= 10 + unsafe_interval = Float(unsafe_interval.s*10,unsafe_interval.e) + digit = fractionals >> -one.e + buffer[len] = 0x30 + digit + len += 1 + fractionals &= one.s - 1 + kappa -= 1 + if fractionals < unsafe_interval.s + r, kappa = roundweed(buffer,len,fractionals,one.s, + unit,kappa,(too_high - w).s*unit,unsafe_interval.s) + return r, kappa, len + end + end +end + +function fastshortest(v, buffer = Vector{UInt8}(undef, 17)) + f = normalize(Float64(v)) + bound_minus, bound_plus = normalizedbound(v) + ten_mk_min_exp = kMinExp - (f.e + FloatSignificandSize) + ten_mk_max_exp = kMaxExp - (f.e + FloatSignificandSize) + cp = binexp_cache(ten_mk_min_exp,ten_mk_max_exp) + scaled_w = f * cp + scaled_bound_minus = bound_minus * cp + scaled_bound_plus = bound_plus * cp + r, kappa, len = digitgen(scaled_bound_minus,scaled_w, + scaled_bound_plus,buffer) + decimal_exponent = -cp.de + kappa + return r, len, decimal_exponent+len-1 +end diff --git a/base/grisu/float.jl b/base/grisu/float.jl new file mode 100644 index 0000000..a92b94c --- /dev/null +++ b/base/grisu/float.jl @@ -0,0 +1,258 @@ +# This file is a part of Julia, but is derived from +# https://github.com/google/double-conversion which has the following license +# +# Copyright 2006-2014, the V8 project authors. All rights reserved. +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT +# OWNER 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. + +import Base: -, * + +struct Float + s::UInt64 + e::Int32 + de::Int32 +end + +Float() = Float(0,0,0) +Float(x,y) = Float(x,y,Int32(0)) +Float(d::AbstractFloat) = Float(_significand(d), _exponent(d)) + +# Consts +const Float10MSBits = 0xFFC0000000000000 # used normalize(Float) +const FloatSignMask = 0x8000000000000000 # used in normalize(Float) +const FloatSignificandSize = Int32(64) + +function normalize(v::Float) + f = v.s + e::Int32 = v.e + while (f & Float10MSBits) == 0 + f <<= 10 + e -= 10 + end + while (f & FloatSignMask) == 0 + f <<= 1 + e -= 1 + end + return Float(f,e) +end +function normalize(v::Float64) + s = _significand(v); e = _exponent(v) + while (s & HiddenBit(Float64)) == 0 + s <<= UInt64(1) + e -= Int32(1) + end + s <<= UInt64(FloatSignificandSize - SignificandSize(Float64)) + e -= Int32( FloatSignificandSize - SignificandSize(Float64)) + return Float(s, e) +end + +# Float128 +#DenormalExponent(::Type{Float128}) = Int32(-ExponentBias(Float128) + 1) +#ExponentMask(::Type{Float128}) = 0x7fff0000000000000000000000000000 +#PhysicalSignificandSize(::Type{Float128}) = Int32(112) +#SignificandSize(::Type{Float128}) = Int32(113) +#ExponentBias(::Type{Float128}) = Int32(0x00003fff + PhysicalSignificandSize(Float128)) +#SignificandMask(::Type{Float128}) = 0x0000ffffffffffffffffffffffffffff +#HiddenBit(::Type{Float128}) = 0x00010000000000000000000000000000 +#uint_t(d::Float128) = reinterpret(UInt128,d) +# Float64 +DenormalExponent(::Type{Float64}) = Int32(-ExponentBias(Float64) + 1) +ExponentMask(::Type{Float64}) = 0x7FF0000000000000 +PhysicalSignificandSize(::Type{Float64}) = Int32(52) +SignificandSize(::Type{Float64}) = Int32(53) +ExponentBias(::Type{Float64}) = Int32(0x3FF + PhysicalSignificandSize(Float64)) +SignificandMask(::Type{Float64}) = 0x000FFFFFFFFFFFFF +HiddenBit(::Type{Float64}) = 0x0010000000000000 +uint_t(d::Float64) = reinterpret(UInt64,d) +# Float32 +DenormalExponent(::Type{Float32}) = Int32(-ExponentBias(Float32) + 1) +ExponentMask(::Type{Float32}) = 0x7F800000 +PhysicalSignificandSize(::Type{Float32}) = Int32(23) +SignificandSize(::Type{Float32}) = Int32(24) +ExponentBias(::Type{Float32}) = Int32(0x7F + PhysicalSignificandSize(Float32)) +SignificandMask(::Type{Float32}) = 0x007FFFFF +HiddenBit(::Type{Float32}) = 0x00800000 +uint_t(d::Float32) = reinterpret(UInt32,d) +# Float16 +DenormalExponent(::Type{Float16}) = Int32(-ExponentBias(Float16) + 1) +ExponentMask(::Type{Float16}) = 0x7c00 +PhysicalSignificandSize(::Type{Float16}) = Int32(10) +SignificandSize(::Type{Float16}) = Int32(11) +ExponentBias(::Type{Float16}) = Int32(0x000f + PhysicalSignificandSize(Float16)) +SignificandMask(::Type{Float16}) = 0x03ff +HiddenBit(::Type{Float16}) = 0x0400 +uint_t(d::Float16) = reinterpret(UInt16,d) + +function _exponent(d::T) where T<:AbstractFloat + isdenormal(d) && return DenormalExponent(T) + biased_e::Int32 = Int32((uint_t(d) & ExponentMask(T)) >> PhysicalSignificandSize(T)) + return Int32(biased_e - ExponentBias(T)) +end +function _significand(d::T) where T<:AbstractFloat + s = uint_t(d) & SignificandMask(T) + return !isdenormal(d) ? s + HiddenBit(T) : s +end +isdenormal(d::T) where {T<:AbstractFloat} = (uint_t(d) & ExponentMask(T)) == 0 + +function normalizedbound(f::AbstractFloat) + v = Float(_significand(f),_exponent(f)) + m_plus = normalize(Float((v.s << 1) + 1, v.e - 1)) + if lowerboundaryiscloser(f) + m_minus = Float((v.s << 2) - 1, v.e - 2) + else + m_minus = Float((v.s << 1) - 1, v.e - 1) + end + return Float(m_minus.s << (m_minus.e - m_plus.e), m_plus.e), m_plus +end +function lowerboundaryiscloser(f::T) where T<:AbstractFloat + physical_significand_is_zero = (uint_t(f) & SignificandMask(T)) == 0 + return physical_significand_is_zero && (_exponent(f) != DenormalExponent(T)) +end + +(-)(a::Float,b::Float) = Float(a.s - b.s,a.e,a.de) + +const FloatM32 = 0xFFFFFFFF + +function (*)(this::Float,other::Float) + a::UInt64 = this.s >> 32 + b::UInt64 = this.s & FloatM32 + c::UInt64 = other.s >> 32 + d::UInt64 = other.s & FloatM32 + ac::UInt64 = a * c + bc::UInt64 = b * c + ad::UInt64 = a * d + bd::UInt64 = b * d + tmp::UInt64 = (bd >> 32) + (ad & FloatM32) + (bc & FloatM32) + # By adding 1U << 31 to tmp we round the final result. + # Halfway cases will be round up. + tmp += UInt64(1) << 31 + result_f::UInt64 = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32) + return Float(result_f,this.e + other.e + 64,this.de) +end + +const CachedPowers = Float[ + Float(0xfa8fd5a0081c0288, -1220, -348), + Float(0xbaaee17fa23ebf76, -1193, -340), + Float(0x8b16fb203055ac76, -1166, -332), + Float(0xcf42894a5dce35ea, -1140, -324), + Float(0x9a6bb0aa55653b2d, -1113, -316), + Float(0xe61acf033d1a45df, -1087, -308), + Float(0xab70fe17c79ac6ca, -1060, -300), + Float(0xff77b1fcbebcdc4f, -1034, -292), + Float(0xbe5691ef416bd60c, -1007, -284), + Float(0x8dd01fad907ffc3c, -980, -276), + Float(0xd3515c2831559a83, -954, -268), + Float(0x9d71ac8fada6c9b5, -927, -260), + Float(0xea9c227723ee8bcb, -901, -252), + Float(0xaecc49914078536d, -874, -244), + Float(0x823c12795db6ce57, -847, -236), + Float(0xc21094364dfb5637, -821, -228), + Float(0x9096ea6f3848984f, -794, -220), + Float(0xd77485cb25823ac7, -768, -212), + Float(0xa086cfcd97bf97f4, -741, -204), + Float(0xef340a98172aace5, -715, -196), + Float(0xb23867fb2a35b28e, -688, -188), + Float(0x84c8d4dfd2c63f3b, -661, -180), + Float(0xc5dd44271ad3cdba, -635, -172), + Float(0x936b9fcebb25c996, -608, -164), + Float(0xdbac6c247d62a584, -582, -156), + Float(0xa3ab66580d5fdaf6, -555, -148), + Float(0xf3e2f893dec3f126, -529, -140), + Float(0xb5b5ada8aaff80b8, -502, -132), + Float(0x87625f056c7c4a8b, -475, -124), + Float(0xc9bcff6034c13053, -449, -116), + Float(0x964e858c91ba2655, -422, -108), + Float(0xdff9772470297ebd, -396, -100), + Float(0xa6dfbd9fb8e5b88f, -369, -92), + Float(0xf8a95fcf88747d94, -343, -84), + Float(0xb94470938fa89bcf, -316, -76), + Float(0x8a08f0f8bf0f156b, -289, -68), + Float(0xcdb02555653131b6, -263, -60), + Float(0x993fe2c6d07b7fac, -236, -52), + Float(0xe45c10c42a2b3b06, -210, -44), + Float(0xaa242499697392d3, -183, -36), + Float(0xfd87b5f28300ca0e, -157, -28), + Float(0xbce5086492111aeb, -130, -20), + Float(0x8cbccc096f5088cc, -103, -12), + Float(0xd1b71758e219652c, -77, -4), + Float(0x9c40000000000000, -50, 4), + Float(0xe8d4a51000000000, -24, 12), + Float(0xad78ebc5ac620000, 3, 20), + Float(0x813f3978f8940984, 30, 28), + Float(0xc097ce7bc90715b3, 56, 36), + Float(0x8f7e32ce7bea5c70, 83, 44), + Float(0xd5d238a4abe98068, 109, 52), + Float(0x9f4f2726179a2245, 136, 60), + Float(0xed63a231d4c4fb27, 162, 68), + Float(0xb0de65388cc8ada8, 189, 76), + Float(0x83c7088e1aab65db, 216, 84), + Float(0xc45d1df942711d9a, 242, 92), + Float(0x924d692ca61be758, 269, 100), + Float(0xda01ee641a708dea, 295, 108), + Float(0xa26da3999aef774a, 322, 116), + Float(0xf209787bb47d6b85, 348, 124), + Float(0xb454e4a179dd1877, 375, 132), + Float(0x865b86925b9bc5c2, 402, 140), + Float(0xc83553c5c8965d3d, 428, 148), + Float(0x952ab45cfa97a0b3, 455, 156), + Float(0xde469fbd99a05fe3, 481, 164), + Float(0xa59bc234db398c25, 508, 172), + Float(0xf6c69a72a3989f5c, 534, 180), + Float(0xb7dcbf5354e9bece, 561, 188), + Float(0x88fcf317f22241e2, 588, 196), + Float(0xcc20ce9bd35c78a5, 614, 204), + Float(0x98165af37b2153df, 641, 212), + Float(0xe2a0b5dc971f303a, 667, 220), + Float(0xa8d9d1535ce3b396, 694, 228), + Float(0xfb9b7cd9a4a7443c, 720, 236), + Float(0xbb764c4ca7a44410, 747, 244), + Float(0x8bab8eefb6409c1a, 774, 252), + Float(0xd01fef10a657842c, 800, 260), + Float(0x9b10a4e5e9913129, 827, 268), + Float(0xe7109bfba19c0c9d, 853, 276), + Float(0xac2820d9623bf429, 880, 284), + Float(0x80444b5e7aa7cf85, 907, 292), + Float(0xbf21e44003acdd2d, 933, 300), + Float(0x8e679c2f5e44ff8f, 960, 308), + Float(0xd433179d9c8cb841, 986, 316), + Float(0x9e19db92b4e31ba9, 1013, 324), + Float(0xeb96bf6ebadf77d9, 1039, 332), + Float(0xaf87023b9bf0ee6b, 1066, 340)] + +const CachedPowersLength = length(CachedPowers) +const CachedPowersOffset = 348 # -1 * the first decimal_exponent. +const D_1_LOG2_10 = 0.30102999566398114 # 1 / lg(10) +# Difference between the decimal exponents in the table above. +const DecimalExponentDistance = 8 +const MinDecimalExponent = -348 +const MaxDecimalExponent = 340 + +function binexp_cache(min_exponent,max_exponent) + k = ceil(Integer,(min_exponent+63)*D_1_LOG2_10) + index = div(CachedPowersOffset+k-1,DecimalExponentDistance) + 1 + cp = CachedPowers[index+1] + return cp +end diff --git a/base/grisu/grisu.jl b/base/grisu/grisu.jl new file mode 100644 index 0000000..e5419de --- /dev/null +++ b/base/grisu/grisu.jl @@ -0,0 +1,241 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +module Grisu + +export print_shortest +export DIGITS, DIGITSs, grisu + +const SHORTEST = 1 +const FIXED = 2 +const PRECISION = 3 + +include("grisu/float.jl") +include("grisu/fastshortest.jl") +include("grisu/fastprecision.jl") +include("grisu/fastfixed.jl") +include("grisu/bignums.jl") +include("grisu/bignum.jl") + +const DIGITS = Vector{UInt8}(undef, 309+17) +const BIGNUMS = [Bignums.Bignum(),Bignums.Bignum(),Bignums.Bignum(),Bignums.Bignum()] + +# NOTE: DIGITS[s] is deprecated; you should use getbuf() instead. +const DIGITSs = [DIGITS] +const BIGNUMSs = [BIGNUMS] +function __init__() + Threads.resize_nthreads!(DIGITSs) + Threads.resize_nthreads!(BIGNUMSs) +end + +function getbuf() + tls = task_local_storage() + d = get(tls, :DIGITS, nothing) + if d === nothing + d = Vector{UInt8}(undef, 309+17) + tls[:DIGITS] = d + end + return d::Vector{UInt8} +end + +""" + (len, point, neg) = Grisu.grisu(v::AbstractFloat, mode, requested_digits, [buffer], [bignums]) + +Convert the number `v` to decimal using the Grisu algorithm. + +`mode` can be one of: + - `Grisu.SHORTEST`: convert to the shortest decimal representation which can be "round-tripped" back to `v`. + - `Grisu.FIXED`: round to `requested_digits` digits. + - `Grisu.PRECISION`: round to `requested_digits` significant digits. + +The characters are written as bytes to `buffer`, with a terminating NUL byte, and `bignums` are used internally as part of the correction step. You can call `Grisu.getbuf()` to obtain a suitable task-local buffer. + +The returned tuple contains: + + - `len`: the number of digits written to `buffer` (excluding NUL) + - `point`: the location of the radix point relative to the start of the array (e.g. if + `point == 3`, then the radix point should be inserted between the 3rd and 4th + digit). Note that this can be negative (for very small values), or greater than `len` + (for very large values). + - `neg`: the signbit of `v` (see [`signbit`](@ref)). +""" +function grisu(v::AbstractFloat,mode,requested_digits,buffer=DIGITSs[Threads.threadid()],bignums=BIGNUMSs[Threads.threadid()]) + if signbit(v) + neg = true + v = -v + else + neg = false + end + if mode == PRECISION && requested_digits == 0 + buffer[1] = 0x00 + len = 0 + return 0, 0, neg + end + if v == 0.0 + buffer[1] = 0x30 + buffer[2] = 0x00 + len = point = 1 + return len, point, neg + end + if mode == SHORTEST + status,len,point = fastshortest(v,buffer) + elseif mode == FIXED + status,len,point = fastfixedtoa(v,0,requested_digits,buffer) + elseif mode == PRECISION + status,len,point = fastprecision(v,requested_digits,buffer) + end + status && return len-1, point, neg + status, len, point = bignumdtoa(v,mode,requested_digits,buffer,bignums) + return len-1, point, neg +end + +nanstr(x::AbstractFloat) = "NaN" +nanstr(x::Float32) = "NaN32" +nanstr(x::Float16) = "NaN16" +infstr(x::AbstractFloat) = "Inf" +infstr(x::Float32) = "Inf32" +infstr(x::Float16) = "Inf16" + +function _show(io::IO, x::AbstractFloat, mode, n::Int, typed, compact) + isnan(x) && return print(io, typed ? nanstr(x) : "NaN") + if isinf(x) + signbit(x) && print(io,'-') + print(io, typed ? infstr(x) : "Inf") + return + end + typed && isa(x,Float16) && print(io, "Float16(") + buffer = getbuf() + len, pt, neg = grisu(x,mode,n,buffer) + pdigits = pointer(buffer) + if mode == PRECISION + while len > 1 && buffer[len] == 0x30 + len -= 1 + end + end + neg && print(io,'-') + exp_form = pt <= -4 || pt > 6 + exp_form = exp_form || (pt >= len && abs(mod(x + 0.05, 10^(pt - len)) - 0.05) > 0.05) # see issue #6608 + if exp_form # .00001 to 100000. + # => #.#######e### + # assumes ASCII/UTF8 encoding of digits is okay for out: + unsafe_write(io, pdigits, 1) + print(io, '.') + if len > 1 + unsafe_write(io, pdigits+1, len-1) + else + print(io, '0') + end + print(io, (typed && isa(x,Float32)) ? 'f' : 'e') + print(io, string(pt - 1)) + typed && isa(x,Float16) && print(io, ")") + return + elseif pt <= 0 + # => 0.00######## + print(io, "0.") + while pt < 0 + print(io, '0') + pt += 1 + end + unsafe_write(io, pdigits, len) + elseif pt >= len + # => ########00.0 + unsafe_write(io, pdigits, len) + while pt > len + print(io, '0') + len += 1 + end + print(io, ".0") + else # => ####.#### + unsafe_write(io, pdigits, pt) + print(io, '.') + unsafe_write(io, pdigits+pt, len-pt) + end + typed && !compact && isa(x,Float32) && print(io, "f0") + typed && isa(x,Float16) && print(io, ")") + nothing +end + +function Base.show(io::IO, x::Union{Float64,Float32}) + if get(io, :compact, false) + _show(io, x, PRECISION, 6, x isa Float64, true) + else + _show(io, x, SHORTEST, 0, get(io, :typeinfo, Any) !== typeof(x), false) + end +end + +function Base.show(io::IO, x::Float16) + hastypeinfo = Float16 === get(io, :typeinfo, Any) + # if hastypeinfo, the printing would be more compact using `SHORTEST` + # while still retaining all the information + # BUT: we want to print all digits in `show`, not in display, so we rely + # on the :compact property to make the decision + # (cf. https://github.com/JuliaLang/julia/pull/24651#issuecomment-345535687) + if get(io, :compact, false) && !hastypeinfo + _show(io, x, PRECISION, 5, false, true) + else + _show(io, x, SHORTEST, 0, !hastypeinfo, false) + end +end + +Base.print(io::IO, x::Float32) = _show(io, x, SHORTEST, 0, false, false) +Base.print(io::IO, x::Float16) = _show(io, x, SHORTEST, 0, false, false) + +# normal: +# 0 < pt < len ####.#### len+1 +# pt <= 0 0.000######## len-pt+1 +# len <= pt (dot) ########000. pt+1 +# len <= pt (no dot) ########000 pt +# exponential: +# pt <= 0 ########e-### len+k+2 +# 0 < pt ########e### len+k+1 + +function _print_shortest(io::IO, x::AbstractFloat, dot::Bool, mode, n::Int) + isnan(x) && return print(io, "NaN") + x < 0 && print(io,'-') + isinf(x) && return print(io, "Inf") + buffer = getbuf() + len, pt, neg = grisu(x,mode,n,buffer) + pdigits = pointer(buffer) + e = pt-len + k = -9<=e<=9 ? 1 : 2 + if -pt > k+1 || e+dot > k+1 + # => ########e### + unsafe_write(io, pdigits+0, len) + print(io, 'e') + print(io, string(e)) + return + elseif pt <= 0 + # => 0.000######## + print(io, "0.") + while pt < 0 + print(io, '0') + pt += 1 + end + unsafe_write(io, pdigits+0, len) + elseif e >= dot + # => ########000. + unsafe_write(io, pdigits+0, len) + while e > 0 + print(io, '0') + e -= 1 + end + if dot + print(io, '.') + end + else # => ####.#### + unsafe_write(io, pdigits+0, pt) + print(io, '.') + unsafe_write(io, pdigits+pt, len-pt) + end + nothing +end + +""" + print_shortest(io::IO, x) + +Print the shortest possible representation, with the minimum number of consecutive non-zero +digits, of number `x`, ensuring that it would parse to the exact same number. +""" +print_shortest(io::IO, x::AbstractFloat, dot::Bool) = _print_shortest(io, x, dot, SHORTEST, 0) +print_shortest(io::IO, x::Union{AbstractFloat,Integer}) = print_shortest(io, float(x), false) + +end # module diff --git a/base/hashing.jl b/base/hashing.jl new file mode 100644 index 0000000..7b962a5 --- /dev/null +++ b/base/hashing.jl @@ -0,0 +1,77 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## hashing a single value ## + +""" + hash(x[, h::UInt]) + +Compute an integer hash code such that `isequal(x,y)` implies `hash(x)==hash(y)`. The +optional second argument `h` is a hash code to be mixed with the result. + +New types should implement the 2-argument form, typically by calling the 2-argument `hash` +method recursively in order to mix hashes of the contents with each other (and with `h`). +Typically, any type that implements `hash` should also implement its own `==` (hence +`isequal`) to guarantee the property mentioned above. Types supporting subtraction +(operator `-`) should also implement [`widen`](@ref), which is required to hash +values inside heterogeneous arrays. +""" +hash(x::Any) = hash(x, zero(UInt)) +hash(w::WeakRef, h::UInt) = hash(w.value, h) + +## hashing general objects ## + +hash(@nospecialize(x), h::UInt) = hash_uint(3h - objectid(x)) + +## core data hashing functions ## + +function hash_64_64(n::UInt64) + a::UInt64 = n + a = ~a + a << 21 + a = a ⊻ a >> 24 + a = a + a << 3 + a << 8 + a = a ⊻ a >> 14 + a = a + a << 2 + a << 4 + a = a ⊻ a >> 28 + a = a + a << 31 + return a +end + +function hash_64_32(n::UInt64) + a::UInt64 = n + a = ~a + a << 18 + a = a ⊻ a >> 31 + a = a * 21 + a = a ⊻ a >> 11 + a = a + a << 6 + a = a ⊻ a >> 22 + return a % UInt32 +end + +function hash_32_32(n::UInt32) + a::UInt32 = n + a = a + 0x7ed55d16 + a << 12 + a = a ⊻ 0xc761c23c ⊻ a >> 19 + a = a + 0x165667b1 + a << 5 + a = a + 0xd3a2646c ⊻ a << 9 + a = a + 0xfd7046c5 + a << 3 + a = a ⊻ 0xb55a4f09 ⊻ a >> 16 + return a +end + +if UInt === UInt64 + hash_uint64(x::UInt64) = hash_64_64(x) + hash_uint(x::UInt) = hash_64_64(x) +else + hash_uint64(x::UInt64) = hash_64_32(x) + hash_uint(x::UInt) = hash_32_32(x) +end + +## symbol & expression hashing ## + +if UInt === UInt64 + hash(x::Expr, h::UInt) = hash(x.args, hash(x.head, h + 0x83c7900696d26dc6)) + hash(x::QuoteNode, h::UInt) = hash(x.value, h + 0x2c97bf8b3de87020) +else + hash(x::Expr, h::UInt) = hash(x.args, hash(x.head, h + 0x96d26dc6)) + hash(x::QuoteNode, h::UInt) = hash(x.value, h + 0x469d72af) +end diff --git a/base/hashing2.jl b/base/hashing2.jl new file mode 100644 index 0000000..196d3cd --- /dev/null +++ b/base/hashing2.jl @@ -0,0 +1,181 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## efficient value-based hashing of integers ## + +function hash_integer(n::Integer, h::UInt) + h ⊻= hash_uint((n % UInt) ⊻ h) + n = abs(n) + n >>>= sizeof(UInt) << 3 + while n != 0 + h ⊻= hash_uint((n % UInt) ⊻ h) + n >>>= sizeof(UInt) << 3 + end + return h +end + +function hash_integer(n::BigInt, h::UInt) + s = n.size + s == 0 && return hash_integer(0, h) + p = convert(Ptr{UInt}, n.d) + b = unsafe_load(p) + h ⊻= hash_uint(ifelse(s < 0, -b, b) ⊻ h) + for k = 2:abs(s) + h ⊻= hash_uint(unsafe_load(p, k) ⊻ h) + end + return h +end + +## generic hashing for rational values ## + +function hash(x::Real, h::UInt) + # decompose x as num*2^pow/den + num, pow, den = decompose(x) + + # handle special values + num == 0 && den == 0 && return hash(NaN, h) + num == 0 && return hash(ifelse(den > 0, 0.0, -0.0), h) + den == 0 && return hash(ifelse(num > 0, Inf, -Inf), h) + + # normalize decomposition + if den < 0 + num = -num + den = -den + end + z = trailing_zeros(num) + if z != 0 + num >>= z + pow += z + end + z = trailing_zeros(den) + if z != 0 + den >>= z + pow -= z + end + + # handle values representable as Int64, UInt64, Float64 + if den == 1 + left = ndigits0z(num,2) + pow + right = trailing_zeros(num) + pow + if -1074 <= right + if 0 <= right && left <= 64 + left <= 63 && return hash(Int64(num) << Int(pow), h) + signbit(num) == signbit(den) && return hash(UInt64(num) << Int(pow), h) + end # typemin(Int64) handled by Float64 case + left <= 1024 && left - right <= 53 && return hash(ldexp(Float64(num),pow), h) + end + end + + # handle generic rational values + h = hash_integer(den, h) + h = hash_integer(pow, h) + h = hash_integer(num, h) + return h +end + +#= +`decompose(x)`: non-canonical decomposition of rational values as `num*2^pow/den`. + +The decompose function is the point where rational-valued numeric types that support +hashing hook into the hashing protocol. `decompose(x)` should return three integer +values `num, pow, den`, such that the value of `x` is mathematically equal to + + num*2^pow/den + +The decomposition need not be canonical in the sense that it just needs to be *some* +way to express `x` in this form, not any particular way – with the restriction that +`num` and `den` may not share any odd common factors. They may, however, have powers +of two in common – the generic hashing code will normalize those as necessary. + +Special values: + + - `x` is zero: `num` should be zero and `den` should have the same sign as `x` + - `x` is infinite: `den` should be zero and `num` should have the same sign as `x` + - `x` is not a number: `num` and `den` should both be zero +=# + +decompose(x::Integer) = x, 0, 1 +decompose(x::Rational) = numerator(x), 0, denominator(x) + +function decompose(x::Float16)::NTuple{3,Int} + isnan(x) && return 0, 0, 0 + isinf(x) && return ifelse(x < 0, -1, 1), 0, 0 + n = reinterpret(UInt16, x) + s = (n & 0x03ff) % Int16 + e = ((n & 0x7c00) >> 10) % Int + s |= Int16(e != 0) << 10 + d = ifelse(signbit(x), -1, 1) + s, e - 25 + (e == 0), d +end + +function decompose(x::Float32)::NTuple{3,Int} + isnan(x) && return 0, 0, 0 + isinf(x) && return ifelse(x < 0, -1, 1), 0, 0 + n = reinterpret(UInt32, x) + s = (n & 0x007fffff) % Int32 + e = ((n & 0x7f800000) >> 23) % Int + s |= Int32(e != 0) << 23 + d = ifelse(signbit(x), -1, 1) + s, e - 150 + (e == 0), d +end + +function decompose(x::Float64)::Tuple{Int64, Int, Int} + isnan(x) && return 0, 0, 0 + isinf(x) && return ifelse(x < 0, -1, 1), 0, 0 + n = reinterpret(UInt64, x) + s = (n & 0x000fffffffffffff) % Int64 + e = ((n & 0x7ff0000000000000) >> 52) % Int + s |= Int64(e != 0) << 52 + d = ifelse(signbit(x), -1, 1) + s, e - 1075 + (e == 0), d +end + +function decompose(x::BigFloat)::Tuple{BigInt, Int, Int} + isnan(x) && return 0, 0, 0 + isinf(x) && return x.sign, 0, 0 + x == 0 && return 0, 0, x.sign + s = BigInt() + s.size = cld(x.prec, 8*sizeof(GMP.Limb)) # limbs + b = s.size * sizeof(GMP.Limb) # bytes + ccall((:__gmpz_realloc2, :libgmp), Cvoid, (Ref{BigInt}, Culong), s, 8b) # bits + ccall(:memcpy, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), s.d, x.d, b) # bytes + s, x.exp - 8b, x.sign +end + +## streamlined hashing for smallish rational types ## + +function hash(x::Rational{<:BitInteger64}, h::UInt) + num, den = Base.numerator(x), Base.denominator(x) + den == 1 && return hash(num, h) + den == 0 && return hash(ifelse(num > 0, Inf, -Inf), h) + if isodd(den) + pow = trailing_zeros(num) + num >>= pow + else + pow = trailing_zeros(den) + den >>= pow + pow = -pow + if den == 1 && abs(num) < 9007199254740992 + return hash(ldexp(Float64(num),pow),h) + end + end + h = hash_integer(den, h) + h = hash_integer(pow, h) + h = hash_integer(num, h) + return h +end + +## hashing Float16s ## + +hash(x::Float16, h::UInt) = hash(Float64(x), h) + +## hashing strings ## + +const memhash = UInt === UInt64 ? :memhash_seed : :memhash32_seed +const memhash_seed = UInt === UInt64 ? 0x71e729fd56419c81 : 0x56419c81 + +function hash(s::Union{String,SubString{String}}, h::UInt) + h += memhash_seed + # note: use pointer(s) here (see #6058). + ccall(memhash, UInt, (Ptr{UInt8}, Csize_t, UInt32), pointer(s), sizeof(s), h % UInt32) + h +end +hash(s::AbstractString, h::UInt) = hash(String(s), h) diff --git a/base/indices.jl b/base/indices.jl new file mode 100644 index 0000000..1077ad7 --- /dev/null +++ b/base/indices.jl @@ -0,0 +1,465 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" + Dims{N} + +An `NTuple` of `N` `Int`s used to represent the dimensions +of an [`AbstractArray`](@ref). +""" +Dims{N} = NTuple{N,Int} +DimsInteger{N} = NTuple{N,Integer} +Indices{N} = NTuple{N,AbstractUnitRange} + +## Traits for array types ## + +abstract type IndexStyle end +""" + IndexLinear() + +Subtype of [`IndexStyle`](@ref) used to describe arrays which +are optimally indexed by one linear index. + +A linear indexing style uses one integer to describe the position in the array +(even if it's a multidimensional array) and column-major +ordering is used to access the elements. For example, +if `A` were a `(2, 3)` custom matrix type with linear indexing, +and we referenced `A[5]` (using linear style), this would +be equivalent to referencing `A[1, 3]` (since `2*1 + 3 = 5`). +See also [`IndexCartesian`](@ref). +""" +struct IndexLinear <: IndexStyle end +""" + IndexCartesian() + +Subtype of [`IndexStyle`](@ref) used to describe arrays which +are optimally indexed by a Cartesian index. + +A cartesian indexing style uses multiple integers/indices to describe the position in the array. +For example, if `A` were a `(2, 3, 4)` custom matrix type with cartesian indexing, +we could reference `A[2, 1, 3]` and Julia would automatically convert this into the +correct location in the underlying memory. See also [`IndexLinear`](@ref). +""" +struct IndexCartesian <: IndexStyle end + +""" + IndexStyle(A) + IndexStyle(typeof(A)) + +`IndexStyle` specifies the "native indexing style" for array `A`. When +you define a new [`AbstractArray`](@ref) type, you can choose to implement +either linear indexing (with [`IndexLinear`](@ref)) or cartesian indexing. +If you decide to implement linear indexing, then you must set this trait for your array +type: + + Base.IndexStyle(::Type{<:MyArray}) = IndexLinear() + +The default is [`IndexCartesian()`](@ref). + +Julia's internal indexing machinery will automatically (and invisibly) +convert all indexing operations into the preferred style. This allows users +to access elements of your array using any indexing style, even when explicit +methods have not been provided. + +If you define both styles of indexing for your `AbstractArray`, this +trait can be used to select the most performant indexing style. Some +methods check this trait on their inputs, and dispatch to different +algorithms depending on the most efficient access pattern. In +particular, [`eachindex`](@ref) creates an iterator whose type depends +on the setting of this trait. +""" +IndexStyle(A::AbstractArray) = IndexStyle(typeof(A)) +IndexStyle(::Type{Union{}}) = IndexLinear() +IndexStyle(::Type{<:AbstractArray}) = IndexCartesian() +IndexStyle(::Type{<:Array}) = IndexLinear() +IndexStyle(::Type{<:AbstractRange}) = IndexLinear() + +IndexStyle(A::AbstractArray, B::AbstractArray) = IndexStyle(IndexStyle(A), IndexStyle(B)) +IndexStyle(A::AbstractArray, B::AbstractArray...) = IndexStyle(IndexStyle(A), IndexStyle(B...)) +IndexStyle(::IndexLinear, ::IndexLinear) = IndexLinear() +IndexStyle(::IndexStyle, ::IndexStyle) = IndexCartesian() + +# array shape rules + +promote_shape(::Tuple{}, ::Tuple{}) = () + +function promote_shape(a::Tuple{Int,}, b::Tuple{Int,}) + if a[1] != b[1] + throw(DimensionMismatch("dimensions must match")) + end + return a +end + +function promote_shape(a::Tuple{Int,Int}, b::Tuple{Int,}) + if a[1] != b[1] || a[2] != 1 + throw(DimensionMismatch("dimensions must match")) + end + return a +end + +promote_shape(a::Tuple{Int,}, b::Tuple{Int,Int}) = promote_shape(b, a) + +function promote_shape(a::Tuple{Int, Int}, b::Tuple{Int, Int}) + if a[1] != b[1] || a[2] != b[2] + throw(DimensionMismatch("dimensions must match")) + end + return a +end + +""" + promote_shape(s1, s2) + +Check two array shapes for compatibility, allowing trailing singleton dimensions, and return +whichever shape has more dimensions. + +# Examples +```jldoctest +julia> a = fill(1, (3,4,1,1,1)); + +julia> b = fill(1, (3,4)); + +julia> promote_shape(a,b) +(Base.OneTo(3), Base.OneTo(4), Base.OneTo(1), Base.OneTo(1), Base.OneTo(1)) + +julia> promote_shape((2,3,1,4), (2, 3, 1, 4, 1)) +(2, 3, 1, 4, 1) +``` +""" +function promote_shape(a::Dims, b::Dims) + if length(a) < length(b) + return promote_shape(b, a) + end + for i=1:length(b) + if a[i] != b[i] + throw(DimensionMismatch("dimensions must match")) + end + end + for i=length(b)+1:length(a) + if a[i] != 1 + throw(DimensionMismatch("dimensions must match")) + end + end + return a +end + +function promote_shape(a::AbstractArray, b::AbstractArray) + promote_shape(axes(a), axes(b)) +end + +function promote_shape(a::Indices, b::Indices) + if length(a) < length(b) + return promote_shape(b, a) + end + for i=1:length(b) + if a[i] != b[i] + throw(DimensionMismatch("dimensions must match")) + end + end + for i=length(b)+1:length(a) + if a[i] != 1:1 + throw(DimensionMismatch("dimensions must match")) + end + end + return a +end + +function throw_setindex_mismatch(X, I) + if length(I) == 1 + throw(DimensionMismatch("tried to assign $(length(X)) elements to $(I[1]) destinations")) + else + throw(DimensionMismatch("tried to assign $(dims2string(size(X))) array to $(dims2string(I)) destination")) + end +end + +# check for valid sizes in A[I...] = X where X <: AbstractArray +# we want to allow dimensions that are equal up to permutation, but only +# for permutations that leave array elements in the same linear order. +# those are the permutations that preserve the order of the non-singleton +# dimensions. +function setindex_shape_check(X::AbstractArray, I::Integer...) + li = ndims(X) + lj = length(I) + i = j = 1 + while true + ii = length(axes(X,i)) + jj = I[j] + if i == li || j == lj + while i < li + i += 1 + ii *= length(axes(X,i)) + end + while j < lj + j += 1 + jj *= I[j] + end + if ii != jj + throw_setindex_mismatch(X, I) + end + return + end + if ii == jj + i += 1 + j += 1 + elseif ii == 1 + i += 1 + elseif jj == 1 + j += 1 + else + throw_setindex_mismatch(X, I) + end + end +end + +setindex_shape_check(X::AbstractArray) = + (length(X)==1 || throw_setindex_mismatch(X,())) + +setindex_shape_check(X::AbstractArray, i::Integer) = + (length(X)==i || throw_setindex_mismatch(X, (i,))) + +setindex_shape_check(X::AbstractArray{<:Any,1}, i::Integer) = + (length(X)==i || throw_setindex_mismatch(X, (i,))) + +setindex_shape_check(X::AbstractArray{<:Any,1}, i::Integer, j::Integer) = + (length(X)==i*j || throw_setindex_mismatch(X, (i,j))) + +function setindex_shape_check(X::AbstractArray{<:Any,2}, i::Integer, j::Integer) + if length(X) != i*j + throw_setindex_mismatch(X, (i,j)) + end + sx1 = length(axes(X,1)) + if !(i == 1 || i == sx1 || sx1 == 1) + throw_setindex_mismatch(X, (i,j)) + end +end + +setindex_shape_check(::Any...) = + throw(ArgumentError("indexed assignment with a single value to many locations is not supported; perhaps use broadcasting `.=` instead?")) + +# convert to a supported index type (array or Int) +""" + to_index(A, i) + +Convert index `i` to an `Int` or array of indices to be used as an index into array `A`. + +Custom array types may specialize `to_index(::CustomArray, i)` to provide +special indexing behaviors. Note that some index types (like `Colon`) require +more context in order to transform them into an array of indices; those get +converted in the more complicated `to_indices` function. By default, this +simply calls the generic `to_index(i)`. This must return either an `Int` or an +`AbstractArray` of scalar indices that are supported by `A`. +""" +to_index(A, i) = to_index(i) + +# This is ok for Array because values larger than +# typemax(Int) will BoundsError anyway +to_index(A::Array, i::UInt) = reinterpret(Int, i) + +""" + to_index(i) + +Convert index `i` to an `Int` or array of `Int`s to be used as an index for all arrays. + +Custom index types may specialize `to_index(::CustomIndex)` to provide special +indexing behaviors. This must return either an `Int` or an `AbstractArray` of +`Int`s. +""" +to_index(i::Integer) = convert(Int,i)::Int +to_index(i::Bool) = throw(ArgumentError("invalid index: $i of type Bool")) +to_index(I::AbstractArray{Bool}) = LogicalIndex(I) +to_index(I::AbstractArray) = I +to_index(I::AbstractArray{Union{}}) = I +to_index(I::AbstractArray{<:Union{AbstractArray, Colon}}) = + throw(ArgumentError("invalid index: $(limitrepr(I)) of type $(typeof(I))")) +to_index(::Colon) = throw(ArgumentError("colons must be converted by to_indices(...)")) +to_index(i) = throw(ArgumentError("invalid index: $(limitrepr(i)) of type $(typeof(i))")) + +# The general to_indices is mostly defined in multidimensional.jl, but this +# definition is required for bootstrap: +""" + to_indices(A, I::Tuple) + +Convert the tuple `I` to a tuple of indices for use in indexing into array `A`. + +The returned tuple must only contain either `Int`s or `AbstractArray`s of +scalar indices that are supported by array `A`. It will error upon encountering +a novel index type that it does not know how to process. + +For simple index types, it defers to the unexported `Base.to_index(A, i)` to +process each index `i`. While this internal function is not intended to be +called directly, `Base.to_index` may be extended by custom array or index types +to provide custom indexing behaviors. + +More complicated index types may require more context about the dimension into +which they index. To support those cases, `to_indices(A, I)` calls +`to_indices(A, axes(A), I)`, which then recursively walks through both the +given tuple of indices and the dimensional indices of `A` in tandem. As such, +not all index types are guaranteed to propagate to `Base.to_index`. +""" +to_indices(A, I::Tuple) = (@_inline_meta; to_indices(A, axes(A), I)) +to_indices(A, I::Tuple{Any}) = (@_inline_meta; to_indices(A, (eachindex(IndexLinear(), A),), I)) +to_indices(A, inds, ::Tuple{}) = () +to_indices(A, inds, I::Tuple{Any, Vararg{Any}}) = + (@_inline_meta; (to_index(A, I[1]), to_indices(A, _maybetail(inds), tail(I))...)) + +_maybetail(::Tuple{}) = () +_maybetail(t::Tuple) = tail(t) + +""" + Slice(indices) + +Represent an AbstractUnitRange of indices as a vector of the indices themselves, +with special handling to signal they represent a complete slice of a dimension (:). + +Upon calling `to_indices`, Colons are converted to Slice objects to represent +the indices over which the Colon spans. Slice objects are themselves unit +ranges with the same indices as those they wrap. This means that indexing into +Slice objects with an integer always returns that exact integer, and they +iterate over all the wrapped indices, even supporting offset indices. +""" +struct Slice{T<:AbstractUnitRange} <: AbstractUnitRange{Int} + indices::T +end +Slice(S::Slice) = S +axes(S::Slice) = (IdentityUnitRange(S.indices),) +unsafe_indices(S::Slice) = (IdentityUnitRange(S.indices),) +axes1(S::Slice) = IdentityUnitRange(S.indices) +axes(S::Slice{<:OneTo}) = (S.indices,) +unsafe_indices(S::Slice{<:OneTo}) = (S.indices,) +axes1(S::Slice{<:OneTo}) = S.indices + +first(S::Slice) = first(S.indices) +last(S::Slice) = last(S.indices) +size(S::Slice) = (length(S.indices),) +length(S::Slice) = length(S.indices) +unsafe_length(S::Slice) = unsafe_length(S.indices) +getindex(S::Slice, i::Int) = (@_inline_meta; @boundscheck checkbounds(S, i); i) +getindex(S::Slice, i::AbstractUnitRange{<:Integer}) = (@_inline_meta; @boundscheck checkbounds(S, i); i) +getindex(S::Slice, i::StepRange{<:Integer}) = (@_inline_meta; @boundscheck checkbounds(S, i); i) +show(io::IO, r::Slice) = print(io, "Base.Slice(", r.indices, ")") +iterate(S::Slice, s...) = iterate(S.indices, s...) + + +""" + IdentityUnitRange(range::AbstractUnitRange) + +Represent an AbstractUnitRange `range` as an offset vector such that `range[i] == i`. + +`IdentityUnitRange`s are frequently used as axes for offset arrays. +""" +struct IdentityUnitRange{T<:AbstractUnitRange} <: AbstractUnitRange{Int} + indices::T +end +IdentityUnitRange(S::IdentityUnitRange) = S +# IdentityUnitRanges are offset and thus have offset axes, so they are their own axes... but +# we need to strip the wholedim marker because we don't know how they'll be used +axes(S::IdentityUnitRange) = (S,) +unsafe_indices(S::IdentityUnitRange) = (S,) +axes1(S::IdentityUnitRange) = S +axes(S::IdentityUnitRange{<:OneTo}) = (S.indices,) +unsafe_indices(S::IdentityUnitRange{<:OneTo}) = (S.indices,) +axes1(S::IdentityUnitRange{<:OneTo}) = S.indices + +first(S::IdentityUnitRange) = first(S.indices) +last(S::IdentityUnitRange) = last(S.indices) +size(S::IdentityUnitRange) = (length(S.indices),) +length(S::IdentityUnitRange) = length(S.indices) +unsafe_length(S::IdentityUnitRange) = unsafe_length(S.indices) +getindex(S::IdentityUnitRange, i::Int) = (@_inline_meta; @boundscheck checkbounds(S, i); i) +getindex(S::IdentityUnitRange, i::AbstractUnitRange{<:Integer}) = (@_inline_meta; @boundscheck checkbounds(S, i); i) +getindex(S::IdentityUnitRange, i::StepRange{<:Integer}) = (@_inline_meta; @boundscheck checkbounds(S, i); i) +show(io::IO, r::IdentityUnitRange) = print(io, "Base.IdentityUnitRange(", r.indices, ")") +iterate(S::IdentityUnitRange, s...) = iterate(S.indices, s...) + +""" + LinearIndices(A::AbstractArray) + +Return a `LinearIndices` array with the same shape and [`axes`](@ref) as `A`, +holding the linear index of each entry in `A`. Indexing this array with +cartesian indices allows mapping them to linear indices. + +For arrays with conventional indexing (indices start at 1), or any multidimensional +array, linear indices range from 1 to `length(A)`. However, for `AbstractVector`s +linear indices are `axes(A, 1)`, and therefore do not start at 1 for vectors with +unconventional indexing. + +Calling this function is the "safe" way to write algorithms that +exploit linear indexing. + +# Examples +```jldoctest +julia> A = fill(1, (5,6,7)); + +julia> b = LinearIndices(A); + +julia> extrema(b) +(1, 210) +``` + + LinearIndices(inds::CartesianIndices) -> R + LinearIndices(sz::Dims) -> R + LinearIndices((istart:istop, jstart:jstop, ...)) -> R + +Return a `LinearIndices` array with the specified shape or [`axes`](@ref). + +# Example + +The main purpose of this constructor is intuitive conversion +from cartesian to linear indexing: + +```jldoctest +julia> linear = LinearIndices((1:3, 1:2)) +3×2 LinearIndices{2,Tuple{UnitRange{Int64},UnitRange{Int64}}}: + 1 4 + 2 5 + 3 6 + +julia> linear[1,2] +4 +``` +""" +struct LinearIndices{N,R<:NTuple{N,AbstractUnitRange{Int}}} <: AbstractArray{Int,N} + indices::R +end + +LinearIndices(::Tuple{}) = LinearIndices{0,typeof(())}(()) +LinearIndices(inds::NTuple{N,AbstractUnitRange{<:Integer}}) where {N} = + LinearIndices(map(r->convert(AbstractUnitRange{Int}, r), inds)) +LinearIndices(sz::NTuple{N,<:Integer}) where {N} = LinearIndices(map(Base.OneTo, sz)) +LinearIndices(inds::NTuple{N,Union{<:Integer,AbstractUnitRange{<:Integer}}}) where {N} = + LinearIndices(map(i->first(i):last(i), inds)) +LinearIndices(A::Union{AbstractArray,SimpleVector}) = LinearIndices(axes(A)) + +promote_rule(::Type{LinearIndices{N,R1}}, ::Type{LinearIndices{N,R2}}) where {N,R1,R2} = + LinearIndices{N,indices_promote_type(R1,R2)} + +function indices_promote_type(::Type{Tuple{R1,Vararg{R1,N}}}, ::Type{Tuple{R2,Vararg{R2,N}}}) where {R1,R2,N} + R = promote_type(R1, R2) + Tuple{R,Vararg{R,N}} +end + +convert(::Type{LinearIndices{N,R}}, inds::LinearIndices{N}) where {N,R} = + LinearIndices(convert(R, inds.indices)) + +# AbstractArray implementation +IndexStyle(::Type{<:LinearIndices}) = IndexLinear() +axes(iter::LinearIndices) = map(axes1, iter.indices) +size(iter::LinearIndices) = map(unsafe_length, iter.indices) +function getindex(iter::LinearIndices, i::Int) + @_inline_meta + @boundscheck checkbounds(iter, i) + i +end +function getindex(iter::LinearIndices, i::AbstractRange{<:Integer}) + @_inline_meta + @boundscheck checkbounds(iter, i) + @inbounds isa(iter, LinearIndices{1}) ? iter.indices[1][i] : (first(iter):last(iter))[i] +end +# More efficient iteration — predominantly for non-vector LinearIndices +# but one-dimensional LinearIndices must be special-cased to support OffsetArrays +iterate(iter::LinearIndices{1}, s...) = iterate(axes1(iter.indices[1]), s...) +iterate(iter::LinearIndices, i=1) = i > length(iter) ? nothing : (i, i+1) + +# Needed since firstindex and lastindex are defined in terms of LinearIndices +first(iter::LinearIndices) = 1 +first(iter::LinearIndices{1}) = (@_inline_meta; first(axes1(iter.indices[1]))) +last(iter::LinearIndices) = (@_inline_meta; length(iter)) +last(iter::LinearIndices{1}) = (@_inline_meta; last(axes1(iter.indices[1]))) diff --git a/base/initdefs.jl b/base/initdefs.jl new file mode 100644 index 0000000..a810ceb --- /dev/null +++ b/base/initdefs.jl @@ -0,0 +1,342 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## initdefs.jl - initialization and runtime management definitions + +""" + PROGRAM_FILE + +A string containing the script name passed to Julia from the command line. Note that the +script name remains unchanged from within included files. Alternatively see +[`@__FILE__`](@ref). +""" +global PROGRAM_FILE = "" + +""" + ARGS + +An array of the command line arguments passed to Julia, as strings. +""" +const ARGS = String[] + +""" + exit(code=0) + +Stop the program with an exit code. The default exit code is zero, indicating that the +program completed successfully. In an interactive session, `exit()` can be called with +the keyboard shortcut `^D`. +""" +exit(n) = ccall(:jl_exit, Cvoid, (Int32,), n) +exit() = exit(0) + +const roottask = current_task() + +is_interactive = false + +""" + isinteractive() -> Bool + +Determine whether Julia is running an interactive session. +""" +isinteractive() = (is_interactive::Bool) + +## package depots (registries, packages, environments) ## + +""" + DEPOT_PATH + +A stack of "depot" locations where the package manager, as well as Julia's code +loading mechanisms, look for package registries, installed packages, named +environments, repo clones, cached compiled package images, and configuration +files. By default it includes: + +1. `~/.julia` where `~` is the user home as appropriate on the system; +2. an architecture-specific shared system directory, e.g. `/usr/local/share/julia`; +3. an architecture-independent shared system directory, e.g. `/usr/share/julia`. + +So `DEPOT_PATH` might be: +```julia +[joinpath(homedir(), ".julia"), "/usr/local/share/julia", "/usr/share/julia"] +``` +The first entry is the "user depot" and should be writable by and owned by the +current user. The user depot is where: registries are cloned, new package versions +are installed, named environments are created and updated, package repos are cloned, +newly compiled package image files are saved, log files are written, development +packages are checked out by default, and global configuration data is saved. Later +entries in the depot path are treated as read-only and are appropriate for +registries, packages, etc. installed and managed by system administrators. + +`DEPOT_PATH` is populated based on the [`JULIA_DEPOT_PATH`](@ref JULIA_DEPOT_PATH) +environment variable if set. + +See also: +[`JULIA_DEPOT_PATH`](@ref JULIA_DEPOT_PATH), and +[Code Loading](@ref Code-Loading). +""" +const DEPOT_PATH = String[] + +function append_default_depot_path!(DEPOT_PATH) + path = joinpath(homedir(), ".julia") + path in DEPOT_PATH || push!(DEPOT_PATH, path) + path = abspath(Sys.BINDIR, "..", "local", "share", "julia") + path in DEPOT_PATH || push!(DEPOT_PATH, path) + path = abspath(Sys.BINDIR, "..", "share", "julia") + path in DEPOT_PATH || push!(DEPOT_PATH, path) +end + +function init_depot_path() + empty!(DEPOT_PATH) + if haskey(ENV, "JULIA_DEPOT_PATH") + str = ENV["JULIA_DEPOT_PATH"] + isempty(str) && return + for path in split(str, Sys.iswindows() ? ';' : ':') + if isempty(path) + append_default_depot_path!(DEPOT_PATH) + else + path = expanduser(path) + path in DEPOT_PATH || push!(DEPOT_PATH, path) + end + end + else + append_default_depot_path!(DEPOT_PATH) + end +end + +## LOAD_PATH, HOME_PROJECT & ACTIVE_PROJECT ## + +# JULIA_LOAD_PATH: split on `:` (or `;` on Windows) +# first empty entry is replaced with DEFAULT_LOAD_PATH, the rest are skipped +# entries starting with `@` are named environments: +# - the first three `#`s in a named environment are replaced with version numbers +# - `@stdlib` is a special name for the standard library and expands to its path + +# if you want a current env setup, use direnv and +# have your .envrc do something like this: +# +# export JULIA_LOAD_PATH="$(pwd):$JULIA_LOAD_PATH" +# +# this will inherit an existing JULIA_LOAD_PATH value or if there is none, leave +# a trailing empty entry in JULIA_LOAD_PATH which will be replaced with defaults. + +const DEFAULT_LOAD_PATH = ["@", "@v#.#", "@stdlib"] + +""" + LOAD_PATH + +An array of paths for `using` and `import` statements to consider as project +environments or package directories when loading code. It is populated based on +the [`JULIA_LOAD_PATH`](@ref JULIA_LOAD_PATH) environment variable if set; +otherwise it defaults to `["@", "@v#.#", "@stdlib"]`. Entries starting with `@` +have special meanings: + +- `@` refers to the "current active environment", the initial value of which is + initially determined by the [`JULIA_PROJECT`](@ref JULIA_PROJECT) environment + variable or the `--project` command-line option. + +- `@stdlib` expands to the absolute path of the current Julia installation's + standard library directory. + +- `@name` refers to a named environment, which are stored in depots (see + [`JULIA_DEPOT_PATH`](@ref JULIA_DEPOT_PATH)) under the `environments` + subdirectory. The user's named environments are stored in + `~/.julia/environments` so `@name` would refer to the environment in + `~/.julia/environments/name` if it exists and contains a `Project.toml` file. + If `name` contains `#` characters, then they are replaced with the major, minor + and patch components of the Julia version number. For example, if you are + running Julia 1.2 then `@v#.#` expands to `@v1.2` and will look for an + environment by that name, typically at `~/.julia/environments/v1.2`. + +The fully expanded value of `LOAD_PATH` that is searched for projects and packages +can be seen by calling the `Base.load_path()` function. + +See also: +[`JULIA_LOAD_PATH`](@ref JULIA_LOAD_PATH), +[`JULIA_PROJECT`](@ref JULIA_PROJECT), +[`JULIA_DEPOT_PATH`](@ref JULIA_DEPOT_PATH), and +[Code Loading](@ref Code-Loading). +""" +const LOAD_PATH = copy(DEFAULT_LOAD_PATH) +const HOME_PROJECT = Ref{Union{String,Nothing}}(nothing) +const ACTIVE_PROJECT = Ref{Union{String,Nothing}}(nothing) + +function current_project(dir::AbstractString) + # look for project file in current dir and parents + home = homedir() + while true + for proj in project_names + file = joinpath(dir, proj) + isfile_casesensitive(file) && return file + end + # bail at home directory + dir == home && break + old, dir = dir, dirname(dir) + dir == old && break + end +end + +function current_project() + dir = try pwd() + catch err + err isa IOError || rethrow() + return nothing + end + return current_project(dir) +end + +function parse_load_path(str::String) + envs = String[] + isempty(str) && return envs + for env in split(str, Sys.iswindows() ? ';' : ':') + if isempty(env) + for env′ in DEFAULT_LOAD_PATH + env′ in envs || push!(envs, env′) + end + else + if env == "@." + env = current_project() + env === nothing && continue + end + env = expanduser(env) + env in envs || push!(envs, env) + end + end + return envs +end + +function init_load_path() + if Base.creating_sysimg + paths = ["@stdlib"] + elseif haskey(ENV, "JULIA_LOAD_PATH") + paths = parse_load_path(ENV["JULIA_LOAD_PATH"]) + else + paths = filter!(env -> env !== nothing, + [env == "@." ? current_project() : env for env in DEFAULT_LOAD_PATH]) + end + project = (JLOptions().project != C_NULL ? + unsafe_string(Base.JLOptions().project) : + get(ENV, "JULIA_PROJECT", nothing)) + HOME_PROJECT[] = + project === nothing ? nothing : + project == "" ? nothing : + project == "@." ? current_project() : abspath(expanduser(project)) + append!(empty!(LOAD_PATH), paths) +end + +## load path expansion: turn LOAD_PATH entries into concrete paths ## + +function load_path_expand(env::AbstractString)::Union{String, Nothing} + # named environment? + if startswith(env, '@') + # `@` in JULIA_LOAD_PATH is expanded early (at startup time) + # if you put a `@` in LOAD_PATH manually, it's expanded late + env == "@" && return active_project(false) + env == "@." && return current_project() + env == "@stdlib" && return Sys.STDLIB + env = replace(env, '#' => VERSION.major, count=1) + env = replace(env, '#' => VERSION.minor, count=1) + env = replace(env, '#' => VERSION.patch, count=1) + name = env[2:end] + # look for named env in each depot + for depot in DEPOT_PATH + path = joinpath(depot, "environments", name) + isdir(path) || continue + for proj in project_names + file = abspath(path, proj) + isfile_casesensitive(file) && return file + end + end + isempty(DEPOT_PATH) && return nothing + return abspath(DEPOT_PATH[1], "environments", name, project_names[end]) + end + # otherwise, it's a path + path = abspath(env) + if isdir(path) + # directory with a project file? + for proj in project_names + file = joinpath(path, proj) + isfile_casesensitive(file) && return file + end + end + # package dir or path to project file + return path +end +load_path_expand(::Nothing) = nothing + +function active_project(search_load_path::Bool=true) + for project in (ACTIVE_PROJECT[], HOME_PROJECT[]) + project == "@" && continue + project = load_path_expand(project) + project === nothing && continue + if !isfile_casesensitive(project) && basename(project) ∉ project_names + project = abspath(project, "Project.toml") + end + return project + end + search_load_path || return + for project in LOAD_PATH + project == "@" && continue + project = load_path_expand(project) + project === nothing && continue + isfile_casesensitive(project) && return project + ispath(project) && continue + basename(project) in project_names && return project + end +end + +function load_path() + paths = String[] + for env in LOAD_PATH + path = load_path_expand(env) + path !== nothing && path ∉ paths && push!(paths, path) + end + return paths +end + +## atexit: register exit hooks ## + +const atexit_hooks = Callable[Filesystem.temp_cleanup_purge] + +""" + atexit(f) + +Register a zero-argument function `f()` to be called at process exit. `atexit()` hooks are +called in last in first out (LIFO) order and run before object finalizers. +""" +atexit(f::Function) = (pushfirst!(atexit_hooks, f); nothing) + +function _atexit() + for f in atexit_hooks + try + f() + catch err + show(stderr, err) + println(stderr) + end + end +end + +## hook for disabling threaded libraries ## + +library_threading_enabled = true +const disable_library_threading_hooks = [] + +function at_disable_library_threading(f) + push!(disable_library_threading_hooks, f) + if !library_threading_enabled + disable_library_threading() + end + return +end + +function disable_library_threading() + global library_threading_enabled = false + while !isempty(disable_library_threading_hooks) + f = pop!(disable_library_threading_hooks) + try + f() + catch err + @warn("a hook from a library to disable threading failed:", + exception = (err, catch_backtrace())) + end + end + return +end diff --git a/base/int.jl b/base/int.jl new file mode 100644 index 0000000..9390116 --- /dev/null +++ b/base/int.jl @@ -0,0 +1,876 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## integer arithmetic ## + +# The tuples and types that do not include 128 bit sizes are necessary to handle +# certain issues on 32-bit machines, and also to simplify promotion rules, as +# they are also used elsewhere where Int128/UInt128 support is separated out, +# such as in hashing2.jl + +const BitSigned32_types = (Int8, Int16, Int32) +const BitUnsigned32_types = (UInt8, UInt16, UInt32) +const BitInteger32_types = (BitSigned32_types..., BitUnsigned32_types...) + +const BitSigned64_types = (BitSigned32_types..., Int64) +const BitUnsigned64_types = (BitUnsigned32_types..., UInt64) +const BitInteger64_types = (BitSigned64_types..., BitUnsigned64_types...) + +const BitSigned_types = (BitSigned64_types..., Int128) +const BitUnsigned_types = (BitUnsigned64_types..., UInt128) +const BitInteger_types = (BitSigned_types..., BitUnsigned_types...) + +const BitSignedSmall_types = Int === Int64 ? ( Int8, Int16, Int32) : ( Int8, Int16) +const BitUnsignedSmall_types = Int === Int64 ? (UInt8, UInt16, UInt32) : (UInt8, UInt16) +const BitIntegerSmall_types = (BitSignedSmall_types..., BitUnsignedSmall_types...) + +const BitSigned32 = Union{BitSigned32_types...} +const BitUnsigned32 = Union{BitUnsigned32_types...} +const BitInteger32 = Union{BitInteger32_types...} + +const BitSigned64 = Union{BitSigned64_types...} +const BitUnsigned64 = Union{BitUnsigned64_types...} +const BitInteger64 = Union{BitInteger64_types...} + +const BitSigned = Union{BitSigned_types...} +const BitUnsigned = Union{BitUnsigned_types...} +const BitInteger = Union{BitInteger_types...} + +const BitSignedSmall = Union{BitSignedSmall_types...} +const BitUnsignedSmall = Union{BitUnsignedSmall_types...} +const BitIntegerSmall = Union{BitIntegerSmall_types...} + +const BitSigned64T = Union{Type{Int8}, Type{Int16}, Type{Int32}, Type{Int64}} +const BitUnsigned64T = Union{Type{UInt8}, Type{UInt16}, Type{UInt32}, Type{UInt64}} + +const BitIntegerType = Union{map(T->Type{T}, BitInteger_types)...} + +## integer comparisons ## + +(<)(x::T, y::T) where {T<:BitSigned} = slt_int(x, y) + +(-)(x::BitInteger) = neg_int(x) +(-)(x::T, y::T) where {T<:BitInteger} = sub_int(x, y) +(+)(x::T, y::T) where {T<:BitInteger} = add_int(x, y) +(*)(x::T, y::T) where {T<:BitInteger} = mul_int(x, y) + +inv(x::Integer) = float(one(x)) / float(x) +(/)(x::T, y::T) where {T<:Integer} = float(x) / float(y) +# skip promotion for system integer types +(/)(x::BitInteger, y::BitInteger) = float(x) / float(y) + +""" + isodd(x::Integer) -> Bool + +Return `true` if `x` is odd (that is, not divisible by 2), and `false` otherwise. + +# Examples +```jldoctest +julia> isodd(9) +true + +julia> isodd(10) +false +``` +""" +isodd(n::Integer) = rem(n, 2) != 0 + +""" + iseven(x::Integer) -> Bool + +Return `true` is `x` is even (that is, divisible by 2), and `false` otherwise. + +# Examples +```jldoctest +julia> iseven(9) +false + +julia> iseven(10) +true +``` +""" +iseven(n::Integer) = !isodd(n) + +signbit(x::Integer) = x < 0 +signbit(x::Unsigned) = false + +flipsign(x::T, y::T) where {T<:BitSigned} = flipsign_int(x, y) +flipsign(x::BitSigned, y::BitSigned) = flipsign_int(promote(x, y)...) % typeof(x) + +flipsign(x::Signed, y::Float16) = flipsign(x, bitcast(Int16, y)) +flipsign(x::Signed, y::Float32) = flipsign(x, bitcast(Int32, y)) +flipsign(x::Signed, y::Float64) = flipsign(x, bitcast(Int64, y)) +flipsign(x::Signed, y::Real) = flipsign(x, -oftype(x, signbit(y))) + +copysign(x::Signed, y::Signed) = flipsign(x, x ⊻ y) +copysign(x::Signed, y::Float16) = copysign(x, bitcast(Int16, y)) +copysign(x::Signed, y::Float32) = copysign(x, bitcast(Int32, y)) +copysign(x::Signed, y::Float64) = copysign(x, bitcast(Int64, y)) +copysign(x::Signed, y::Real) = copysign(x, -oftype(x, signbit(y))) + +""" + abs(x) + +The absolute value of `x`. + +When `abs` is applied to signed integers, overflow may occur, +resulting in the return of a negative value. This overflow occurs only +when `abs` is applied to the minimum representable value of a signed +integer. That is, when `x == typemin(typeof(x))`, `abs(x) == x < 0`, +not `-x` as might be expected. + +# Examples +```jldoctest +julia> abs(-3) +3 + +julia> abs(1 + im) +1.4142135623730951 + +julia> abs(typemin(Int64)) +-9223372036854775808 +``` +""" +function abs end + +abs(x::Unsigned) = x +abs(x::Signed) = flipsign(x,x) + +~(n::Integer) = -n-1 + +unsigned(x::BitSigned) = reinterpret(typeof(convert(Unsigned, zero(x))), x) +unsigned(x::Bool) = convert(Unsigned, x) + +""" + unsigned(x) -> Unsigned + +Convert a number to an unsigned integer. If the argument is signed, it is reinterpreted as +unsigned without checking for negative values. + +# Examples +```jldoctest +julia> unsigned(-2) +0xfffffffffffffffe + +julia> unsigned(2) +0x0000000000000002 + +julia> signed(unsigned(-2)) +-2 +``` +""" +unsigned(x) = convert(Unsigned, x) +signed(x::Unsigned) = reinterpret(typeof(convert(Signed, zero(x))), x) + +""" + signed(x) + +Convert a number to a signed integer. If the argument is unsigned, it is reinterpreted as +signed without checking for overflow. +""" +signed(x) = convert(Signed, x) + +div(x::BitSigned, y::Unsigned) = flipsign(signed(div(unsigned(abs(x)), y)), x) +div(x::Unsigned, y::BitSigned) = unsigned(flipsign(signed(div(x, unsigned(abs(y)))), y)) + +rem(x::BitSigned, y::Unsigned) = flipsign(signed(rem(unsigned(abs(x)), y)), x) +rem(x::Unsigned, y::BitSigned) = rem(x, unsigned(abs(y))) + +fld(x::Signed, y::Unsigned) = div(x, y) - (signbit(x) & (rem(x, y) != 0)) +fld(x::Unsigned, y::Signed) = div(x, y) - (signbit(y) & (rem(x, y) != 0)) + + +""" + mod(x, y) + rem(x, y, RoundDown) + +The reduction of `x` modulo `y`, or equivalently, the remainder of `x` after floored +division by `y`, i.e. `x - y*fld(x,y)` if computed without intermediate rounding. + +The result will have the same sign as `y`, and magnitude less than `abs(y)` (with some +exceptions, see note below). + +!!! note + + When used with floating point values, the exact result may not be representable by the + type, and so rounding error may occur. In particular, if the exact result is very + close to `y`, then it may be rounded to `y`. + +```jldoctest +julia> mod(8, 3) +2 + +julia> mod(9, 3) +0 + +julia> mod(8.9, 3) +2.9000000000000004 + +julia> mod(eps(), 3) +2.220446049250313e-16 + +julia> mod(-eps(), 3) +3.0 +``` +""" +function mod(x::T, y::T) where T<:Integer + y == -1 && return T(0) # avoid potential overflow in fld + return x - fld(x, y) * y +end +mod(x::BitSigned, y::Unsigned) = rem(y + unsigned(rem(x, y)), y) +mod(x::Unsigned, y::Signed) = rem(y + signed(rem(x, y)), y) +mod(x::T, y::T) where {T<:Unsigned} = rem(x, y) + +cld(x::Signed, y::Unsigned) = div(x, y) + (!signbit(x) & (rem(x, y) != 0)) +cld(x::Unsigned, y::Signed) = div(x, y) + (!signbit(y) & (rem(x, y) != 0)) + +# Don't promote integers for div/rem/mod since there is no danger of overflow, +# while there is a substantial performance penalty to 64-bit promotion. +div(x::T, y::T) where {T<:BitSigned64} = checked_sdiv_int(x, y) +rem(x::T, y::T) where {T<:BitSigned64} = checked_srem_int(x, y) +div(x::T, y::T) where {T<:BitUnsigned64} = checked_udiv_int(x, y) +rem(x::T, y::T) where {T<:BitUnsigned64} = checked_urem_int(x, y) + + +# fld(x,y) == div(x,y) - ((x>=0) != (y>=0) && rem(x,y) != 0 ? 1 : 0) +fld(x::T, y::T) where {T<:Unsigned} = div(x,y) +function fld(x::T, y::T) where T<:Integer + d = div(x, y) + return d - (signbit(x ⊻ y) & (d * y != x)) +end + +# cld(x,y) = div(x,y) + ((x>0) == (y>0) && rem(x,y) != 0 ? 1 : 0) +function cld(x::T, y::T) where T<:Unsigned + d = div(x, y) + return d + (d * y != x) +end +function cld(x::T, y::T) where T<:Integer + d = div(x, y) + return d + (((x > 0) == (y > 0)) & (d * y != x)) +end + +## integer bitwise operations ## + +""" + ~(x) + +Bitwise not. + +# Examples +```jldoctest +julia> ~4 +-5 + +julia> ~10 +-11 + +julia> ~true +false +``` +""" +(~)(x::BitInteger) = not_int(x) + +""" + &(x, y) + +Bitwise and. Implements [three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic), +returning [`missing`](@ref) if one operand is `missing` and the other is `true`. + +# Examples +```jldoctest +julia> 4 & 10 +0 + +julia> 4 & 12 +4 + +julia> true & missing +missing + +julia> false & missing +false +``` +""" +(&)(x::T, y::T) where {T<:BitInteger} = and_int(x, y) + +""" + |(x, y) + +Bitwise or. Implements [three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic), +returning [`missing`](@ref) if one operand is `missing` and the other is `false`. + +# Examples +```jldoctest +julia> 4 | 10 +14 + +julia> 4 | 1 +5 + +julia> true | missing +true + +julia> false | missing +missing +``` +""" +(|)(x::T, y::T) where {T<:BitInteger} = or_int(x, y) +xor(x::T, y::T) where {T<:BitInteger} = xor_int(x, y) + +""" + bswap(n) + +Reverse the byte order of `n`. + +# Examples +```jldoctest +julia> a = bswap(0x10203040) +0x40302010 + +julia> bswap(a) +0x10203040 + +julia> string(1, base = 2) +"1" + +julia> string(bswap(1), base = 2) +"100000000000000000000000000000000000000000000000000000000" +``` +""" +bswap(x::Union{Int8, UInt8}) = x +bswap(x::Union{Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128}) = + bswap_int(x) + +""" + count_ones(x::Integer) -> Integer + +Number of ones in the binary representation of `x`. + +# Examples +```jldoctest +julia> count_ones(7) +3 +``` +""" +count_ones(x::BitInteger) = Int(ctpop_int(x)) + +""" + leading_zeros(x::Integer) -> Integer + +Number of zeros leading the binary representation of `x`. + +# Examples +```jldoctest +julia> leading_zeros(Int32(1)) +31 +``` +""" +leading_zeros(x::BitInteger) = Int(ctlz_int(x)) + +""" + trailing_zeros(x::Integer) -> Integer + +Number of zeros trailing the binary representation of `x`. + +# Examples +```jldoctest +julia> trailing_zeros(2) +1 +``` +""" +trailing_zeros(x::BitInteger) = Int(cttz_int(x)) + +""" + count_zeros(x::Integer) -> Integer + +Number of zeros in the binary representation of `x`. + +# Examples +```jldoctest +julia> count_zeros(Int32(2 ^ 16 - 1)) +16 +``` +""" +count_zeros(x::Integer) = count_ones(~x) + +""" + leading_ones(x::Integer) -> Integer + +Number of ones leading the binary representation of `x`. + +# Examples +```jldoctest +julia> leading_ones(UInt32(2 ^ 32 - 2)) +31 +``` +""" +leading_ones(x::Integer) = leading_zeros(~x) + +""" + trailing_ones(x::Integer) -> Integer + +Number of ones trailing the binary representation of `x`. + +# Examples +```jldoctest +julia> trailing_ones(3) +2 +``` +""" +trailing_ones(x::Integer) = trailing_zeros(~x) + +## integer comparisons ## + +(< )(x::T, y::T) where {T<:BitUnsigned} = ult_int(x, y) +(<=)(x::T, y::T) where {T<:BitSigned} = sle_int(x, y) +(<=)(x::T, y::T) where {T<:BitUnsigned} = ule_int(x, y) + +==(x::BitSigned, y::BitUnsigned) = (x >= 0) & (unsigned(x) == y) +==(x::BitUnsigned, y::BitSigned ) = (y >= 0) & (x == unsigned(y)) +<( x::BitSigned, y::BitUnsigned) = (x < 0) | (unsigned(x) < y) +<( x::BitUnsigned, y::BitSigned ) = (y >= 0) & (x < unsigned(y)) +<=(x::BitSigned, y::BitUnsigned) = (x < 0) | (unsigned(x) <= y) +<=(x::BitUnsigned, y::BitSigned ) = (y >= 0) & (x <= unsigned(y)) + +## integer shifts ## + +# unsigned shift counts always shift in the same direction +>>(x::BitSigned, y::BitUnsigned) = ashr_int(x, y) +>>(x::BitUnsigned, y::BitUnsigned) = lshr_int(x, y) +<<(x::BitInteger, y::BitUnsigned) = shl_int(x, y) +>>>(x::BitInteger, y::BitUnsigned) = lshr_int(x, y) +# signed shift counts can shift in either direction +# note: this early during bootstrap, `>=` is not yet available +# note: we only define Int shift counts here; the generic case is handled later +>>(x::BitInteger, y::Int) = + ifelse(0 <= y, x >> unsigned(y), x << unsigned(-y)) +<<(x::BitInteger, y::Int) = + ifelse(0 <= y, x << unsigned(y), x >> unsigned(-y)) +>>>(x::BitInteger, y::Int) = + ifelse(0 <= y, x >>> unsigned(y), x << unsigned(-y)) + +for to in BitInteger_types, from in (BitInteger_types..., Bool) + if !(to === from) + if to.size < from.size + @eval rem(x::($from), ::Type{$to}) = trunc_int($to, x) + elseif from === Bool + @eval rem(x::($from), ::Type{$to}) = convert($to, x) + elseif from.size < to.size + if from <: Signed + @eval rem(x::($from), ::Type{$to}) = sext_int($to, x) + else + @eval rem(x::($from), ::Type{$to}) = convert($to, x) + end + else + @eval rem(x::($from), ::Type{$to}) = bitcast($to, x) + end + end +end + +# @doc isn't available when running in Core at this point. +# Tuple syntax for documentation two function signatures at the same time +# doesn't work either at this point. +if nameof(@__MODULE__) === :Base + for fname in (:mod, :rem) + @eval @doc """ + rem(x::Integer, T::Type{<:Integer}) -> T + mod(x::Integer, T::Type{<:Integer}) -> T + %(x::Integer, T::Type{<:Integer}) -> T + + Find `y::T` such that `x` ≡ `y` (mod n), where n is the number of integers representable + in `T`, and `y` is an integer in `[typemin(T),typemax(T)]`. + If `T` can represent any integer (e.g. `T == BigInt`), then this operation corresponds to + a conversion to `T`. + + # Examples + ```jldoctest + julia> 129 % Int8 + -127 + ``` + """ $fname(x::Integer, T::Type{<:Integer}) + end +end + +rem(x::T, ::Type{T}) where {T<:Integer} = x +rem(x::Integer, T::Type{<:Integer}) = convert(T, x) # `x % T` falls back to `convert` +rem(x::Integer, ::Type{Bool}) = ((x & 1) != 0) +mod(x::Integer, ::Type{T}) where {T<:Integer} = rem(x, T) + +unsafe_trunc(::Type{T}, x::Integer) where {T<:Integer} = rem(x, T) + +""" + trunc([T,] x) + trunc(x; digits::Integer= [, base = 10]) + trunc(x; sigdigits::Integer= [, base = 10]) + +`trunc(x)` returns the nearest integral value of the same type as `x` whose absolute value +is less than or equal to `x`. + +`trunc(T, x)` converts the result to type `T`, throwing an `InexactError` if the value is +not representable. + +`digits`, `sigdigits` and `base` work as for [`round`](@ref). +""" +function trunc end + +""" + floor([T,] x) + floor(x; digits::Integer= [, base = 10]) + floor(x; sigdigits::Integer= [, base = 10]) + +`floor(x)` returns the nearest integral value of the same type as `x` that is less than or +equal to `x`. + +`floor(T, x)` converts the result to type `T`, throwing an `InexactError` if the value is +not representable. + +`digits`, `sigdigits` and `base` work as for [`round`](@ref). +""" +function floor end + +""" + ceil([T,] x) + ceil(x; digits::Integer= [, base = 10]) + ceil(x; sigdigits::Integer= [, base = 10]) + +`ceil(x)` returns the nearest integral value of the same type as `x` that is greater than or +equal to `x`. + +`ceil(T, x)` converts the result to type `T`, throwing an `InexactError` if the value is not +representable. + +`digits`, `sigdigits` and `base` work as for [`round`](@ref). +""" +function ceil end + +round(::Type{T}, x::Integer) where {T<:Integer} = convert(T, x) +trunc(::Type{T}, x::Integer) where {T<:Integer} = convert(T, x) +floor(::Type{T}, x::Integer) where {T<:Integer} = convert(T, x) + ceil(::Type{T}, x::Integer) where {T<:Integer} = convert(T, x) + +## integer construction ## + +""" + @int128_str str + @int128_str(str) + +`@int128_str` parses a string into a Int128 +Throws an `ArgumentError` if the string is not a valid integer +""" +macro int128_str(s) + return parse(Int128, s) +end + +""" + @uint128_str str + @uint128_str(str) + +`@uint128_str` parses a string into a UInt128 +Throws an `ArgumentError` if the string is not a valid integer +""" +macro uint128_str(s) + return parse(UInt128, s) +end + +""" + @big_str str + @big_str(str) + +Parse a string into a [`BigInt`](@ref) or [`BigFloat`](@ref), +and throw an `ArgumentError` if the string is not a valid number. +For integers `_` is allowed in the string as a separator. + +# Examples +```jldoctest +julia> big"123_456" +123456 + +julia> big"7891.5" +7891.5 +``` +""" +macro big_str(s) + if '_' in s + # remove _ in s[2:end-1] + bf = IOBuffer(maxsize=lastindex(s)) + print(bf, s[1]) + for c in SubString(s, 2, lastindex(s)-1) + c != '_' && print(bf, c) + end + print(bf, s[end]) + seekstart(bf) + n = tryparse(BigInt, String(take!(bf))) + n === nothing || return n + else + n = tryparse(BigInt, s) + n === nothing || return n + n = tryparse(BigFloat, s) + n === nothing || return n + end + message = "invalid number format $s for BigInt or BigFloat" + return :(throw(ArgumentError($message))) +end + +## integer promotions ## + +# with different sizes, promote to larger type +promote_rule(::Type{Int16}, ::Union{Type{Int8}, Type{UInt8}}) = Int16 +promote_rule(::Type{Int32}, ::Union{Type{Int16}, Type{Int8}, Type{UInt16}, Type{UInt8}}) = Int32 +promote_rule(::Type{Int64}, ::Union{Type{Int16}, Type{Int32}, Type{Int8}, Type{UInt16}, Type{UInt32}, Type{UInt8}}) = Int64 +promote_rule(::Type{Int128}, ::Union{Type{Int16}, Type{Int32}, Type{Int64}, Type{Int8}, Type{UInt16}, Type{UInt32}, Type{UInt64}, Type{UInt8}}) = Int128 +promote_rule(::Type{UInt16}, ::Union{Type{Int8}, Type{UInt8}}) = UInt16 +promote_rule(::Type{UInt32}, ::Union{Type{Int16}, Type{Int8}, Type{UInt16}, Type{UInt8}}) = UInt32 +promote_rule(::Type{UInt64}, ::Union{Type{Int16}, Type{Int32}, Type{Int8}, Type{UInt16}, Type{UInt32}, Type{UInt8}}) = UInt64 +promote_rule(::Type{UInt128}, ::Union{Type{Int16}, Type{Int32}, Type{Int64}, Type{Int8}, Type{UInt16}, Type{UInt32}, Type{UInt64}, Type{UInt8}}) = UInt128 +# with mixed signedness and same size, Unsigned wins +promote_rule(::Type{UInt8}, ::Type{Int8} ) = UInt8 +promote_rule(::Type{UInt16}, ::Type{Int16} ) = UInt16 +promote_rule(::Type{UInt32}, ::Type{Int32} ) = UInt32 +promote_rule(::Type{UInt64}, ::Type{Int64} ) = UInt64 +promote_rule(::Type{UInt128}, ::Type{Int128}) = UInt128 + +## traits ## + +""" + typemin(T) + +The lowest value representable by the given (real) numeric DataType `T`. + +# Examples +```jldoctest +julia> typemin(Float16) +-Inf16 + +julia> typemin(Float32) +-Inf32 +``` +""" +function typemin end + +""" + typemax(T) + +The highest value representable by the given (real) numeric `DataType`. + +# Examples +```jldoctest +julia> typemax(Int8) +127 + +julia> typemax(UInt32) +0xffffffff +``` +""" +function typemax end + +typemin(::Type{Int8 }) = Int8(-128) +typemax(::Type{Int8 }) = Int8(127) +typemin(::Type{UInt8 }) = UInt8(0) +typemax(::Type{UInt8 }) = UInt8(255) +typemin(::Type{Int16 }) = Int16(-32768) +typemax(::Type{Int16 }) = Int16(32767) +typemin(::Type{UInt16}) = UInt16(0) +typemax(::Type{UInt16}) = UInt16(65535) +typemin(::Type{Int32 }) = Int32(-2147483648) +typemax(::Type{Int32 }) = Int32(2147483647) +typemin(::Type{UInt32}) = UInt32(0) +typemax(::Type{UInt32}) = UInt32(4294967295) +typemin(::Type{Int64 }) = -9223372036854775808 +typemax(::Type{Int64 }) = 9223372036854775807 +typemin(::Type{UInt64}) = UInt64(0) +typemax(::Type{UInt64}) = 0xffffffffffffffff +@eval typemin(::Type{UInt128}) = $(convert(UInt128, 0)) +@eval typemax(::Type{UInt128}) = $(bitcast(UInt128, convert(Int128, -1))) +@eval typemin(::Type{Int128} ) = $(convert(Int128, 1) << 127) +@eval typemax(::Type{Int128} ) = $(bitcast(Int128, typemax(UInt128) >> 1)) + + +widen(::Type{Int8}) = Int16 +widen(::Type{Int16}) = Int32 +widen(::Type{Int32}) = Int64 +widen(::Type{Int64}) = Int128 +widen(::Type{UInt8}) = UInt16 +widen(::Type{UInt16}) = UInt32 +widen(::Type{UInt32}) = UInt64 +widen(::Type{UInt64}) = UInt128 + +# a few special cases, +# Int64*UInt64 => Int128 +# |x|<=2^(k-1), |y|<=2^k-1 => |x*y|<=2^(2k-1)-1 +widemul(x::Signed,y::Unsigned) = widen(x) * signed(widen(y)) +widemul(x::Unsigned,y::Signed) = signed(widen(x)) * widen(y) +# multplication by Bool doesn't require widening +widemul(x::Bool,y::Bool) = x * y +widemul(x::Bool,y::Number) = x * y +widemul(x::Number,y::Bool) = x * y + + +## wide multiplication, Int128 multiply and divide ## + +if Core.sizeof(Int) == 4 + function widemul(u::Int64, v::Int64) + local u0::UInt64, v0::UInt64, w0::UInt64 + local u1::Int64, v1::Int64, w1::UInt64, w2::Int64, t::UInt64 + + u0 = u & 0xffffffff; u1 = u >> 32 + v0 = v & 0xffffffff; v1 = v >> 32 + w0 = u0 * v0 + t = reinterpret(UInt64, u1) * v0 + (w0 >>> 32) + w2 = reinterpret(Int64, t) >> 32 + w1 = u0 * reinterpret(UInt64, v1) + (t & 0xffffffff) + hi = u1 * v1 + w2 + (reinterpret(Int64, w1) >> 32) + lo = w0 & 0xffffffff + (w1 << 32) + return Int128(hi) << 64 + Int128(lo) + end + + function widemul(u::UInt64, v::UInt64) + local u0::UInt64, v0::UInt64, w0::UInt64 + local u1::UInt64, v1::UInt64, w1::UInt64, w2::UInt64, t::UInt64 + + u0 = u & 0xffffffff; u1 = u >>> 32 + v0 = v & 0xffffffff; v1 = v >>> 32 + w0 = u0 * v0 + t = u1 * v0 + (w0 >>> 32) + w2 = t >>> 32 + w1 = u0 * v1 + (t & 0xffffffff) + hi = u1 * v1 + w2 + (w1 >>> 32) + lo = w0 & 0xffffffff + (w1 << 32) + return UInt128(hi) << 64 + UInt128(lo) + end + + function *(u::Int128, v::Int128) + u0 = u % UInt64; u1 = Int64(u >> 64) + v0 = v % UInt64; v1 = Int64(v >> 64) + lolo = widemul(u0, v0) + lohi = widemul(reinterpret(Int64, u0), v1) + hilo = widemul(u1, reinterpret(Int64, v0)) + t = reinterpret(UInt128, hilo) + (lolo >>> 64) + w1 = reinterpret(UInt128, lohi) + (t & 0xffffffffffffffff) + return Int128(lolo & 0xffffffffffffffff) + reinterpret(Int128, w1) << 64 + end + + function *(u::UInt128, v::UInt128) + u0 = u % UInt64; u1 = UInt64(u>>>64) + v0 = v % UInt64; v1 = UInt64(v>>>64) + lolo = widemul(u0, v0) + lohi = widemul(u0, v1) + hilo = widemul(u1, v0) + t = hilo + (lolo >>> 64) + w1 = lohi + (t & 0xffffffffffffffff) + return (lolo & 0xffffffffffffffff) + UInt128(w1) << 64 + end + + function _setbit(x::UInt128, i) + # faster version of `return x | (UInt128(1) << i)` + j = i >> 5 + y = UInt128(one(UInt32) << (i & 0x1f)) + if j == 0 + return x | y + elseif j == 1 + return x | (y << 32) + elseif j == 2 + return x | (y << 64) + elseif j == 3 + return x | (y << 96) + end + return x + end + + function divrem(x::UInt128, y::UInt128) + iszero(y) && throw(DivideError()) + if (x >> 64) % UInt64 == 0 + if (y >> 64) % UInt64 == 0 + # fast path: upper 64 bits are zero, so we can fallback to UInt64 division + q64, x64 = divrem(x % UInt64, y % UInt64) + return UInt128(q64), UInt128(x64) + else + # this implies y>x, so + return zero(UInt128), x + end + end + n = leading_zeros(y) - leading_zeros(x) + q = zero(UInt128) + ys = y << n + while n >= 0 + # ys == y * 2^n + if ys <= x + x -= ys + q = _setbit(q, n) + if (x >> 64) % UInt64 == 0 + # exit early, similar to above fast path + if (y >> 64) % UInt64 == 0 + q64, x64 = divrem(x % UInt64, y % UInt64) + q |= q64 + x = UInt128(x64) + end + return q, x + end + end + ys >>>= 1 + n -= 1 + end + return q, x + end + + function div(x::Int128, y::Int128) + (x == typemin(Int128)) & (y == -1) && throw(DivideError()) + return Int128(div(BigInt(x), BigInt(y)))::Int128 + end + div(x::UInt128, y::UInt128) = divrem(x, y)[1] + + function rem(x::Int128, y::Int128) + return Int128(rem(BigInt(x), BigInt(y)))::Int128 + end + + function rem(x::UInt128, y::UInt128) + iszero(y) && throw(DivideError()) + if (x >> 64) % UInt64 == 0 + if (y >> 64) % UInt64 == 0 + # fast path: upper 64 bits are zero, so we can fallback to UInt64 division + return UInt128(rem(x % UInt64, y % UInt64)) + else + # this implies y>x, so + return x + end + end + n = leading_zeros(y) - leading_zeros(x) + ys = y << n + while n >= 0 + # ys == y * 2^n + if ys <= x + x -= ys + if (x >> 64) % UInt64 == 0 + # exit early, similar to above fast path + if (y >> 64) % UInt64 == 0 + x = UInt128(rem(x % UInt64, y % UInt64)) + end + return x + end + end + ys >>>= 1 + n -= 1 + end + return x + end + + function mod(x::Int128, y::Int128) + return Int128(mod(BigInt(x), BigInt(y)))::Int128 + end +else + *(x::T, y::T) where {T<:Union{Int128,UInt128}} = mul_int(x, y) + + div(x::Int128, y::Int128) = checked_sdiv_int(x, y) + div(x::UInt128, y::UInt128) = checked_udiv_int(x, y) + + rem(x::Int128, y::Int128) = checked_srem_int(x, y) + rem(x::UInt128, y::UInt128) = checked_urem_int(x, y) +end + +# issue #15489: since integer ops are unchecked, they shouldn't check promotion +for op in (:+, :-, :*, :&, :|, :xor) + @eval function $op(a::Integer, b::Integer) + T = promote_typeof(a, b) + aT, bT = a % T, b % T + not_sametype((a, b), (aT, bT)) + return $op(aT, bT) + end +end diff --git a/base/intfuncs.jl b/base/intfuncs.jl new file mode 100644 index 0000000..a05b688 --- /dev/null +++ b/base/intfuncs.jl @@ -0,0 +1,908 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## number-theoretic functions ## + +""" + gcd(x,y) + +Greatest common (positive) divisor (or zero if `x` and `y` are both zero). + +# Examples +```jldoctest +julia> gcd(6,9) +3 + +julia> gcd(6,-9) +3 +``` +""" +function gcd(a::T, b::T) where T<:Integer + while b != 0 + t = b + b = rem(a, b) + a = t + end + checked_abs(a) +end + +# binary GCD (aka Stein's) algorithm +# about 1.7x (2.1x) faster for random Int64s (Int128s) +function gcd(a::T, b::T) where T<:Union{Int8,UInt8,Int16,UInt16,Int32,UInt32,Int64,UInt64,Int128,UInt128} + a == 0 && return abs(b) + b == 0 && return abs(a) + za = trailing_zeros(a) + zb = trailing_zeros(b) + k = min(za, zb) + u = unsigned(abs(a >> za)) + v = unsigned(abs(b >> zb)) + while u != v + if u > v + u, v = v, u + end + v -= u + v >>= trailing_zeros(v) + end + r = u << k + # T(r) would throw InexactError; we want OverflowError instead + r > typemax(T) && __throw_gcd_overflow(a, b) + r % T +end +@noinline __throw_gcd_overflow(a, b) = throw(OverflowError("gcd($a, $b) overflows")) + +""" + lcm(x,y) + +Least common (non-negative) multiple. + +# Examples +```jldoctest +julia> lcm(2,3) +6 + +julia> lcm(-2,3) +6 +``` +""" +function lcm(a::T, b::T) where T<:Integer + # explicit a==0 test is to handle case of lcm(0,0) correctly + if a == 0 + return a + else + return checked_abs(checked_mul(a, div(b, gcd(b,a)))) + end +end + +gcd(a::Integer) = a +lcm(a::Integer) = a +gcd(a::Integer, b::Integer) = gcd(promote(a,b)...) +lcm(a::Integer, b::Integer) = lcm(promote(a,b)...) +gcd(a::Integer, b::Integer...) = gcd(a, gcd(b...)) +lcm(a::Integer, b::Integer...) = lcm(a, lcm(b...)) + +lcm(abc::AbstractArray{<:Integer}) = reduce(lcm, abc; init=one(eltype(abc))) + +function gcd(abc::AbstractArray{<:Integer}) + a = zero(eltype(abc)) + for b in abc + a = gcd(a,b) + if a == 1 + return a + end + end + return a +end + +# return (gcd(a,b),x,y) such that ax+by == gcd(a,b) +""" + gcdx(x,y) + +Computes the greatest common (positive) divisor of `x` and `y` and their Bézout +coefficients, i.e. the integer coefficients `u` and `v` that satisfy +``ux+vy = d = gcd(x,y)``. ``gcdx(x,y)`` returns ``(d,u,v)``. + +# Examples +```jldoctest +julia> gcdx(12, 42) +(6, -3, 1) + +julia> gcdx(240, 46) +(2, -9, 47) +``` + +!!! note + Bézout coefficients are *not* uniquely defined. `gcdx` returns the minimal + Bézout coefficients that are computed by the extended Euclidean algorithm. + (Ref: D. Knuth, TAoCP, 2/e, p. 325, Algorithm X.) + For signed integers, these coefficients `u` and `v` are minimal in + the sense that ``|u| < |y/d|`` and ``|v| < |x/d|``. Furthermore, + the signs of `u` and `v` are chosen so that `d` is positive. + For unsigned integers, the coefficients `u` and `v` might be near + their `typemax`, and the identity then holds only via the unsigned + integers' modulo arithmetic. +""" +function gcdx(a::T, b::T) where T<:Integer + # a0, b0 = a, b + s0, s1 = oneunit(T), zero(T) + t0, t1 = s1, s0 + # The loop invariant is: s0*a0 + t0*b0 == a + while b != 0 + q = div(a, b) + a, b = b, rem(a, b) + s0, s1 = s1, s0 - q*s1 + t0, t1 = t1, t0 - q*t1 + end + a < 0 ? (-a, -s0, -t0) : (a, s0, t0) +end +gcdx(a::Integer, b::Integer) = gcdx(promote(a,b)...) + +# multiplicative inverse of n mod m, error if none + +""" + invmod(x,m) + +Take the inverse of `x` modulo `m`: `y` such that ``x y = 1 \\pmod m``, +with ``div(x,y) = 0``. This is undefined for ``m = 0``, or if +``gcd(x,m) \\neq 1``. + +# Examples +```jldoctest +julia> invmod(2,5) +3 + +julia> invmod(2,3) +2 + +julia> invmod(5,6) +5 +``` +""" +function invmod(n::T, m::T) where T<:Integer + g, x, y = gcdx(n, m) + g != 1 && throw(DomainError((n, m), "Greatest common divisor is $g.")) + m == 0 && throw(DomainError(m, "`m` must not be 0.")) + # Note that m might be negative here. + # For unsigned T, x might be close to typemax; add m to force a wrap-around. + r = mod(x + m, m) + # The postcondition is: mod(r * n, m) == mod(T(1), m) && div(r, m) == 0 + r +end +invmod(n::Integer, m::Integer) = invmod(promote(n,m)...) + +# ^ for any x supporting * +to_power_type(x) = convert(Base._return_type(*, Tuple{typeof(x), typeof(x)}), x) +@noinline throw_domerr_powbysq(::Any, p) = throw(DomainError(p, + string("Cannot raise an integer x to a negative power ", p, '.', + "\nConvert input to float."))) +@noinline throw_domerr_powbysq(::Integer, p) = throw(DomainError(p, + string("Cannot raise an integer x to a negative power ", p, '.', + "\nMake x or $p a float by adding a zero decimal ", + "(e.g., 2.0^$p or 2^$(float(p)) instead of 2^$p), ", + "or write 1/x^$(-p), float(x)^$p, x^float($p) or (x//1)^$p"))) +@noinline throw_domerr_powbysq(::AbstractMatrix, p) = throw(DomainError(p, + string("Cannot raise an integer matrix x to a negative power ", p, '.', + "\nMake x a float matrix by adding a zero decimal ", + "(e.g., [2.0 1.0;1.0 0.0]^$p instead ", + "of [2 1;1 0]^$p), or write float(x)^$p or Rational.(x)^$p"))) +function power_by_squaring(x_, p::Integer) + x = to_power_type(x_) + if p == 1 + return copy(x) + elseif p == 0 + return one(x) + elseif p == 2 + return x*x + elseif p < 0 + isone(x) && return copy(x) + isone(-x) && return iseven(p) ? one(x) : copy(x) + throw_domerr_powbysq(x, p) + end + t = trailing_zeros(p) + 1 + p >>= t + while (t -= 1) > 0 + x *= x + end + y = x + while p > 0 + t = trailing_zeros(p) + 1 + p >>= t + while (t -= 1) >= 0 + x *= x + end + y *= x + end + return y +end +power_by_squaring(x::Bool, p::Unsigned) = ((p==0) | x) +function power_by_squaring(x::Bool, p::Integer) + p < 0 && !x && throw_domerr_powbysq(x, p) + return (p==0) | x +end + +^(x::T, p::T) where {T<:Integer} = power_by_squaring(x,p) +^(x::Number, p::Integer) = power_by_squaring(x,p) + +# x^p for any literal integer p is lowered to Base.literal_pow(^, x, Val(p)) +# to enable compile-time optimizations specialized to p. +# However, we still need a fallback that calls the function ^ which may either +# mean Base.^ or something else, depending on context. +# We mark these @inline since if the target is marked @inline, +# we want to make sure that gets propagated, +# even if it is over the inlining threshold. +@inline literal_pow(f, x, ::Val{p}) where {p} = f(x,p) + +# Restrict inlining to hardware-supported arithmetic types, which +# are fast enough to benefit from inlining. +const HWReal = Union{Int8,Int16,Int32,Int64,UInt8,UInt16,UInt32,UInt64,Float32,Float64} +const HWNumber = Union{HWReal, Complex{<:HWReal}, Rational{<:HWReal}} + +# Core.Compiler has complicated logic to inline x^2 and x^3 for +# numeric types. In terms of Val we can do it much more simply. +# (The first argument prevents unexpected behavior if a function ^ +# is defined that is not equal to Base.^) +@inline literal_pow(::typeof(^), x::HWNumber, ::Val{0}) = one(x) +@inline literal_pow(::typeof(^), x::HWNumber, ::Val{1}) = x +@inline literal_pow(::typeof(^), x::HWNumber, ::Val{2}) = x*x +@inline literal_pow(::typeof(^), x::HWNumber, ::Val{3}) = x*x*x + +# don't use the inv(x) transformation here since float^p is slightly more accurate +@inline literal_pow(::typeof(^), x::AbstractFloat, ::Val{p}) where {p} = x^p +@inline literal_pow(::typeof(^), x::AbstractFloat, ::Val{-1}) = inv(x) + +# for other types, define x^-n as inv(x)^n so that negative literal powers can +# be computed in a type-stable way even for e.g. integers. +@inline @generated function literal_pow(f::typeof(^), x, ::Val{p}) where {p} + if p < 0 + :(literal_pow(^, inv(x), $(Val{-p}()))) + else + :(f(x,$p)) + end +end + +# note: it is tempting to add optimized literal_pow(::typeof(^), x, ::Val{n}) +# methods here for various n, but this easily leads to method ambiguities +# if anyone has defined literal_pow(::typeof(^), x::T, ::Val). + +# b^p mod m + +""" + powermod(x::Integer, p::Integer, m) + +Compute ``x^p \\pmod m``. + +# Examples +```jldoctest +julia> powermod(2, 6, 5) +4 + +julia> mod(2^6, 5) +4 + +julia> powermod(5, 2, 20) +5 + +julia> powermod(5, 2, 19) +6 + +julia> powermod(5, 3, 19) +11 +``` +""" +function powermod(x::Integer, p::Integer, m::T) where T<:Integer + p < 0 && return powermod(invmod(x, m), -p, m) + p == 0 && return mod(one(m),m) + (m == 1 || m == -1) && return zero(m) + b = oftype(m,mod(x,m)) # this also checks for divide by zero + + t = prevpow(2, p) + r::T = 1 + while true + if p >= t + r = mod(widemul(r,b),m) + p -= t + end + t >>>= 1 + t <= 0 && break + r = mod(widemul(r,r),m) + end + return r +end + +# optimization: promote the modulus m to BigInt only once (cf. widemul in generic powermod above) +powermod(x::Integer, p::Integer, m::Union{Int128,UInt128}) = oftype(m, powermod(x, p, big(m))) + +_nextpow2(x::Unsigned) = oneunit(x)<<((sizeof(x)<<3)-leading_zeros(x-oneunit(x))) +_nextpow2(x::Integer) = reinterpret(typeof(x),x < 0 ? -_nextpow2(unsigned(-x)) : _nextpow2(unsigned(x))) +_prevpow2(x::Unsigned) = one(x) << unsigned((sizeof(x)<<3)-leading_zeros(x)-1) +_prevpow2(x::Integer) = reinterpret(typeof(x),x < 0 ? -_prevpow2(unsigned(-x)) : _prevpow2(unsigned(x))) + +""" + ispow2(n::Integer) -> Bool + +Test whether `n` is a power of two. + +# Examples +```jldoctest +julia> ispow2(4) +true + +julia> ispow2(5) +false +``` +""" +ispow2(x::Integer) = x > 0 && count_ones(x) == 1 + +""" + nextpow(a, x) + +The smallest `a^n` not less than `x`, where `n` is a non-negative integer. `a` must be +greater than 1, and `x` must be greater than 0. + +# Examples +```jldoctest +julia> nextpow(2, 7) +8 + +julia> nextpow(2, 9) +16 + +julia> nextpow(5, 20) +25 + +julia> nextpow(4, 16) +16 +``` + +See also [`prevpow`](@ref). +""" +function nextpow(a::Real, x::Real) + x <= 0 && throw(DomainError(x, "`x` must be positive.")) + # Special case fast path for x::Integer, a == 2. + # This is a very common case. Constant prop will make sure that a call site + # specified as `nextpow(2, x)` will get this special case inlined. + a == 2 && isa(x, Integer) && return _nextpow2(x) + a <= 1 && throw(DomainError(a, "`a` must be greater than 1.")) + x <= 1 && return one(a) + n = ceil(Integer,log(a, x)) + p = a^(n-1) + # guard against roundoff error, e.g., with a=5 and x=125 + p >= x ? p : a^n +end + +""" + prevpow(a, x) + +The largest `a^n` not greater than `x`, where `n` is a non-negative integer. +`a` must be greater than 1, and `x` must not be less than 1. + +# Examples +```jldoctest +julia> prevpow(2, 7) +4 + +julia> prevpow(2, 9) +8 + +julia> prevpow(5, 20) +5 + +julia> prevpow(4, 16) +16 +``` +See also [`nextpow`](@ref). +""" +function prevpow(a::Real, x::Real) + x < 1 && throw(DomainError(x, "`x` must be ≥ 1.")) + # See comment in nextpos() for a == special case. + a == 2 && isa(x, Integer) && return _prevpow2(x) + a <= 1 && throw(DomainError(a, "`a` must be greater than 1.")) + n = floor(Integer,log(a, x)) + p = a^(n+1) + p <= x ? p : a^n +end + +## ndigits (number of digits) in base 10 ## + +# decimal digits in an unsigned integer +const powers_of_ten = [ + 0x0000000000000001, 0x000000000000000a, 0x0000000000000064, 0x00000000000003e8, + 0x0000000000002710, 0x00000000000186a0, 0x00000000000f4240, 0x0000000000989680, + 0x0000000005f5e100, 0x000000003b9aca00, 0x00000002540be400, 0x000000174876e800, + 0x000000e8d4a51000, 0x000009184e72a000, 0x00005af3107a4000, 0x00038d7ea4c68000, + 0x002386f26fc10000, 0x016345785d8a0000, 0x0de0b6b3a7640000, 0x8ac7230489e80000, +] +function bit_ndigits0z(x::Base.BitUnsigned64) + lz = (sizeof(x)<<3)-leading_zeros(x) + nd = (1233*lz)>>12+1 + nd -= x < powers_of_ten[nd] +end +function bit_ndigits0z(x::UInt128) + n = 0 + while x > 0x8ac7230489e80000 + x = div(x,0x8ac7230489e80000) + n += 19 + end + return n + ndigits0z(UInt64(x)) +end + +ndigits0z(x::BitSigned) = bit_ndigits0z(unsigned(abs(x))) +ndigits0z(x::BitUnsigned) = bit_ndigits0z(x) +ndigits0z(x::Integer) = ndigits0zpb(x, 10) + +## ndigits with specified base ## + +# The suffix "nb" stands for "negative base" +function ndigits0znb(x::Integer, b::Integer) + d = 0 + if x isa Unsigned + d += (x != 0)::Bool + x = -signed(fld(x, -b)) + end + # precondition: b < -1 && !(typeof(x) <: Unsigned) + while x != 0 + x = cld(x,b) + d += 1 + end + return d +end + +# do first division before conversion with signed here, which can otherwise overflow +ndigits0znb(x::Bool, b::Integer) = x % Int + +# The suffix "pb" stands for "positive base" +function ndigits0zpb(x::Integer, b::Integer) + # precondition: b > 1 + x == 0 && return 0 + b = Int(b) + x = abs(x) + if x isa Base.BitInteger + x = unsigned(x)::Unsigned + b == 2 && return sizeof(x)<<3 - leading_zeros(x) + b == 8 && return (sizeof(x)<<3 - leading_zeros(x) + 2) ÷ 3 + b == 16 && return sizeof(x)<<1 - leading_zeros(x)>>2 + b == 10 && return bit_ndigits0z(x) + if ispow2(b) + dv, rm = divrem(sizeof(x)<<3 - leading_zeros(x), trailing_zeros(b)) + return iszero(rm) ? dv : dv + 1 + end + end + + d = 0 + while x > typemax(Int) + x = div(x,b) + d += 1 + end + x = div(x,b) + d += 1 + + m = 1 + while m <= x + m *= b + d += 1 + end + return d +end + +ndigits0zpb(x::Bool, b::Integer) = x % Int + +# The suffix "0z" means that the output is 0 on input zero (cf. #16841) +""" + ndigits0z(n::Integer, b::Integer=10) + +Return 0 if `n == 0`, otherwise compute the number of digits in +integer `n` written in base `b` (i.e. equal to `ndigits(n, base=b)` +in this case). +The base `b` must not be in `[-1, 0, 1]`. + +# Examples +```jldoctest +julia> Base.ndigits0z(0, 16) +0 + +julia> Base.ndigits(0, base=16) +1 + +julia> Base.ndigits0z(0) +0 + +julia> Base.ndigits0z(10, 2) +4 + +julia> Base.ndigits0z(10) +2 +``` + +See also [`ndigits`](@ref). +""" +function ndigits0z(x::Integer, b::Integer) + if b < -1 + ndigits0znb(x, b) + elseif b > 1 + ndigits0zpb(x, b) + else + throw(DomainError(b, "The base must not be in `[-1, 0, 1]`.")) + end +end + +""" + ndigits(n::Integer; base::Integer=10, pad::Integer=1) + +Compute the number of digits in integer `n` written in base `base` +(`base` must not be in `[-1, 0, 1]`), optionally padded with zeros +to a specified size (the result will never be less than `pad`). + +# Examples +```jldoctest +julia> ndigits(12345) +5 + +julia> ndigits(1022, base=16) +3 + +julia> string(1022, base=16) +"3fe" + +julia> ndigits(123, pad=5) +5 +``` +""" +ndigits(x::Integer; base::Integer=10, pad::Integer=1) = max(pad, ndigits0z(x, base)) + +## integer to string functions ## + +function bin(x::Unsigned, pad::Integer, neg::Bool) + i = neg + max(pad,sizeof(x)<<3-leading_zeros(x)) + a = StringVector(i) + while i > neg + @inbounds a[i] = 48+(x&0x1) + x >>= 1 + i -= 1 + end + if neg; @inbounds a[1]=0x2d; end + String(a) +end + +function oct(x::Unsigned, pad::Integer, neg::Bool) + i = neg + max(pad,div((sizeof(x)<<3)-leading_zeros(x)+2,3)) + a = StringVector(i) + while i > neg + @inbounds a[i] = 48+(x&0x7) + x >>= 3 + i -= 1 + end + if neg; @inbounds a[1]=0x2d; end + String(a) +end + +function dec(x::Unsigned, pad::Integer, neg::Bool) + i = neg + ndigits(x, base=10, pad=pad) + a = StringVector(i) + while i > neg + @inbounds a[i] = 48+rem(x,10) + x = oftype(x,div(x,10)) + i -= 1 + end + if neg; @inbounds a[1]=0x2d; end + String(a) +end + +function hex(x::Unsigned, pad::Integer, neg::Bool) + i = neg + max(pad,(sizeof(x)<<1)-(leading_zeros(x)>>2)) + a = StringVector(i) + while i > neg + d = x & 0xf + @inbounds a[i] = 48+d+39*(d>9) + x >>= 4 + i -= 1 + end + if neg; @inbounds a[1]=0x2d; end + String(a) +end + +const base36digits = ['0':'9';'a':'z'] +const base62digits = ['0':'9';'A':'Z';'a':'z'] + +function _base(b::Integer, x::Integer, pad::Integer, neg::Bool) + (x >= 0) | (b < 0) || throw(DomainError(x, "For negative `x`, `b` must be negative.")) + 2 <= abs(b) <= 62 || throw(DomainError(b, "base must satisfy 2 ≤ abs(base) ≤ 62")) + digits = abs(b) <= 36 ? base36digits : base62digits + i = neg + ndigits(x, base=b, pad=pad) + a = StringVector(i) + @inbounds while i > neg + if b > 0 + a[i] = digits[1+rem(x,b)] + x = div(x,b) + else + a[i] = digits[1+mod(x,-b)] + x = cld(x,b) + end + i -= 1 + end + if neg; a[1]='-'; end + String(a) +end + +split_sign(n::Integer) = unsigned(abs(n)), n < 0 +split_sign(n::Unsigned) = n, false + +""" + string(n::Integer; base::Integer = 10, pad::Integer = 1) + +Convert an integer `n` to a string in the given `base`, +optionally specifying a number of digits to pad to. + +```jldoctest +julia> string(5, base = 13, pad = 4) +"0005" + +julia> string(13, base = 5, pad = 4) +"0023" +``` +""" +function string(n::Integer; base::Integer = 10, pad::Integer = 1) + if base == 2 + (n_positive, neg) = split_sign(n) + bin(n_positive, pad, neg) + elseif base == 8 + (n_positive, neg) = split_sign(n) + oct(n_positive, pad, neg) + elseif base == 10 + (n_positive, neg) = split_sign(n) + dec(n_positive, pad, neg) + elseif base == 16 + (n_positive, neg) = split_sign(n) + hex(n_positive, pad, neg) + else + _base(base, base > 0 ? unsigned(abs(n)) : convert(Signed, n), pad, (base>0) & (n<0)) + end +end + +string(b::Bool) = b ? "true" : "false" + +""" + bitstring(n) + +A string giving the literal bit representation of a number. + +# Examples +```jldoctest +julia> bitstring(4) +"0000000000000000000000000000000000000000000000000000000000000100" + +julia> bitstring(2.2) +"0100000000000001100110011001100110011001100110011001100110011010" +``` +""" +function bitstring end + +bitstring(x::Union{Bool,Int8,UInt8}) = string(reinterpret(UInt8,x), pad = 8, base = 2) +bitstring(x::Union{Int16,UInt16,Float16}) = string(reinterpret(UInt16,x), pad = 16, base = 2) +bitstring(x::Union{Char,Int32,UInt32,Float32}) = string(reinterpret(UInt32,x), pad = 32, base = 2) +bitstring(x::Union{Int64,UInt64,Float64}) = string(reinterpret(UInt64,x), pad = 64, base = 2) +bitstring(x::Union{Int128,UInt128}) = string(reinterpret(UInt128,x), pad = 128, base = 2) + +""" + digits([T<:Integer], n::Integer; base::T = 10, pad::Integer = 1) + +Return an array with element type `T` (default `Int`) of the digits of `n` in the given +base, optionally padded with zeros to a specified size. More significant digits are at +higher indices, such that `n == sum([digits[k]*base^(k-1) for k=1:length(digits)])`. + +# Examples +```jldoctest +julia> digits(10, base = 10) +2-element Array{Int64,1}: + 0 + 1 + +julia> digits(10, base = 2) +4-element Array{Int64,1}: + 0 + 1 + 0 + 1 + +julia> digits(10, base = 2, pad = 6) +6-element Array{Int64,1}: + 0 + 1 + 0 + 1 + 0 + 0 +``` +""" +digits(n::Integer; base::Integer = 10, pad::Integer = 1) = + digits(typeof(base), n, base = base, pad = pad) + +function digits(T::Type{<:Integer}, n::Integer; base::Integer = 10, pad::Integer = 1) + digits!(zeros(T, ndigits(n, base=base, pad=pad)), n, base=base) +end + +""" + hastypemax(T::Type) -> Bool + +Return `true` if and only if `typemax(T)` is defined. +""" +hastypemax(::Base.BitIntegerType) = true +hastypemax(::Type{T}) where {T} = applicable(typemax, T) + +""" + digits!(array, n::Integer; base::Integer = 10) + +Fills an array of the digits of `n` in the given base. More significant digits are at higher +indices. If the array length is insufficient, the least significant digits are filled up to +the array length. If the array length is excessive, the excess portion is filled with zeros. + +# Examples +```jldoctest +julia> digits!([2,2,2,2], 10, base = 2) +4-element Array{Int64,1}: + 0 + 1 + 0 + 1 + +julia> digits!([2,2,2,2,2,2], 10, base = 2) +6-element Array{Int64,1}: + 0 + 1 + 0 + 1 + 0 + 0 +``` +""" +function digits!(a::AbstractVector{T}, n::Integer; base::Integer = 10) where T<:Integer + 2 <= abs(base) || throw(DomainError(base, "base must be ≥ 2 or ≤ -2")) + hastypemax(T) && abs(base) - 1 > typemax(T) && + throw(ArgumentError("type $T too small for base $base")) + isempty(a) && return a + + if base > 0 + if ispow2(base) && n >= 0 && n isa Base.BitInteger && base <= typemax(Int) + base = Int(base) + k = trailing_zeros(base) + c = base - 1 + for i in eachindex(a) + a[i] = (n >> (k * (i - firstindex(a)))) & c + end + else + for i in eachindex(a) + n, d = divrem(n, base) + a[i] = d + end + end + else + # manually peel one loop iteration for type stability + n, d = fldmod(n, -base) + a[firstindex(a)] = d + n = -signed(n) + for i in firstindex(a)+1:lastindex(a) + n, d = fldmod(n, -base) + a[i] = d + n = -n + end + end + return a +end + +""" + isqrt(n::Integer) + +Integer square root: the largest integer `m` such that `m*m <= n`. + +```jldoctest +julia> isqrt(5) +2 +``` +""" +isqrt(x::Integer) = oftype(x, trunc(sqrt(x))) + +function isqrt(x::Union{Int64,UInt64,Int128,UInt128}) + x==0 && return x + s = oftype(x, trunc(sqrt(x))) + # fix with a Newton iteration, since conversion to float discards + # too many bits. + s = (s + div(x,s)) >> 1 + s*s > x ? s-1 : s +end + +""" + factorial(n::Integer) + +Factorial of `n`. If `n` is an [`Integer`](@ref), the factorial is computed as an +integer (promoted to at least 64 bits). Note that this may overflow if `n` is not small, +but you can use `factorial(big(n))` to compute the result exactly in arbitrary precision. + +# Examples +```jldoctest +julia> factorial(6) +720 + +julia> factorial(21) +ERROR: OverflowError: 21 is too large to look up in the table; consider using `factorial(big(21))` instead +Stacktrace: +[...] + +julia> factorial(big(21)) +51090942171709440000 +``` + +# See also +* [`binomial`](@ref) + +# External links +* [Factorial](https://en.wikipedia.org/wiki/Factorial) on Wikipedia. +""" +function factorial(n::Integer) + n < 0 && throw(DomainError(n, "`n` must be nonnegative.")) + f::typeof(n*n) = 1 + for i::typeof(n*n) = 2:n + f *= i + end + return f +end + +""" + binomial(n::Integer, k::Integer) + +The _binomial coefficient_ ``\\binom{n}{k}``, being the coefficient of the ``k``th term in +the polynomial expansion of ``(1+x)^n``. + +If ``n`` is non-negative, then it is the number of ways to choose `k` out of `n` items: +```math +\\binom{n}{k} = \\frac{n!}{k! (n-k)!} +``` +where ``n!`` is the [`factorial`](@ref) function. + +If ``n`` is negative, then it is defined in terms of the identity +```math +\\binom{n}{k} = (-1)^k \\binom{k-n-1}{k} +``` + +# Examples +```jldoctest +julia> binomial(5, 3) +10 + +julia> factorial(5) ÷ (factorial(5-3) * factorial(3)) +10 + +julia> binomial(-5, 3) +-35 +``` + +# See also +* [`factorial`](@ref) + +# External links +* [Binomial coeffient](https://en.wikipedia.org/wiki/Binomial_coefficient) on Wikipedia. +""" +function binomial(n::T, k::T) where T<:Integer + n0, k0 = n, k + k < 0 && return zero(T) + sgn = one(T) + if n < 0 + n = -n + k -1 + if isodd(k) + sgn = -sgn + end + end + k > n && return zero(T) + (k == 0 || k == n) && return sgn + k == 1 && return sgn*n + if k > (n>>1) + k = (n - k) + end + x::T = nn = n - k + 1 + nn += 1 + rr = 2 + while rr <= k + xt = div(widemul(x, nn), rr) + x = xt % T + x == xt || throw(OverflowError("binomial($n0, $k0) overflows")) + rr += 1 + nn += 1 + end + convert(T, copysign(x, sgn)) +end diff --git a/base/io.jl b/base/io.jl new file mode 100644 index 0000000..d339b77 --- /dev/null +++ b/base/io.jl @@ -0,0 +1,1109 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Generic IO stubs -- all subtypes should implement these (if meaningful) + +""" + EOFError() + +No more data was available to read from a file or stream. +""" +struct EOFError <: Exception end + +""" + SystemError(prefix::AbstractString, [errno::Int32]) + +A system call failed with an error code (in the `errno` global variable). +""" +struct SystemError <: Exception + prefix::AbstractString + errnum::Int32 + extrainfo + SystemError(p::AbstractString, e::Integer, extrainfo) = new(p, e, extrainfo) + SystemError(p::AbstractString, e::Integer) = new(p, e, nothing) + SystemError(p::AbstractString) = new(p, Libc.errno()) +end + +lock(::IO) = nothing +unlock(::IO) = nothing +reseteof(x::IO) = nothing + +const SZ_UNBUFFERED_IO = 65536 +buffer_writes(x::IO, bufsize=SZ_UNBUFFERED_IO) = x + +""" + isopen(object) -> Bool + +Determine whether an object - such as a stream or timer +-- is not yet closed. Once an object is closed, it will never produce a new event. +However, since a closed stream may still have data to read in its buffer, +use [`eof`](@ref) to check for the ability to read data. +Use the `FileWatching` package to be notified when a stream might be writable or readable. + +# Examples +```jldoctest +julia> io = open("my_file.txt", "w+"); + +julia> isopen(io) +true + +julia> close(io) + +julia> isopen(io) +false +``` +""" +function isopen end + +""" + close(stream) + +Close an I/O stream. Performs a [`flush`](@ref) first. +""" +function close end +function flush end +function wait_readnb end +function wait_close end +function bytesavailable end + +""" + readavailable(stream) + +Read all available data on the stream, blocking the task only if no data is available. The +result is a `Vector{UInt8,1}`. +""" +function readavailable end + +""" + isreadable(io) -> Bool + +Return `true` if the specified IO object is readable (if that can be determined). + +# Examples +```jldoctest +julia> open("myfile.txt", "w") do io + print(io, "Hello world!"); + isreadable(io) + end +false + +julia> open("myfile.txt", "r") do io + isreadable(io) + end +true + +julia> rm("myfile.txt") +``` +""" +function isreadable end + +""" + iswritable(io) -> Bool + +Return `true` if the specified IO object is writable (if that can be determined). + +# Examples +```jldoctest +julia> open("myfile.txt", "w") do io + print(io, "Hello world!"); + iswritable(io) + end +true + +julia> open("myfile.txt", "r") do io + iswritable(io) + end +false + +julia> rm("myfile.txt") +``` +""" +function iswritable end +function copy end +function eof end + +""" + read(io::IO, T) + +Read a single value of type `T` from `io`, in canonical binary representation. + + read(io::IO, String) + +Read the entirety of `io`, as a `String`. + +# Examples +```jldoctest +julia> io = IOBuffer("JuliaLang is a GitHub organization"); + +julia> read(io, Char) +'J': ASCII/Unicode U+004a (category Lu: Letter, uppercase) + +julia> io = IOBuffer("JuliaLang is a GitHub organization"); + +julia> read(io, String) +"JuliaLang is a GitHub organization" +``` +""" +read(stream, t) + +""" + write(io::IO, x) + write(filename::AbstractString, x) + +Write the canonical binary representation of a value to the given I/O stream or file. +Return the number of bytes written into the stream. See also [`print`](@ref) to +write a text representation (with an encoding that may depend upon `io`). + +You can write multiple values with the same `write` call. i.e. the following are equivalent: + + write(io, x, y...) + write(io, x) + write(io, y...) + +# Examples +```jldoctest +julia> io = IOBuffer(); + +julia> write(io, "JuliaLang is a GitHub organization.", " It has many members.") +56 + +julia> String(take!(io)) +"JuliaLang is a GitHub organization. It has many members." + +julia> write(io, "Sometimes those members") + write(io, " write documentation.") +44 + +julia> String(take!(io)) +"Sometimes those members write documentation." +``` +User-defined plain-data types without `write` methods can be written when wrapped in a `Ref`: +```jldoctest +julia> struct MyStruct; x::Float64; end + +julia> io = IOBuffer() +IOBuffer(data=UInt8[...], readable=true, writable=true, seekable=true, append=false, size=0, maxsize=Inf, ptr=1, mark=-1) + +julia> write(io, Ref(MyStruct(42.0))) +8 + +julia> seekstart(io); read!(io, Ref(MyStruct(NaN))) +Base.RefValue{MyStruct}(MyStruct(42.0)) +``` +""" +function write end + +read(s::IO, ::Type{UInt8}) = error(typeof(s)," does not support byte I/O") +write(s::IO, x::UInt8) = error(typeof(s)," does not support byte I/O") + +""" + unsafe_write(io::IO, ref, nbytes::UInt) + +Copy `nbytes` from `ref` (converted to a pointer) into the `IO` object. + +It is recommended that subtypes `T<:IO` override the following method signature +to provide more efficient implementations: +`unsafe_write(s::T, p::Ptr{UInt8}, n::UInt)` +""" +function unsafe_write(s::IO, p::Ptr{UInt8}, n::UInt) + written::Int = 0 + for i = 1:n + written += write(s, unsafe_load(p, i)) + end + return written +end + +""" + unsafe_read(io::IO, ref, nbytes::UInt) + +Copy `nbytes` from the `IO` stream object into `ref` (converted to a pointer). + +It is recommended that subtypes `T<:IO` override the following method signature +to provide more efficient implementations: +`unsafe_read(s::T, p::Ptr{UInt8}, n::UInt)` +""" +function unsafe_read(s::IO, p::Ptr{UInt8}, n::UInt) + for i = 1:n + unsafe_store!(p, read(s, UInt8)::UInt8, i) + end + nothing +end + +function peek(s::IO) + mark(s) + try read(s, UInt8) + finally + reset(s) + end +end + +# Generic `open` methods + +""" + open_flags(; keywords...) -> NamedTuple + +Compute the `read`, `write`, `create`, `truncate`, `append` flag value for +a given set of keyword arguments to [`open`](@ref) a [`NamedTuple`](@ref). +""" +function open_flags(; + read :: Union{Bool,Nothing} = nothing, + write :: Union{Bool,Nothing} = nothing, + create :: Union{Bool,Nothing} = nothing, + truncate :: Union{Bool,Nothing} = nothing, + append :: Union{Bool,Nothing} = nothing, +) + if write === true && read !== true && append !== true + create === nothing && (create = true) + truncate === nothing && (truncate = true) + end + + if truncate === true || append === true + write === nothing && (write = true) + create === nothing && (create = true) + end + + write === nothing && (write = false) + read === nothing && (read = !write) + create === nothing && (create = false) + truncate === nothing && (truncate = false) + append === nothing && (append = false) + + return ( + read = read, + write = write, + create = create, + truncate = truncate, + append = append, + ) +end + +""" + open(f::Function, args...; kwargs....) + +Apply the function `f` to the result of `open(args...; kwargs...)` and close the resulting file +descriptor upon completion. + +# Examples +```jldoctest +julia> open("myfile.txt", "w") do io + write(io, "Hello world!") + end; + +julia> open(f->read(f, String), "myfile.txt") +"Hello world!" + +julia> rm("myfile.txt") +``` +""" +function open(f::Function, args...; kwargs...) + io = open(args...; kwargs...) + try + f(io) + finally + close(io) + end +end + +# Generic wrappers around other IO objects +abstract type AbstractPipe <: IO end +function pipe_reader end +function pipe_writer end + +write(io::AbstractPipe, byte::UInt8) = write(pipe_writer(io), byte) +unsafe_write(io::AbstractPipe, p::Ptr{UInt8}, nb::UInt) = unsafe_write(pipe_writer(io), p, nb) +buffer_writes(io::AbstractPipe, args...) = buffer_writes(pipe_writer(io), args...) +flush(io::AbstractPipe) = flush(pipe_writer(io)) + +read(io::AbstractPipe, byte::Type{UInt8}) = read(pipe_reader(io), byte) +unsafe_read(io::AbstractPipe, p::Ptr{UInt8}, nb::UInt) = unsafe_read(pipe_reader(io), p, nb) +read(io::AbstractPipe) = read(pipe_reader(io)) +readuntil(io::AbstractPipe, arg::UInt8; kw...) = readuntil(pipe_reader(io), arg; kw...) +readuntil(io::AbstractPipe, arg::AbstractChar; kw...) = readuntil(pipe_reader(io), arg; kw...) +readuntil(io::AbstractPipe, arg::AbstractString; kw...) = readuntil(pipe_reader(io), arg; kw...) +readuntil(io::AbstractPipe, arg::AbstractVector; kw...) = readuntil(pipe_reader(io), arg; kw...) +readuntil_vector!(io::AbstractPipe, target::AbstractVector, keep::Bool, out) = readuntil_vector!(pipe_reader(io), target, keep, out) + +for f in ( + # peek/mark interface + :peek, :mark, :unmark, :reset, :ismarked, + # Simple reader functions + :readavailable, :isreadable) + @eval $(f)(io::AbstractPipe) = $(f)(pipe_reader(io)) +end + +iswritable(io::AbstractPipe) = iswritable(pipe_writer(io)) +isopen(io::AbstractPipe) = isopen(pipe_writer(io)) || isopen(pipe_reader(io)) +close(io::AbstractPipe) = (close(pipe_writer(io)); close(pipe_reader(io))) +wait_readnb(io::AbstractPipe, nb::Int) = wait_readnb(pipe_reader(io), nb) +wait_close(io::AbstractPipe) = (wait_close(pipe_writer(io)); wait_close(pipe_reader(io))) + +""" + bytesavailable(io) + +Return the number of bytes available for reading before a read from this stream or buffer will block. + +# Examples +```jldoctest +julia> io = IOBuffer("JuliaLang is a GitHub organization"); + +julia> bytesavailable(io) +34 +``` +""" +bytesavailable(io::AbstractPipe) = bytesavailable(pipe_reader(io)) + +""" + eof(stream) -> Bool + +Test whether an I/O stream is at end-of-file. If the stream is not yet exhausted, this +function will block to wait for more data if necessary, and then return `false`. Therefore +it is always safe to read one byte after seeing `eof` return `false`. `eof` will return +`false` as long as buffered data is still available, even if the remote end of a connection +is closed. +""" +eof(io::AbstractPipe) = eof(pipe_reader(io)) +reseteof(io::AbstractPipe) = reseteof(pipe_reader(io)) + + +# Exception-safe wrappers (io = open(); try f(io) finally close(io)) + +write(filename::AbstractString, a1, args...) = open(io->write(io, a1, args...), filename, "w") + +""" + read(filename::AbstractString, args...) + +Open a file and read its contents. `args` is passed to `read`: this is equivalent to +`open(io->read(io, args...), filename)`. + + read(filename::AbstractString, String) + +Read the entire contents of a file as a string. +""" +read(filename::AbstractString, args...) = open(io->read(io, args...), filename) + +read(filename::AbstractString, ::Type{T}) where {T} = open(io->read(io, T), filename) + +""" + read!(stream::IO, array::Union{Array, BitArray}) + read!(filename::AbstractString, array::Union{Array, BitArray}) + +Read binary data from an I/O stream or file, filling in `array`. +""" +function read! end + +read!(filename::AbstractString, a) = open(io->read!(io, a), filename) + +""" + readuntil(stream::IO, delim; keep::Bool = false) + readuntil(filename::AbstractString, delim; keep::Bool = false) + +Read a string from an I/O stream or a file, up to the given delimiter. +The delimiter can be a `UInt8`, `AbstractChar`, string, or vector. +Keyword argument `keep` controls whether the delimiter is included in the result. +The text is assumed to be encoded in UTF-8. + +# Examples +```jldoctest +julia> open("my_file.txt", "w") do io + write(io, "JuliaLang is a GitHub organization.\\nIt has many members.\\n"); + end +57 + +julia> readuntil("my_file.txt", 'L') +"Julia" + +julia> readuntil("my_file.txt", '.', keep = true) +"JuliaLang is a GitHub organization." + +julia> rm("my_file.txt") +``` +""" +readuntil(filename::AbstractString, args...; kw...) = open(io->readuntil(io, args...; kw...), filename) + +""" + readline(io::IO=stdin; keep::Bool=false) + readline(filename::AbstractString; keep::Bool=false) + +Read a single line of text from the given I/O stream or file (defaults to `stdin`). +When reading from a file, the text is assumed to be encoded in UTF-8. Lines in the +input end with `'\\n'` or `"\\r\\n"` or the end of an input stream. When `keep` is +false (as it is by default), these trailing newline characters are removed from the +line before it is returned. When `keep` is true, they are returned as part of the +line. + +# Examples +```jldoctest +julia> open("my_file.txt", "w") do io + write(io, "JuliaLang is a GitHub organization.\\nIt has many members.\\n"); + end +57 + +julia> readline("my_file.txt") +"JuliaLang is a GitHub organization." + +julia> readline("my_file.txt", keep=true) +"JuliaLang is a GitHub organization.\\n" + +julia> rm("my_file.txt") +``` +""" +function readline(filename::AbstractString; keep::Bool=false) + open(filename) do f + readline(f, keep=keep) + end +end + +function readline(s::IO=stdin; keep::Bool=false)::String + line = readuntil(s, 0x0a, keep=true) + i = length(line) + if keep || i == 0 || line[i] != 0x0a + return String(line) + elseif i < 2 || line[i-1] != 0x0d + return String(resize!(line,i-1)) + else + return String(resize!(line,i-2)) + end +end + +""" + readlines(io::IO=stdin; keep::Bool=false) + readlines(filename::AbstractString; keep::Bool=false) + +Read all lines of an I/O stream or a file as a vector of strings. Behavior is +equivalent to saving the result of reading [`readline`](@ref) repeatedly with the same +arguments and saving the resulting lines as a vector of strings. + +# Examples +```jldoctest +julia> open("my_file.txt", "w") do io + write(io, "JuliaLang is a GitHub organization.\\nIt has many members.\\n"); + end +57 + +julia> readlines("my_file.txt") +2-element Array{String,1}: + "JuliaLang is a GitHub organization." + "It has many members." + +julia> readlines("my_file.txt", keep=true) +2-element Array{String,1}: + "JuliaLang is a GitHub organization.\\n" + "It has many members.\\n" + +julia> rm("my_file.txt") +``` +""" +function readlines(filename::AbstractString; kw...) + open(filename) do f + readlines(f; kw...) + end +end +readlines(s=stdin; kw...) = collect(eachline(s; kw...)) + +## byte-order mark, ntoh & hton ## + +let a = UInt32[0x01020304] + endian_bom = GC.@preserve a unsafe_load(convert(Ptr{UInt8}, pointer(a))) + global ntoh, hton, ltoh, htol + if endian_bom == 0x01 + ntoh(x) = x + hton(x) = x + ltoh(x) = bswap(x) + htol(x) = bswap(x) + const global ENDIAN_BOM = 0x01020304 + elseif endian_bom == 0x04 + ntoh(x) = bswap(x) + hton(x) = bswap(x) + ltoh(x) = x + htol(x) = x + const global ENDIAN_BOM = 0x04030201 + else + error("seriously? what is this machine?") + end +end + +""" + ENDIAN_BOM + +The 32-bit byte-order-mark indicates the native byte order of the host machine. +Little-endian machines will contain the value `0x04030201`. Big-endian machines will contain +the value `0x01020304`. +""" +ENDIAN_BOM + +""" + ntoh(x) + +Convert the endianness of a value from Network byte order (big-endian) to that used by the Host. +""" +ntoh(x) + +""" + hton(x) + +Convert the endianness of a value from that used by the Host to Network byte order (big-endian). +""" +hton(x) + +""" + ltoh(x) + +Convert the endianness of a value from Little-endian to that used by the Host. +""" +ltoh(x) + +""" + htol(x) + +Convert the endianness of a value from that used by the Host to Little-endian. +""" +htol(x) + + +""" + isreadonly(io) -> Bool + +Determine whether a stream is read-only. + +# Examples +```jldoctest +julia> io = IOBuffer("JuliaLang is a GitHub organization"); + +julia> isreadonly(io) +true + +julia> io = IOBuffer(); + +julia> isreadonly(io) +false +``` +""" +isreadonly(s) = isreadable(s) && !iswritable(s) + +## binary I/O ## + +write(io::IO, x) = throw(MethodError(write, (io, x))) +function write(io::IO, x1, xs...) + written::Int = write(io, x1) + for x in xs + written += write(io, x) + end + return written +end + +@noinline unsafe_write(s::IO, p::Ref{T}, n::Integer) where {T} = + unsafe_write(s, unsafe_convert(Ref{T}, p)::Ptr, n) # mark noinline to ensure ref is gc-rooted somewhere (by the caller) +unsafe_write(s::IO, p::Ptr, n::Integer) = unsafe_write(s, convert(Ptr{UInt8}, p), convert(UInt, n)) +write(s::IO, x::Ref{T}) where {T} = unsafe_write(s, x, Core.sizeof(T)) +write(s::IO, x::Int8) = write(s, reinterpret(UInt8, x)) +function write(s::IO, x::Union{Int16,UInt16,Int32,UInt32,Int64,UInt64,Int128,UInt128,Float16,Float32,Float64}) + return write(s, Ref(x)) +end + +write(s::IO, x::Bool) = write(s, UInt8(x)) +write(to::IO, p::Ptr) = write(to, convert(UInt, p)) + +function write(s::IO, A::AbstractArray) + if !isbitstype(eltype(A)) + error("`write` is not supported on non-isbits arrays") + end + nb = 0 + for a in A + nb += write(s, a) + end + return nb +end + +function write(s::IO, a::Array) + if isbitstype(eltype(a)) + return GC.@preserve a unsafe_write(s, pointer(a), sizeof(a)) + else + error("`write` is not supported on non-isbits arrays") + end +end + +function write(s::IO, a::SubArray{T,N,<:Array}) where {T,N} + if !isbitstype(T) || !isa(a, StridedArray) + return invoke(write, Tuple{IO, AbstractArray}, s, a) + end + elsz = sizeof(T) + colsz = size(a,1) * elsz + GC.@preserve a if stride(a,1) != 1 + for idxs in CartesianIndices(size(a)) + unsafe_write(s, pointer(a, idxs.I), elsz) + end + return elsz * length(a) + elseif N <= 1 + return unsafe_write(s, pointer(a, 1), colsz) + else + for idxs in CartesianIndices((1, size(a)[2:end]...)) + unsafe_write(s, pointer(a, idxs.I), colsz) + end + return colsz * trailingsize(a,2) + end +end + +function write(io::IO, c::Char) + u = bswap(reinterpret(UInt32, c)) + n = 1 + while true + write(io, u % UInt8) + (u >>= 8) == 0 && return n + n += 1 + end +end +# write(io, ::AbstractChar) is not defined: implementations +# must provide their own encoding-specific method. + +function write(io::IO, s::Symbol) + pname = unsafe_convert(Ptr{UInt8}, s) + return unsafe_write(io, pname, Int(ccall(:strlen, Csize_t, (Cstring,), pname))) +end + +function write(to::IO, from::IO) + n = 0 + while !eof(from) + n += write(to, readavailable(from)) + end + return n +end + +@noinline unsafe_read(s::IO, p::Ref{T}, n::Integer) where {T} = unsafe_read(s, unsafe_convert(Ref{T}, p)::Ptr, n) # mark noinline to ensure ref is gc-rooted somewhere (by the caller) +unsafe_read(s::IO, p::Ptr, n::Integer) = unsafe_read(s, convert(Ptr{UInt8}, p), convert(UInt, n)) +read!(s::IO, x::Ref{T}) where {T} = (unsafe_read(s, x, Core.sizeof(T)); x) + +read(s::IO, ::Type{Int8}) = reinterpret(Int8, read(s, UInt8)) +function read(s::IO, T::Union{Type{Int16},Type{UInt16},Type{Int32},Type{UInt32},Type{Int64},Type{UInt64},Type{Int128},Type{UInt128},Type{Float16},Type{Float32},Type{Float64}}) + return read!(s, Ref{T}(0))[]::T +end + +read(s::IO, ::Type{Bool}) = (read(s, UInt8) != 0) +read(s::IO, ::Type{Ptr{T}}) where {T} = convert(Ptr{T}, read(s, UInt)) + +function read!(s::IO, a::Array{UInt8}) + GC.@preserve a unsafe_read(s, pointer(a), sizeof(a)) + return a +end + +function read!(s::IO, a::Array{T}) where T + if isbitstype(T) + GC.@preserve a unsafe_read(s, pointer(a), sizeof(a)) + else + for i in eachindex(a) + a[i] = read(s, T) + end + end + return a +end + +function read(io::IO, ::Type{Char}) + b0 = read(io, UInt8) + l = 8(4-leading_ones(b0)) + c = UInt32(b0) << 24 + if l < 24 + s = 16 + while s ≥ l && !eof(io) + peek(io) & 0xc0 == 0x80 || break + b = read(io, UInt8) + c |= UInt32(b) << s + s -= 8 + end + end + return reinterpret(Char, c) +end +# read(io, T) is not defined for other AbstractChar: implementations +# must provide their own encoding-specific method. + +# readuntil_string is useful below since it has +# an optimized method for s::IOStream +readuntil_string(s::IO, delim::UInt8, keep::Bool) = String(readuntil(s, delim, keep=keep)) + +function readuntil(s::IO, delim::AbstractChar; keep::Bool=false) + if delim ≤ '\x7f' + return readuntil_string(s, delim % UInt8, keep) + end + out = IOBuffer() + while !eof(s) + c = read(s, Char) + if c == delim + keep && write(out, c) + break + end + write(out, c) + end + return String(take!(out)) +end + +function readuntil(s::IO, delim::T; keep::Bool=false) where T + out = (T === UInt8 ? StringVector(0) : Vector{T}()) + while !eof(s) + c = read(s, T) + if c == delim + keep && push!(out, c) + break + end + push!(out, c) + end + return out +end + +# requires that indices for target are the integer unit range from firstindex to lastindex +# returns whether the delimiter was matched +# uses the Knuth–Morris–Pratt_algorithm, with the first and second cache entries unrolled +# For longer targets, the cache improves the big-O efficiency of scanning of sequences +# with repeated patterns +# Each cache entry tells us which index we should start the search at. +# We assume this is unlikely, so we only lazy-initialize as much of the cache as we need to use +# When we allocate the cache, we initialize it to 0 (and offset by the first index afterwards). +# Suppose target is: +# Index: 1245689 +# Value: "aδcaδcx" +# We would set the cache to +# 0 0 0 1 2 3 4 0 +# So after if we mismatch after the second aδc sequence, +# we can immediately jump back to index 5 (4 + 1). +function readuntil_vector!(io::IO, target::AbstractVector{T}, keep::Bool, out) where {T} + first = firstindex(target) + last = lastindex(target) + len = last - first + 1 + if len < 1 + return true + end + pos = 0 # array-offset + max_pos = 1 # array-offset in cache + local cache # will be lazy initialized when needed + output! = (isa(out, IO) ? write : push!) + while !eof(io) + c = read(io, T) + # Backtrack until the next target character matches what was found + while true + c1 = target[pos + first] + if c == c1 + pos += 1 + break + elseif pos == 0 + break + elseif pos == 1 + if !keep + output!(out, target[first]) + end + pos = 0 + else + # grow cache to contain up to `pos` + if !@isdefined(cache) + cache = zeros(Int, len) + end + while max_pos < pos + ci = target[max_pos + first] + b = max_pos + max_pos += 1 + while b != 0 + b = cache[b] + cb = target[b + first] + if ci == cb + cache[max_pos] = b + 1 + break + end + end + end + # read new position from cache + pos1 = cache[pos] + if !keep + # and add the removed prefix from the target to the output + # if not always keeping the match + for b in 1:(pos - pos1) + output!(out, target[b - 1 + first]) + end + end + pos = pos1 + end + end + if keep || pos == 0 + output!(out, c) + end + pos == len && return true + end + if !keep + # failed early without finishing the match, + # add the partial match to the output + # if not always keeping the match + for b in 1:pos + output!(out, target[b - 1 + first]) + end + end + return false +end + +function readuntil(io::IO, target::AbstractString; keep::Bool=false) + # small-string target optimizations + isempty(target) && return "" + c, rest = Iterators.peel(target) + if isempty(rest) && c <= '\x7f' + return readuntil_string(io, c % UInt8, keep) + end + # convert String to a utf8-byte-iterator + if !(target isa String) && !(target isa SubString{String}) + target = String(target) + end + target = codeunits(target)::AbstractVector + return String(readuntil(io, target, keep=keep)) +end + +function readuntil(io::IO, target::AbstractVector{T}; keep::Bool=false) where T + out = (T === UInt8 ? StringVector(0) : Vector{T}()) + readuntil_vector!(io, target, keep, out) + return out +end + +""" + readchomp(x) + +Read the entirety of `x` as a string and remove a single trailing newline +if there is one. Equivalent to `chomp(read(x, String))`. + +# Examples +```jldoctest +julia> open("my_file.txt", "w") do io + write(io, "JuliaLang is a GitHub organization.\\nIt has many members.\\n"); + end; + +julia> readchomp("my_file.txt") +"JuliaLang is a GitHub organization.\\nIt has many members." + +julia> rm("my_file.txt"); +``` +""" +readchomp(x) = chomp(read(x, String)) + +# read up to nb bytes into nb, returning # bytes read + +""" + readbytes!(stream::IO, b::AbstractVector{UInt8}, nb=length(b)) + +Read at most `nb` bytes from `stream` into `b`, returning the number of bytes read. +The size of `b` will be increased if needed (i.e. if `nb` is greater than `length(b)` +and enough bytes could be read), but it will never be decreased. +""" +function readbytes!(s::IO, b::AbstractArray{UInt8}, nb=length(b)) + require_one_based_indexing(b) + olb = lb = length(b) + nr = 0 + while nr < nb && !eof(s) + a = read(s, UInt8) + nr += 1 + if nr > lb + lb = nr * 2 + resize!(b, lb) + end + b[nr] = a + end + if lb > olb + resize!(b, nr) # shrink to just contain input data if was resized + end + return nr +end + +""" + read(s::IO, nb=typemax(Int)) + +Read at most `nb` bytes from `s`, returning a `Vector{UInt8}` of the bytes read. +""" +function read(s::IO, nb::Integer = typemax(Int)) + # Let readbytes! grow the array progressively by default + # instead of taking of risk of over-allocating + b = Vector{UInt8}(undef, nb == typemax(Int) ? 1024 : nb) + nr = readbytes!(s, b, nb) + return resize!(b, nr) +end + +read(s::IO, ::Type{String}) = String(read(s)) +read(s::IO, T::Type) = error("The IO stream does not support reading objects of type $T.") + +## high-level iterator interfaces ## + +struct EachLine{IOT <: IO} + stream::IOT + ondone::Function + keep::Bool + EachLine(stream::IO=stdin; ondone::Function=()->nothing, keep::Bool=false) = + new{typeof(stream)}(stream, ondone, keep) +end + +""" + eachline(io::IO=stdin; keep::Bool=false) + eachline(filename::AbstractString; keep::Bool=false) + +Create an iterable `EachLine` object that will yield each line from an I/O stream +or a file. Iteration calls [`readline`](@ref) on the stream argument repeatedly with +`keep` passed through, determining whether trailing end-of-line characters are +retained. When called with a file name, the file is opened once at the beginning of +iteration and closed at the end. If iteration is interrupted, the file will be +closed when the `EachLine` object is garbage collected. + +# Examples +```jldoctest +julia> open("my_file.txt", "w") do io + write(io, "JuliaLang is a GitHub organization.\\n It has many members.\\n"); + end; + +julia> for line in eachline("my_file.txt") + print(line) + end +JuliaLang is a GitHub organization. It has many members. + +julia> rm("my_file.txt"); +``` +""" +function eachline(stream::IO=stdin; keep::Bool=false) + EachLine(stream, keep=keep)::EachLine +end + +function eachline(filename::AbstractString; keep::Bool=false) + s = open(filename) + EachLine(s, ondone=()->close(s), keep=keep)::EachLine +end + +function iterate(itr::EachLine, state=nothing) + eof(itr.stream) && return (itr.ondone(); nothing) + (readline(itr.stream, keep=itr.keep), nothing) +end + +eltype(::Type{<:EachLine}) = String + +IteratorSize(::Type{<:EachLine}) = SizeUnknown() + +# IOStream Marking +# Note that these functions expect that io.mark exists for +# the concrete IO type. This may not be true for IO types +# not in base. + +""" + mark(s) + +Add a mark at the current position of stream `s`. Return the marked position. + +See also [`unmark`](@ref), [`reset`](@ref), [`ismarked`](@ref). +""" +function mark(io::IO) + io.mark = position(io) +end + +""" + unmark(s) + +Remove a mark from stream `s`. Return `true` if the stream was marked, `false` otherwise. + +See also [`mark`](@ref), [`reset`](@ref), [`ismarked`](@ref). +""" +function unmark(io::IO) + !ismarked(io) && return false + io.mark = -1 + return true +end + +""" + reset(s) + +Reset a stream `s` to a previously marked position, and remove the mark. Return the +previously marked position. Throw an error if the stream is not marked. + +See also [`mark`](@ref), [`unmark`](@ref), [`ismarked`](@ref). +""" +function reset(io::T) where T<:IO + ismarked(io) || throw(ArgumentError("$T not marked")) + m = io.mark + seek(io, m) + io.mark = -1 # must be after seek, or seek may fail + return m +end + +""" + ismarked(s) + +Return `true` if stream `s` is marked. + +See also [`mark`](@ref), [`unmark`](@ref), [`reset`](@ref). +""" +ismarked(io::IO) = io.mark >= 0 + +# Make sure all IO streams support flush, even if only as a no-op, +# to make it easier to write generic I/O code. + +""" + flush(stream) + +Commit all currently buffered writes to the given stream. +""" +flush(io::IO) = nothing + +""" + skipchars(predicate, io::IO; linecomment=nothing) + +Advance the stream `io` such that the next-read character will be the first remaining for +which `predicate` returns `false`. If the keyword argument `linecomment` is specified, all +characters from that character until the start of the next line are ignored. + +# Examples +```jldoctest +julia> buf = IOBuffer(" text") +IOBuffer(data=UInt8[...], readable=true, writable=false, seekable=true, append=false, size=8, maxsize=Inf, ptr=1, mark=-1) + +julia> skipchars(isspace, buf) +IOBuffer(data=UInt8[...], readable=true, writable=false, seekable=true, append=false, size=8, maxsize=Inf, ptr=5, mark=-1) + +julia> String(readavailable(buf)) +"text" +``` +""" +function skipchars(predicate, io::IO; linecomment=nothing) + while !eof(io) + c = read(io, Char) + if c === linecomment + readline(io) + elseif !predicate(c) + skip(io, -ncodeunits(c)) + break + end + end + return io +end + +""" + countlines(io::IO; eol::AbstractChar = '\\n') + +Read `io` until the end of the stream/file and count the number of lines. To specify a file +pass the filename as the first argument. EOL markers other than `'\\n'` are supported by +passing them as the second argument. The last non-empty line of `io` is counted even if it does not +end with the EOL, matching the length returned by [`eachline`](@ref) and [`readlines`](@ref). + +# Examples +```jldoctest +julia> io = IOBuffer("JuliaLang is a GitHub organization.\\n"); + +julia> countlines(io) +1 + +julia> io = IOBuffer("JuliaLang is a GitHub organization."); + +julia> countlines(io) +1 + +julia> countlines(io, eol = '.') +0 +``` +""" +function countlines(io::IO; eol::AbstractChar='\n') + isascii(eol) || throw(ArgumentError("only ASCII line terminators are supported")) + aeol = UInt8(eol) + a = Vector{UInt8}(undef, 8192) + nl = nb = 0 + while !eof(io) + nb = readbytes!(io, a) + @simd for i=1:nb + @inbounds nl += a[i] == aeol + end + end + if nb > 0 && a[nb] != aeol + nl += 1 # final line is not terminated with eol + end + nl +end + +countlines(f::AbstractString; eol::AbstractChar = '\n') = open(io->countlines(io, eol = eol), f)::Int diff --git a/base/iobuffer.jl b/base/iobuffer.jl new file mode 100644 index 0000000..bb2d9af --- /dev/null +++ b/base/iobuffer.jl @@ -0,0 +1,521 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## work with AbstractVector{UInt8} via I/O primitives ## + +# Stateful string +mutable struct GenericIOBuffer{T<:AbstractVector{UInt8}} <: IO + data::T # T should support: getindex, setindex!, length, copyto!, and resize! + readable::Bool + writable::Bool + seekable::Bool # if not seekable, implementation is free to destroy (compact) past read data + append::Bool # add data at end instead of at pointer + size::Int # end pointer (and write pointer if append == true) + maxsize::Int # fixed array size (typically pre-allocated) + ptr::Int # read (and maybe write) pointer + mark::Int # reset mark location for ptr (or <0 for no mark) + + function GenericIOBuffer{T}(data::T, readable::Bool, writable::Bool, seekable::Bool, append::Bool, + maxsize::Integer) where T<:AbstractVector{UInt8} + require_one_based_indexing(data) + new(data,readable,writable,seekable,append,length(data),maxsize,1,-1) + end +end +const IOBuffer = GenericIOBuffer{Vector{UInt8}} + +function GenericIOBuffer(data::T, readable::Bool, writable::Bool, seekable::Bool, append::Bool, + maxsize::Integer) where T<:AbstractVector{UInt8} + GenericIOBuffer{T}(data, readable, writable, seekable, append, maxsize) +end + +# allocate Vector{UInt8}s for IOBuffer storage that can efficiently become Strings +StringVector(n::Integer) = unsafe_wrap(Vector{UInt8}, _string_n(n)) + +# IOBuffers behave like Files. They are typically readable and writable. They are seekable. (They can be appendable). + +""" + IOBuffer([data::AbstractVector{UInt8}]; keywords...) -> IOBuffer + +Create an in-memory I/O stream, which may optionally operate on a pre-existing array. + +It may take optional keyword arguments: +- `read`, `write`, `append`: restricts operations to the buffer; see `open` for details. +- `truncate`: truncates the buffer size to zero length. +- `maxsize`: specifies a size beyond which the buffer may not be grown. +- `sizehint`: suggests a capacity of the buffer (`data` must implement `sizehint!(data, size)`). + +When `data` is not given, the buffer will be both readable and writable by default. + +# Examples +```jldoctest +julia> io = IOBuffer(); + +julia> write(io, "JuliaLang is a GitHub organization.", " It has many members.") +56 + +julia> String(take!(io)) +"JuliaLang is a GitHub organization. It has many members." + +julia> io = IOBuffer(b"JuliaLang is a GitHub organization.") +IOBuffer(data=UInt8[...], readable=true, writable=false, seekable=true, append=false, size=35, maxsize=Inf, ptr=1, mark=-1) + +julia> read(io, String) +"JuliaLang is a GitHub organization." + +julia> write(io, "This isn't writable.") +ERROR: ArgumentError: ensureroom failed, IOBuffer is not writeable + +julia> io = IOBuffer(UInt8[], read=true, write=true, maxsize=34) +IOBuffer(data=UInt8[...], readable=true, writable=true, seekable=true, append=false, size=0, maxsize=34, ptr=1, mark=-1) + +julia> write(io, "JuliaLang is a GitHub organization.") +34 + +julia> String(take!(io)) +"JuliaLang is a GitHub organization" + +julia> length(read(IOBuffer(b"data", read=true, truncate=false))) +4 + +julia> length(read(IOBuffer(b"data", read=true, truncate=true))) +0 +``` +""" +function IOBuffer( + data::AbstractVector{UInt8}; + read::Union{Bool,Nothing}=nothing, + write::Union{Bool,Nothing}=nothing, + append::Union{Bool,Nothing}=nothing, + truncate::Union{Bool,Nothing}=nothing, + maxsize::Integer=typemax(Int), + sizehint::Union{Integer,Nothing}=nothing) + if maxsize < 0 + throw(ArgumentError("negative maxsize")) + end + if sizehint !== nothing + sizehint!(data, sizehint) + end + flags = open_flags(read=read, write=write, append=append, truncate=truncate) + buf = GenericIOBuffer(data, flags.read, flags.write, true, flags.append, Int(maxsize)) + if flags.truncate + buf.size = 0 + end + return buf +end + +function IOBuffer(; + read::Union{Bool,Nothing}=true, + write::Union{Bool,Nothing}=true, + append::Union{Bool,Nothing}=nothing, + truncate::Union{Bool,Nothing}=true, + maxsize::Integer=typemax(Int), + sizehint::Union{Integer,Nothing}=nothing) + size = sizehint !== nothing ? Int(sizehint) : maxsize != typemax(Int) ? Int(maxsize) : 32 + flags = open_flags(read=read, write=write, append=append, truncate=truncate) + buf = IOBuffer( + StringVector(size), + read=flags.read, + write=flags.write, + append=flags.append, + truncate=flags.truncate, + maxsize=maxsize) + fill!(buf.data, 0) + return buf +end + +# PipeBuffers behave like Unix Pipes. They are typically readable and writable, they act appendable, and are not seekable. + +""" + PipeBuffer(data::Vector{UInt8}=UInt8[]; maxsize::Integer = typemax(Int)) + +An [`IOBuffer`](@ref) that allows reading and performs writes by appending. +Seeking and truncating are not supported. +See [`IOBuffer`](@ref) for the available constructors. +If `data` is given, creates a `PipeBuffer` to operate on a data vector, +optionally specifying a size beyond which the underlying `Array` may not be grown. +""" +PipeBuffer(data::Vector{UInt8}=UInt8[]; maxsize::Int = typemax(Int)) = + GenericIOBuffer(data,true,true,false,true,maxsize) +PipeBuffer(maxsize::Integer) = (x = PipeBuffer(StringVector(maxsize), maxsize = maxsize); x.size=0; x) + +function copy(b::GenericIOBuffer) + ret = typeof(b)(b.writable ? copy(b.data) : b.data, + b.readable, b.writable, b.seekable, b.append, b.maxsize) + ret.size = b.size + ret.ptr = b.ptr + return ret +end + +show(io::IO, b::GenericIOBuffer) = print(io, "IOBuffer(data=UInt8[...], ", + "readable=", b.readable, ", ", + "writable=", b.writable, ", ", + "seekable=", b.seekable, ", ", + "append=", b.append, ", ", + "size=", b.size, ", ", + "maxsize=", b.maxsize == typemax(Int) ? "Inf" : b.maxsize, ", ", + "ptr=", b.ptr, ", ", + "mark=", b.mark, ")") + +@noinline function _throw_not_readable() + # See https://github.com/JuliaLang/julia/issues/29688. + throw(ArgumentError("read failed, IOBuffer is not readable")) +end + +function unsafe_read(from::GenericIOBuffer, p::Ptr{UInt8}, nb::UInt) + from.readable || _throw_not_readable() + avail = bytesavailable(from) + adv = min(avail, nb) + GC.@preserve from unsafe_copyto!(p, pointer(from.data, from.ptr), adv) + from.ptr += adv + if nb > avail + throw(EOFError()) + end + nothing +end + +function read(from::GenericIOBuffer, T::Union{Type{Int16},Type{UInt16},Type{Int32},Type{UInt32},Type{Int64},Type{UInt64},Type{Int128},Type{UInt128},Type{Float16},Type{Float32},Type{Float64}}) + from.readable || _throw_not_readable() + avail = bytesavailable(from) + nb = sizeof(T) + if nb > avail + throw(EOFError()) + end + GC.@preserve from begin + ptr::Ptr{T} = pointer(from.data, from.ptr) + x = unsafe_load(ptr) + end + from.ptr += nb + return x +end + +function read_sub(from::GenericIOBuffer, a::AbstractArray{T}, offs, nel) where T + require_one_based_indexing(a) + from.readable || _throw_not_readable() + if offs+nel-1 > length(a) || offs < 1 || nel < 0 + throw(BoundsError()) + end + if isbitstype(T) && isa(a,Array) + nb = UInt(nel * sizeof(T)) + GC.@preserve a unsafe_read(from, pointer(a, offs), nb) + else + for i = offs:offs+nel-1 + a[i] = read(to, T) + end + end + return a +end + +@inline function read(from::GenericIOBuffer, ::Type{UInt8}) + from.readable || _throw_not_readable() + ptr = from.ptr + size = from.size + if ptr > size + throw(EOFError()) + end + @inbounds byte = from.data[ptr] + from.ptr = ptr + 1 + return byte +end + +function peek(from::GenericIOBuffer) + from.readable || _throw_not_readable() + if from.ptr > from.size + throw(EOFError()) + end + return from.data[from.ptr] +end + +read(from::GenericIOBuffer, ::Type{Ptr{T}}) where {T} = convert(Ptr{T}, read(from, UInt)) + +isreadable(io::GenericIOBuffer) = io.readable +iswritable(io::GenericIOBuffer) = io.writable + +# TODO: GenericIOBuffer is not iterable, so doesn't really have a length. +# This should maybe be sizeof() instead. +#length(io::GenericIOBuffer) = (io.seekable ? io.size : bytesavailable(io)) +bytesavailable(io::GenericIOBuffer) = io.size - io.ptr + 1 +position(io::GenericIOBuffer) = io.ptr-1 + +function skip(io::GenericIOBuffer, n::Integer) + seekto = io.ptr + n + n < 0 && return seek(io, seekto-1) # Does error checking + io.ptr = min(seekto, io.size+1) + return io +end + +function seek(io::GenericIOBuffer, n::Integer) + if !io.seekable + ismarked(io) || throw(ArgumentError("seek failed, IOBuffer is not seekable and is not marked")) + n == io.mark || throw(ArgumentError("seek failed, IOBuffer is not seekable and n != mark")) + end + # TODO: REPL.jl relies on the fact that this does not throw (by seeking past the beginning or end + # of an GenericIOBuffer), so that would need to be fixed in order to throw an error here + #(n < 0 || n > io.size) && throw(ArgumentError("Attempted to seek outside IOBuffer boundaries.")) + #io.ptr = n+1 + io.ptr = max(min(n+1, io.size+1), 1) + return io +end + +function seekend(io::GenericIOBuffer) + io.ptr = io.size+1 + return io +end + +function truncate(io::GenericIOBuffer, n::Integer) + io.writable || throw(ArgumentError("truncate failed, IOBuffer is not writeable")) + io.seekable || throw(ArgumentError("truncate failed, IOBuffer is not seekable")) + n < 0 && throw(ArgumentError("truncate failed, n bytes must be ≥ 0, got $n")) + n > io.maxsize && throw(ArgumentError("truncate failed, $(n) bytes is exceeds IOBuffer maxsize $(io.maxsize)")) + if n > length(io.data) + resize!(io.data, n) + end + io.data[io.size+1:n] .= 0 + io.size = n + io.ptr = min(io.ptr, n+1) + ismarked(io) && io.mark > n && unmark(io) + return io +end + +function compact(io::GenericIOBuffer) + io.writable || throw(ArgumentError("compact failed, IOBuffer is not writeable")) + io.seekable && throw(ArgumentError("compact failed, IOBuffer is seekable")) + local ptr::Int, bytes_to_move::Int + if ismarked(io) && io.mark < io.ptr + if io.mark == 0 return end + ptr = io.mark + bytes_to_move = bytesavailable(io) + (io.ptr-io.mark) + else + ptr = io.ptr + bytes_to_move = bytesavailable(io) + end + copyto!(io.data, 1, io.data, ptr, bytes_to_move) + io.size -= ptr - 1 + io.ptr -= ptr - 1 + io.mark -= ptr - 1 + return io +end + +@noinline function ensureroom_slowpath(io::GenericIOBuffer, nshort::UInt) + io.writable || throw(ArgumentError("ensureroom failed, IOBuffer is not writeable")) + if !io.seekable + if !ismarked(io) && io.ptr > 1 && io.size <= io.ptr - 1 + io.ptr = 1 + io.size = 0 + else + datastart = ismarked(io) ? io.mark : io.ptr + if (io.size+nshort > io.maxsize) || + (datastart > 4096 && datastart > io.size - io.ptr) || + (datastart > 262144) + # apply somewhat arbitrary heuristics to decide when to destroy + # old, read data to make more room for new data + compact(io) + end + end + end + return +end + +@inline ensureroom(io::GenericIOBuffer, nshort::Int) = ensureroom(io, UInt(nshort)) +@inline function ensureroom(io::GenericIOBuffer, nshort::UInt) + if !io.writable || (!io.seekable && io.ptr > 1) + ensureroom_slowpath(io, nshort) + end + n = min((nshort % Int) + (io.append ? io.size : io.ptr-1), io.maxsize) + l = length(io.data) + if n > l + _growend!(io.data, (n - l) % UInt) + end + return io +end + +eof(io::GenericIOBuffer) = (io.ptr-1 == io.size) + +@noinline function close(io::GenericIOBuffer{T}) where T + io.readable = false + io.writable = false + io.seekable = false + io.size = 0 + io.maxsize = 0 + io.ptr = 1 + io.mark = -1 + if io.writable + resize!(io.data, 0) + end + nothing +end + +isopen(io::GenericIOBuffer) = io.readable || io.writable || io.seekable || bytesavailable(io) > 0 + +""" + take!(b::IOBuffer) + +Obtain the contents of an `IOBuffer` as an array, without copying. Afterwards, the +`IOBuffer` is reset to its initial state. + +# Examples +```jldoctest +julia> io = IOBuffer(); + +julia> write(io, "JuliaLang is a GitHub organization.", " It has many members.") +56 + +julia> String(take!(io)) +"JuliaLang is a GitHub organization. It has many members." +``` +""" +function take!(io::GenericIOBuffer) + ismarked(io) && unmark(io) + if io.seekable + nbytes = io.size + data = copyto!(StringVector(nbytes), 1, io.data, 1, nbytes) + else + nbytes = bytesavailable(io) + data = read!(io,StringVector(nbytes)) + end + if io.writable + io.ptr = 1 + io.size = 0 + end + return data +end +function take!(io::IOBuffer) + ismarked(io) && unmark(io) + if io.seekable + data = io.data + if io.writable + maxsize = (io.maxsize == typemax(Int) ? 0 : min(length(io.data),io.maxsize)) + io.data = StringVector(maxsize) + else + data = copy(data) + end + resize!(data,io.size) + else + nbytes = bytesavailable(io) + a = StringVector(nbytes) + data = read!(io, a) + end + if io.writable + io.ptr = 1 + io.size = 0 + end + return data +end + +function write(to::GenericIOBuffer, from::GenericIOBuffer) + if to === from + from.ptr = from.size + 1 + return 0 + end + written::Int = write_sub(to, from.data, from.ptr, bytesavailable(from)) + from.ptr += written + return written +end + +function unsafe_write(to::GenericIOBuffer, p::Ptr{UInt8}, nb::UInt) + ensureroom(to, nb) + ptr = (to.append ? to.size+1 : to.ptr) + written = Int(min(nb, length(to.data) - ptr + 1)) + towrite = written + d = to.data + while towrite > 0 + @inbounds d[ptr] = unsafe_load(p) + ptr += 1 + p += 1 + towrite -= 1 + end + to.size = max(to.size, ptr - 1) + if !to.append + to.ptr += written + end + return written +end + +function write_sub(to::GenericIOBuffer, a::AbstractArray{UInt8}, offs, nel) + require_one_based_indexing(a) + if offs+nel-1 > length(a) || offs < 1 || nel < 0 + throw(BoundsError()) + end + GC.@preserve a unsafe_write(to, pointer(a, offs), UInt(nel)) +end + +@inline function write(to::GenericIOBuffer, a::UInt8) + ensureroom(to, UInt(1)) + ptr = (to.append ? to.size+1 : to.ptr) + if ptr > to.maxsize + return 0 + else + to.data[ptr] = a + end + to.size = max(to.size, ptr) + if !to.append + to.ptr += 1 + end + return sizeof(UInt8) +end + +readbytes!(io::GenericIOBuffer, b::Array{UInt8}, nb=length(b)) = readbytes!(io, b, Int(nb)) +function readbytes!(io::GenericIOBuffer, b::Array{UInt8}, nb::Int) + nr = min(nb, bytesavailable(io)) + if length(b) < nr + resize!(b, nr) + end + read_sub(io, b, 1, nr) + return nr +end +read(io::GenericIOBuffer) = read!(io,StringVector(bytesavailable(io))) +readavailable(io::GenericIOBuffer) = read(io) +read(io::GenericIOBuffer, nb::Integer) = read!(io,StringVector(min(nb, bytesavailable(io)))) + +function occursin(delim::UInt8, buf::IOBuffer) + p = pointer(buf.data, buf.ptr) + q = GC.@preserve buf ccall(:memchr,Ptr{UInt8},(Ptr{UInt8},Int32,Csize_t),p,delim,bytesavailable(buf)) + return q != C_NULL +end + +function occursin(delim::UInt8, buf::GenericIOBuffer) + data = buf.data + for i = buf.ptr:buf.size + @inbounds b = data[i] + b == delim && return true + end + return false +end + +function readuntil(io::GenericIOBuffer, delim::UInt8; keep::Bool=false) + lb = 70 + A = StringVector(lb) + nread = 0 + nout = 0 + data = io.data + for i = io.ptr : io.size + @inbounds b = data[i] + nread += 1 + if keep || b != delim + nout += 1 + if nout > lb + lb = nout*2 + resize!(A, lb) + end + @inbounds A[nout] = b + end + if b == delim + break + end + end + io.ptr += nread + if lb != nout + resize!(A, nout) + end + A +end + +# copy-free crc32c of IOBuffer: +function _crc32c(io::IOBuffer, nb::Integer, crc::UInt32=0x00000000) + nb < 0 && throw(ArgumentError("number of bytes to checksum must be ≥ 0, got $nb")) + io.readable || _throw_not_readable() + n = min(nb, bytesavailable(io)) + n == 0 && return crc + crc = GC.@preserve io unsafe_crc32c(pointer(io.data, io.ptr), n, crc) + io.ptr += n + return crc +end +_crc32c(io::IOBuffer, crc::UInt32=0x00000000) = _crc32c(io, bytesavailable(io), crc) diff --git a/base/iostream.jl b/base/iostream.jl new file mode 100644 index 0000000..70b08d8 --- /dev/null +++ b/base/iostream.jl @@ -0,0 +1,494 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## IOStream + +const sizeof_ios_t = Int(ccall(:jl_sizeof_ios_t, Cint, ())) + +""" + IOStream + +A buffered IO stream wrapping an OS file descriptor. +Mostly used to represent files returned by [`open`](@ref). +""" +mutable struct IOStream <: IO + handle::Ptr{Cvoid} + ios::Array{UInt8,1} + name::AbstractString + mark::Int64 + lock::ReentrantLock + + IOStream(name::AbstractString, buf::Array{UInt8,1}) = new(pointer(buf), buf, name, -1, ReentrantLock()) +end + +function IOStream(name::AbstractString, finalize::Bool) + buf = zeros(UInt8,sizeof_ios_t) + x = IOStream(name, buf) + if finalize + finalizer(close, x) + end + return x +end +IOStream(name::AbstractString) = IOStream(name, true) + +unsafe_convert(T::Type{Ptr{Cvoid}}, s::IOStream) = convert(T, pointer(s.ios)) +show(io::IO, s::IOStream) = print(io, "IOStream(", s.name, ")") + +""" + fd(stream) + +Return the file descriptor backing the stream or file. Note that this function only applies +to synchronous `File`'s and `IOStream`'s not to any of the asynchronous streams. +""" +fd(s::IOStream) = Int(ccall(:jl_ios_fd, Clong, (Ptr{Cvoid},), s.ios)) + +stat(s::IOStream) = stat(fd(s)) +close(s::IOStream) = @lock_nofail s.lock ccall(:ios_close, Cvoid, (Ptr{Cvoid},), s.ios) +isopen(s::IOStream) = ccall(:ios_isopen, Cint, (Ptr{Cvoid},), s.ios) != 0 +function flush(s::IOStream) + sigatomic_begin() + bad = @lock_nofail s.lock ccall(:ios_flush, Cint, (Ptr{Cvoid},), s.ios) != 0 + sigatomic_end() + systemerror("flush", bad) +end +iswritable(s::IOStream) = ccall(:ios_get_writable, Cint, (Ptr{Cvoid},), s.ios)!=0 +isreadable(s::IOStream) = ccall(:ios_get_readable, Cint, (Ptr{Cvoid},), s.ios)!=0 + +""" + truncate(file, n) + +Resize the file or buffer given by the first argument to exactly `n` bytes, filling +previously unallocated space with '\\0' if the file or buffer is grown. + +# Examples +```jldoctest +julia> io = IOBuffer(); + +julia> write(io, "JuliaLang is a GitHub organization.") +35 + +julia> truncate(io, 15) +IOBuffer(data=UInt8[...], readable=true, writable=true, seekable=true, append=false, size=15, maxsize=Inf, ptr=16, mark=-1) + +julia> String(take!(io)) +"JuliaLang is a " + +julia> io = IOBuffer(); + +julia> write(io, "JuliaLang is a GitHub organization."); + +julia> truncate(io, 40); + +julia> String(take!(io)) +"JuliaLang is a GitHub organization.\\0\\0\\0\\0\\0" +``` +""" +function truncate(s::IOStream, n::Integer) + err = @lock_nofail s.lock ccall(:ios_trunc, Cint, (Ptr{Cvoid}, Csize_t), s.ios, n) != 0 + systemerror("truncate", err) + return s +end + +""" + seek(s, pos) + +Seek a stream to the given position. + +# Examples +```jldoctest +julia> io = IOBuffer("JuliaLang is a GitHub organization."); + +julia> seek(io, 5); + +julia> read(io, Char) +'L': ASCII/Unicode U+004c (category Lu: Letter, uppercase) +``` +""" +function seek(s::IOStream, n::Integer) + ret = @lock_nofail s.lock ccall(:ios_seek, Int64, (Ptr{Cvoid}, Int64), s.ios, n) + systemerror("seek", ret == -1) + ret < -1 && error("seek failed") + return s +end + +""" + seekstart(s) + +Seek a stream to its beginning. + +# Examples +```jldoctest +julia> io = IOBuffer("JuliaLang is a GitHub organization."); + +julia> seek(io, 5); + +julia> read(io, Char) +'L': ASCII/Unicode U+004c (category Lu: Letter, uppercase) + +julia> seekstart(io); + +julia> read(io, Char) +'J': ASCII/Unicode U+004a (category Lu: Letter, uppercase) +``` +""" +seekstart(s::IO) = seek(s,0) + +""" + seekend(s) + +Seek a stream to its end. +""" +function seekend(s::IOStream) + err = @lock_nofail s.lock ccall(:ios_seek_end, Int64, (Ptr{Cvoid},), s.ios) != 0 + systemerror("seekend", err) + return s +end + +""" + skip(s, offset) + +Seek a stream relative to the current position. + +# Examples +```jldoctest +julia> io = IOBuffer("JuliaLang is a GitHub organization."); + +julia> seek(io, 5); + +julia> skip(io, 10); + +julia> read(io, Char) +'G': ASCII/Unicode U+0047 (category Lu: Letter, uppercase) +``` +""" +function skip(s::IOStream, delta::Integer) + ret = @lock_nofail s.lock ccall(:ios_skip, Int64, (Ptr{Cvoid}, Int64), s.ios, delta) + systemerror("skip", ret == -1) + ret < -1 && error("skip failed") + return s +end + +""" + position(s) + +Get the current position of a stream. + +# Examples +```jldoctest +julia> io = IOBuffer("JuliaLang is a GitHub organization."); + +julia> seek(io, 5); + +julia> position(io) +5 + +julia> skip(io, 10); + +julia> position(io) +15 + +julia> seekend(io); + +julia> position(io) +35 +``` +""" +function position(s::IOStream) + pos = @lock_nofail s.lock ccall(:ios_pos, Int64, (Ptr{Cvoid},), s.ios) + systemerror("position", pos == -1) + return pos +end + +_eof_nolock(s::IOStream) = ccall(:ios_eof_blocking, Cint, (Ptr{Cvoid},), s.ios) != 0 +eof(s::IOStream) = @lock_nofail s.lock _eof_nolock(s) + +## constructing and opening streams ## + +# "own" means the descriptor will be closed with the IOStream + +""" + fdio([name::AbstractString, ]fd::Integer[, own::Bool=false]) -> IOStream + +Create an [`IOStream`](@ref) object from an integer file descriptor. If `own` is `true`, closing +this object will close the underlying descriptor. By default, an `IOStream` is closed when +it is garbage collected. `name` allows you to associate the descriptor with a named file. +""" +function fdio(name::AbstractString, fd::Integer, own::Bool=false) + s = IOStream(name) + ccall(:ios_fd, Ptr{Cvoid}, (Ptr{Cvoid}, Clong, Cint, Cint), + s.ios, fd, 0, own) + return s +end +fdio(fd::Integer, own::Bool=false) = fdio(string(""), fd, own) + +""" + open(filename::AbstractString; keywords...) -> IOStream + +Open a file in a mode specified by five boolean keyword arguments: + +| Keyword | Description | Default | +|:-----------|:-----------------------|:----------------------------------------| +| `read` | open for reading | `!write` | +| `write` | open for writing | `truncate \\| append` | +| `create` | create if non-existent | `!read & write \\| truncate \\| append` | +| `truncate` | truncate to zero size | `!read & write` | +| `append` | seek to end | `false` | + +The default when no keywords are passed is to open files for reading only. +Returns a stream for accessing the opened file. +""" +function open(fname::AbstractString; + read :: Union{Bool,Nothing} = nothing, + write :: Union{Bool,Nothing} = nothing, + create :: Union{Bool,Nothing} = nothing, + truncate :: Union{Bool,Nothing} = nothing, + append :: Union{Bool,Nothing} = nothing, +) + flags = open_flags( + read = read, + write = write, + create = create, + truncate = truncate, + append = append, + ) + s = IOStream(string("")) + systemerror("opening file $(repr(fname))", + ccall(:ios_file, Ptr{Cvoid}, + (Ptr{UInt8}, Cstring, Cint, Cint, Cint, Cint), + s.ios, fname, flags.read, flags.write, flags.create, flags.truncate) == C_NULL) + if flags.append + systemerror("seeking to end of file $fname", ccall(:ios_seek_end, Int64, (Ptr{Cvoid},), s.ios) != 0) + end + return s +end + +""" + open(filename::AbstractString, [mode::AbstractString]) -> IOStream + +Alternate syntax for open, where a string-based mode specifier is used instead of the five +booleans. The values of `mode` correspond to those from `fopen(3)` or Perl `open`, and are +equivalent to setting the following boolean groups: + +| Mode | Description | Keywords | +|:-----|:------------------------------|:------------------------------------| +| `r` | read | none | +| `w` | write, create, truncate | `write = true` | +| `a` | write, create, append | `append = true` | +| `r+` | read, write | `read = true, write = true` | +| `w+` | read, write, create, truncate | `truncate = true, read = true` | +| `a+` | read, write, create, append | `append = true, read = true` | + +# Examples +```jldoctest +julia> io = open("myfile.txt", "w"); + +julia> write(io, "Hello world!"); + +julia> close(io); + +julia> io = open("myfile.txt", "r"); + +julia> read(io, String) +"Hello world!" + +julia> write(io, "This file is read only") +ERROR: ArgumentError: write failed, IOStream is not writeable +[...] + +julia> close(io) + +julia> io = open("myfile.txt", "a"); + +julia> write(io, "This stream is not read only") +28 + +julia> close(io) + +julia> rm("myfile.txt") +``` +""" +function open(fname::AbstractString, mode::AbstractString) + mode == "r" ? open(fname, read = true) : + mode == "r+" ? open(fname, read = true, write = true) : + mode == "w" ? open(fname, truncate = true) : + mode == "w+" ? open(fname, truncate = true, read = true) : + mode == "a" ? open(fname, append = true) : + mode == "a+" ? open(fname, append = true, read = true) : + throw(ArgumentError("invalid open mode: $mode")) +end + +## low-level calls ## + +function write(s::IOStream, b::UInt8) + iswritable(s) || throw(ArgumentError("write failed, IOStream is not writeable")) + Int(@lock_nofail s.lock ccall(:ios_putc, Cint, (Cint, Ptr{Cvoid}), b, s.ios)) +end + +function unsafe_write(s::IOStream, p::Ptr{UInt8}, nb::UInt) + iswritable(s) || throw(ArgumentError("write failed, IOStream is not writeable")) + return Int(@lock_nofail s.lock ccall(:ios_write, Csize_t, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), s.ios, p, nb)) +end + +# num bytes available without blocking +bytesavailable(s::IOStream) = @lock_nofail s.lock ccall(:jl_nb_available, Int32, (Ptr{Cvoid},), s.ios) + +function readavailable(s::IOStream) + lock(s.lock) + nb = ccall(:jl_nb_available, Int32, (Ptr{Cvoid},), s.ios) + a = Vector{UInt8}(undef, nb) + nr = ccall(:ios_readall, Csize_t, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), s, a, nb) + if nr != nb + unlock(s.lock) + throw(EOFError()) + end + unlock(s.lock) + return a +end + +function read(s::IOStream, ::Type{UInt8}) + b = @lock_nofail s.lock ccall(:ios_getc, Cint, (Ptr{Cvoid},), s.ios) + if b == -1 + throw(EOFError()) + end + return b % UInt8 +end + +if ENDIAN_BOM == 0x04030201 +function read(s::IOStream, T::Union{Type{Int16},Type{UInt16},Type{Int32},Type{UInt32},Type{Int64},Type{UInt64}}) + n = sizeof(T) + lock(s.lock) + if ccall(:jl_ios_buffer_n, Cint, (Ptr{Cvoid}, Csize_t), s.ios, n) != 0 + unlock(s.lock) + throw(EOFError()) + end + x = ccall(:jl_ios_get_nbyte_int, UInt64, (Ptr{Cvoid}, Csize_t), s.ios, n) % T + unlock(s.lock) + return x +end + +read(s::IOStream, ::Type{Float16}) = reinterpret(Float16, read(s, Int16)) +read(s::IOStream, ::Type{Float32}) = reinterpret(Float32, read(s, Int32)) +read(s::IOStream, ::Type{Float64}) = reinterpret(Float64, read(s, Int64)) +end + +function unsafe_read(s::IOStream, p::Ptr{UInt8}, nb::UInt) + nr = @lock_nofail s.lock ccall(:ios_readall, Csize_t, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), s, p, nb) + if nr != nb + throw(EOFError()) + end + nothing +end + +## text I/O ## + +take!(s::IOStream) = + @lock_nofail s.lock ccall(:jl_take_buffer, Vector{UInt8}, (Ptr{Cvoid},), s.ios) + +function readuntil(s::IOStream, delim::UInt8; keep::Bool=false) + @lock_nofail s.lock ccall(:jl_readuntil, Array{UInt8,1}, (Ptr{Cvoid}, UInt8, UInt8, UInt8), s.ios, delim, 0, !keep) +end + +# like readuntil, above, but returns a String without requiring a copy +function readuntil_string(s::IOStream, delim::UInt8, keep::Bool) + @lock_nofail s.lock ccall(:jl_readuntil, Ref{String}, (Ptr{Cvoid}, UInt8, UInt8, UInt8), s.ios, delim, 1, !keep) +end + +function readline(s::IOStream; keep::Bool=false) + @lock_nofail s.lock ccall(:jl_readuntil, Ref{String}, (Ptr{Cvoid}, UInt8, UInt8, UInt8), s.ios, '\n', 1, keep ? 0 : 2) +end + +function readbytes_all!(s::IOStream, b::Array{UInt8}, nb) + olb = lb = length(b) + nr = 0 + @lock_nofail s.lock begin + GC.@preserve b while nr < nb + if lb < nr+1 + lb = max(65536, (nr+1) * 2) + resize!(b, lb) + end + nr += Int(ccall(:ios_readall, Csize_t, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), + s.ios, pointer(b, nr+1), min(lb-nr, nb-nr))) + _eof_nolock(s) && break + end + end + if lb > olb && lb > nr + resize!(b, max(olb, nr)) # shrink to just contain input data if was resized + end + return nr +end + +function readbytes_some!(s::IOStream, b::Array{UInt8}, nb) + olb = length(b) + if nb > olb + resize!(b, nb) + end + local nr + @lock_nofail s.lock begin + nr = GC.@preserve b Int(ccall(:ios_read, Csize_t, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), + s.ios, pointer(b), nb)) + end + lb = length(b) + if lb > olb && lb > nr + resize!(b, max(olb, nr)) # shrink to just contain input data if was resized + end + return nr +end + +""" + readbytes!(stream::IOStream, b::AbstractVector{UInt8}, nb=length(b); all::Bool=true) + +Read at most `nb` bytes from `stream` into `b`, returning the number of bytes read. +The size of `b` will be increased if needed (i.e. if `nb` is greater than `length(b)` +and enough bytes could be read), but it will never be decreased. + +If `all` is `true` (the default), this function will block repeatedly trying to read all +requested bytes, until an error or end-of-file occurs. If `all` is `false`, at most one +`read` call is performed, and the amount of data returned is device-dependent. Note that not +all stream types support the `all` option. +""" +function readbytes!(s::IOStream, b::Array{UInt8}, nb=length(b); all::Bool=true) + return all ? readbytes_all!(s, b, nb) : readbytes_some!(s, b, nb) +end + +function read(s::IOStream) + sz = try # filesize is just a hint, so ignore if `fstat` fails + filesize(s) + catch ex + ex isa IOError || rethrow() + Int64(0) + end + if sz > 0 + pos = position(s) + if pos > 0 + sz -= pos + end + end + b = StringVector(sz <= 0 ? 1024 : sz) + nr = readbytes_all!(s, b, typemax(Int)) + resize!(b, nr) + return b +end + +""" + read(s::IOStream, nb::Integer; all=true) + +Read at most `nb` bytes from `s`, returning a `Vector{UInt8}` of the bytes read. + +If `all` is `true` (the default), this function will block repeatedly trying to read all +requested bytes, until an error or end-of-file occurs. If `all` is `false`, at most one +`read` call is performed, and the amount of data returned is device-dependent. Note that not +all stream types support the `all` option. +""" +function read(s::IOStream, nb::Integer; all::Bool=true) + # When all=false we have to allocate a buffer of the requested size upfront + # since a single call will be made + b = Vector{UInt8}(undef, all && nb == typemax(Int) ? 1024 : nb) + nr = readbytes!(s, b, nb, all=all) + resize!(b, nr) + return b +end + +## peek ## + +function peek(s::IOStream) + @lock_nofail s.lock ccall(:ios_peekc, Cint, (Ptr{Cvoid},), s) +end diff --git a/base/irrationals.jl b/base/irrationals.jl new file mode 100644 index 0000000..b147959 --- /dev/null +++ b/base/irrationals.jl @@ -0,0 +1,189 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## general machinery for irrational mathematical constants + +""" + AbstractIrrational <: Real + +Number type representing an exact irrational value. +""" +abstract type AbstractIrrational <: Real end + +""" + Irrational{sym} <: AbstractIrrational + +Number type representing an exact irrational value denoted by the +symbol `sym`. +""" +struct Irrational{sym} <: AbstractIrrational end + +show(io::IO, x::Irrational{sym}) where {sym} = print(io, sym) + +function show(io::IO, ::MIME"text/plain", x::Irrational{sym}) where {sym} + print(io, sym, " = ", string(float(x))[1:15], "...") +end + +promote_rule(::Type{<:AbstractIrrational}, ::Type{Float16}) = Float16 +promote_rule(::Type{<:AbstractIrrational}, ::Type{Float32}) = Float32 +promote_rule(::Type{<:AbstractIrrational}, ::Type{<:AbstractIrrational}) = Float64 +promote_rule(::Type{<:AbstractIrrational}, ::Type{T}) where {T<:Real} = promote_type(Float64, T) +promote_rule(::Type{S}, ::Type{T}) where {S<:AbstractIrrational,T<:Number} = promote_type(promote_type(S, real(T)), T) + +AbstractFloat(x::AbstractIrrational) = Float64(x) +Float16(x::AbstractIrrational) = Float16(Float32(x)) +Complex{T}(x::AbstractIrrational) where {T<:Real} = Complex{T}(T(x)) + +@pure function Rational{T}(x::AbstractIrrational) where T<:Integer + o = precision(BigFloat) + p = 256 + while true + setprecision(BigFloat, p) + bx = BigFloat(x) + r = rationalize(T, bx, tol=0) + if abs(BigFloat(r) - bx) > eps(bx) + setprecision(BigFloat, o) + return r + end + p += 32 + end +end +(::Type{Rational{BigInt}})(x::AbstractIrrational) = throw(ArgumentError("Cannot convert an AbstractIrrational to a Rational{BigInt}: use rationalize(Rational{BigInt}, x) instead")) + +@pure function (t::Type{T})(x::AbstractIrrational, r::RoundingMode) where T<:Union{Float32,Float64} + setprecision(BigFloat, 256) do + T(BigFloat(x), r) + end +end + +float(::Type{<:AbstractIrrational}) = Float64 + +==(::Irrational{s}, ::Irrational{s}) where {s} = true +==(::AbstractIrrational, ::AbstractIrrational) = false + +<(::Irrational{s}, ::Irrational{s}) where {s} = false +function <(x::AbstractIrrational, y::AbstractIrrational) + Float64(x) != Float64(y) || throw(MethodError(<, (x, y))) + return Float64(x) < Float64(y) +end + +<=(::Irrational{s}, ::Irrational{s}) where {s} = true +<=(x::AbstractIrrational, y::AbstractIrrational) = x==y || x foreach(println, Iterators.reverse(1:5)) +5 +4 +3 +2 +1 +``` +""" +reverse(itr) = Reverse(itr) + +struct Reverse{T} + itr::T +end +eltype(::Type{Reverse{T}}) where {T} = eltype(T) +length(r::Reverse) = length(r.itr) +size(r::Reverse) = size(r.itr) +IteratorSize(::Type{Reverse{T}}) where {T} = IteratorSize(T) +IteratorEltype(::Type{Reverse{T}}) where {T} = IteratorEltype(T) +last(r::Reverse) = first(r.itr) # the first shall be last +first(r::Reverse) = last(r.itr) # and the last shall be first + +# reverse-order array iterators: assumes more-specialized Reverse for eachindex +@propagate_inbounds function iterate(A::Reverse{<:AbstractArray}, state=(reverse(eachindex(A.itr)),)) + y = iterate(state...) + y === nothing && return y + idx, itrs = y + (A.itr[idx], (state[1], itrs)) +end + +reverse(R::AbstractRange) = Base.reverse(R) # copying ranges is cheap +reverse(G::Generator) = Generator(G.f, reverse(G.iter)) +reverse(r::Reverse) = r.itr +reverse(x::Union{Number,AbstractChar}) = x +reverse(p::Pair) = Base.reverse(p) # copying pairs is cheap + +iterate(r::Reverse{<:Tuple}, i::Int = length(r.itr)) = i < 1 ? nothing : (r.itr[i], i-1) + +# enumerate + +struct Enumerate{I} + itr::I +end + +""" + enumerate(iter) + +An iterator that yields `(i, x)` where `i` is a counter starting at 1, +and `x` is the `i`th value from the given iterator. It's useful when +you need not only the values `x` over which you are iterating, but +also the number of iterations so far. Note that `i` may not be valid +for indexing `iter`; it's also possible that `x != iter[i]`, if `iter` +has indices that do not start at 1. See the `pairs(IndexLinear(), +iter)` method if you want to ensure that `i` is an index. + +# Examples +```jldoctest +julia> a = ["a", "b", "c"]; + +julia> for (index, value) in enumerate(a) + println("\$index \$value") + end +1 a +2 b +3 c +``` +""" +enumerate(iter) = Enumerate(iter) + +length(e::Enumerate) = length(e.itr) +size(e::Enumerate) = size(e.itr) +@propagate_inbounds function iterate(e::Enumerate, state=(1,)) + i, rest = state[1], tail(state) + n = iterate(e.itr, rest...) + n === nothing && return n + (i, n[1]), (i+1, n[2]) +end + +eltype(::Type{Enumerate{I}}) where {I} = Tuple{Int, eltype(I)} + +IteratorSize(::Type{Enumerate{I}}) where {I} = IteratorSize(I) +IteratorEltype(::Type{Enumerate{I}}) where {I} = IteratorEltype(I) + +@inline function iterate(r::Reverse{<:Enumerate}) + ri = reverse(r.itr.itr) + iterate(r, (length(ri), ri)) +end +@inline function iterate(r::Reverse{<:Enumerate}, state) + i, ri, rest = state[1], state[2], tail(tail(state)) + n = iterate(ri, rest...) + n === nothing && return n + (i, n[1]), (i-1, ri, n[2]) +end + +""" + Iterators.Pairs(values, keys) <: AbstractDict{eltype(keys), eltype(values)} + +Transforms an indexable container into an Dictionary-view of the same data. +Modifying the key-space of the underlying data may invalidate this object. +""" +struct Pairs{K, V, I, A} <: AbstractDict{K, V} + data::A + itr::I + Pairs(data::A, itr::I) where {A, I} = new{eltype(I), eltype(A), I, A}(data, itr) +end + +""" + pairs(IndexLinear(), A) + pairs(IndexCartesian(), A) + pairs(IndexStyle(A), A) + +An iterator that accesses each element of the array `A`, returning +`i => x`, where `i` is the index for the element and `x = A[i]`. +Identical to `pairs(A)`, except that the style of index can be selected. +Also similar to `enumerate(A)`, except `i` will be a valid index +for `A`, while `enumerate` always counts from 1 regardless of the indices +of `A`. + +Specifying [`IndexLinear()`](@ref) ensures that `i` will be an integer; +specifying [`IndexCartesian()`](@ref) ensures that `i` will be a +[`CartesianIndex`](@ref); specifying `IndexStyle(A)` chooses whichever has +been defined as the native indexing style for array `A`. + +Mutation of the bounds of the underlying array will invalidate this iterator. + +# Examples +```jldoctest +julia> A = ["a" "d"; "b" "e"; "c" "f"]; + +julia> for (index, value) in pairs(IndexStyle(A), A) + println("\$index \$value") + end +1 a +2 b +3 c +4 d +5 e +6 f + +julia> S = view(A, 1:2, :); + +julia> for (index, value) in pairs(IndexStyle(S), S) + println("\$index \$value") + end +CartesianIndex(1, 1) a +CartesianIndex(2, 1) b +CartesianIndex(1, 2) d +CartesianIndex(2, 2) e +``` + +See also: [`IndexStyle`](@ref), [`axes`](@ref). +""" +pairs(::IndexLinear, A::AbstractArray) = Pairs(A, LinearIndices(A)) +pairs(::IndexCartesian, A::AbstractArray) = Pairs(A, CartesianIndices(axes(A))) + +# preserve indexing capabilities for known indexable types +# faster than zip(keys(a), values(a)) for arrays +pairs(A::AbstractArray) = pairs(IndexCartesian(), A) +pairs(A::AbstractVector) = pairs(IndexLinear(), A) +pairs(tuple::Tuple) = Pairs(tuple, keys(tuple)) +pairs(nt::NamedTuple) = Pairs(nt, keys(nt)) +pairs(v::Core.SimpleVector) = Pairs(v, LinearIndices(v)) +# pairs(v::Pairs) = v # listed for reference, but already defined from being an AbstractDict + +length(v::Pairs) = length(v.itr) +axes(v::Pairs) = axes(v.itr) +size(v::Pairs) = size(v.itr) +@propagate_inbounds function iterate(v::Pairs{K, V}, state...) where {K, V} + x = iterate(v.itr, state...) + x === nothing && return x + indx, n = x + item = v.data[indx] + return (Pair{K, V}(indx, item), n) +end +@inline isdone(v::Pairs, state...) = isdone(v.itr, state...) + +IteratorSize(::Type{<:Pairs{<:Any, <:Any, I}}) where {I} = IteratorSize(I) +IteratorSize(::Type{<:Pairs{<:Any, <:Any, <:Base.AbstractUnitRange, <:Tuple}}) = HasLength() + +reverse(v::Pairs) = Pairs(v.data, reverse(v.itr)) + +haskey(v::Pairs, key) = (key in v.itr) +keys(v::Pairs) = v.itr +values(v::Pairs) = v.data +getindex(v::Pairs, key) = v.data[key] +setindex!(v::Pairs, value, key) = (v.data[key] = value; v) +get(v::Pairs, key, default) = get(v.data, key, default) +get(f::Base.Callable, v::Pairs, key) = get(f, v.data, key) + +# zip + +zip_iteratorsize(a, b) = and_iteratorsize(a,b) # as `and_iteratorsize` but inherit `Union{HasLength,IsInfinite}` of the shorter iterator +zip_iteratorsize(::HasLength, ::IsInfinite) = HasLength() +zip_iteratorsize(::HasShape, ::IsInfinite) = HasLength() +zip_iteratorsize(a::IsInfinite, b) = zip_iteratorsize(b,a) +zip_iteratorsize(a::IsInfinite, b::IsInfinite) = IsInfinite() + +struct Zip{Is<:Tuple} + is::Is +end + +""" + zip(iters...) + +Run multiple iterators at the same time, until any of them is exhausted. The value type of +the `zip` iterator is a tuple of values of its subiterators. + +Note: `zip` orders the calls to its subiterators in such a way that stateful iterators will +not advance when another iterator finishes in the current iteration. + +# Examples +```jldoctest +julia> a = 1:5 +1:5 + +julia> b = ["e","d","b","c","a"] +5-element Array{String,1}: + "e" + "d" + "b" + "c" + "a" + +julia> c = zip(a,b) +Base.Iterators.Zip{Tuple{UnitRange{Int64},Array{String,1}}}((1:5, ["e", "d", "b", "c", "a"])) + +julia> length(c) +5 + +julia> first(c) +(1, "e") +``` +""" +zip(a...) = Zip(a) +function length(z::Zip) + n = _zip_min_length(z.is) + n === nothing && throw(ArgumentError("iterator is of undefined length")) + return n +end +function _zip_min_length(is) + i = is[1] + n = _zip_min_length(tail(is)) + if IteratorSize(i) isa IsInfinite + return n + else + return n === nothing ? length(i) : min(n, length(i)) + end +end +_zip_min_length(is::Tuple{}) = nothing +size(z::Zip) = _promote_shape(map(size, z.is)...) +axes(z::Zip) = _promote_shape(map(axes, z.is)...) +_promote_shape(a, b...) = promote_shape(a, _promote_shape(b...)) +_promote_shape(a) = a +eltype(::Type{Zip{Is}}) where {Is<:Tuple} = _zip_eltype(Is) +_zip_eltype(::Type{Is}) where {Is<:Tuple} = + tuple_type_cons(eltype(tuple_type_head(Is)), _zip_eltype(tuple_type_tail(Is))) +_zip_eltype(::Type{Tuple{}}) = Tuple{} +@inline isdone(z::Zip) = _zip_any_isdone(z.is, map(_ -> (), z.is)) +@inline isdone(z::Zip, ss) = _zip_any_isdone(z.is, map(tuple, ss)) +@inline function _zip_any_isdone(is, ss) + d1 = isdone(is[1], ss[1]...) + d1 === true && return true + return d1 | _zip_any_isdone(tail(is), tail(ss)) +end +@inline _zip_any_isdone(::Tuple{}, ::Tuple{}) = false + +@propagate_inbounds iterate(z::Zip) = _zip_iterate_all(z.is, map(_ -> (), z.is)) +@propagate_inbounds iterate(z::Zip, ss) = _zip_iterate_all(z.is, map(tuple, ss)) + +# This first queries isdone from every iterator. If any gives true, it immediately returns +# nothing. It then iterates all those where isdone returned missing, afterwards all those +# it returned false, again terminating immediately if any iterator is exhausted. Finally, +# the results are interleaved appropriately. +@propagate_inbounds function _zip_iterate_all(is, ss) + d, ds = _zip_isdone(is, ss) + d && return nothing + xs1 = _zip_iterate_some(is, ss, ds, missing) + xs1 === nothing && return nothing + xs2 = _zip_iterate_some(is, ss, ds, false) + xs2 === nothing && return nothing + return _zip_iterate_interleave(xs1, xs2, ds) +end + +@propagate_inbounds function _zip_iterate_some(is, ss, ds::Tuple{T,Vararg{Any}}, f::T) where T + x = iterate(is[1], ss[1]...) + x === nothing && return nothing + y = _zip_iterate_some(tail(is), tail(ss), tail(ds), f) + y === nothing && return nothing + return (x, y...) +end +@propagate_inbounds _zip_iterate_some(is, ss, ds::Tuple{Any,Vararg{Any}}, f) = + _zip_iterate_some(tail(is), tail(ss), tail(ds), f) +_zip_iterate_some(::Tuple{}, ::Tuple{}, ::Tuple{}, ::Any) = () + +function _zip_iterate_interleave(xs1, xs2, ds) + t = _zip_iterate_interleave(tail(xs1), xs2, tail(ds)) + ((xs1[1][1], t[1]...), (xs1[1][2], t[2]...)) +end +function _zip_iterate_interleave(xs1, xs2, ds::Tuple{Bool,Vararg{Any}}) + t = _zip_iterate_interleave(xs1, tail(xs2), tail(ds)) + ((xs2[1][1], t[1]...), (xs2[1][2], t[2]...)) +end +_zip_iterate_interleave(::Tuple{}, ::Tuple{}, ::Tuple{}) = ((), ()) + +function _zip_isdone(is, ss) + d = isdone(is[1], ss[1]...) + d´, ds = _zip_isdone(tail(is), tail(ss)) + return (d === true || d´, (d, ds...)) +end +_zip_isdone(::Tuple{}, ::Tuple{}) = (false, ()) + +IteratorSize(::Type{Zip{Is}}) where {Is<:Tuple} = _zip_iterator_size(Is) +_zip_iterator_size(::Type{Is}) where {Is<:Tuple} = + zip_iteratorsize(IteratorSize(tuple_type_head(Is)), + _zip_iterator_size(tuple_type_tail(Is))) +_zip_iterator_size(::Type{Tuple{I}}) where {I} = IteratorSize(I) +_zip_iterator_size(::Type{Tuple{}}) = IsInfinite() +IteratorEltype(::Type{Zip{Is}}) where {Is<:Tuple} = _zip_iterator_eltype(Is) +_zip_iterator_eltype(::Type{Is}) where {Is<:Tuple} = + and_iteratoreltype(IteratorEltype(tuple_type_head(Is)), + _zip_iterator_eltype(tuple_type_tail(Is))) +_zip_iterator_eltype(::Type{Tuple{}}) = HasEltype() + +reverse(z::Zip) = Zip(map(reverse, z.is)) + +# filter + +struct Filter{F,I} + flt::F + itr::I +end + +""" + Iterators.filter(flt, itr) + +Given a predicate function `flt` and an iterable object `itr`, return an +iterable object which upon iteration yields the elements `x` of `itr` that +satisfy `flt(x)`. The order of the original iterator is preserved. + +This function is *lazy*; that is, it is guaranteed to return in ``Θ(1)`` time +and use ``Θ(1)`` additional space, and `flt` will not be called by an +invocation of `filter`. Calls to `flt` will be made when iterating over the +returned iterable object. These calls are not cached and repeated calls will be +made when reiterating. + +See [`Base.filter`](@ref) for an eager implementation of filtering for arrays. + +# Examples +```jldoctest +julia> f = Iterators.filter(isodd, [1, 2, 3, 4, 5]) +Base.Iterators.Filter{typeof(isodd),Array{Int64,1}}(isodd, [1, 2, 3, 4, 5]) + +julia> foreach(println, f) +1 +3 +5 +``` +""" +filter(flt, itr) = Filter(flt, itr) + +function iterate(f::Filter, state...) + y = iterate(f.itr, state...) + while y !== nothing + if f.flt(y[1]) + return y + end + y = iterate(f.itr, y[2]) + end + nothing +end + +eltype(::Type{Filter{F,I}}) where {F,I} = eltype(I) +IteratorEltype(::Type{Filter{F,I}}) where {F,I} = IteratorEltype(I) +IteratorSize(::Type{<:Filter}) = SizeUnknown() + +reverse(f::Filter) = Filter(f.flt, reverse(f.itr)) + +# Rest -- iterate starting at the given state + +struct Rest{I,S} + itr::I + st::S +end + +""" + rest(iter, state) + +An iterator that yields the same elements as `iter`, but starting at the given `state`. + +# Examples +```jldoctest +julia> collect(Iterators.rest([1,2,3,4], 2)) +3-element Array{Int64,1}: + 2 + 3 + 4 +``` +""" +rest(itr,state) = Rest(itr,state) +rest(itr::Rest,state) = Rest(itr.itr,state) +rest(itr) = itr + +""" + peel(iter) + +Returns the first element and an iterator over the remaining elements. + +# Examples +```jldoctest +julia> (a, rest) = Iterators.peel("abc"); + +julia> a +'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase) + +julia> collect(rest) +2-element Array{Char,1}: + 'b' + 'c' +``` +""" +function peel(itr) + y = iterate(itr) + y === nothing && throw(BoundsError()) + val, s = y + val, rest(itr, s) +end + +@propagate_inbounds iterate(i::Rest, st=i.st) = iterate(i.itr, st) +isdone(i::Rest, st...) = isdone(i.itr, st...) + +eltype(::Type{<:Rest{I}}) where {I} = eltype(I) +IteratorEltype(::Type{<:Rest{I}}) where {I} = IteratorEltype(I) +rest_iteratorsize(a) = SizeUnknown() +rest_iteratorsize(::IsInfinite) = IsInfinite() +IteratorSize(::Type{<:Rest{I}}) where {I} = rest_iteratorsize(IteratorSize(I)) + +# Count -- infinite counting + +struct Count{S<:Number} + start::S + step::S +end + +""" + countfrom(start=1, step=1) + +An iterator that counts forever, starting at `start` and incrementing by `step`. + +# Examples +```jldoctest +julia> for v in Iterators.countfrom(5, 2) + v > 10 && break + println(v) + end +5 +7 +9 +``` +""" +countfrom(start::Number, step::Number) = Count(promote(start, step)...) +countfrom(start::Number) = Count(start, oneunit(start)) +countfrom() = Count(1, 1) + +eltype(::Type{Count{S}}) where {S} = S + +iterate(it::Count, state=it.start) = (state, state + it.step) + +IteratorSize(::Type{<:Count}) = IsInfinite() + +# Take -- iterate through the first n elements + +struct Take{I} + xs::I + n::Int + function Take(xs::I, n::Integer) where {I} + n < 0 && throw(ArgumentError("Take length must be nonnegative")) + return new{I}(xs, n) + end +end + +""" + take(iter, n) + +An iterator that generates at most the first `n` elements of `iter`. + +# Examples +```jldoctest +julia> a = 1:2:11 +1:2:11 + +julia> collect(a) +6-element Array{Int64,1}: + 1 + 3 + 5 + 7 + 9 + 11 + +julia> collect(Iterators.take(a,3)) +3-element Array{Int64,1}: + 1 + 3 + 5 +``` +""" +take(xs, n::Integer) = Take(xs, Int(n)) +take(xs::Take, n::Integer) = Take(xs.xs, min(Int(n), xs.n)) + +eltype(::Type{Take{I}}) where {I} = eltype(I) +IteratorEltype(::Type{Take{I}}) where {I} = IteratorEltype(I) +take_iteratorsize(a) = HasLength() +take_iteratorsize(::SizeUnknown) = SizeUnknown() +IteratorSize(::Type{Take{I}}) where {I} = take_iteratorsize(IteratorSize(I)) +length(t::Take) = _min_length(t.xs, 1:t.n, IteratorSize(t.xs), HasLength()) +isdone(t::Take) = isdone(t.xs) +isdone(t::Take, state) = (state[1] <= 0) | isdone(t.xs, tail(state)) + +@propagate_inbounds function iterate(it::Take, state=(it.n,)) + n, rest = state[1], tail(state) + n <= 0 && return nothing + y = iterate(it.xs, rest...) + y === nothing && return nothing + return y[1], (n - 1, y[2]) +end + +# Drop -- iterator through all but the first n elements + +struct Drop{I} + xs::I + n::Int + function Drop(xs::I, n::Integer) where {I} + n < 0 && throw(ArgumentError("Drop length must be nonnegative")) + return new{I}(xs, n) + end +end + +""" + drop(iter, n) + +An iterator that generates all but the first `n` elements of `iter`. + +# Examples +```jldoctest +julia> a = 1:2:11 +1:2:11 + +julia> collect(a) +6-element Array{Int64,1}: + 1 + 3 + 5 + 7 + 9 + 11 + +julia> collect(Iterators.drop(a,4)) +2-element Array{Int64,1}: + 9 + 11 +``` +""" +drop(xs, n::Integer) = Drop(xs, Int(n)) +drop(xs::Take, n::Integer) = Take(drop(xs.xs, Int(n)), max(0, xs.n - Int(n))) +drop(xs::Drop, n::Integer) = Drop(xs.xs, Int(n) + xs.n) + +eltype(::Type{Drop{I}}) where {I} = eltype(I) +IteratorEltype(::Type{Drop{I}}) where {I} = IteratorEltype(I) +drop_iteratorsize(::SizeUnknown) = SizeUnknown() +drop_iteratorsize(::Union{HasShape, HasLength}) = HasLength() +drop_iteratorsize(::IsInfinite) = IsInfinite() +IteratorSize(::Type{Drop{I}}) where {I} = drop_iteratorsize(IteratorSize(I)) +length(d::Drop) = _diff_length(d.xs, 1:d.n, IteratorSize(d.xs), HasLength()) + +function iterate(it::Drop) + y = iterate(it.xs) + for i in 1:it.n + y === nothing && return y + y = iterate(it.xs, y[2]) + end + y +end +iterate(it::Drop, state) = iterate(it.xs, state) +isdone(it::Drop, state) = isdone(it.xs, state) + +# Cycle an iterator forever + +struct Cycle{I} + xs::I +end + +""" + cycle(iter) + +An iterator that cycles through `iter` forever. +If `iter` is empty, so is `cycle(iter)`. + +# Examples +```jldoctest +julia> for (i, v) in enumerate(Iterators.cycle("hello")) + print(v) + i > 10 && break + end +hellohelloh +``` +""" +cycle(xs) = Cycle(xs) + +eltype(::Type{Cycle{I}}) where {I} = eltype(I) +IteratorEltype(::Type{Cycle{I}}) where {I} = IteratorEltype(I) +IteratorSize(::Type{Cycle{I}}) where {I} = IsInfinite() + +iterate(it::Cycle) = iterate(it.xs) +isdone(it::Cycle) = isdone(it.xs) +isdone(it::Cycle, state) = false +function iterate(it::Cycle, state) + y = iterate(it.xs, state) + y === nothing && return iterate(it) + y +end + +reverse(it::Cycle) = Cycle(reverse(it.xs)) + +# Repeated - repeat an object infinitely many times + +struct Repeated{O} + x::O +end +repeated(x) = Repeated(x) + +""" + repeated(x[, n::Int]) + +An iterator that generates the value `x` forever. If `n` is specified, generates `x` that +many times (equivalent to `take(repeated(x), n)`). + +# Examples +```jldoctest +julia> a = Iterators.repeated([1 2], 4); + +julia> collect(a) +4-element Array{Array{Int64,2},1}: + [1 2] + [1 2] + [1 2] + [1 2] +``` +""" +repeated(x, n::Integer) = take(repeated(x), Int(n)) + +eltype(::Type{Repeated{O}}) where {O} = O + +iterate(it::Repeated, state...) = (it.x, nothing) + +IteratorSize(::Type{<:Repeated}) = IsInfinite() +IteratorEltype(::Type{<:Repeated}) = HasEltype() + +reverse(it::Union{Repeated,Take{<:Repeated}}) = it + +# Product -- cartesian product of iterators +struct ProductIterator{T<:Tuple} + iterators::T +end + +""" + product(iters...) + +Return an iterator over the product of several iterators. Each generated element is +a tuple whose `i`th element comes from the `i`th argument iterator. The first iterator +changes the fastest. + +# Examples +```jldoctest +julia> collect(Iterators.product(1:2, 3:5)) +2×3 Array{Tuple{Int64,Int64},2}: + (1, 3) (1, 4) (1, 5) + (2, 3) (2, 4) (2, 5) +``` +""" +product(iters...) = ProductIterator(iters) + +IteratorSize(::Type{ProductIterator{Tuple{}}}) = HasShape{0}() +IteratorSize(::Type{ProductIterator{T}}) where {T<:Tuple} = + prod_iteratorsize( IteratorSize(tuple_type_head(T)), IteratorSize(ProductIterator{tuple_type_tail(T)}) ) + +prod_iteratorsize(::HasLength, ::HasLength) = HasShape{2}() +prod_iteratorsize(::HasLength, ::HasShape{N}) where {N} = HasShape{N+1}() +prod_iteratorsize(::HasShape{N}, ::HasLength) where {N} = HasShape{N+1}() +prod_iteratorsize(::HasShape{M}, ::HasShape{N}) where {M,N} = HasShape{M+N}() + +# products can have an infinite iterator +prod_iteratorsize(::IsInfinite, ::IsInfinite) = IsInfinite() +prod_iteratorsize(a, ::IsInfinite) = IsInfinite() +prod_iteratorsize(::IsInfinite, b) = IsInfinite() +prod_iteratorsize(a, b) = SizeUnknown() + +size(P::ProductIterator) = _prod_size(P.iterators) +_prod_size(::Tuple{}) = () +_prod_size(t::Tuple) = (_prod_size1(t[1], IteratorSize(t[1]))..., _prod_size(tail(t))...) +_prod_size1(a, ::HasShape) = size(a) +_prod_size1(a, ::HasLength) = (length(a),) +_prod_size1(a, A) = + throw(ArgumentError("Cannot compute size for object of type $(typeof(a))")) + +axes(P::ProductIterator) = _prod_indices(P.iterators) +_prod_indices(::Tuple{}) = () +_prod_indices(t::Tuple) = (_prod_axes1(t[1], IteratorSize(t[1]))..., _prod_indices(tail(t))...) +_prod_axes1(a, ::HasShape) = axes(a) +_prod_axes1(a, ::HasLength) = (OneTo(length(a)),) +_prod_axes1(a, A) = + throw(ArgumentError("Cannot compute indices for object of type $(typeof(a))")) + +ndims(p::ProductIterator) = length(axes(p)) +length(P::ProductIterator) = prod(size(P)) + +IteratorEltype(::Type{ProductIterator{Tuple{}}}) = HasEltype() +IteratorEltype(::Type{ProductIterator{Tuple{I}}}) where {I} = IteratorEltype(I) +function IteratorEltype(::Type{ProductIterator{T}}) where {T<:Tuple} + I = tuple_type_head(T) + P = ProductIterator{tuple_type_tail(T)} + IteratorEltype(I) == EltypeUnknown() ? EltypeUnknown() : IteratorEltype(P) +end + +eltype(::Type{<:ProductIterator{I}}) where {I} = _prod_eltype(I) +_prod_eltype(::Type{Tuple{}}) = Tuple{} +_prod_eltype(::Type{I}) where {I<:Tuple} = + Base.tuple_type_cons(eltype(tuple_type_head(I)),_prod_eltype(tuple_type_tail(I))) + +iterate(::ProductIterator{Tuple{}}) = (), true +iterate(::ProductIterator{Tuple{}}, state) = nothing + +@inline isdone(P::ProductIterator) = any(isdone, P.iterators) +@inline function _pisdone(iters, states) + iter1 = first(iters) + done1 = isdone(iter1, first(states)[2]) # check step + done1 === true || return done1 # false or missing + done1 = isdone(iter1) # check restart + done1 === true || return done1 # false or missing + return _pisdone(tail(iters), tail(states)) # check tail +end +@inline isdone(P::ProductIterator, states) = _pisdone(P.iterators, states) + +@inline _piterate() = () +@inline function _piterate(iter1, rest...) + next = iterate(iter1) + next === nothing && return nothing + restnext = _piterate(rest...) + restnext === nothing && return nothing + return (next, restnext...) +end +@inline function iterate(P::ProductIterator) + isdone(P) === true && return nothing + next = _piterate(P.iterators...) + next === nothing && return nothing + return (map(x -> x[1], next), next) +end + +@inline _piterate1(::Tuple{}, ::Tuple{}) = nothing +@inline function _piterate1(iters, states) + iter1 = first(iters) + next = iterate(iter1, first(states)[2]) + restnext = tail(states) + if next === nothing + isdone(iter1) === true && return nothing + restnext = _piterate1(tail(iters), restnext) + restnext === nothing && return nothing + next = iterate(iter1) + next === nothing && return nothing + end + return (next, restnext...) +end +@inline function iterate(P::ProductIterator, states) + isdone(P, states) === true && return nothing + next = _piterate1(P.iterators, states) + next === nothing && return nothing + return (map(x -> x[1], next), next) +end + +reverse(p::ProductIterator) = ProductIterator(map(reverse, p.iterators)) + +# flatten an iterator of iterators + +struct Flatten{I} + it::I +end + +""" + flatten(iter) + +Given an iterator that yields iterators, return an iterator that yields the +elements of those iterators. +Put differently, the elements of the argument iterator are concatenated. + +# Examples +```jldoctest +julia> collect(Iterators.flatten((1:2, 8:9))) +4-element Array{Int64,1}: + 1 + 2 + 8 + 9 +``` +""" +flatten(itr) = Flatten(itr) + +eltype(::Type{Flatten{I}}) where {I} = eltype(eltype(I)) +eltype(::Type{Flatten{Tuple{}}}) = eltype(Tuple{}) +IteratorEltype(::Type{Flatten{I}}) where {I} = _flatteneltype(I, IteratorEltype(I)) +IteratorEltype(::Type{Flatten{Tuple{}}}) = IteratorEltype(Tuple{}) +_flatteneltype(I, ::HasEltype) = IteratorEltype(eltype(I)) +_flatteneltype(I, et) = EltypeUnknown() + +flatten_iteratorsize(::Union{HasShape, HasLength}, ::Type{<:NTuple{N,Any}}) where {N} = HasLength() +flatten_iteratorsize(::Union{HasShape, HasLength}, ::Type{<:Tuple}) = SizeUnknown() +flatten_iteratorsize(::Union{HasShape, HasLength}, ::Type{<:Number}) = HasLength() +flatten_iteratorsize(a, b) = SizeUnknown() + +_flatten_iteratorsize(sz, ::EltypeUnknown, I) = SizeUnknown() +_flatten_iteratorsize(sz, ::HasEltype, I) = flatten_iteratorsize(sz, eltype(I)) +_flatten_iteratorsize(sz, ::HasEltype, ::Type{Tuple{}}) = HasLength() + +IteratorSize(::Type{Flatten{I}}) where {I} = _flatten_iteratorsize(IteratorSize(I), IteratorEltype(I), I) + +function flatten_length(f, T::Type{<:NTuple{N,Any}}) where {N} + fieldcount(T)*length(f.it) +end +flatten_length(f, ::Type{<:Number}) = length(f.it) +flatten_length(f, T) = throw(ArgumentError( + "Iterates of the argument to Flatten are not known to have constant length")) +length(f::Flatten{I}) where {I} = flatten_length(f, eltype(I)) +length(f::Flatten{Tuple{}}) = 0 + +@propagate_inbounds function iterate(f::Flatten, state=()) + if state !== () + y = iterate(tail(state)...) + y !== nothing && return (y[1], (state[1], state[2], y[2])) + end + x = (state === () ? iterate(f.it) : iterate(f.it, state[1])) + x === nothing && return nothing + y = iterate(x[1]) + while y === nothing + x = iterate(f.it, x[2]) + x === nothing && return nothing + y = iterate(x[1]) + end + return y[1], (x[2], x[1], y[2]) +end + +reverse(f::Flatten) = Flatten(reverse(itr) for itr in reverse(f.it)) + +""" + partition(collection, n) + +Iterate over a collection `n` elements at a time. + +# Examples +```jldoctest +julia> collect(Iterators.partition([1,2,3,4,5], 2)) +3-element Array{Array{Int64,1},1}: + [1, 2] + [3, 4] + [5] +``` +""" +partition(c::T, n::Integer) where {T} = PartitionIterator{T}(c, Int(n)) + + +struct PartitionIterator{T} + c::T + n::Int +end + +eltype(::Type{PartitionIterator{T}}) where {T} = Vector{eltype(T)} +partition_iteratorsize(::HasShape) = HasLength() +partition_iteratorsize(isz) = isz +function IteratorSize(::Type{PartitionIterator{T}}) where {T} + partition_iteratorsize(IteratorSize(T)) +end + +IteratorEltype(::Type{<:PartitionIterator{T}}) where {T} = IteratorEltype(T) + +function length(itr::PartitionIterator) + l = length(itr.c) + return div(l, itr.n) + ((mod(l, itr.n) > 0) ? 1 : 0) +end + +function iterate(itr::PartitionIterator{<:Vector}, state=1) + state > length(itr.c) && return nothing + r = min(state + itr.n - 1, length(itr.c)) + return view(itr.c, state:r), r + 1 +end + +struct IterationCutShort; end + +function iterate(itr::PartitionIterator, state...) + v = Vector{eltype(itr.c)}(undef, itr.n) + # This is necessary to remember whether we cut the + # last element short. In such cases, we do return that + # element, but not the next one + state === (IterationCutShort(),) && return nothing + i = 0 + y = iterate(itr.c, state...) + while y !== nothing + i += 1 + v[i] = y[1] + if i >= itr.n + break + end + y = iterate(itr.c, y[2]) + end + i === 0 && return nothing + return resize!(v, i), y === nothing ? IterationCutShort() : y[2] +end + +""" + Stateful(itr) + +There are several different ways to think about this iterator wrapper: + +1. It provides a mutable wrapper around an iterator and + its iteration state. +2. It turns an iterator-like abstraction into a `Channel`-like + abstraction. +3. It's an iterator that mutates to become its own rest iterator + whenever an item is produced. + +`Stateful` provides the regular iterator interface. Like other mutable iterators +(e.g. [`Channel`](@ref)), if iteration is stopped early (e.g. by a `break` in a `for` loop), +iteration can be resumed from the same spot by continuing to iterate over the +same iterator object (in contrast, an immutable iterator would restart from the +beginning). + +# Examples +```jldoctest +julia> a = Iterators.Stateful("abcdef"); + +julia> isempty(a) +false + +julia> popfirst!(a) +'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase) + +julia> collect(Iterators.take(a, 3)) +3-element Array{Char,1}: + 'b' + 'c' + 'd' + +julia> collect(a) +2-element Array{Char,1}: + 'e' + 'f' +``` + +```jldoctest +julia> a = Iterators.Stateful([1,1,1,2,3,4]); + +julia> for x in a; x == 1 || break; end + +julia> Base.peek(a) +3 + +julia> sum(a) # Sum the remaining elements +7 +``` +""" +mutable struct Stateful{T, VS} + itr::T + # A bit awkward right now, but adapted to the new iteration protocol + nextvalstate::Union{VS, Nothing} + taken::Int + @inline function Stateful{<:Any, Any}(itr::T) where {T} + new{T, Any}(itr, iterate(itr), 0) + end + @inline function Stateful(itr::T) where {T} + VS = approx_iter_type(T) + return new{T, VS}(itr, iterate(itr)::VS, 0) + end +end + +function reset!(s::Stateful{T,VS}, itr::T) where {T,VS} + s.itr = itr + setfield!(s, :nextvalstate, iterate(itr)) + s.taken = 0 + s +end + +if Base === Core.Compiler + approx_iter_type(a::Type) = Any +else + # Try to find an appropriate type for the (value, state tuple), + # by doing a recursive unrolling of the iteration protocol up to + # fixpoint. + approx_iter_type(itrT::Type) = _approx_iter_type(itrT, Base._return_type(iterate, Tuple{itrT})) + # Not actually called, just passed to return type to avoid + # having to typesubtract + function doiterate(itr, valstate::Union{Nothing, Tuple{Any, Any}}) + valstate === nothing && return nothing + val, st = valstate + return iterate(itr, st) + end + function _approx_iter_type(itrT::Type, vstate::Type) + vstate <: Union{Nothing, Tuple{Any, Any}} || return Any + vstate <: Union{} && return Union{} + nextvstate = Base._return_type(doiterate, Tuple{itrT, vstate}) + return (nextvstate <: vstate ? vstate : Any) + end +end + +convert(::Type{Stateful}, itr) = Stateful(itr) + +@inline isdone(s::Stateful, st=nothing) = s.nextvalstate === nothing + +@inline function popfirst!(s::Stateful) + vs = s.nextvalstate + if vs === nothing + throw(EOFError()) + else + val, state = vs + Core.setfield!(s, :nextvalstate, iterate(s.itr, state)) + s.taken += 1 + return val + end +end + +@inline peek(s::Stateful, sentinel=nothing) = s.nextvalstate !== nothing ? s.nextvalstate[1] : sentinel +@inline iterate(s::Stateful, state=nothing) = s.nextvalstate === nothing ? nothing : (popfirst!(s), nothing) +IteratorSize(::Type{Stateful{T,VS}}) where {T,VS} = IteratorSize(T) isa HasShape ? HasLength() : IteratorSize(T) +eltype(::Type{Stateful{T, VS}} where VS) where {T} = eltype(T) +IteratorEltype(::Type{Stateful{T,VS}}) where {T,VS} = IteratorEltype(T) +length(s::Stateful) = length(s.itr) - s.taken + +end diff --git a/base/libc.jl b/base/libc.jl new file mode 100644 index 0000000..9fd6746 --- /dev/null +++ b/base/libc.jl @@ -0,0 +1,396 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +module Libc +@doc """ +Interface to libc, the C standard library. +""" Libc + +import Base: transcode, windowserror +import Core.Intrinsics: bitcast + +export FILE, TmStruct, strftime, strptime, getpid, gethostname, free, malloc, calloc, realloc, + errno, strerror, flush_cstdio, systemsleep, time, transcode +if Sys.iswindows() + export GetLastError, FormatMessage +end + +include(string(length(Core.ARGS) >= 2 ? Core.ARGS[2] : "", "errno_h.jl")) # include($BUILDROOT/base/errno_h.jl) + +## RawFD ## + +# Wrapper for an OS file descriptor (on both Unix and Windows) +""" + RawFD + +Primitive type which wraps the native OS file descriptor. +`RawFD`s can be passed to methods like [`stat`](@ref) to +discover information about the underlying file, and can +also be used to open streams, with the `RawFD` describing +the OS file backing the stream. +""" +primitive type RawFD 32 end +RawFD(fd::Integer) = bitcast(RawFD, Cint(fd)) +RawFD(fd::RawFD) = fd +Base.cconvert(::Type{Cint}, fd::RawFD) = bitcast(Cint, fd) + +dup(x::RawFD) = ccall((@static Sys.iswindows() ? :_dup : :dup), RawFD, (RawFD,), x) +dup(src::RawFD, target::RawFD) = systemerror("dup", -1 == + ccall((@static Sys.iswindows() ? :_dup2 : :dup2), Int32, + (RawFD, RawFD), src, target)) + +# Wrapper for an OS file descriptor (for Windows) +if Sys.iswindows() + primitive type WindowsRawSocket sizeof(Ptr) * 8 end # On Windows file descriptors are HANDLE's and 64-bit on 64-bit Windows + WindowsRawSocket(handle::Ptr{Cvoid}) = bitcast(WindowsRawSocket, handle) + WindowsRawSocket(handle::WindowsRawSocket) = handle + + Base.cconvert(::Type{Ptr{Cvoid}}, fd::WindowsRawSocket) = bitcast(Ptr{Cvoid}, fd) + _get_osfhandle(fd::RawFD) = ccall(:_get_osfhandle, WindowsRawSocket, (RawFD,), fd) + _get_osfhandle(fd::WindowsRawSocket) = fd + function dup(src::WindowsRawSocket) + new_handle = Ref(WindowsRawSocket(Ptr{Cvoid}(-1))) + my_process = ccall(:GetCurrentProcess, stdcall, Ptr{Cvoid}, ()) + DUPLICATE_SAME_ACCESS = 0x2 + status = ccall(:DuplicateHandle, stdcall, Int32, + (Ptr{Cvoid}, WindowsRawSocket, Ptr{Cvoid}, Ptr{WindowsRawSocket}, UInt32, Int32, UInt32), + my_process, src, my_process, new_handle, 0, false, DUPLICATE_SAME_ACCESS) + windowserror("dup failed", status == 0) + return new_handle[] + end + function dup(src::WindowsRawSocket, target::RawFD) + fd = ccall(:_open_osfhandle, RawFD, (WindowsRawSocket, Int32), dup(src), 0) + dup(fd, target) + ccall(:_close, Int32, (RawFD,), fd) + nothing + end + +else + _get_osfhandle(fd::RawFD) = fd +end + +## FILE (not auto-finalized) ## + +struct FILE + ptr::Ptr{Cvoid} +end + +modestr(s::IO) = modestr(isreadable(s), iswritable(s)) +modestr(r::Bool, w::Bool) = r ? (w ? "r+" : "r") : (w ? "w" : throw(ArgumentError("neither readable nor writable"))) + +function FILE(fd::RawFD, mode) + FILEp = ccall((@static Sys.iswindows() ? :_fdopen : :fdopen), Ptr{Cvoid}, (Cint, Cstring), fd, mode) + systemerror("fdopen", FILEp == C_NULL) + FILE(FILEp) +end + +function FILE(s::IO) + f = FILE(dup(RawFD(fd(s))),modestr(s)) + seek(f, position(s)) + f +end + +Base.unsafe_convert(T::Union{Type{Ptr{Cvoid}},Type{Ptr{FILE}}}, f::FILE) = convert(T, f.ptr) +Base.close(f::FILE) = systemerror("fclose", ccall(:fclose, Cint, (Ptr{Cvoid},), f.ptr) != 0) + +function Base.seek(h::FILE, offset::Integer) + systemerror("fseek", ccall(:fseek, Cint, (Ptr{Cvoid}, Clong, Cint), + h.ptr, offset, 0) != 0) + h +end + +Base.position(h::FILE) = ccall(:ftell, Clong, (Ptr{Cvoid},), h.ptr) + +# flush C stdio output from external libraries + +""" + flush_cstdio() + +Flushes the C `stdout` and `stderr` streams (which may have been written to by external C code). +""" +flush_cstdio() = ccall(:jl_flush_cstdio, Cvoid, ()) + +## time-related functions ## + +# TODO: check for usleep errors? +if Sys.isunix() + systemsleep(s::Real) = ccall(:usleep, Int32, (UInt32,), round(UInt32, s*1e6)) +elseif Sys.iswindows() + function systemsleep(s::Real) + ccall(:Sleep, stdcall, Cvoid, (UInt32,), round(UInt32, s * 1e3)) + return Int32(0) + end +else + error("systemsleep undefined for this OS") +end +""" + systemsleep(s::Real) + +Suspends execution for `s` seconds. +This function does not yield to Julia's scheduler and therefore blocks +the Julia thread that it is running on for the duration of the sleep time. + +See also: [`sleep`](@ref) +""" +systemsleep + +struct TimeVal + sec::Int64 + usec::Int64 +end + +function TimeVal() + tv = Ref{TimeVal}() + status = ccall(:jl_gettimeofday, Cint, (Ref{TimeVal},), tv) + status != 0 && error("unable to determine current time: ", status) + return tv[] +end + +""" + TmStruct([seconds]) + +Convert a number of seconds since the epoch to broken-down format, with fields `sec`, `min`, +`hour`, `mday`, `month`, `year`, `wday`, `yday`, and `isdst`. +""" +mutable struct TmStruct + sec::Int32 + min::Int32 + hour::Int32 + mday::Int32 + month::Int32 + year::Int32 + wday::Int32 + yday::Int32 + isdst::Int32 + # on some platforms the struct is 14 words, even though 9 are specified + _10::Int32 + _11::Int32 + _12::Int32 + _13::Int32 + _14::Int32 + + TmStruct(sec, min, hour, mday, month, year, wday, yday, isdst) = + new(sec, min, hour, mday, month, year, wday, yday, isdst, 0,0,0,0,0) + TmStruct() = new(0,0,0,0,0,0,0,0,0,0,0,0,0,0) + function TmStruct(t::Real) + t = floor(t) + tm = TmStruct() + # TODO: add support for UTC via gmtime_r() + ccall(:localtime_r, Ptr{TmStruct}, (Ref{Int}, Ref{TmStruct}), t, tm) + return tm + end +end + +""" + strftime([format], time) + +Convert time, given as a number of seconds since the epoch or a `TmStruct`, to a formatted +string using the given format. Supported formats are the same as those in the standard C +library. +""" +strftime(t) = strftime("%c", t) +strftime(fmt::AbstractString, t::Real) = strftime(fmt, TmStruct(t)) +# Use wcsftime instead of strftime to support different locales +function strftime(fmt::AbstractString, tm::TmStruct) + wctimestr = Vector{Cwchar_t}(undef, 128) + n = ccall(:wcsftime, Csize_t, (Ptr{Cwchar_t}, Csize_t, Cwstring, Ref{TmStruct}), + wctimestr, length(wctimestr), fmt, tm) + n == 0 && return "" + return transcode(String, resize!(wctimestr, n)) +end + +""" + strptime([format], timestr) + +Parse a formatted time string into a `TmStruct` giving the seconds, minute, hour, date, etc. +Supported formats are the same as those in the standard C library. On some platforms, +timezones will not be parsed correctly. If the result of this function will be passed to +`time` to convert it to seconds since the epoch, the `isdst` field should be filled in +manually. Setting it to `-1` will tell the C library to use the current system settings to +determine the timezone. +""" +strptime(timestr::AbstractString) = strptime("%c", timestr) +function strptime(fmt::AbstractString, timestr::AbstractString) + tm = TmStruct() + r = ccall(:strptime, Cstring, (Cstring, Cstring, Ref{TmStruct}), timestr, fmt, tm) + # the following would tell mktime() that this is a local time, and that + # it should try to guess the timezone. not sure if/how this should be + # exposed in the API. + # tm.isdst = -1 + if r == C_NULL + # TODO: better error message + throw(ArgumentError("invalid arguments")) + end + @static if Sys.isapple() + # if we didn't explicitly parse the weekday or year day, use mktime + # to fill them in automatically. + if !occursin(r"([^%]|^)%(a|A|j|w|Ow)", fmt) + ccall(:mktime, Int, (Ref{TmStruct},), tm) + end + end + return tm +end + +# system date in seconds + +""" + time(t::TmStruct) + +Converts a `TmStruct` struct to a number of seconds since the epoch. +""" +time(tm::TmStruct) = Float64(ccall(:mktime, Int, (Ref{TmStruct},), tm)) + +""" + time() + +Get the system time in seconds since the epoch, with fairly high (typically, microsecond) resolution. +""" +time() = ccall(:jl_clock_now, Float64, ()) + +## process-related functions ## + +""" + getpid() -> Int32 + +Get Julia's process ID. +""" +getpid() = ccall(:jl_getpid, Int32, ()) + +## network functions ## + +""" + gethostname() -> AbstractString + +Get the local machine's host name. +""" +function gethostname() + hn = Vector{UInt8}(undef, 256) + err = @static if Sys.iswindows() + ccall(:gethostname, stdcall, Int32, (Ptr{UInt8}, UInt32), hn, length(hn)) + else + ccall(:gethostname, Int32, (Ptr{UInt8}, UInt), hn, length(hn)) + end + systemerror("gethostname", err != 0) + return GC.@preserve hn unsafe_string(pointer(hn)) +end + +## system error handling ## + +""" + errno([code]) + +Get the value of the C library's `errno`. If an argument is specified, it is used to set the +value of `errno`. + +The value of `errno` is only valid immediately after a `ccall` to a C library routine that +sets it. Specifically, you cannot call `errno` at the next prompt in a REPL, because lots of +code is executed between prompts. +""" +errno() = ccall(:jl_errno, Cint, ()) +errno(e::Integer) = ccall(:jl_set_errno, Cvoid, (Cint,), e) + +""" + strerror(n=errno()) + +Convert a system call error code to a descriptive string +""" +strerror(e::Integer) = unsafe_string(ccall(:strerror, Cstring, (Int32,), e)) +strerror() = strerror(errno()) + +""" + GetLastError() + +Call the Win32 `GetLastError` function [only available on Windows]. +""" +function GetLastError end + +""" + FormatMessage(n=GetLastError()) + +Convert a Win32 system call error code to a descriptive string [only available on Windows]. +""" +function FormatMessage end + +if Sys.iswindows() + GetLastError() = ccall(:GetLastError, stdcall, UInt32, ()) + + function FormatMessage(e=GetLastError()) + FORMAT_MESSAGE_ALLOCATE_BUFFER = UInt32(0x100) + FORMAT_MESSAGE_FROM_SYSTEM = UInt32(0x1000) + FORMAT_MESSAGE_IGNORE_INSERTS = UInt32(0x200) + FORMAT_MESSAGE_MAX_WIDTH_MASK = UInt32(0xFF) + lpMsgBuf = Ref{Ptr{UInt16}}() + lpMsgBuf[] = 0 + len = ccall(:FormatMessageW, stdcall, UInt32, (Cint, Ptr{Cvoid}, Cint, Cint, Ptr{Ptr{UInt16}}, Cint, Ptr{Cvoid}), + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, + C_NULL, e, 0, lpMsgBuf, 0, C_NULL) + p = lpMsgBuf[] + len == 0 && return "" + buf = Vector{UInt16}(undef, len) + GC.@preserve buf unsafe_copyto!(pointer(buf), p, len) + ccall(:LocalFree, stdcall, Ptr{Cvoid}, (Ptr{Cvoid},), p) + return transcode(String, buf) + end +end + +## Memory related ## + +""" + free(addr::Ptr) + +Call `free` from the C standard library. Only use this on memory obtained from [`malloc`](@ref), not +on pointers retrieved from other C libraries. [`Ptr`](@ref) objects obtained from C libraries should +be freed by the free functions defined in that library, to avoid assertion failures if +multiple `libc` libraries exist on the system. +""" +free(p::Ptr) = ccall(:free, Cvoid, (Ptr{Cvoid},), p) + +""" + malloc(size::Integer) -> Ptr{Cvoid} + +Call `malloc` from the C standard library. +""" +malloc(size::Integer) = ccall(:malloc, Ptr{Cvoid}, (Csize_t,), size) + +""" + realloc(addr::Ptr, size::Integer) -> Ptr{Cvoid} + +Call `realloc` from the C standard library. + +See warning in the documentation for [`free`](@ref) regarding only using this on memory originally +obtained from [`malloc`](@ref). +""" +realloc(p::Ptr, size::Integer) = ccall(:realloc, Ptr{Cvoid}, (Ptr{Cvoid}, Csize_t), p, size) + +""" + calloc(num::Integer, size::Integer) -> Ptr{Cvoid} + +Call `calloc` from the C standard library. +""" +calloc(num::Integer, size::Integer) = ccall(:calloc, Ptr{Cvoid}, (Csize_t, Csize_t), num, size) + +free(p::Cstring) = free(convert(Ptr{UInt8}, p)) +free(p::Cwstring) = free(convert(Ptr{Cwchar_t}, p)) + +## Random numbers ## + +# To limit dependency on rand functionality implemented in the Random module, +# Libc.rand is used in file.jl, and could be used in error.jl (but it breaks a test) +""" + rand([T::Type]) + +Interface to the C `rand()` function. If `T` is provided, generate a value of type `T` +by composing two calls to `rand()`. `T` can be `UInt32` or `Float64`. +""" +rand() = ccall(:rand, Cint, ()) +# RAND_MAX at least 2^15-1 in theory, but we assume 2^16-1 (in practice, it's 2^31-1) +rand(::Type{UInt32}) = ((rand() % UInt32) << 16) ⊻ (rand() % UInt32) +rand(::Type{Float64}) = rand(UInt32) / 2^32 + +""" + srand([seed]) + +Interface to the C `srand(seed)` function. +""" +srand(seed=floor(time())) = ccall(:srand, Cvoid, (Cuint,), seed) + +end # module diff --git a/base/libuv.jl b/base/libuv.jl new file mode 100644 index 0000000..737dcc6 --- /dev/null +++ b/base/libuv.jl @@ -0,0 +1,148 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Core definitions for interacting with the libuv library from Julia + +include(string(length(Core.ARGS) >= 2 ? Core.ARGS[2] : "", "uv_constants.jl")) # include($BUILDROOT/base/uv_constants.jl) + +# convert UV handle data to julia object, checking for null +function uv_sizeof_handle(handle) + if !(UV_UNKNOWN_HANDLE < handle < UV_HANDLE_TYPE_MAX) + throw(DomainError(handle)) + end + return ccall(:uv_handle_size, Csize_t, (Int32,), handle) +end + +function uv_sizeof_req(req) + if !(UV_UNKNOWN_REQ < req < UV_REQ_TYPE_MAX) + throw(DomainError(req)) + end + return ccall(:uv_req_size, Csize_t, (Int32,), req) +end + +for h in uv_handle_types +@eval const $(Symbol("_sizeof_", lowercase(string(h)))) = uv_sizeof_handle($h) +end +for r in uv_req_types +@eval const $(Symbol("_sizeof_", lowercase(string(r)))) = uv_sizeof_req($r) +end + +uv_handle_data(handle) = ccall(:jl_uv_handle_data, Ptr{Cvoid}, (Ptr{Cvoid},), handle) +uv_req_data(handle) = ccall(:jl_uv_req_data, Ptr{Cvoid}, (Ptr{Cvoid},), handle) +uv_req_set_data(req, data) = ccall(:jl_uv_req_set_data, Cvoid, (Ptr{Cvoid}, Any), req, data) +uv_req_set_data(req, data::Ptr{Cvoid}) = ccall(:jl_uv_req_set_data, Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}), req, data) + +macro handle_as(hand, typ) + return quote + local data = uv_handle_data($(esc(hand))) + data == C_NULL && return + unsafe_pointer_to_objref(data)::($(esc(typ))) + end +end + +associate_julia_struct(handle::Ptr{Cvoid}, @nospecialize(jlobj)) = + ccall(:jl_uv_associate_julia_struct, Cvoid, (Ptr{Cvoid}, Any), handle, jlobj) +disassociate_julia_struct(uv) = disassociate_julia_struct(uv.handle) +disassociate_julia_struct(handle::Ptr{Cvoid}) = + handle != C_NULL && ccall(:jl_uv_disassociate_julia_struct, Cvoid, (Ptr{Cvoid},), handle) + +iolock_begin() = ccall(:jl_iolock_begin, Cvoid, ()) +iolock_end() = ccall(:jl_iolock_end, Cvoid, ()) + +# A dict of all libuv handles that are being waited on somewhere in the system +# and should thus not be garbage collected +const uvhandles = IdDict() +const preserve_handle_lock = Threads.SpinLock() +function preserve_handle(x) + lock(preserve_handle_lock) + v = get(uvhandles, x, 0)::Int + uvhandles[x] = v + 1 + unlock(preserve_handle_lock) + nothing +end +function unpreserve_handle(x) + lock(preserve_handle_lock) + v = uvhandles[x]::Int + if v == 1 + pop!(uvhandles, x) + else + uvhandles[x] = v - 1 + end + unlock(preserve_handle_lock) + nothing +end + +## Libuv error handling ## + +struct IOError <: Exception + msg::AbstractString + code::Int32 + IOError(msg::AbstractString, code::Integer) = new(msg, code) +end + +showerror(io::IO, e::IOError) = print(io, "IOError: ", e.msg) + +function _UVError(pfx::AbstractString, code::Integer) + code = Int32(code) + IOError(string(pfx, ": ", struverror(code), " (", uverrorname(code), ")"), code) +end +function _UVError(pfx::AbstractString, code::Integer, sfxs::AbstractString...) + code = Int32(code) + IOError(string(pfx, ": ", struverror(code), " (", uverrorname(code), ")", " ", sfxs...), code) +end + +struverror(err::Int32) = unsafe_string(ccall(:uv_strerror, Cstring, (Int32,), err)) +uverrorname(err::Int32) = unsafe_string(ccall(:uv_err_name, Cstring, (Int32,), err)) + +uv_error(prefix::Symbol, c::Integer) = uv_error(string(prefix), c) +uv_error(prefix::AbstractString, c::Integer) = c < 0 ? throw(_UVError(prefix, c)) : nothing + +## event loop ## + +eventloop() = uv_eventloop::Ptr{Cvoid} + +function process_events() + return ccall(:jl_process_events, Int32, (Ptr{Cvoid},), eventloop()) +end + +function uv_alloc_buf end +function uv_readcb end +function uv_writecb_task end +function uv_return_spawn end +function uv_asynccb end +function uv_timercb end + +function reinit_stdio() + global uv_jl_alloc_buf = @cfunction(uv_alloc_buf, Cvoid, (Ptr{Cvoid}, Csize_t, Ptr{Cvoid})) + global uv_jl_readcb = @cfunction(uv_readcb, Cvoid, (Ptr{Cvoid}, Cssize_t, Ptr{Cvoid})) + global uv_jl_writecb_task = @cfunction(uv_writecb_task, Cvoid, (Ptr{Cvoid}, Cint)) + global uv_jl_return_spawn = @cfunction(uv_return_spawn, Cvoid, (Ptr{Cvoid}, Int64, Int32)) + global uv_jl_asynccb = @cfunction(uv_asynccb, Cvoid, (Ptr{Cvoid},)) + global uv_jl_timercb = @cfunction(uv_timercb, Cvoid, (Ptr{Cvoid},)) + + global uv_eventloop = ccall(:jl_global_event_loop, Ptr{Cvoid}, ()) + global stdin = init_stdio(ccall(:jl_stdin_stream, Ptr{Cvoid}, ())) + global stdout = init_stdio(ccall(:jl_stdout_stream, Ptr{Cvoid}, ())) + global stderr = init_stdio(ccall(:jl_stderr_stream, Ptr{Cvoid}, ())) + nothing +end + +""" + stdin + +Global variable referring to the standard input stream. +""" +:stdin + +""" + stdout + +Global variable referring to the standard out stream. +""" +:stdout + +""" + stderr + +Global variable referring to the standard error stream. +""" +:stderr diff --git a/base/linked_list.jl b/base/linked_list.jl new file mode 100644 index 0000000..beceb24 --- /dev/null +++ b/base/linked_list.jl @@ -0,0 +1,151 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +mutable struct InvasiveLinkedList{T} + # Invasive list requires that T have a field `.next >: U{T, Nothing}` and `.queue >: U{ILL{T}, Nothing}` + head::Union{T, Nothing} + tail::Union{T, Nothing} + InvasiveLinkedList{T}() where {T} = new{T}(nothing, nothing) +end + +#const list_append!! = append! +#const list_deletefirst! = delete! + +eltype(::Type{<:InvasiveLinkedList{T}}) where {T} = @isdefined(T) ? T : Any + +iterate(q::InvasiveLinkedList) = (h = q.head; h === nothing ? nothing : (h, h)) +iterate(q::InvasiveLinkedList{T}, v::T) where {T} = (h = v.next; h === nothing ? nothing : (h, h)) + +isempty(q::InvasiveLinkedList) = (q.head === nothing) + +function length(q::InvasiveLinkedList) + i = 0 + head = q.head + while head !== nothing + i += 1 + head = head.next + end + return i +end + +function list_append!!(q::InvasiveLinkedList{T}, q2::InvasiveLinkedList{T}) where T + q === q2 && error("can't append list to itself") + head2 = q2.head + if head2 !== nothing + tail2 = q2.tail::T + q2.head = nothing + q2.tail = nothing + tail = q.tail + q.tail = tail2 + if tail === nothing + q.head = head2 + else + tail.next = head2 + end + while head2 !== nothing + head2.queue = q + head2 = head2.next + end + end + return q +end + +function push!(q::InvasiveLinkedList{T}, val::T) where T + val.queue === nothing || error("val already in a list") + val.queue = q + tail = q.tail + if tail === nothing + q.head = q.tail = val + else + tail.next = val + q.tail = val + end + return q +end + +function pushfirst!(q::InvasiveLinkedList{T}, val::T) where T + val.queue === nothing || error("val already in a list") + val.queue = q + head = q.head + if head === nothing + q.head = q.tail = val + else + val.next = head + q.head = val + end + return q +end + +function pop!(q::InvasiveLinkedList{T}) where {T} + val = q.tail::T + list_deletefirst!(q, val) # expensive! + return val +end + +function popfirst!(q::InvasiveLinkedList{T}) where {T} + val = q.head::T + list_deletefirst!(q, val) # cheap + return val +end + +function list_deletefirst!(q::InvasiveLinkedList{T}, val::T) where T + val.queue === q || return + head = q.head::T + if head === val + if q.tail::T === val + q.head = q.tail = nothing + else + q.head = val.next::T + end + else + head_next = head.next + while head_next !== val + head = head_next + head_next = head.next::Union{T, Nothing} + end + if q.tail::T === val + head.next = nothing + q.tail = head + else + head.next = val.next::T + end + end + val.next = nothing + val.queue = nothing + return q +end + +#function list_deletefirst!(q::Array{T}, val::T) where T +# i = findfirst(isequal(val), q) +# i === nothing || deleteat!(q, i) +# return q +#end + + +mutable struct LinkedListItem{T} + # Adapter class to use any `T` in a LinkedList + next::Union{LinkedListItem{T}, Nothing} + queue::Union{InvasiveLinkedList{LinkedListItem{T}}, Nothing} + value::T + LinkedListItem{T}(value::T) where {T} = new{T}(nothing, nothing, value) +end +const LinkedList{T} = InvasiveLinkedList{LinkedListItem{T}} + +# delegate methods, as needed +eltype(::Type{<:LinkedList{T}}) where {T} = @isdefined(T) ? T : Any +iterate(q::LinkedList) = (h = q.head; h === nothing ? nothing : (h.value, h)) +iterate(q::InvasiveLinkedList{LLT}, v::LLT) where {LLT<:LinkedListItem} = (h = v.next; h === nothing ? nothing : (h.value, h)) +push!(q::LinkedList{T}, val::T) where {T} = push!(q, LinkedListItem{T}(val)) +pushfirst!(q::LinkedList{T}, val::T) where {T} = pushfirst!(q, LinkedListItem{T}(val)) +pop!(q::LinkedList) = invoke(pop!, Tuple{InvasiveLinkedList,}, q).value +popfirst!(q::LinkedList) = invoke(popfirst!, Tuple{InvasiveLinkedList,}, q).value +function list_deletefirst!(q::LinkedList{T}, val::T) where T + h = q.head + while h !== nothing + if isequal(h.value, val) + list_deletefirst!(q, h) + break + end + h = h.next + end + return q +end diff --git a/base/loading.jl b/base/loading.jl new file mode 100644 index 0000000..7f11a2d --- /dev/null +++ b/base/loading.jl @@ -0,0 +1,1515 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Base.require is the implementation for the `import` statement + +# Cross-platform case-sensitive path canonicalization + +if Sys.isunix() && !Sys.isapple() + # assume case-sensitive filesystems, don't have to do anything + isfile_casesensitive(path) = isfile(path) +elseif Sys.iswindows() + # GetLongPathName Win32 function returns the case-preserved filename on NTFS. + function isfile_casesensitive(path) + isfile(path) || return false # Fail fast + basename(Filesystem.longpath(path)) == basename(path) + end +elseif Sys.isapple() + # HFS+ filesystem is case-preserving. The getattrlist API returns + # a case-preserved filename. In the rare event that HFS+ is operating + # in case-sensitive mode, this will still work but will be redundant. + + # Constants from + const ATRATTR_BIT_MAP_COUNT = 5 + const ATTR_CMN_NAME = 1 + const BITMAPCOUNT = 1 + const COMMONATTR = 5 + const FSOPT_NOFOLLOW = 1 # Don't follow symbolic links + + const attr_list = zeros(UInt8, 24) + attr_list[BITMAPCOUNT] = ATRATTR_BIT_MAP_COUNT + attr_list[COMMONATTR] = ATTR_CMN_NAME + + # This essentially corresponds to the following C code: + # attrlist attr_list; + # memset(&attr_list, 0, sizeof(attr_list)); + # attr_list.bitmapcount = ATTR_BIT_MAP_COUNT; + # attr_list.commonattr = ATTR_CMN_NAME; + # struct Buffer { + # u_int32_t total_length; + # u_int32_t filename_offset; + # u_int32_t filename_length; + # char filename[max_filename_length]; + # }; + # Buffer buf; + # getattrpath(path, &attr_list, &buf, sizeof(buf), FSOPT_NOFOLLOW); + function isfile_casesensitive(path) + isfile(path) || return false + path_basename = String(basename(path)) + local casepreserved_basename + header_size = 12 + buf = Vector{UInt8}(undef, length(path_basename) + header_size + 1) + while true + ret = ccall(:getattrlist, Cint, + (Cstring, Ptr{Cvoid}, Ptr{Cvoid}, Csize_t, Culong), + path, attr_list, buf, sizeof(buf), FSOPT_NOFOLLOW) + systemerror(:getattrlist, ret ≠ 0) + filename_length = GC.@preserve buf unsafe_load( + convert(Ptr{UInt32}, pointer(buf) + 8)) + if (filename_length + header_size) > length(buf) + resize!(buf, filename_length + header_size) + continue + end + casepreserved_basename = + view(buf, (header_size+1):(header_size+filename_length-1)) + break + end + # Hack to compensate for inability to create a string from a subarray with no allocations. + codeunits(path_basename) == casepreserved_basename && return true + + # If there is no match, it's possible that the file does exist but HFS+ + # performed unicode normalization. See https://developer.apple.com/library/mac/qa/qa1235/_index.html. + isascii(path_basename) && return false + codeunits(Unicode.normalize(path_basename, :NFD)) == casepreserved_basename + end +else + # Generic fallback that performs a slow directory listing. + function isfile_casesensitive(path) + isfile(path) || return false + dir, filename = splitdir(path) + any(readdir(dir) .== filename) + end +end + +## SHA1 ## + +struct SHA1 + bytes::Vector{UInt8} + function SHA1(bytes::Vector{UInt8}) + length(bytes) == 20 || + throw(ArgumentError("wrong number of bytes for SHA1 hash: $(length(bytes))")) + return new(bytes) + end +end +SHA1(s::AbstractString) = SHA1(hex2bytes(s)) + +string(hash::SHA1) = bytes2hex(hash.bytes) +print(io::IO, hash::SHA1) = bytes2hex(io, hash.bytes) +show(io::IO, hash::SHA1) = print(io, "SHA1(\"", hash, "\")") + +isless(a::SHA1, b::SHA1) = isless(a.bytes, b.bytes) +hash(a::SHA1, h::UInt) = hash((SHA1, a.bytes), h) +==(a::SHA1, b::SHA1) = a.bytes == b.bytes + +# fake uuid5 function (for self-assigned UUIDs) +# TODO: delete and use real uuid5 once it's in stdlib + +function uuid5(namespace::UUID, key::String) + u::UInt128 = 0 + h = hash(namespace) + for _ = 1:sizeof(u)÷sizeof(h) + u <<= sizeof(h) << 3 + u |= (h = hash(key, h)) + end + u &= 0xffffffffffff0fff3fffffffffffffff + u |= 0x00000000000050008000000000000000 + return UUID(u) +end + +const ns_dummy_uuid = UUID("fe0723d6-3a44-4c41-8065-ee0f42c8ceab") + +dummy_uuid(project_file::String) = uuid5(ns_dummy_uuid, realpath(project_file)) + +## package path slugs: turning UUID + SHA1 into a pair of 4-byte "slugs" ## + +const slug_chars = String(['A':'Z'; 'a':'z'; '0':'9']) + +function slug(x::UInt32, p::Int) + sprint(sizehint=p) do io + n = length(slug_chars) + for i = 1:p + x, d = divrem(x, n) + write(io, slug_chars[1+d]) + end + end +end + +function package_slug(uuid::UUID, p::Int=5) + crc = _crc32c(uuid) + return slug(crc, p) +end + +function version_slug(uuid::UUID, sha1::SHA1, p::Int=5) + crc = _crc32c(uuid) + crc = _crc32c(sha1.bytes, crc) + return slug(crc, p) +end + +## package identification: determine unique identity of package to be loaded ## + +function find_package(args...) + pkg = identify_package(args...) + pkg === nothing && return nothing + return locate_package(pkg) +end + +struct PkgId + uuid::Union{UUID,Nothing} + name::String + + PkgId(u::UUID, name::AbstractString) = new(UInt128(u) == 0 ? nothing : u, name) + PkgId(::Nothing, name::AbstractString) = new(nothing, name) +end +PkgId(name::AbstractString) = PkgId(nothing, name) + +function PkgId(m::Module, name::String = String(nameof(moduleroot(m)))) + uuid = UUID(ccall(:jl_module_uuid, NTuple{2, UInt64}, (Any,), m)) + UInt128(uuid) == 0 ? PkgId(name) : PkgId(uuid, name) +end + +==(a::PkgId, b::PkgId) = a.uuid == b.uuid && a.name == b.name + +function hash(pkg::PkgId, h::UInt) + h += 0xc9f248583a0ca36c % UInt + h = hash(pkg.uuid, h) + h = hash(pkg.name, h) + return h +end + +show(io::IO, pkg::PkgId) = + print(io, pkg.name, " [", pkg.uuid === nothing ? "top-level" : pkg.uuid, "]") + +function binpack(pkg::PkgId) + io = IOBuffer() + write(io, UInt8(0)) + uuid = pkg.uuid + write(io, uuid === nothing ? UInt128(0) : UInt128(uuid)) + write(io, pkg.name) + return String(take!(io)) +end + +function binunpack(s::String) + io = IOBuffer(s) + @assert read(io, UInt8) === 0x00 + uuid = read(io, UInt128) + name = read(io, String) + return PkgId(UUID(uuid), name) +end + +## package identity: given a package name and a context, try to return its identity ## + +identify_package(where::Module, name::String) = identify_package(PkgId(where), name) + +# identify_package computes the PkgId for `name` from the context of `where` +# or return `nothing` if no mapping exists for it +function identify_package(where::PkgId, name::String)::Union{Nothing,PkgId} + where.name === name && return where + where.uuid === nothing && return identify_package(name) # ignore `where` + for env in load_path() + uuid = manifest_deps_get(env, where, name) + uuid === nothing && continue # not found--keep looking + uuid.uuid === nothing || return uuid # found in explicit environment--use it + return nothing # found in implicit environment--return "not found" + end + return nothing +end + +# identify_package computes the PkgId for `name` from toplevel context +# by looking through the Project.toml files and directories +function identify_package(name::String)::Union{Nothing,PkgId} + for env in load_path() + uuid = project_deps_get(env, name) + uuid === nothing || return uuid # found--return it + end + return nothing +end + +function identify_package(name::String, names::String...) + pkg = identify_package(name) + pkg === nothing && return nothing + return identify_package(pkg, names...) +end + +# locate `tail(names)` package by following the search path graph through `names` starting from `where` +function identify_package(where::PkgId, name::String, names::String...) + pkg = identify_package(where, name) + pkg === nothing && return nothing + return identify_package(pkg, names...) +end + +## package location: given a package identity, find file to load ## + +function locate_package(pkg::PkgId)::Union{Nothing,String} + if pkg.uuid === nothing + for env in load_path() + # look for the toplevel pkg `pkg.name` in this entry + found = project_deps_get(env, pkg.name) + found === nothing && continue + if pkg == found + # pkg.name is present in this directory or project file, + # return the path the entry point for the code, if it could be found + # otherwise, signal failure + return implicit_manifest_uuid_path(env, pkg) + end + @assert found.uuid !== nothing + return locate_package(found) # restart search now that we know the uuid for pkg + end + else + for env in load_path() + path = manifest_uuid_path(env, pkg) + path === nothing || return entry_path(path, pkg.name) + end + end + return nothing +end + +""" + pathof(m::Module) + +Return the path of `m.jl` file that was used to `import` module `m`, +or `nothing` if `m` was not imported from a package. + +Use [`dirname`](@ref) to get the directory part and [`basename`](@ref) +to get the file name part of the path. +""" +function pathof(m::Module) + pkgid = get(Base.module_keys, m, nothing) + pkgid === nothing && return nothing + return Base.locate_package(pkgid) +end + +## generic project & manifest API ## + +const project_names = ("JuliaProject.toml", "Project.toml") +const manifest_names = ("JuliaManifest.toml", "Manifest.toml") + +# classify the LOAD_PATH entry to be one of: +# - `false`: nonexistant / nothing to see here +# - `true`: `env` is an implicit environment +# - `path`: the path of an explicit project file +function env_project_file(env::String)::Union{Bool,String} + if isdir(env) + for proj in project_names + project_file = joinpath(env, proj) + isfile_casesensitive(project_file) && return project_file + end + return true + elseif basename(env) in project_names && isfile_casesensitive(env) + return env + end + return false +end + +function project_deps_get(env::String, name::String)::Union{Nothing,PkgId} + project_file = env_project_file(env) + if project_file isa String + pkg_uuid = explicit_project_deps_get(project_file, name) + pkg_uuid === nothing || return PkgId(pkg_uuid, name) + elseif project_file + return implicit_project_deps_get(env, name) + end + return nothing +end + +function manifest_deps_get(env::String, where::PkgId, name::String)::Union{Nothing,PkgId} + @assert where.uuid !== nothing + project_file = env_project_file(env) + if project_file isa String + # first check if `where` names the Project itself + proj = project_file_name_uuid(project_file, where.name) + if proj == where + # if `where` matches the project, use [deps] section as manifest, and stop searching + pkg_uuid = explicit_project_deps_get(project_file, name) + return PkgId(pkg_uuid, name) + end + # look for manifest file and `where` stanza + return explicit_manifest_deps_get(project_file, where.uuid, name) + elseif project_file + # if env names a directory, search it + return implicit_manifest_deps_get(env, where, name) + end + return nothing +end + +function manifest_uuid_path(env::String, pkg::PkgId)::Union{Nothing,String} + project_file = env_project_file(env) + if project_file isa String + proj = project_file_name_uuid(project_file, pkg.name) + if proj == pkg + # if `pkg` matches the project, return the project itself + return project_file_path(project_file, pkg.name) + end + # look for manifest file and `where` stanza + return explicit_manifest_uuid_path(project_file, pkg) + elseif project_file + # if env names a directory, search it + return implicit_manifest_uuid_path(env, pkg) + end + return nothing +end + +# regular expressions for scanning project & manifest files + +const re_section = r"^\s*\[" +const re_array_of_tables = r"^\s*\[\s*\[" +const re_section_deps = r"^\s*\[\s*\"?deps\"?\s*\]\s*(?:#|$)" +const re_section_capture = r"^\s*\[\s*\[\s*\"?(\w+)\"?\s*\]\s*\]\s*(?:#|$)" +const re_subsection_deps = r"^\s*\[\s*\"?(\w+)\"?\s*\.\s*\"?deps\"?\s*\]\s*(?:#|$)" +const re_key_to_string = r"^\s*(\w+)\s*=\s*\"(.*)\"\s*(?:#|$)" +const re_uuid_to_string = r"^\s*uuid\s*=\s*\"(.*)\"\s*(?:#|$)" +const re_name_to_string = r"^\s*name\s*=\s*\"(.*)\"\s*(?:#|$)" +const re_path_to_string = r"^\s*path\s*=\s*\"(.*)\"\s*(?:#|$)" +const re_hash_to_string = r"^\s*git-tree-sha1\s*=\s*\"(.*)\"\s*(?:#|$)" +const re_manifest_to_string = r"^\s*manifest\s*=\s*\"(.*)\"\s*(?:#|$)" +const re_deps_to_any = r"^\s*deps\s*=\s*(.*?)\s*(?:#|$)" + +# find project file's top-level UUID entry (or nothing) +function project_file_name_uuid(project_file::String, name::String)::PkgId + pkg = open(project_file) do io + uuid = dummy_uuid(project_file) + for line in eachline(io) + occursin(re_section, line) && break + if (m = match(re_name_to_string, line)) !== nothing + name = String(m.captures[1]) + elseif (m = match(re_uuid_to_string, line)) !== nothing + uuid = UUID(m.captures[1]) + end + end + return PkgId(uuid, name) + end + return pkg +end + +function project_file_path(project_file::String, name::String)::String + path = open(project_file) do io + for line in eachline(io) + occursin(re_section, line) && break + if (m = match(re_path_to_string, line)) !== nothing + return String(m.captures[1]) + end + end + return "" + end + return joinpath(dirname(project_file), path) +end + + +# find project file's corresponding manifest file +function project_file_manifest_path(project_file::String)::Union{Nothing,String} + open(project_file) do io + dir = abspath(dirname(project_file)) + for line in eachline(io) + occursin(re_section, line) && break + if (m = match(re_manifest_to_string, line)) !== nothing + manifest_file = normpath(joinpath(dir, m.captures[1])) + isfile_casesensitive(manifest_file) && return manifest_file + return nothing # silently stop if the explicitly listed manifest file is not present + end + end + for mfst in manifest_names + manifest_file = joinpath(dir, mfst) + isfile_casesensitive(manifest_file) && return manifest_file + end + return nothing + end +end + +# find `name` in a manifest file and return its UUID +# return `nothing` on failure +function manifest_file_name_uuid(manifest_file::IO, name::String)::Union{Nothing,UUID} + name_section = false + uuid = nothing + for line in eachline(manifest_file) + if (m = match(re_section_capture, line)) !== nothing + name_section && break + name_section = (m.captures[1] == name) + elseif name_section + if (m = match(re_uuid_to_string, line)) !== nothing + uuid = UUID(m.captures[1]) + end + end + end + return uuid +end + +# given a directory (implicit env from LOAD_PATH) and a name, +# check if it is an implicit package +function entry_point_and_project_file_inside(dir::String, name::String)::Union{Tuple{Nothing,Nothing},Tuple{String,Nothing},Tuple{String,String}} + path = normpath(joinpath(dir, "src", "$name.jl")) + isfile_casesensitive(path) || return nothing, nothing + for proj in project_names + project_file = normpath(joinpath(dir, proj)) + isfile_casesensitive(project_file) || continue + return path, project_file + end + return path, nothing +end + +# given a project directory (implicit env from LOAD_PATH) and a name, +# find an entry point for `name`, and see if it has an associated project file +function entry_point_and_project_file(dir::String, name::String)::Union{Tuple{Nothing,Nothing},Tuple{String,Nothing},Tuple{String,String}} + path = normpath(joinpath(dir, "$name.jl")) + isfile_casesensitive(path) && return path, nothing + dir = joinpath(dir, name) + path, project_file = entry_point_and_project_file_inside(dir, name) + path === nothing || return path, project_file + dir = dir * ".jl" + path, project_file = entry_point_and_project_file_inside(dir, name) + path === nothing || return path, project_file + return nothing, nothing +end + +# given a path and a name, return the entry point +function entry_path(path::String, name::String)::Union{Nothing,String} + isfile_casesensitive(path) && return normpath(path) + path = normpath(joinpath(path, "src", "$name.jl")) + isfile_casesensitive(path) && return path + return nothing # source not found +end + +# given a project path (project directory or entry point) +# return the project file +function package_path_to_project_file(path::String)::Union{Nothing,String} + if !isdir(path) + dir = dirname(path) + basename(dir) == "src" || return nothing + path = dirname(dir) + end + for proj in project_names + project_file = joinpath(path, proj) + isfile_casesensitive(project_file) && return project_file + end + return nothing +end + +## explicit project & manifest API ## + +# find project file root or deps `name => uuid` mapping +# return `nothing` if `name` is not found +function explicit_project_deps_get(project_file::String, name::String)::Union{Nothing,UUID} + pkg_uuid = open(project_file) do io + root_name = nothing + root_uuid = dummy_uuid(project_file) + state = :top + for line in eachline(io) + if occursin(re_section, line) + state === :top && root_name == name && return root_uuid + state = occursin(re_section_deps, line) ? :deps : :other + elseif state === :top + if (m = match(re_name_to_string, line)) !== nothing + root_name = String(m.captures[1]) + elseif (m = match(re_uuid_to_string, line)) !== nothing + root_uuid = UUID(m.captures[1]) + end + elseif state === :deps + if (m = match(re_key_to_string, line)) !== nothing + m.captures[1] == name && return UUID(m.captures[2]) + end + end + end + return root_name == name ? root_uuid : nothing + end + return pkg_uuid +end + +# find `where` stanza and return the PkgId for `name` +# return `nothing` if it did not find `where` (indicating caller should continue searching) +function explicit_manifest_deps_get(project_file::String, where::UUID, name::String)::Union{Nothing,PkgId} + manifest_file = project_file_manifest_path(project_file) + manifest_file === nothing && return nothing # manifest not found--keep searching LOAD_PATH + found_or_uuid = open(manifest_file) do io + uuid = deps = nothing + state = :other + # first search the manifest for the deps section associated with `where` (by uuid) + for line in eachline(io) + if occursin(re_array_of_tables, line) + uuid == where && break + uuid = deps = nothing + state = :stanza + elseif state === :stanza + if (m = match(re_uuid_to_string, line)) !== nothing + uuid = UUID(m.captures[1]) + elseif (m = match(re_deps_to_any, line)) !== nothing + deps = String(m.captures[1]) + elseif occursin(re_subsection_deps, line) + state = :deps + elseif occursin(re_section, line) + state = :other + end + elseif state === :deps && uuid == where + # [deps] section format gives both name and uuid + if (m = match(re_key_to_string, line)) !== nothing + m.captures[1] == name && return UUID(m.captures[2]) + end + end + end + # now search through `deps = []` string to see if we have an entry for `name` + uuid == where || return false + deps === nothing && return true + # TODO: handle inline table syntax + if deps[1] != '[' || deps[end] != ']' + @warn "Unexpected TOML deps format:\n$deps" + return false + end + occursin(repr(name), deps) || return true + seekstart(io) # rewind IO handle + # finally, find out the `uuid` associated with `name` + return something(manifest_file_name_uuid(io, name), false) + end + found_or_uuid isa UUID && return PkgId(found_or_uuid, name) + found_or_uuid && return PkgId(name) + return nothing +end + +# find `uuid` stanza, return the corresponding path +function explicit_manifest_uuid_path(project_file::String, pkg::PkgId)::Union{Nothing,String} + manifest_file = project_file_manifest_path(project_file) + manifest_file === nothing && return nothing # no manifest, skip env + open(manifest_file) do io + uuid = name = path = hash = nothing + for line in eachline(io) + if (m = match(re_section_capture, line)) !== nothing + uuid == pkg.uuid && break + name = String(m.captures[1]) + path = hash = nothing + elseif (m = match(re_uuid_to_string, line)) !== nothing + uuid = UUID(m.captures[1]) + elseif (m = match(re_path_to_string, line)) !== nothing + path = String(m.captures[1]) + elseif (m = match(re_hash_to_string, line)) !== nothing + hash = SHA1(m.captures[1]) + end + end + uuid == pkg.uuid || return nothing + name == pkg.name || return nothing # TODO: allow a mismatch? + if path !== nothing + path = normpath(abspath(dirname(manifest_file), path)) + return path + end + hash === nothing && return nothing + # Keep the 4 since it used to be the default + for slug in (version_slug(uuid, hash, 4), version_slug(uuid, hash)) + for depot in DEPOT_PATH + path = abspath(depot, "packages", name, slug) + ispath(path) && return path + end + end + return nothing + end +end + +## implicit project & manifest API ## + +# look for an entry point for `name` from a top-level package (no environment) +# otherwise return `nothing` to indicate the caller should keep searching +function implicit_project_deps_get(dir::String, name::String)::Union{Nothing,PkgId} + path, project_file = entry_point_and_project_file(dir, name) + if project_file === nothing + path === nothing && return nothing + return PkgId(name) + end + proj = project_file_name_uuid(project_file, name) + proj.name == name || return nothing + return proj +end + +# look for an entry-point for `name`, check that UUID matches +# if there's a project file, look up `name` in its deps and return that +# otherwise return `nothing` to indicate the caller should keep searching +function implicit_manifest_deps_get(dir::String, where::PkgId, name::String)::Union{Nothing,PkgId} + @assert where.uuid !== nothing + project_file = entry_point_and_project_file(dir, where.name)[2] + project_file === nothing && return nothing # a project file is mandatory for a package with a uuid + proj = project_file_name_uuid(project_file, where.name) + proj == where || return nothing # verify that this is the correct project file + # this is the correct project, so stop searching here + pkg_uuid = explicit_project_deps_get(project_file, name) + return PkgId(pkg_uuid, name) +end + +# look for an entry-point for `pkg` and return its path if UUID matches +function implicit_manifest_uuid_path(dir::String, pkg::PkgId)::Union{Nothing,String} + path, project_file = entry_point_and_project_file(dir, pkg.name) + if project_file === nothing + pkg.uuid === nothing || return nothing + return path + end + proj = project_file_name_uuid(project_file, pkg.name) + proj == pkg || return nothing + return path +end + +## other code loading functionality ## + +function find_source_file(path::AbstractString) + (isabspath(path) || isfile(path)) && return path + base_path = joinpath(Sys.BINDIR::String, DATAROOTDIR, "julia", "base", path) + return isfile(base_path) ? base_path : nothing +end + +cache_file_entry(pkg::PkgId) = joinpath( + "compiled", + "v$(VERSION.major).$(VERSION.minor)", + pkg.uuid === nothing ? "" : pkg.name), + pkg.uuid === nothing ? pkg.name : package_slug(pkg.uuid) + +function find_all_in_cache_path(pkg::PkgId) + paths = String[] + entrypath, entryfile = cache_file_entry(pkg) + for path in joinpath.(DEPOT_PATH, entrypath) + isdir(path) || continue + for file in readdir(path) + if !((pkg.uuid === nothing && file == entryfile * ".ji") || + (pkg.uuid !== nothing && startswith(file, entryfile * "_"))) + continue + end + filepath = joinpath(path, file) + isfile_casesensitive(filepath) && push!(paths, filepath) + end + end + return paths +end + +# these return either the array of modules loaded from the path / content given +# or an Exception that describes why it couldn't be loaded +# and it reconnects the Base.Docs.META +function _include_from_serialized(path::String, depmods::Vector{Any}) + sv = ccall(:jl_restore_incremental, Any, (Cstring, Any), path, depmods) + if isa(sv, Exception) + return sv + end + restored = sv[1] + if !isa(restored, Exception) + for M in restored::Vector{Any} + M = M::Module + if isdefined(M, Base.Docs.META) + push!(Base.Docs.modules, M) + end + if parentmodule(M) === M + register_root_module(M) + end + end + end + isassigned(sv, 2) && ccall(:jl_init_restored_modules, Cvoid, (Any,), sv[2]) + return restored +end + +function _tryrequire_from_serialized(modkey::PkgId, build_id::UInt64, modpath::Union{Nothing, String}) + if root_module_exists(modkey) + M = root_module(modkey) + if PkgId(M) == modkey && module_build_id(M) === build_id + return M + end + else + if modpath === nothing + modpath = locate_package(modkey) + modpath === nothing && return nothing + end + mod = _require_search_from_serialized(modkey, String(modpath)) + if !isa(mod, Bool) + for callback in package_callbacks + invokelatest(callback, modkey) + end + for M in mod::Vector{Any} + if PkgId(M) == modkey && module_build_id(M) === build_id + return M + end + end + end + end + return nothing +end + +function _require_from_serialized(path::String) + # loads a precompile cache file, ignoring stale_cachfile tests + # load all of the dependent modules first + local depmodnames + io = open(path, "r") + try + isvalid_cache_header(io) || return ArgumentError("Invalid header in cache file $path.") + depmodnames = parse_cache_header(io)[3] + isvalid_file_crc(io) || return ArgumentError("Invalid checksum in cache file $path.") + finally + close(io) + end + ndeps = length(depmodnames) + depmods = Vector{Any}(undef, ndeps) + for i in 1:ndeps + modkey, build_id = depmodnames[i] + dep = _tryrequire_from_serialized(modkey, build_id, nothing) + dep === nothing && return ErrorException("Required dependency $modkey failed to load from a cache file.") + depmods[i] = dep::Module + end + # then load the file + return _include_from_serialized(path, depmods) +end + +# returns `true` if require found a precompile cache for this sourcepath, but couldn't load it +# returns `false` if the module isn't known to be precompilable +# returns the set of modules restored if the cache load succeeded +function _require_search_from_serialized(pkg::PkgId, sourcepath::String) + paths = find_all_in_cache_path(pkg) + for path_to_try in paths::Vector{String} + staledeps = stale_cachefile(sourcepath, path_to_try) + if staledeps === true + continue + end + try + touch(path_to_try) # update timestamp of precompilation file + catch # file might be read-only and then we fail to update timestamp, which is fine + end + # finish loading module graph into staledeps + for i in 1:length(staledeps) + dep = staledeps[i] + dep isa Module && continue + modpath, modkey, build_id = dep::Tuple{String, PkgId, UInt64} + dep = _tryrequire_from_serialized(modkey, build_id, modpath) + if dep === nothing + @debug "Required dependency $modkey failed to load from cache file for $modpath." + staledeps = true + break + end + staledeps[i] = dep::Module + end + if staledeps === true + continue + end + restored = _include_from_serialized(path_to_try, staledeps) + if isa(restored, Exception) + @debug "Deserialization checks failed while attempting to load cache from $path_to_try" exception=restored + else + return restored + end + end + return !isempty(paths) +end + +# to synchronize multiple tasks trying to import/using something +const package_locks = Dict{PkgId,Condition}() + +# to notify downstream consumers that a module was successfully loaded +# Callbacks take the form (mod::Base.PkgId) -> nothing. +# WARNING: This is an experimental feature and might change later, without deprecation. +const package_callbacks = Any[] +# to notify downstream consumers that a file has been included into a particular module +# Callbacks take the form (mod::Module, filename::String) -> nothing +# WARNING: This is an experimental feature and might change later, without deprecation. +const include_callbacks = Any[] + +# used to optionally track dependencies when requiring a module: +const _concrete_dependencies = Pair{PkgId,UInt64}[] # these dependency versions are "set in stone", and the process should try to avoid invalidating them +const _require_dependencies = Any[] # a list of (mod, path, mtime) tuples that are the file dependencies of the module currently being precompiled +const _track_dependencies = Ref(false) # set this to true to track the list of file dependencies +function _include_dependency(mod::Module, _path::AbstractString) + prev = source_path(nothing) + if prev === nothing + path = abspath(_path) + else + path = normpath(joinpath(dirname(prev), _path)) + end + if _track_dependencies[] + push!(_require_dependencies, (mod, path, mtime(path))) + end + return path, prev +end + +""" + include_dependency(path::AbstractString) + +In a module, declare that the file specified by `path` (relative or absolute) is a +dependency for precompilation; that is, the module will need to be recompiled if this file +changes. + +This is only needed if your module depends on a file that is not used via [`include`](@ref). It has +no effect outside of compilation. +""" +function include_dependency(path::AbstractString) + _include_dependency(Main, path) + return nothing +end + +# we throw PrecompilableError when a module doesn't want to be precompiled +struct PrecompilableError <: Exception end +function show(io::IO, ex::PrecompilableError) + print(io, "Declaring __precompile__(false) is not allowed in files that are being precompiled.") +end +precompilableerror(ex::PrecompilableError) = true +precompilableerror(ex::WrappedException) = precompilableerror(ex.error) +precompilableerror(@nospecialize ex) = false + +# Call __precompile__(false) at the top of a tile prevent it from being precompiled (false) +""" + __precompile__(isprecompilable::Bool) + +Specify whether the file calling this function is precompilable, defaulting to `true`. +If a module or file is *not* safely precompilable, it should call `__precompile__(false)` in +order to throw an error if Julia attempts to precompile it. +""" +@noinline function __precompile__(isprecompilable::Bool=true) + if !isprecompilable && ccall(:jl_generating_output, Cint, ()) != 0 + throw(PrecompilableError()) + end + nothing +end + +# require always works in Main scope and loads files from node 1 +const toplevel_load = Ref(true) + +const full_warning_showed = Ref(false) +const modules_warned_for = Set{PkgId}() + +""" + require(into::Module, module::Symbol) + +This function is part of the implementation of [`using`](@ref) / [`import`](@ref), if a module is not +already defined in `Main`. It can also be called directly to force reloading a module, +regardless of whether it has been loaded before (for example, when interactively developing +libraries). + +Loads a source file, in the context of the `Main` module, on every active node, searching +standard locations for files. `require` is considered a top-level operation, so it sets the +current `include` path but does not use it to search for files (see help for [`include`](@ref)). +This function is typically used to load library code, and is implicitly called by `using` to +load packages. + +When searching for files, `require` first looks for package code in the global array +[`LOAD_PATH`](@ref). `require` is case-sensitive on all platforms, including those with +case-insensitive filesystems like macOS and Windows. + +For more details regarding code loading, see the manual sections on [modules](@ref modules) and +[parallel computing](@ref code-availability). +""" +function require(into::Module, mod::Symbol) + uuidkey = identify_package(into, String(mod)) + # Core.println("require($(PkgId(into)), $mod) -> $uuidkey") + if uuidkey === nothing + where = PkgId(into) + if where.uuid === nothing + throw(ArgumentError(""" + Package $mod not found in current path: + - Run `import Pkg; Pkg.add($(repr(String(mod))))` to install the $mod package. + """)) + else + s = """ + Package $(where.name) does not have $mod in its dependencies: + - If you have $(where.name) checked out for development and have + added $mod as a dependency but haven't updated your primary + environment's manifest file, try `Pkg.resolve()`. + - Otherwise you may need to report an issue with $(where.name)""" + + uuidkey = identify_package(PkgId(string(into)), String(mod)) + uuidkey === nothing && throw(ArgumentError(s)) + + # fall back to toplevel loading with a warning + if !(where in modules_warned_for) + @warn string( + full_warning_showed[] ? "" : s, "\n", + string("Loading $(mod) into $(where.name) from project dependency, ", + "future warnings for $(where.name) are suppressed.") + ) _module = nothing _file = nothing _group = nothing + push!(modules_warned_for, where) + end + full_warning_showed[] = true + end + end + if _track_dependencies[] + push!(_require_dependencies, (into, binpack(uuidkey), 0.0)) + end + return require(uuidkey) +end + +function require(uuidkey::PkgId) + if !root_module_exists(uuidkey) + _require(uuidkey) + # After successfully loading, notify downstream consumers + for callback in package_callbacks + invokelatest(callback, uuidkey) + end + end + return root_module(uuidkey) +end + +const loaded_modules = Dict{PkgId,Module}() +const module_keys = IdDict{Module,PkgId}() # the reverse + +is_root_module(m::Module) = haskey(module_keys, m) +root_module_key(m::Module) = module_keys[m] + +function register_root_module(m::Module) + key = PkgId(m, String(nameof(m))) + if haskey(loaded_modules, key) + oldm = loaded_modules[key] + if oldm !== m + @warn "Replacing module `$(key.name)`" + end + end + loaded_modules[key] = m + module_keys[m] = key + nothing +end + +register_root_module(Core) +register_root_module(Base) +register_root_module(Main) + +# This is used as the current module when loading top-level modules. +# It has the special behavior that modules evaluated in it get added +# to the loaded_modules table instead of getting bindings. +baremodule __toplevel__ +using Base +end + +# get a top-level Module from the given key +root_module(key::PkgId) = loaded_modules[key] +root_module(where::Module, name::Symbol) = + root_module(identify_package(where, String(name))) + +root_module_exists(key::PkgId) = haskey(loaded_modules, key) +loaded_modules_array() = collect(values(loaded_modules)) + +function unreference_module(key::PkgId) + if haskey(loaded_modules, key) + m = pop!(loaded_modules, key) + # need to ensure all modules are GC rooted; will still be referenced + # in module_keys + end +end + +function _require(pkg::PkgId) + # handle recursive calls to require + loading = get(package_locks, pkg, false) + if loading !== false + # load already in progress for this module + wait(loading) + return + end + package_locks[pkg] = Condition() + + last = toplevel_load[] + try + toplevel_load[] = false + # perform the search operation to select the module file require intends to load + path = locate_package(pkg) + if path === nothing + throw(ArgumentError(""" + Package $pkg is required but does not seem to be installed: + - Run `Pkg.instantiate()` to install all recorded dependencies. + """)) + end + + # attempt to load the module file via the precompile cache locations + if JLOptions().use_compiled_modules != 0 + m = _require_search_from_serialized(pkg, path) + if !isa(m, Bool) + return + end + end + + # if the module being required was supposed to have a particular version + # but it was not handled by the precompile loader, complain + for (concrete_pkg, concrete_build_id) in _concrete_dependencies + if pkg == concrete_pkg + @warn """Module $(pkg.name) with build ID $concrete_build_id is missing from the cache. + This may mean $pkg does not support precompilation but is imported by a module that does.""" + if JLOptions().incremental != 0 + # during incremental precompilation, this should be fail-fast + throw(PrecompilableError()) + end + end + end + + if JLOptions().use_compiled_modules != 0 + if (0 == ccall(:jl_generating_output, Cint, ())) || (JLOptions().incremental != 0) + # spawn off a new incremental pre-compile task for recursive `require` calls + # or if the require search declared it was pre-compiled before (and therefore is expected to still be pre-compilable) + cachefile = compilecache(pkg, path) + if isa(cachefile, Exception) + if precompilableerror(cachefile) + verbosity = isinteractive() ? CoreLogging.Info : CoreLogging.Debug + @logmsg verbosity "Skipping precompilation since __precompile__(false). Importing $pkg." + else + @warn "The call to compilecache failed to create a usable precompiled cache file for $pkg" exception=m + end + # fall-through to loading the file locally + else + m = _require_from_serialized(cachefile) + if isa(m, Exception) + @warn "The call to compilecache failed to create a usable precompiled cache file for $pkg" exception=m + else + return + end + end + end + end + + # just load the file normally via include + # for unknown dependencies + uuid = pkg.uuid + uuid = (uuid === nothing ? (UInt64(0), UInt64(0)) : convert(NTuple{2, UInt64}, uuid)) + old_uuid = ccall(:jl_module_uuid, NTuple{2, UInt64}, (Any,), __toplevel__) + if uuid !== old_uuid + ccall(:jl_set_module_uuid, Cvoid, (Any, NTuple{2, UInt64}), __toplevel__, uuid) + end + try + include_relative(__toplevel__, path) + return + finally + if uuid !== old_uuid + ccall(:jl_set_module_uuid, Cvoid, (Any, NTuple{2, UInt64}), __toplevel__, old_uuid) + end + end + finally + toplevel_load[] = last + loading = pop!(package_locks, pkg) + notify(loading, all=true) + end + nothing +end + +# relative-path load + +""" + include_string(m::Module, code::AbstractString, filename::AbstractString="string") + +Like [`include`](@ref), except reads code from the given string rather than from a file. +""" +include_string(m::Module, txt::String, fname::String) = + ccall(:jl_load_file_string, Any, (Ptr{UInt8}, Csize_t, Cstring, Any), + txt, sizeof(txt), fname, m) + +include_string(m::Module, txt::AbstractString, fname::AbstractString="string") = + include_string(m, String(txt), String(fname)) + +function source_path(default::Union{AbstractString,Nothing}="") + s = current_task().storage + if s !== nothing && haskey(s, :SOURCE_PATH) + return s[:SOURCE_PATH] + end + return default +end + +function source_dir() + p = source_path(nothing) + return p === nothing ? pwd() : dirname(p) +end + +include_relative(mod::Module, path::AbstractString) = include_relative(mod, String(path)) +function include_relative(mod::Module, _path::String) + path, prev = _include_dependency(mod, _path) + for callback in include_callbacks # to preserve order, must come before Core.include + invokelatest(callback, mod, path) + end + tls = task_local_storage() + tls[:SOURCE_PATH] = path + local result + try + result = Core.include(mod, path) + finally + if prev === nothing + delete!(tls, :SOURCE_PATH) + else + tls[:SOURCE_PATH] = prev + end + end + return result +end + +""" + Base.include([m::Module,] path::AbstractString) + +Evaluate the contents of the input source file in the global scope of module `m`. +Every module (except those defined with [`baremodule`](@ref)) has its own 1-argument +definition of `include`, which evaluates the file in that module. +Returns the result of the last evaluated expression of the input file. During including, +a task-local include path is set to the directory containing the file. Nested calls to +`include` will search relative to that path. This function is typically used to load source +interactively, or to combine files in packages that are broken into multiple source files. +""" +Base.include # defined in sysimg.jl + +""" + evalfile(path::AbstractString, args::Vector{String}=String[]) + +Load the file using [`include`](@ref), evaluate all expressions, +and return the value of the last one. +""" +function evalfile(path::AbstractString, args::Vector{String}=String[]) + return Core.eval(Module(:__anon__), + Expr(:toplevel, + :(const ARGS = $args), + :(eval(x) = $(Expr(:core, :eval))(__anon__, x)), + :(include(x) = $(Expr(:top, :include))(__anon__, x)), + :(include($path)))) +end +evalfile(path::AbstractString, args::Vector) = evalfile(path, String[args...]) + +function load_path_setup_code(load_path::Bool=true) + code = """ + append!(empty!(Base.DEPOT_PATH), $(repr(map(abspath, DEPOT_PATH)))) + append!(empty!(Base.DL_LOAD_PATH), $(repr(map(abspath, DL_LOAD_PATH)))) + """ + if load_path + load_path = map(abspath, Base.load_path()) + path_sep = Sys.iswindows() ? ';' : ':' + any(path -> path_sep in path, load_path) && + error("LOAD_PATH entries cannot contain $(repr(path_sep))") + code *= """ + append!(empty!(Base.LOAD_PATH), $(repr(load_path))) + ENV["JULIA_LOAD_PATH"] = $(repr(join(load_path, Sys.iswindows() ? ';' : ':'))) + Base.HOME_PROJECT[] = Base.ACTIVE_PROJECT[] = nothing + """ + end + return code +end + +function create_expr_cache(input::String, output::String, concrete_deps::typeof(_concrete_dependencies), uuid::Union{Nothing,UUID}) + rm(output, force=true) # Remove file if it exists + code_object = """ + while !eof(stdin) + code = readuntil(stdin, '\\0') + eval(Meta.parse(code)) + end + """ + io = open(pipeline(`$(julia_cmd()) -O0 + --output-ji $output --output-incremental=yes + --startup-file=no --history-file=no --warn-overwrite=yes + --color=$(have_color ? "yes" : "no") + --eval $code_object`, stderr=stderr), + "w", stdout) + in = io.in + try + write(in, """ + begin + $(Base.load_path_setup_code()) + Base._track_dependencies[] = true + Base.empty!(Base._concrete_dependencies) + """) + for (pkg, build_id) in concrete_deps + pkg_str = if pkg.uuid === nothing + "Base.PkgId($(repr(pkg.name)))" + else + "Base.PkgId(Base.UUID(\"$(pkg.uuid)\"), $(repr(pkg.name)))" + end + write(in, "Base.push!(Base._concrete_dependencies, $pkg_str => $(repr(build_id)))\n") + end + write(io, "end\0") + uuid_tuple = uuid === nothing ? (0, 0) : convert(NTuple{2, UInt64}, uuid) + write(in, "ccall(:jl_set_module_uuid, Cvoid, (Any, NTuple{2, UInt64}), Base.__toplevel__, $uuid_tuple)\0") + source = source_path(nothing) + if source !== nothing + write(in, "task_local_storage()[:SOURCE_PATH] = $(repr(source))\0") + end + write(in, """ + try + Base.include(Base.__toplevel__, $(repr(abspath(input)))) + catch ex + Base.precompilableerror(ex) || Base.rethrow() + Base.@debug "Aborting `createexprcache'" exception=(Base.ErrorException("Declaration of __precompile__(false) not allowed"), Base.catch_backtrace()) + Base.exit(125) # we define status = 125 means PrecompileableError + end\0""") + # TODO: cleanup is probably unnecessary here + if source !== nothing + write(in, "delete!(task_local_storage(), :SOURCE_PATH)\0") + end + write(in, "ccall(:jl_set_module_uuid, Cvoid, (Any, NTuple{2, UInt64}), Base.__toplevel__, (0, 0))\0") + close(in) + catch + close(in) + process_running(io) && Timer(t -> kill(io), 5.0) # wait a short time before killing the process to give it a chance to clean up on its own first + rethrow() + end + return io +end + +function compilecache_path(pkg::PkgId)::String + entrypath, entryfile = cache_file_entry(pkg) + cachepath = joinpath(DEPOT_PATH[1], entrypath) + isdir(cachepath) || mkpath(cachepath) + if pkg.uuid === nothing + abspath(cachepath, entryfile) * ".ji" + else + project_precompile_slug = slug(_crc32c(something(Base.active_project(), "")), 5) + abspath(cachepath, string(entryfile, "_", project_precompile_slug, ".ji")) + end +end + +""" + Base.compilecache(module::PkgId) + +Creates a precompiled cache file for a module and all of its dependencies. +This can be used to reduce package load times. Cache files are stored in +`DEPOT_PATH[1]/compiled`. See [Module initialization and precompilation](@ref) +for important notes. +""" +function compilecache(pkg::PkgId) + path = locate_package(pkg) + path === nothing && throw(ArgumentError("$pkg not found during precompilation")) + return compilecache(pkg, path) +end + +const MAX_NUM_PRECOMPILE_FILES = 10 + +function compilecache(pkg::PkgId, path::String) + # decide where to put the resulting cache file + cachefile = compilecache_path(pkg) + # prune the directory with cache files + if pkg.uuid !== nothing + cachepath = dirname(cachefile) + entrypath, entryfile = cache_file_entry(pkg) + cachefiles = filter!(x -> startswith(x, entryfile * "_"), readdir(cachepath)) + if length(cachefiles) >= MAX_NUM_PRECOMPILE_FILES + idx = findmin(mtime.(joinpath.(cachepath, cachefiles)))[2] + rm(joinpath(cachepath, cachefiles[idx])) + end + end + # build up the list of modules that we want the precompile process to preserve + concrete_deps = copy(_concrete_dependencies) + for (key, mod) in loaded_modules + if !(mod === Main || mod === Core || mod === Base) + push!(concrete_deps, key => module_build_id(mod)) + end + end + # run the expression and cache the result + verbosity = isinteractive() ? CoreLogging.Info : CoreLogging.Debug + @logmsg verbosity "Precompiling $pkg" + p = create_expr_cache(path, cachefile, concrete_deps, pkg.uuid) + if success(p) + # append checksum to the end of the .ji file: + open(cachefile, "a+") do f + write(f, _crc32c(seekstart(f))) + end + elseif p.exitcode == 125 + return PrecompilableError() + else + error("Failed to precompile $pkg to $cachefile.") + end + return cachefile +end + +module_build_id(m::Module) = ccall(:jl_module_build_id, UInt64, (Any,), m) + +isvalid_cache_header(f::IOStream) = (0 != ccall(:jl_read_verify_header, Cint, (Ptr{Cvoid},), f.ios)) +isvalid_file_crc(f::IOStream) = (_crc32c(seekstart(f), filesize(f) - 4) == read(f, UInt32)) + +function parse_cache_header(f::IO) + modules = Vector{Pair{PkgId, UInt64}}() + while true + n = read(f, Int32) + n == 0 && break + sym = String(read(f, n)) # module name + uuid = UUID((read(f, UInt64), read(f, UInt64))) # pkg UUID + build_id = read(f, UInt64) # build UUID (mostly just a timestamp) + push!(modules, PkgId(uuid, sym) => build_id) + end + totbytes = read(f, Int64) # total bytes for file dependencies + # read the list of requirements + # and split the list into include and requires statements + includes = Tuple{PkgId, String, Float64}[] + requires = Pair{PkgId, PkgId}[] + while true + n2 = read(f, Int32) + n2 == 0 && break + depname = String(read(f, n2)) + mtime = read(f, Float64) + n1 = read(f, Int32) + # map ids to keys + modkey = (n1 == 0) ? PkgId("") : modules[n1].first + if n1 != 0 + # consume (and ignore) the module path too + while true + n1 = read(f, Int32) + totbytes -= 4 + n1 == 0 && break + skip(f, n1) # String(read(f, n1)) + totbytes -= n1 + end + end + if depname[1] == '\0' + push!(requires, modkey => binunpack(depname)) + else + push!(includes, (modkey, depname, mtime)) + end + totbytes -= 4 + 4 + n2 + 8 + end + @assert totbytes == 12 "header of cache file appears to be corrupt" + srctextpos = read(f, Int64) + # read the list of modules that are required to be present during loading + required_modules = Vector{Pair{PkgId, UInt64}}() + while true + n = read(f, Int32) + n == 0 && break + sym = String(read(f, n)) # module name + uuid = UUID((read(f, UInt64), read(f, UInt64))) # pkg UUID + build_id = read(f, UInt64) # build id + push!(required_modules, PkgId(uuid, sym) => build_id) + end + return modules, (includes, requires), required_modules, srctextpos +end + +function parse_cache_header(cachefile::String) + io = open(cachefile, "r") + try + !isvalid_cache_header(io) && throw(ArgumentError("Invalid header in cache file $cachefile.")) + return parse_cache_header(io) + finally + close(io) + end +end + +function cache_dependencies(f::IO) + defs, (includes, requires), modules = parse_cache_header(f) + return modules, map(mod_fl_mt -> (mod_fl_mt[2], mod_fl_mt[3]), includes) # discard the module +end + +function cache_dependencies(cachefile::String) + io = open(cachefile, "r") + try + !isvalid_cache_header(io) && throw(ArgumentError("Invalid header in cache file $cachefile.")) + return cache_dependencies(io) + finally + close(io) + end +end + +function read_dependency_src(io::IO, filename::AbstractString) + modules, (includes, requires), required_modules, srctextpos = parse_cache_header(io) + srctextpos == 0 && error("no source-text stored in cache file") + seek(io, srctextpos) + return _read_dependency_src(io, filename) +end + +function _read_dependency_src(io::IO, filename::AbstractString) + while !eof(io) + filenamelen = read(io, Int32) + filenamelen == 0 && break + fn = String(read(io, filenamelen)) + len = read(io, UInt64) + if fn == filename + return String(read(io, len)) + end + seek(io, position(io) + len) + end + error(filename, " is not stored in the source-text cache") +end + +function read_dependency_src(cachefile::String, filename::AbstractString) + io = open(cachefile, "r") + try + !isvalid_cache_header(io) && throw(ArgumentError("Invalid header in cache file $cachefile.")) + return read_dependency_src(io, filename) + finally + close(io) + end +end + +# returns true if it "cachefile.ji" is stale relative to "modpath.jl" +# otherwise returns the list of dependencies to also check +function stale_cachefile(modpath::String, cachefile::String) + io = open(cachefile, "r") + try + if !isvalid_cache_header(io) + @debug "Rejecting cache file $cachefile due to it containing an invalid cache header" + return true # invalid cache file + end + (modules, (includes, requires), required_modules) = parse_cache_header(io) + modules = Dict{PkgId, UInt64}(modules) + + # Check if transitive dependencies can be fulfilled + ndeps = length(required_modules) + depmods = Vector{Any}(undef, ndeps) + for i in 1:ndeps + req_key, req_build_id = required_modules[i] + # Module is already loaded + if root_module_exists(req_key) + M = root_module(req_key) + if PkgId(M) == req_key && module_build_id(M) === req_build_id + depmods[i] = M + else + @debug "Rejecting cache file $cachefile because module $req_key is already loaded and incompatible." + return true # Won't be able to fulfill dependency + end + else + path = locate_package(req_key) + if path === nothing + @debug "Rejecting cache file $cachefile because dependency $req_key not found." + return true # Won't be able to fulfill dependency + end + depmods[i] = (path, req_key, req_build_id) + end + end + + # check if this file is going to provide one of our concrete dependencies + # or if it provides a version that conflicts with our concrete dependencies + # or neither + skip_timecheck = false + for (req_key, req_build_id) in _concrete_dependencies + build_id = get(modules, req_key, UInt64(0)) + if build_id !== UInt64(0) + if build_id === req_build_id + skip_timecheck = true + break + end + @debug "Rejecting cache file $cachefile because it provides the wrong uuid (got $build_id) for $mod (want $req_build_id)" + return true # cachefile doesn't provide the required version of the dependency + end + end + + # now check if this file is fresh relative to its source files + if !skip_timecheck + if !samefile(includes[1][2], modpath) + @debug "Rejecting cache file $cachefile because it is for file $(includes[1][2])) not file $modpath" + return true # cache file was compiled from a different path + end + for (modkey, req_modkey) in requires + # verify that `require(modkey, name(req_modkey))` ==> `req_modkey` + if identify_package(modkey, req_modkey.name) != req_modkey + @debug "Rejecting cache file $cachefile because uuid mapping for $modkey => $req_modkey has changed" + return true + end + end + for (_, f, ftime_req) in includes + # Issue #13606: compensate for Docker images rounding mtimes + # Issue #20837: compensate for GlusterFS truncating mtimes to microseconds + ftime = mtime(f) + if ftime != ftime_req && ftime != floor(ftime_req) && ftime != trunc(ftime_req, digits=6) + @debug "Rejecting stale cache file $cachefile (mtime $ftime_req) because file $f (mtime $ftime) has changed" + return true + end + end + end + + if !isvalid_file_crc(io) + @debug "Rejecting cache file $cachefile because it has an invalid checksum" + return true + end + + return depmods # fresh cachefile + finally + close(io) + end +end + +""" + @__FILE__ -> AbstractString + +Expand to a string with the path to the file containing the +macrocall, or an empty string if evaluated by `julia -e `. +Return `nothing` if the macro was missing parser source information. +Alternatively see [`PROGRAM_FILE`](@ref). +""" +macro __FILE__() + __source__.file === nothing && return nothing + return String(__source__.file) +end + +""" + @__DIR__ -> AbstractString + +Expand to a string with the absolute path to the directory of the file +containing the macrocall. +Return the current working directory if run from a REPL or if evaluated by `julia -e `. +""" +macro __DIR__() + __source__.file === nothing && return nothing + _dirname = dirname(String(__source__.file)) + return isempty(_dirname) ? pwd() : abspath(_dirname) +end diff --git a/base/lock.jl b/base/lock.jl new file mode 100644 index 0000000..ad72828 --- /dev/null +++ b/base/lock.jl @@ -0,0 +1,314 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Advisory reentrant lock +""" + ReentrantLock() + +Creates a re-entrant lock for synchronizing [`Task`](@ref)s. +The same task can acquire the lock as many times as required. +Each [`lock`](@ref) must be matched with an [`unlock`](@ref). +""" +mutable struct ReentrantLock <: AbstractLock + locked_by::Union{Task, Nothing} + cond_wait::GenericCondition{Threads.SpinLock} + reentrancy_cnt::Int + + ReentrantLock() = new(nothing, GenericCondition{Threads.SpinLock}(), 0) +end + +assert_havelock(l::ReentrantLock) = assert_havelock(l, l.locked_by) + +""" + islocked(lock) -> Status (Boolean) + +Check whether the `lock` is held by any task/thread. +This should not be used for synchronization (see instead [`trylock`](@ref)). +""" +function islocked(rl::ReentrantLock) + return rl.reentrancy_cnt != 0 +end + +""" + trylock(lock) -> Success (Boolean) + +Acquire the lock if it is available, +and return `true` if successful. +If the lock is already locked by a different task/thread, +return `false`. + +Each successful `trylock` must be matched by an [`unlock`](@ref). +""" +function trylock(rl::ReentrantLock) + t = current_task() + lock(rl.cond_wait) + if rl.reentrancy_cnt == 0 + rl.locked_by = t + rl.reentrancy_cnt = 1 + got = true + elseif t === notnothing(rl.locked_by) + rl.reentrancy_cnt += 1 + got = true + else + got = false + end + unlock(rl.cond_wait) + return got +end + +""" + lock(lock) + +Acquire the `lock` when it becomes available. +If the lock is already locked by a different task/thread, +wait for it to become available. + +Each `lock` must be matched by an [`unlock`](@ref). +""" +function lock(rl::ReentrantLock) + t = current_task() + lock(rl.cond_wait) + while true + if rl.reentrancy_cnt == 0 + rl.locked_by = t + rl.reentrancy_cnt = 1 + break + elseif t === notnothing(rl.locked_by) + rl.reentrancy_cnt += 1 + break + end + try + wait(rl.cond_wait) + catch + unlock(rl.cond_wait) + rethrow() + end + end + unlock(rl.cond_wait) + return +end + +""" + unlock(lock) + +Releases ownership of the `lock`. + +If this is a recursive lock which has been acquired before, decrement an +internal counter and return immediately. +""" +function unlock(rl::ReentrantLock) + t = current_task() + rl.reentrancy_cnt == 0 && error("unlock count must match lock count") + rl.locked_by === t || error("unlock from wrong thread") + lock(rl.cond_wait) + rl.reentrancy_cnt -= 1 + if rl.reentrancy_cnt == 0 + rl.locked_by = nothing + if !isempty(rl.cond_wait.waitq) + try + notify(rl.cond_wait) + catch + unlock(rl.cond_wait) + rethrow() + end + end + end + unlock(rl.cond_wait) + return +end + +function unlockall(rl::ReentrantLock) + t = current_task() + n = rl.reentrancy_cnt + rl.locked_by === t || error("unlock from wrong thread") + n == 0 && error("unlock count must match lock count") + lock(rl.cond_wait) + rl.reentrancy_cnt = 0 + rl.locked_by = nothing + if !isempty(rl.cond_wait.waitq) + try + notify(rl.cond_wait) + catch + unlock(rl.cond_wait) + rethrow() + end + end + unlock(rl.cond_wait) + return n +end + +function relockall(rl::ReentrantLock, n::Int) + t = current_task() + lock(rl) + n1 = rl.reentrancy_cnt + rl.reentrancy_cnt = n + n1 == 1 || concurrency_violation() + return +end + +function lock(f, l::AbstractLock) + lock(l) + try + return f() + finally + unlock(l) + end +end + +function trylock(f, l::AbstractLock) + if trylock(l) + try + return f() + finally + unlock(l) + end + end + return false +end + +macro lock(l, expr) + quote + temp = $(esc(l)) + lock(temp) + try + $(esc(expr)) + finally + unlock(temp) + end + end +end + +macro lock_nofail(l, expr) + quote + temp = $(esc(l)) + lock(temp) + val = $(esc(expr)) + unlock(temp) + val + end +end + +@eval Threads begin + """ + Threads.Condition([lock]) + + A thread-safe version of [`Base.Condition`](@ref). + + !!! compat "Julia 1.2" + This functionality requires at least Julia 1.2. + """ + const Condition = Base.GenericCondition{Base.ReentrantLock} + + """ + Special note for [`Threads.Condition`](@ref): + + The caller must be holding the [`lock`](@ref) that owns `c` before calling this method. + The calling task will be blocked until some other task wakes it, + usually by calling [`notify`](@ref)` on the same Condition object. + The lock will be atomically released when blocking (even if it was locked recursively), + and will be reacquired before returning. + """ + wait(c::Condition) +end + +const ThreadSynchronizer = GenericCondition{Threads.SpinLock} + +""" + Semaphore(sem_size) + +Create a counting semaphore that allows at most `sem_size` +acquires to be in use at any time. +Each acquire must be matched with a release. +""" +mutable struct Semaphore + sem_size::Int + curr_cnt::Int + cond_wait::Threads.Condition + Semaphore(sem_size) = sem_size > 0 ? new(sem_size, 0, Threads.Condition()) : throw(ArgumentError("Semaphore size must be > 0")) +end + +""" + acquire(s::Semaphore) + +Wait for one of the `sem_size` permits to be available, +blocking until one can be acquired. +""" +function acquire(s::Semaphore) + lock(s.cond_wait) + try + while s.curr_cnt >= s.sem_size + wait(s.cond_wait) + end + s.curr_cnt = s.curr_cnt + 1 + finally + unlock(s.cond_wait) + end + return +end + +""" + release(s::Semaphore) + +Return one permit to the pool, +possibly allowing another task to acquire it +and resume execution. +""" +function release(s::Semaphore) + lock(s.cond_wait) + try + s.curr_cnt > 0 || error("release count must match acquire count") + s.curr_cnt -= 1 + notify(s.cond_wait; all=false) + finally + unlock(s.cond_wait) + end + return +end + + +""" + Event() + +Create a level-triggered event source. Tasks that call [`wait`](@ref) on an +`Event` are suspended and queued until `notify` is called on the `Event`. +After `notify` is called, the `Event` remains in a signaled state and +tasks will no longer block when waiting for it. + +!!! compat "Julia 1.1" + This functionality requires at least Julia 1.1. +""" +mutable struct Event + notify::Threads.Condition + set::Bool + Event() = new(Threads.Condition(), false) +end + +function wait(e::Event) + e.set && return + lock(e.notify) + try + while !e.set + wait(e.notify) + end + finally + unlock(e.notify) + end + nothing +end + +function notify(e::Event) + lock(e.notify) + try + if !e.set + e.set = true + notify(e.notify) + end + finally + unlock(e.notify) + end + nothing +end + +@eval Threads begin + import .Base: Event + export Event +end diff --git a/base/locks-mt.jl b/base/locks-mt.jl new file mode 100644 index 0000000..a828ade --- /dev/null +++ b/base/locks-mt.jl @@ -0,0 +1,66 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +import .Base: unsafe_convert, lock, trylock, unlock, islocked, wait, notify, AbstractLock + +export SpinLock + +# Important Note: these low-level primitives defined here +# are typically not for general usage + +########################################## +# Atomic Locks +########################################## + +# Test-and-test-and-set spin locks are quickest up to about 30ish +# contending threads. If you have more contention than that, perhaps +# a lock is the wrong way to synchronize. +""" + SpinLock() + +Create a non-reentrant lock. +Recursive use will result in a deadlock. +Each [`lock`](@ref) must be matched with an [`unlock`](@ref). + +Test-and-test-and-set spin locks are quickest up to about 30ish +contending threads. If you have more contention than that, perhaps +a lock is the wrong way to synchronize. +""" +struct SpinLock <: AbstractLock + handle::Atomic{Int} + SpinLock() = new(Atomic{Int}(0)) +end + +# Note: this cannot assert that the lock is held by the correct thread, because we do not +# track which thread locked it. Users beware. +Base.assert_havelock(l::SpinLock) = islocked(l) ? nothing : concurrency_violation() + +function lock(l::SpinLock) + while true + if l.handle[] == 0 + p = atomic_xchg!(l.handle, 1) + if p == 0 + return + end + end + ccall(:jl_cpu_pause, Cvoid, ()) + # Temporary solution before we have gc transition support in codegen. + ccall(:jl_gc_safepoint, Cvoid, ()) + end +end + +function trylock(l::SpinLock) + if l.handle[] == 0 + return atomic_xchg!(l.handle, 1) == 0 + end + return false +end + +function unlock(l::SpinLock) + l.handle[] = 0 + ccall(:jl_cpu_wake, Cvoid, ()) + return +end + +function islocked(l::SpinLock) + return l.handle[] != 0 +end diff --git a/base/logging.jl b/base/logging.jl new file mode 100644 index 0000000..3a0361a --- /dev/null +++ b/base/logging.jl @@ -0,0 +1,550 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +module CoreLogging + +import Base: isless, +, -, convert, show + +export + AbstractLogger, + LogLevel, + NullLogger, + @debug, + @info, + @warn, + @error, + @logmsg, + with_logger, + current_logger, + global_logger, + disable_logging, + SimpleLogger + +#------------------------------------------------------------------------------- +# The AbstractLogger interface +""" +A logger controls how log records are filtered and dispatched. When a log +record is generated, the logger is the first piece of user configurable code +which gets to inspect the record and decide what to do with it. +""" +abstract type AbstractLogger ; end + +""" + handle_message(logger, level, message, _module, group, id, file, line; key1=val1, ...) + +Log a message to `logger` at `level`. The logical location at which the +message was generated is given by module `_module` and `group`; the source +location by `file` and `line`. `id` is an arbitrary unique [`Symbol`](@ref) to be used +as a key to identify the log statement when filtering. +""" +function handle_message end + +""" + shouldlog(logger, level, _module, group, id) + +Return true when `logger` accepts a message at `level`, generated for +`_module`, `group` and with unique log identifier `id`. +""" +function shouldlog end + +""" + min_enabled_level(logger) + +Return the minimum enabled level for `logger` for early filtering. That is, +the log level below or equal to which all messages are filtered. +""" +function min_enabled_level end + +""" + catch_exceptions(logger) + +Return true if the logger should catch exceptions which happen during log +record construction. By default, messages are caught + +By default all exceptions are caught to prevent log message generation from +crashing the program. This lets users confidently toggle little-used +functionality - such as debug logging - in a production system. + +If you want to use logging as an audit trail you should disable this for your +logger type. +""" +catch_exceptions(logger) = true + + + +""" + NullLogger() + +Logger which disables all messages and produces no output - the logger +equivalent of /dev/null. +""" +struct NullLogger <: AbstractLogger; end + +min_enabled_level(::NullLogger) = AboveMaxLevel +shouldlog(::NullLogger, args...) = false +handle_message(::NullLogger, args...; kwargs...) = + error("Null logger handle_message() should not be called") + + +#------------------------------------------------------------------------------- +# Standard log levels +""" + LogLevel(level) + +Severity/verbosity of a log record. + +The log level provides a key against which potential log records may be +filtered, before any other work is done to construct the log record data +structure itself. +""" +struct LogLevel + level::Int32 +end + +LogLevel(level::LogLevel) = level + +isless(a::LogLevel, b::LogLevel) = isless(a.level, b.level) ++(level::LogLevel, inc::Integer) = LogLevel(level.level+inc) +-(level::LogLevel, inc::Integer) = LogLevel(level.level-inc) +convert(::Type{LogLevel}, level::Integer) = LogLevel(level) + +const BelowMinLevel = LogLevel(-1000001) +const Debug = LogLevel( -1000) +const Info = LogLevel( 0) +const Warn = LogLevel( 1000) +const Error = LogLevel( 2000) +const AboveMaxLevel = LogLevel( 1000001) + +function show(io::IO, level::LogLevel) + if level == BelowMinLevel print(io, "BelowMinLevel") + elseif level == Debug print(io, "Debug") + elseif level == Info print(io, "Info") + elseif level == Warn print(io, "Warn") + elseif level == Error print(io, "Error") + elseif level == AboveMaxLevel print(io, "AboveMaxLevel") + else print(io, "LogLevel($(level.level))") + end +end + + +#------------------------------------------------------------------------------- +# Logging macros + +_logmsg_docs = """ + @debug message [key=value | value ...] + @info message [key=value | value ...] + @warn message [key=value | value ...] + @error message [key=value | value ...] + + @logmsg level message [key=value | value ...] + +Create a log record with an informational `message`. For convenience, four +logging macros `@debug`, `@info`, `@warn` and `@error` are defined which log at +the standard severity levels `Debug`, `Info`, `Warn` and `Error`. `@logmsg` +allows `level` to be set programmatically to any `LogLevel` or custom log level +types. + +`message` should be an expression which evaluates to a string which is a human +readable description of the log event. By convention, this string will be +formatted as markdown when presented. + +The optional list of `key=value` pairs supports arbitrary user defined +metadata which will be passed through to the logging backend as part of the +log record. If only a `value` expression is supplied, a key representing the +expression will be generated using [`Symbol`](@ref). For example, `x` becomes `x=x`, +and `foo(10)` becomes `Symbol("foo(10)")=foo(10)`. For splatting a list of +key value pairs, use the normal splatting syntax, `@info "blah" kws...`. + +There are some keys which allow automatically generated log data to be +overridden: + + * `_module=mod` can be used to specify a different originating module from + the source location of the message. + * `_group=symbol` can be used to override the message group (this is + normally derived from the base name of the source file). + * `_id=symbol` can be used to override the automatically generated unique + message identifier. This is useful if you need to very closely associate + messages generated on different source lines. + * `_file=string` and `_line=integer` can be used to override the apparent + source location of a log message. + +There's also some key value pairs which have conventional meaning: + + * `maxlog=integer` should be used as a hint to the backend that the message + should be displayed no more than `maxlog` times. + * `exception=ex` should be used to transport an exception with a log message, + often used with `@error`. An associated backtrace `bt` may be attached + using the tuple `exception=(ex,bt)`. + +# Examples + +``` +@debug "Verbose debugging information. Invisible by default" +@info "An informational message" +@warn "Something was odd. You should pay attention" +@error "A non fatal error occurred" + +x = 10 +@info "Some variables attached to the message" x a=42.0 + +@debug begin + sA = sum(A) + "sum(A) = \$sA is an expensive operation, evaluated only when `shouldlog` returns true" +end + +for i=1:10000 + @info "With the default backend, you will only see (i = \$i) ten times" maxlog=10 + @debug "Algorithm1" i progress=i/10000 +end +``` +""" + +# Get (module,filepath,line) for the location of the caller of a macro. +# Designed to be used from within the body of a macro. +macro _sourceinfo() + esc(quote + (__module__, + __source__.file === nothing ? "?" : String(__source__.file), + __source__.line) + end) +end + +macro logmsg(level, exs...) logmsg_code((@_sourceinfo)..., esc(level), exs...) end +macro debug(exs...) logmsg_code((@_sourceinfo)..., :Debug, exs...) end +macro info(exs...) logmsg_code((@_sourceinfo)..., :Info, exs...) end +macro warn(exs...) logmsg_code((@_sourceinfo)..., :Warn, exs...) end +macro error(exs...) logmsg_code((@_sourceinfo)..., :Error, exs...) end + +# Logging macros share documentation +@eval @doc $_logmsg_docs :(@logmsg) +@eval @doc $_logmsg_docs :(@debug) +@eval @doc $_logmsg_docs :(@info) +@eval @doc $_logmsg_docs :(@warn) +@eval @doc $_logmsg_docs :(@error) + +_log_record_ids = Set{Symbol}() +# Generate a unique, stable, short, somewhat human readable identifier for a +# logging *statement*. The idea here is to have a key against which log events +# can be filtered and otherwise manipulated. The key should uniquely identify +# the source location in the originating module, but ideally should be stable +# across versions of the originating module, provided the log generating +# statement itself doesn't change. +function log_record_id(_module, level, message, log_kws) + modname = _module === nothing ? "" : join(fullname(_module), "_") + # Use an arbitriraly chosen eight hex digits here. TODO: Figure out how to + # make the id exactly the same on 32 and 64 bit systems. + h = UInt32(hash(string(modname, level, message, log_kws)) & 0xFFFFFFFF) + while true + id = Symbol(modname, '_', string(h, base = 16, pad = 8)) + # _log_record_ids is a registry of log record ids for use during + # compilation, to ensure uniqueness of ids. Note that this state will + # only persist during module compilation so it will be empty when a + # precompiled module is loaded. + if !(id in _log_record_ids) + push!(_log_record_ids, id) + return id + end + h += 1 + end +end + +# Generate code for logging macros +function logmsg_code(_module, file, line, level, message, exs...) + id = Expr(:quote, log_record_id(_module, level, message, exs)) + group = nothing + kwargs = Any[] + for ex in exs + if ex isa Expr && ex.head === :(=) && ex.args[1] isa Symbol + k,v = ex.args + if !(k isa Symbol) + throw(ArgumentError("Expected symbol for key in key value pair `$ex`")) + end + k = ex.args[1] + # Recognize several special keyword arguments + if k === :_id + # id may be overridden if you really want several log + # statements to share the same id (eg, several pertaining to + # the same progress step). In those cases it may be wise to + # manually call log_record_id to get a unique id in the same + # format. + id = esc(v) + elseif k === :_module + _module = esc(v) + elseif k === :_line + line = esc(v) + elseif k === :_file + file = esc(v) + elseif k === :_group + group = esc(v) + else + # Copy across key value pairs for structured log records + push!(kwargs, Expr(:kw, k, esc(v))) + end + elseif ex isa Expr && ex.head === :... + # Keyword splatting + push!(kwargs, esc(ex)) + else + # Positional arguments - will be converted to key value pairs + # automatically. + push!(kwargs, Expr(:kw, Symbol(ex), esc(ex))) + end + end + + if group === nothing + group = if isdefined(Base, :basename) && isa(file, String) + # precompute if we can + QuoteNode(splitext(basename(file))[1]) + else + # memoized run-time execution + ref = Ref{Symbol}() + :(isassigned($ref) ? $ref[] + : $ref[] = Symbol(splitext(basename(something($file, "")))[1])) + end + end + + quote + level = $level + std_level = convert(LogLevel, level) + if std_level >= getindex(_min_enabled_level) + group = $group + _module = $_module + logger = current_logger_for_env(std_level, group, _module) + if !(logger === nothing) + id = $id + # Second chance at an early bail-out (before computing the message), + # based on arbitrary logger-specific logic. + if shouldlog(logger, level, _module, group, id) + file = $file + line = $line + try + msg = $(esc(message)) + handle_message(logger, level, msg, _module, group, id, file, line; $(kwargs...)) + catch err + logging_error(logger, level, _module, group, id, file, line, err) + end + end + end + end + nothing + end +end + +# Report an error in log message creation (or in the logger itself). +@noinline function logging_error(logger, level, _module, group, id, + filepath, line, @nospecialize(err)) + if !catch_exceptions(logger) + rethrow(err) + end + try + msg = "Exception while generating log record in module $_module at $filepath:$line" + handle_message(logger, Error, msg, _module, :logevent_error, id, filepath, line; exception=(err,catch_backtrace())) + catch err2 + try + # Give up and write to stderr, in three independent calls to + # increase the odds of it getting through. + print(stderr, "Exception handling log message: ") + println(stderr, err) + println(stderr, " module=$_module file=$filepath line=$line") + println(stderr, " Second exception: ", err2) + catch + end + end + nothing +end + +# Log a message. Called from the julia C code; kwargs is in the format +# Any[key1,val1, ...] for simplicity in construction on the C side. +function logmsg_shim(level, message, _module, group, id, file, line, kwargs) + real_kws = Any[(kwargs[i],kwargs[i+1]) for i in 1:2:length(kwargs)] + @logmsg(convert(LogLevel, level), message, + _module=_module, _id=id, _group=group, + _file=String(file), _line=line, real_kws...) +end + +# Global log limiting mechanism for super fast but inflexible global log +# limiting. +const _min_enabled_level = Ref(Debug) + +# LogState - a concretely typed cache of data extracted from the logger, plus +# the logger itself. +struct LogState + min_enabled_level::LogLevel + logger::AbstractLogger +end + +LogState(logger) = LogState(LogLevel(min_enabled_level(logger)), logger) + +function current_logstate() + logstate = current_task().logstate + return (logstate !== nothing ? logstate : _global_logstate)::LogState +end + +# helper function to get the current logger, if enabled for the specified message type +@noinline function current_logger_for_env(std_level::LogLevel, group, _module) + logstate = current_logstate() + if std_level >= logstate.min_enabled_level || env_override_minlevel(group, _module) + return logstate.logger + end + return nothing +end + +function with_logstate(f::Function, logstate) + t = current_task() + old = t.logstate + try + t.logstate = logstate + f() + finally + t.logstate = old + end +end + + +#------------------------------------------------------------------------------- +# Control of the current logger and early log filtering + +""" + disable_logging(level) + +Disable all log messages at log levels equal to or less than `level`. This is +a *global* setting, intended to make debug logging extremely cheap when +disabled. +""" +function disable_logging(level::LogLevel) + _min_enabled_level[] = level + 1 +end + +let _debug_groups = Symbol[], + _debug_str::String = "" +global function env_override_minlevel(group, _module) + debug = get(ENV, "JULIA_DEBUG", "") + if !(debug === _debug_str) + _debug_str = debug + empty!(_debug_groups) + for g in split(debug, ',') + isempty(g) && continue + if g == "all" + empty!(_debug_groups) + push!(_debug_groups, :all) + break + end + push!(_debug_groups, Symbol(g)) + end + end + if isempty(_debug_groups) + return false + end + if _debug_groups[1] === :all + return true + end + if isa(group, Symbol) && group in _debug_groups + return true + end + if isa(_module, Module) + if nameof(_module) in _debug_groups + return true + end + if nameof(Base.moduleroot(_module)) in _debug_groups + return true + end + end + return false +end +end + + +""" + global_logger() + +Return the global logger, used to receive messages when no specific logger +exists for the current task. + + global_logger(logger) + +Set the global logger to `logger`, and return the previous global logger. +""" +global_logger() = _global_logstate.logger + +function global_logger(logger::AbstractLogger) + prev = _global_logstate.logger + global _global_logstate = LogState(logger) + prev +end + +""" + with_logger(function, logger) + +Execute `function`, directing all log messages to `logger`. + +# Example + +```julia +function test(x) + @info "x = \$x" +end + +with_logger(logger) do + test(1) + test([1,2]) +end +``` +""" +with_logger(f::Function, logger::AbstractLogger) = with_logstate(f, LogState(logger)) + +""" + current_logger() + +Return the logger for the current task, or the global logger if none is +attached to the task. +""" +current_logger() = current_logstate().logger + + +#------------------------------------------------------------------------------- +# SimpleLogger +""" + SimpleLogger(stream=stderr, min_level=Info) + +Simplistic logger for logging all messages with level greater than or equal to +`min_level` to `stream`. +""" +struct SimpleLogger <: AbstractLogger + stream::IO + min_level::LogLevel + message_limits::Dict{Any,Int} +end +SimpleLogger(stream::IO=stderr, level=Info) = SimpleLogger(stream, level, Dict{Any,Int}()) + +shouldlog(logger::SimpleLogger, level, _module, group, id) = + get(logger.message_limits, id, 1) > 0 + +min_enabled_level(logger::SimpleLogger) = logger.min_level + +catch_exceptions(logger::SimpleLogger) = false + +function handle_message(logger::SimpleLogger, level, message, _module, group, id, + filepath, line; maxlog=nothing, kwargs...) + if maxlog !== nothing && maxlog isa Integer + remaining = get!(logger.message_limits, id, maxlog) + logger.message_limits[id] = remaining - 1 + remaining > 0 || return + end + buf = IOBuffer() + iob = IOContext(buf, logger.stream) + levelstr = level == Warn ? "Warning" : string(level) + msglines = split(chomp(string(message)), '\n') + println(iob, "┌ ", levelstr, ": ", msglines[1]) + for i in 2:length(msglines) + println(iob, "│ ", msglines[i]) + end + for (key, val) in kwargs + println(iob, "│ ", key, " = ", val) + end + println(iob, "└ @ ", something(_module, "nothing"), " ", + something(filepath, "nothing"), ":", something(line, "nothing")) + write(logger.stream, take!(buf)) + nothing +end + +_global_logstate = LogState(SimpleLogger(Core.stderr, CoreLogging.Info)) + +end # CoreLogging diff --git a/base/math.jl b/base/math.jl new file mode 100644 index 0000000..ad1e898 --- /dev/null +++ b/base/math.jl @@ -0,0 +1,1142 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +module Math + +export sin, cos, sincos, tan, sinh, cosh, tanh, asin, acos, atan, + asinh, acosh, atanh, sec, csc, cot, asec, acsc, acot, + sech, csch, coth, asech, acsch, acoth, + sinpi, cospi, sinc, cosc, + cosd, cotd, cscd, secd, sind, tand, sincosd, + acosd, acotd, acscd, asecd, asind, atand, + rad2deg, deg2rad, + log, log2, log10, log1p, exponent, exp, exp2, exp10, expm1, + cbrt, sqrt, significand, + hypot, max, min, minmax, ldexp, frexp, + clamp, clamp!, modf, ^, mod2pi, rem2pi, + @evalpoly + +import .Base: log, exp, sin, cos, tan, sinh, cosh, tanh, asin, + acos, atan, asinh, acosh, atanh, sqrt, log2, log10, + max, min, minmax, ^, exp2, muladd, rem, + exp10, expm1, log1p + +using .Base: sign_mask, exponent_mask, exponent_one, + exponent_half, uinttype, significand_mask, + significand_bits, exponent_bits + +using Core.Intrinsics: sqrt_llvm + +using .Base: IEEEFloat + +@noinline function throw_complex_domainerror(f::Symbol, x) + throw(DomainError(x, string("$f will only return a complex result if called with a ", + "complex argument. Try $f(Complex(x))."))) +end +@noinline function throw_exp_domainerror(x) + throw(DomainError(x, string("Exponentiation yielding a complex result requires a ", + "complex argument.\nReplace x^y with (x+0im)^y, ", + "Complex(x)^y, or similar."))) +end + +for T in (Float16, Float32, Float64) + @eval exponent_bias(::Type{$T}) = $(Int(exponent_one(T) >> significand_bits(T))) + # maximum float exponent + @eval exponent_max(::Type{$T}) = $(Int(exponent_mask(T) >> significand_bits(T)) - exponent_bias(T)) + # maximum float exponent without bias + @eval exponent_raw_max(::Type{$T}) = $(Int(exponent_mask(T) >> significand_bits(T))) +end + +# non-type specific math functions + +""" + clamp(x, lo, hi) + +Return `x` if `lo <= x <= hi`. If `x > hi`, return `hi`. If `x < lo`, return `lo`. Arguments +are promoted to a common type. + +# Examples +```jldoctest +julia> clamp.([pi, 1.0, big(10.)], 2., 9.) +3-element Array{BigFloat,1}: + 3.141592653589793238462643383279502884197169399375105820974944592307816406286198 + 2.0 + 9.0 + +julia> clamp.([11,8,5],10,6) # an example where lo > hi +3-element Array{Int64,1}: + 6 + 6 + 10 +``` +""" +clamp(x::X, lo::L, hi::H) where {X,L,H} = + ifelse(x > hi, convert(promote_type(X,L,H), hi), + ifelse(x < lo, + convert(promote_type(X,L,H), lo), + convert(promote_type(X,L,H), x))) + +""" + clamp!(array::AbstractArray, lo, hi) + +Restrict values in `array` to the specified range, in-place. +See also [`clamp`](@ref). +""" +function clamp!(x::AbstractArray, lo, hi) + @inbounds for i in eachindex(x) + x[i] = clamp(x[i], lo, hi) + end + x +end + +""" + @horner(x, p...) + Evaluate p[1] + x * (p[2] + x * (....)), i.e. a polynomial via Horner's rule +""" +macro horner(x, p...) + ex = esc(p[end]) + for i = length(p)-1:-1:1 + ex = :(muladd(t, $ex, $(esc(p[i])))) + end + ex = quote local r = $ex end # structure this to add exactly one line number node for the macro + return Expr(:block, :(local t = $(esc(x))), ex, :r) +end + +# Evaluate p[1] + z*p[2] + z^2*p[3] + ... + z^(n-1)*p[n]. This uses +# Horner's method if z is real, but for complex z it uses a more +# efficient algorithm described in Knuth, TAOCP vol. 2, section 4.6.4, +# equation (3). + +""" + @evalpoly(z, c...) + +Evaluate the polynomial ``\\sum_k c[k] z^{k-1}`` for the coefficients `c[1]`, `c[2]`, ...; +that is, the coefficients are given in ascending order by power of `z`. This macro expands +to efficient inline code that uses either Horner's method or, for complex `z`, a more +efficient Goertzel-like algorithm. + +# Examples +```jldoctest +julia> @evalpoly(3, 1, 0, 1) +10 + +julia> @evalpoly(2, 1, 0, 1) +5 + +julia> @evalpoly(2, 1, 1, 1) +7 +``` +""" +macro evalpoly(z, p...) + a = :($(esc(p[end]))) + b = :($(esc(p[end-1]))) + as = [] + for i = length(p)-2:-1:1 + ai = Symbol("a", i) + push!(as, :($ai = $a)) + a = :(muladd(r, $ai, $b)) + b = :($(esc(p[i])) - s * $ai) # see issue #15985 on fused mul-subtract + end + ai = :a0 + push!(as, :($ai = $a)) + C = Expr(:block, + :(x = real(tt)), + :(y = imag(tt)), + :(r = x + x), + :(s = muladd(x, x, y*y)), + as..., + :(muladd($ai, tt, $b))) + R = Expr(:macrocall, Symbol("@horner"), (), :tt, map(esc, p)...) + :(let tt = $(esc(z)) + isa(tt, Complex) ? $C : $R + end) +end + +""" + rad2deg(x) + +Convert `x` from radians to degrees. + +# Examples +```jldoctest +julia> rad2deg(pi) +180.0 +``` +""" +rad2deg(z::AbstractFloat) = z * (180 / oftype(z, pi)) + +""" + deg2rad(x) + +Convert `x` from degrees to radians. + +# Examples +```jldoctest +julia> deg2rad(90) +1.5707963267948966 +``` +""" +deg2rad(z::AbstractFloat) = z * (oftype(z, pi) / 180) +rad2deg(z::Real) = rad2deg(float(z)) +deg2rad(z::Real) = deg2rad(float(z)) +rad2deg(z::Number) = (z/pi)*180 +deg2rad(z::Number) = (z*pi)/180 + +log(b::T, x::T) where {T<:Number} = log(x)/log(b) + +""" + log(b,x) + +Compute the base `b` logarithm of `x`. Throws [`DomainError`](@ref) for negative +[`Real`](@ref) arguments. + +# Examples +```jldoctest; filter = r"Stacktrace:(\\n \\[[0-9]+\\].*)*" +julia> log(4,8) +1.5 + +julia> log(4,2) +0.5 + +julia> log(-2, 3) +ERROR: DomainError with -2.0: +log will only return a complex result if called with a complex argument. Try log(Complex(x)). +Stacktrace: + [1] throw_complex_domainerror(::Symbol, ::Float64) at ./math.jl:31 +[...] + +julia> log(2, -3) +ERROR: DomainError with -3.0: +log will only return a complex result if called with a complex argument. Try log(Complex(x)). +Stacktrace: + [1] throw_complex_domainerror(::Symbol, ::Float64) at ./math.jl:31 +[...] +``` + +!!! note + If `b` is a power of 2 or 10, [`log2`](@ref) or [`log10`](@ref) should be used, as these will + typically be faster and more accurate. For example, + + ```jldoctest + julia> log(100,1000000) + 2.9999999999999996 + + julia> log10(1000000)/2 + 3.0 + ``` +""" +log(b::Number, x::Number) = log(promote(b,x)...) + +# type specific math functions + +const libm = Base.libm_name + +# functions with no domain error +""" + sinh(x) + +Compute hyperbolic sine of `x`. +""" +sinh(x::Number) + +""" + cosh(x) + +Compute hyperbolic cosine of `x`. +""" +cosh(x::Number) + +""" + tanh(x) + +Compute hyperbolic tangent of `x`. +""" +tanh(x::Number) + +""" + atan(y) + atan(y, x) + +Compute the inverse tangent of `y` or `y/x`, respectively. + +For one argument, this is the angle in radians between the positive *x*-axis and the point +(1, *y*), returning a value in the interval ``[-\\pi/2, \\pi/2]``. + +For two arguments, this is the angle in radians between the positive *x*-axis and the +point (*x*, *y*), returning a value in the interval ``[-\\pi, \\pi]``. This corresponds to a +standard [`atan2`](https://en.wikipedia.org/wiki/Atan2) function. +""" +atan(x::Number) + +""" + asinh(x) + +Compute the inverse hyperbolic sine of `x`. +""" +asinh(x::Number) + +""" + expm1(x) + +Accurately compute ``e^x-1``. +""" +expm1(x) +for f in (:exp2, :expm1) + @eval begin + ($f)(x::Float64) = ccall(($(string(f)),libm), Float64, (Float64,), x) + ($f)(x::Float32) = ccall(($(string(f,"f")),libm), Float32, (Float32,), x) + ($f)(x::Real) = ($f)(float(x)) + end +end + +""" + exp2(x) + +Compute the base 2 exponential of `x`, in other words ``2^x``. + +# Examples +```jldoctest +julia> exp2(5) +32.0 +``` +""" +exp2(x::AbstractFloat) = 2^x + +""" + exp10(x) + +Compute the base 10 exponential of `x`, in other words ``10^x``. + +# Examples +```jldoctest +julia> exp10(2) +100.0 +``` +""" +exp10(x::AbstractFloat) = 10^x + +for f in (:sinh, :cosh, :tanh, :atan, :asinh, :exp, :expm1) + @eval ($f)(x::AbstractFloat) = error("not implemented for ", typeof(x)) +end + +# functions with special cases for integer arguments +@inline function exp2(x::Base.BitInteger) + if x > 1023 + Inf64 + elseif x <= -1023 + # if -1073 < x <= -1023 then Result will be a subnormal number + # Hex literal with padding must be used to work on 32bit machine + reinterpret(Float64, 0x0000_0000_0000_0001 << ((x + 1074) % UInt)) + else + # We will cast everything to Int64 to avoid errors in case of Int128 + # If x is a Int128, and is outside the range of Int64, then it is not -1023 log(2) +0.6931471805599453 + +julia> log(-3) +ERROR: DomainError with -3.0: +log will only return a complex result if called with a complex argument. Try log(Complex(x)). +Stacktrace: + [1] throw_complex_domainerror(::Symbol, ::Float64) at ./math.jl:31 +[...] +``` +""" +log(x::Number) + +""" + log2(x) + +Compute the logarithm of `x` to base 2. Throws [`DomainError`](@ref) for negative +[`Real`](@ref) arguments. + +# Examples +```jldoctest; filter = r"Stacktrace:(\\n \\[[0-9]+\\].*)*" +julia> log2(4) +2.0 + +julia> log2(10) +3.321928094887362 + +julia> log2(-2) +ERROR: DomainError with -2.0: +NaN result for non-NaN input. +Stacktrace: + [1] nan_dom_err at ./math.jl:325 [inlined] +[...] +``` +""" +log2(x) + +""" + log10(x) + +Compute the logarithm of `x` to base 10. +Throws [`DomainError`](@ref) for negative [`Real`](@ref) arguments. + +# Examples +```jldoctest; filter = r"Stacktrace:(\\n \\[[0-9]+\\].*)*" +julia> log10(100) +2.0 + +julia> log10(2) +0.3010299956639812 + +julia> log10(-2) +ERROR: DomainError with -2.0: +NaN result for non-NaN input. +Stacktrace: + [1] nan_dom_err at ./math.jl:325 [inlined] +[...] +``` +""" +log10(x) + +""" + log1p(x) + +Accurate natural logarithm of `1+x`. Throws [`DomainError`](@ref) for [`Real`](@ref) +arguments less than -1. + +# Examples +```jldoctest; filter = r"Stacktrace:(\\n \\[[0-9]+\\].*)*" +julia> log1p(-0.5) +-0.6931471805599453 + +julia> log1p(0) +0.0 + +julia> log1p(-2) +ERROR: DomainError with -2.0: +log1p will only return a complex result if called with a complex argument. Try log1p(Complex(x)). +Stacktrace: + [1] throw_complex_domainerror(::Symbol, ::Float64) at ./math.jl:31 +[...] +``` +""" +log1p(x) +for f in (:log2, :log10) + @eval begin + @inline ($f)(x::Float64) = nan_dom_err(ccall(($(string(f)), libm), Float64, (Float64,), x), x) + @inline ($f)(x::Float32) = nan_dom_err(ccall(($(string(f, "f")), libm), Float32, (Float32,), x), x) + @inline ($f)(x::Real) = ($f)(float(x)) + end +end + +@inline function sqrt(x::Union{Float32,Float64}) + x < zero(x) && throw_complex_domainerror(:sqrt, x) + sqrt_llvm(x) +end + +""" + sqrt(x) + +Return ``\\sqrt{x}``. Throws [`DomainError`](@ref) for negative [`Real`](@ref) arguments. +Use complex negative arguments instead. The prefix operator `√` is equivalent to `sqrt`. + +# Examples +```jldoctest; filter = r"Stacktrace:(\\n \\[[0-9]+\\].*)*" +julia> sqrt(big(81)) +9.0 + +julia> sqrt(big(-81)) +ERROR: DomainError with -81.0: +NaN result for non-NaN input. +Stacktrace: + [1] sqrt(::BigFloat) at ./mpfr.jl:501 +[...] + +julia> sqrt(big(complex(-81))) +0.0 + 9.0im +``` +""" +sqrt(x::Real) = sqrt(float(x)) + +""" + hypot(x, y) + +Compute the hypotenuse ``\\sqrt{|x|^2+|y|^2}`` avoiding overflow and underflow. + +This code is an implementation of the algorithm described in: +An Improved Algorithm for `hypot(a,b)` +by Carlos F. Borges +The article is available online at ArXiv at the link + https://arxiv.org/abs/1904.09481 + +# Examples +```jldoctest; filter = r"Stacktrace:(\\n \\[[0-9]+\\].*)*" +julia> a = Int64(10)^10; + +julia> hypot(a, a) +1.4142135623730951e10 + +julia> √(a^2 + a^2) # a^2 overflows +ERROR: DomainError with -2.914184810805068e18: +sqrt will only return a complex result if called with a complex argument. Try sqrt(Complex(x)). +Stacktrace: +[...] + +julia> hypot(3, 4im) +5.0 +``` +""" +hypot(x::Number, y::Number) = hypot(promote(x, y)...) +hypot(x::Complex, y::Complex) = hypot(promote(abs(x),abs(y))...) +hypot(x::Integer, y::Integer) = hypot(promote(float(x), float(y))...) +function hypot(x::T,y::T) where T<:AbstractFloat + #Return Inf if either or both imputs is Inf (Compliance with IEEE754) + if isinf(x) || isinf(y) + return convert(T,Inf) + end + + # Order the operands + ax,ay = abs(x), abs(y) + if ay > ax + ax,ay = ay,ax + end + + # Widely varying operands + if ay <= ax*sqrt(eps(T)/2) #Note: This also gets ay == 0 + return ax + end + + # Operands do not vary widely + scale = eps(sqrt(floatmin(T))) #Rescaling constant + if ax > sqrt(floatmax(T)/2) + ax = ax*scale + ay = ay*scale + scale = inv(scale) + elseif ay < sqrt(floatmin(T)) + ax = ax/scale + ay = ay/scale + else + scale = one(scale) + end + h = sqrt(muladd(ax,ax,ay*ay)) + # This branch is correctly rounded but requires a native hardware fma. + if Base.Math.FMA_NATIVE + hsquared = h*h + axsquared = ax*ax + h -= (fma(-ay,ay,hsquared-axsquared) + fma(h,h,-hsquared) - fma(ax,ax,-axsquared))/(2*h) + # This branch is within one ulp of correctly rounded. + else + if h <= 2*ay + delta = h-ay + h -= muladd(delta,delta-2*(ax-ay),ax*(2*delta - ax))/(2*h) + else + delta = h-ax + h -= muladd(delta,delta,muladd(ay,(4*delta-ay),2*delta*(ax-2*ay)))/(2*h) + end + end + h*scale +end +function hypot(x::T, y::T) where T<:Number + ax = abs(x) + ay = abs(y) + if ax < ay + ax, ay = ay, ax + end + if iszero(ax) + r = ay / oneunit(ax) + else + r = ay / ax + end + + rr = ax * sqrt(1 + r * r) + + # Use type of rr to make sure that return type is the same for + # all branches + if isnan(r) + isinf(ax) && return oftype(rr, Inf) + isinf(ay) && return oftype(rr, Inf) + return oftype(rr, r) + else + return rr + end +end + +""" + hypot(x...) + +Compute the hypotenuse ``\\sqrt{\\sum |x_i|^2}`` avoiding overflow and underflow. + +# Examples +```jldoctest +julia> hypot(-5.7) +5.7 + +julia> hypot(3, 4im, 12.0) +13.0 +``` +""" +hypot(x::Number...) = sqrt(sum(abs2(y) for y in x)) + +atan(y::Real, x::Real) = atan(promote(float(y),float(x))...) +atan(y::T, x::T) where {T<:AbstractFloat} = Base.no_op_err("atan", T) + +max(x::T, y::T) where {T<:AbstractFloat} = ifelse((y > x) | (signbit(y) < signbit(x)), + ifelse(isnan(x), x, y), ifelse(isnan(y), y, x)) + + +min(x::T, y::T) where {T<:AbstractFloat} = ifelse((y < x) | (signbit(y) > signbit(x)), + ifelse(isnan(x), x, y), ifelse(isnan(y), y, x)) + +minmax(x::T, y::T) where {T<:AbstractFloat} = + ifelse(isnan(x) | isnan(y), ifelse(isnan(x), (x,x), (y,y)), + ifelse((y > x) | (signbit(x) > signbit(y)), (x,y), (y,x))) + + +""" + ldexp(x, n) + +Compute ``x \\times 2^n``. + +# Examples +```jldoctest +julia> ldexp(5., 2) +20.0 +``` +""" +function ldexp(x::T, e::Integer) where T<:IEEEFloat + xu = reinterpret(Unsigned, x) + xs = xu & ~sign_mask(T) + xs >= exponent_mask(T) && return x # NaN or Inf + k = Int(xs >> significand_bits(T)) + if k == 0 # x is subnormal + xs == 0 && return x # +-0 + m = leading_zeros(xs) - exponent_bits(T) + ys = xs << unsigned(m) + xu = ys | (xu & sign_mask(T)) + k = 1 - m + # underflow, otherwise may have integer underflow in the following n + k + e < -50000 && return flipsign(T(0.0), x) + end + # For cases where e of an Integer larger than Int make sure we properly + # overflow/underflow; this is optimized away otherwise. + if e > typemax(Int) + return flipsign(T(Inf), x) + elseif e < typemin(Int) + return flipsign(T(0.0), x) + end + n = e % Int + k += n + # overflow, if k is larger than maximum possible exponent + if k >= exponent_raw_max(T) + return flipsign(T(Inf), x) + end + if k > 0 # normal case + xu = (xu & ~exponent_mask(T)) | (rem(k, uinttype(T)) << significand_bits(T)) + return reinterpret(T, xu) + else # subnormal case + if k <= -significand_bits(T) # underflow + # overflow, for the case of integer overflow in n + k + e > 50000 && return flipsign(T(Inf), x) + return flipsign(T(0.0), x) + end + k += significand_bits(T) + z = T(2.0)^-significand_bits(T) + xu = (xu & ~exponent_mask(T)) | (rem(k, uinttype(T)) << significand_bits(T)) + return z*reinterpret(T, xu) + end +end +ldexp(x::Float16, q::Integer) = Float16(ldexp(Float32(x), q)) + +""" + exponent(x) -> Int + +Get the exponent of a normalized floating-point number. +""" +function exponent(x::T) where T<:IEEEFloat + @noinline throw1(x) = throw(DomainError(x, "Cannot be NaN or Inf.")) + @noinline throw2(x) = throw(DomainError(x, "Cannot be subnormal converted to 0.")) + xs = reinterpret(Unsigned, x) & ~sign_mask(T) + xs >= exponent_mask(T) && throw1(x) + k = Int(xs >> significand_bits(T)) + if k == 0 # x is subnormal + xs == 0 && throw2(x) + m = leading_zeros(xs) - exponent_bits(T) + k = 1 - m + end + return k - exponent_bias(T) +end + +""" + significand(x) + +Extract the `significand(s)` (a.k.a. mantissa), in binary representation, of a +floating-point number. If `x` is a non-zero finite number, then the result will be +a number of the same type on the interval ``[1,2)``. Otherwise `x` is returned. + +# Examples +```jldoctest +julia> significand(15.2)/15.2 +0.125 + +julia> significand(15.2)*8 +15.2 +``` +""" +function significand(x::T) where T<:IEEEFloat + xu = reinterpret(Unsigned, x) + xs = xu & ~sign_mask(T) + xs >= exponent_mask(T) && return x # NaN or Inf + if xs <= (~exponent_mask(T) & ~sign_mask(T)) # x is subnormal + xs == 0 && return x # +-0 + m = unsigned(leading_zeros(xs) - exponent_bits(T)) + xs <<= m + xu = xs | (xu & sign_mask(T)) + end + xu = (xu & ~exponent_mask(T)) | exponent_one(T) + return reinterpret(T, xu) +end + +""" + frexp(val) + +Return `(x,exp)` such that `x` has a magnitude in the interval ``[1/2, 1)`` or 0, +and `val` is equal to ``x \\times 2^{exp}``. +""" +function frexp(x::T) where T<:IEEEFloat + xu = reinterpret(Unsigned, x) + xs = xu & ~sign_mask(T) + xs >= exponent_mask(T) && return x, 0 # NaN or Inf + k = Int(xs >> significand_bits(T)) + if k == 0 # x is subnormal + xs == 0 && return x, 0 # +-0 + m = leading_zeros(xs) - exponent_bits(T) + xs <<= unsigned(m) + xu = xs | (xu & sign_mask(T)) + k = 1 - m + end + k -= (exponent_bias(T) - 1) + xu = (xu & ~exponent_mask(T)) | exponent_half(T) + return reinterpret(T, xu), k +end + +""" + rem(x, y, r::RoundingMode) + +Compute the remainder of `x` after integer division by `y`, with the quotient rounded +according to the rounding mode `r`. In other words, the quantity + + x - y*round(x/y,r) + +without any intermediate rounding. + +- if `r == RoundNearest`, then the result is exact, and in the interval + ``[-|y|/2, |y|/2]``. See also [`RoundNearest`](@ref). + +- if `r == RoundToZero` (default), then the result is exact, and in the interval + ``[0, |y|)`` if `x` is positive, or ``(-|y|, 0]`` otherwise. See also [`RoundToZero`](@ref). + +- if `r == RoundDown`, then the result is in the interval ``[0, y)`` if `y` is positive, or + ``(y, 0]`` otherwise. The result may not be exact if `x` and `y` have different signs, and + `abs(x) < abs(y)`. See also[`RoundDown`](@ref). + +- if `r == RoundUp`, then the result is in the interval `(-y,0]` if `y` is positive, or + `[0,-y)` otherwise. The result may not be exact if `x` and `y` have the same sign, and + `abs(x) < abs(y)`. See also [`RoundUp`](@ref). + +""" +rem(x, y, ::RoundingMode{:ToZero}) = rem(x,y) +rem(x, y, ::RoundingMode{:Down}) = mod(x,y) +rem(x, y, ::RoundingMode{:Up}) = mod(x,-y) + +rem(x::Float64, y::Float64, ::RoundingMode{:Nearest}) = + ccall((:remainder, libm),Float64,(Float64,Float64),x,y) +rem(x::Float32, y::Float32, ::RoundingMode{:Nearest}) = + ccall((:remainderf, libm),Float32,(Float32,Float32),x,y) +rem(x::Float16, y::Float16, r::RoundingMode{:Nearest}) = Float16(rem(Float32(x), Float32(y), r)) + + +""" + modf(x) + +Return a tuple `(fpart, ipart)` of the fractional and integral parts of a number. Both parts +have the same sign as the argument. + +# Examples +```jldoctest +julia> modf(3.5) +(0.5, 3.0) + +julia> modf(-3.5) +(-0.5, -3.0) +``` +""" +modf(x) = rem(x,one(x)), trunc(x) + +function modf(x::Float32) + temp = Ref{Float32}() + f = ccall((:modff, libm), Float32, (Float32, Ptr{Float32}), x, temp) + f, temp[] +end + +function modf(x::Float64) + temp = Ref{Float64}() + f = ccall((:modf, libm), Float64, (Float64, Ptr{Float64}), x, temp) + f, temp[] +end + +@inline function ^(x::Float64, y::Float64) + z = ccall("llvm.pow.f64", llvmcall, Float64, (Float64, Float64), x, y) + if isnan(z) & !isnan(x+y) + throw_exp_domainerror(x) + end + z +end +@inline function ^(x::Float32, y::Float32) + z = ccall("llvm.pow.f32", llvmcall, Float32, (Float32, Float32), x, y) + if isnan(z) & !isnan(x+y) + throw_exp_domainerror(x) + end + z +end +@inline ^(x::Float64, y::Integer) = ccall("llvm.pow.f64", llvmcall, Float64, (Float64, Float64), x, Float64(y)) +@inline ^(x::Float32, y::Integer) = ccall("llvm.pow.f32", llvmcall, Float32, (Float32, Float32), x, Float32(y)) +@inline ^(x::Float16, y::Integer) = Float16(Float32(x) ^ y) +@inline literal_pow(::typeof(^), x::Float16, ::Val{p}) where {p} = Float16(literal_pow(^,Float32(x),Val(p))) + +## rem2pi-related calculations ## + +function add22condh(xh::Float64, xl::Float64, yh::Float64, yl::Float64) + # This algorithm, due to Dekker, computes the sum of two + # double-double numbers and returns the high double. References: + # [1] http://www.digizeitschriften.de/en/dms/img/?PID=GDZPPN001170007 + # [2] https://doi.org/10.1007/BF01397083 + r = xh+yh + s = (abs(xh) > abs(yh)) ? (xh-r+yh+yl+xl) : (yh-r+xh+xl+yl) + zh = r+s + return zh +end + +# multiples of pi/2, as double-double (ie with "tail") +const pi1o2_h = 1.5707963267948966 # convert(Float64, pi * BigFloat(1/2)) +const pi1o2_l = 6.123233995736766e-17 # convert(Float64, pi * BigFloat(1/2) - pi1o2_h) + +const pi2o2_h = 3.141592653589793 # convert(Float64, pi * BigFloat(1)) +const pi2o2_l = 1.2246467991473532e-16 # convert(Float64, pi * BigFloat(1) - pi2o2_h) + +const pi3o2_h = 4.71238898038469 # convert(Float64, pi * BigFloat(3/2)) +const pi3o2_l = 1.8369701987210297e-16 # convert(Float64, pi * BigFloat(3/2) - pi3o2_h) + +const pi4o2_h = 6.283185307179586 # convert(Float64, pi * BigFloat(2)) +const pi4o2_l = 2.4492935982947064e-16 # convert(Float64, pi * BigFloat(2) - pi4o2_h) + +""" + rem2pi(x, r::RoundingMode) + +Compute the remainder of `x` after integer division by `2π`, with the quotient rounded +according to the rounding mode `r`. In other words, the quantity + + x - 2π*round(x/(2π),r) + +without any intermediate rounding. This internally uses a high precision approximation of +2π, and so will give a more accurate result than `rem(x,2π,r)` + +- if `r == RoundNearest`, then the result is in the interval ``[-π, π]``. This will generally + be the most accurate result. See also [`RoundNearest`](@ref). + +- if `r == RoundToZero`, then the result is in the interval ``[0, 2π]`` if `x` is positive,. + or ``[-2π, 0]`` otherwise. See also [`RoundToZero`](@ref). + +- if `r == RoundDown`, then the result is in the interval ``[0, 2π]``. + See also [`RoundDown`](@ref). +- if `r == RoundUp`, then the result is in the interval ``[-2π, 0]``. + See also [`RoundUp`](@ref). + +# Examples +```jldoctest +julia> rem2pi(7pi/4, RoundNearest) +-0.7853981633974485 + +julia> rem2pi(7pi/4, RoundDown) +5.497787143782138 +``` +""" +function rem2pi end +function rem2pi(x::Float64, ::RoundingMode{:Nearest}) + abs(x) < pi && return x + + n,y = rem_pio2_kernel(x) + + if iseven(n) + if n & 2 == 2 # n % 4 == 2: add/subtract pi + if y.hi <= 0 + return add22condh(y.hi,y.lo,pi2o2_h,pi2o2_l) + else + return add22condh(y.hi,y.lo,-pi2o2_h,-pi2o2_l) + end + else # n % 4 == 0: add 0 + return y.hi+y.lo + end + else + if n & 2 == 2 # n % 4 == 3: subtract pi/2 + return add22condh(y.hi,y.lo,-pi1o2_h,-pi1o2_l) + else # n % 4 == 1: add pi/2 + return add22condh(y.hi,y.lo,pi1o2_h,pi1o2_l) + end + end +end +function rem2pi(x::Float64, ::RoundingMode{:ToZero}) + ax = abs(x) + ax <= 2*Float64(pi,RoundDown) && return x + + n,y = rem_pio2_kernel(x) + + if iseven(n) + if n & 2 == 2 # n % 4 == 2: add pi + z = add22condh(y.hi,y.lo,pi2o2_h,pi2o2_l) + else # n % 4 == 0: add 0 or 2pi + if y.hi > 0 + z = y.hi+y.lo + else # negative: add 2pi + z = add22condh(y.hi,y.lo,pi4o2_h,pi4o2_l) + end + end + else + if n & 2 == 2 # n % 4 == 3: add 3pi/2 + z = add22condh(y.hi,y.lo,pi3o2_h,pi3o2_l) + else # n % 4 == 1: add pi/2 + z = add22condh(y.hi,y.lo,pi1o2_h,pi1o2_l) + end + end + copysign(z,x) +end +function rem2pi(x::Float64, ::RoundingMode{:Down}) + if x < pi4o2_h + if x >= 0 + return x + elseif x > -pi4o2_h + return add22condh(x,0.0,pi4o2_h,pi4o2_l) + end + end + + n,y = rem_pio2_kernel(x) + + if iseven(n) + if n & 2 == 2 # n % 4 == 2: add pi + return add22condh(y.hi,y.lo,pi2o2_h,pi2o2_l) + else # n % 4 == 0: add 0 or 2pi + if y.hi > 0 + return y.hi+y.lo + else # negative: add 2pi + return add22condh(y.hi,y.lo,pi4o2_h,pi4o2_l) + end + end + else + if n & 2 == 2 # n % 4 == 3: add 3pi/2 + return add22condh(y.hi,y.lo,pi3o2_h,pi3o2_l) + else # n % 4 == 1: add pi/2 + return add22condh(y.hi,y.lo,pi1o2_h,pi1o2_l) + end + end +end +function rem2pi(x::Float64, ::RoundingMode{:Up}) + if x > -pi4o2_h + if x <= 0 + return x + elseif x < pi4o2_h + return add22condh(x,0.0,-pi4o2_h,-pi4o2_l) + end + end + + n,y = rem_pio2_kernel(x) + + if iseven(n) + if n & 2 == 2 # n % 4 == 2: sub pi + return add22condh(y.hi,y.lo,-pi2o2_h,-pi2o2_l) + else # n % 4 == 0: sub 0 or 2pi + if y.hi < 0 + return y.hi+y.lo + else # positive: sub 2pi + return add22condh(y.hi,y.lo,-pi4o2_h,-pi4o2_l) + end + end + else + if n & 2 == 2 # n % 4 == 3: sub pi/2 + return add22condh(y.hi,y.lo,-pi1o2_h,-pi1o2_l) + else # n % 4 == 1: sub 3pi/2 + return add22condh(y.hi,y.lo,-pi3o2_h,-pi3o2_l) + end + end +end + +rem2pi(x::Float32, r::RoundingMode) = Float32(rem2pi(Float64(x), r)) +rem2pi(x::Float16, r::RoundingMode) = Float16(rem2pi(Float64(x), r)) +rem2pi(x::Int32, r::RoundingMode) = rem2pi(Float64(x), r) +function rem2pi(x::Int64, r::RoundingMode) + fx = Float64(x) + fx == x || throw(ArgumentError("Int64 argument to rem2pi is too large: $x")) + rem2pi(fx, r) +end + +""" + mod2pi(x) + +Modulus after division by `2π`, returning in the range ``[0,2π)``. + +This function computes a floating point representation of the modulus after division by +numerically exact `2π`, and is therefore not exactly the same as `mod(x,2π)`, which would +compute the modulus of `x` relative to division by the floating-point number `2π`. + +# Examples +```jldoctest +julia> mod2pi(9*pi/4) +0.7853981633974481 +``` +""" +mod2pi(x) = rem2pi(x,RoundDown) + +# generic fallback; for number types, promotion.jl does promotion + +""" + muladd(x, y, z) + +Combined multiply-add: computes `x*y+z`, but allowing the add and multiply to be merged +with each other or with surrounding operations for performance. +For example, this may be implemented as an [`fma`](@ref) if the hardware supports it +efficiently. +The result can be different on different machines and can also be different on the same machine +due to constant propagation or other optimizations. +See [`fma`](@ref). + +# Examples +```jldoctest +julia> muladd(3, 2, 1) +7 + +julia> 3 * 2 + 1 +7 +``` +""" +muladd(x,y,z) = x*y+z + +# Float16 definitions + +for func in (:sin,:cos,:tan,:asin,:acos,:atan,:sinh,:cosh,:tanh,:asinh,:acosh, + :atanh,:exp,:exp2,:exp10,:log,:log2,:log10,:sqrt,:lgamma,:log1p) + @eval begin + $func(a::Float16) = Float16($func(Float32(a))) + $func(a::ComplexF16) = ComplexF16($func(ComplexF32(a))) + end +end + +for func in (:atan,:hypot) + @eval begin + $func(a::Float16,b::Float16) = Float16($func(Float32(a),Float32(b))) + end +end + +cbrt(a::Float16) = Float16(cbrt(Float32(a))) +sincos(a::Float16) = Float16.(sincos(Float32(a))) + +# helper functions for Libm functionality + +""" + highword(x) + +Return the high word of `x` as a `UInt32`. +""" +@inline highword(x::Float64) = highword(reinterpret(UInt64, x)) +@inline highword(x::UInt64) = (x >>> 32) % UInt32 +@inline highword(x::Float32) = reinterpret(UInt32, x) + +@inline fromhighword(::Type{Float64}, u::UInt32) = reinterpret(Float64, UInt64(u) << 32) +@inline fromhighword(::Type{Float32}, u::UInt32) = reinterpret(Float32, u) + + +""" + poshighword(x) + +Return positive part of the high word of `x` as a `UInt32`. +""" +@inline poshighword(x::Float64) = poshighword(reinterpret(UInt64, x)) +@inline poshighword(x::UInt64) = highword(x) & 0x7fffffff +@inline poshighword(x::Float32) = highword(x) & 0x7fffffff + +# More special functions +include("special/cbrt.jl") +include("special/exp.jl") +include("special/exp10.jl") +include("special/ldexp_exp.jl") +include("special/hyperbolic.jl") +include("special/trig.jl") +include("special/rem_pio2.jl") +include("special/log.jl") + +# `missing` definitions for functions in this module +for f in (:(acos), :(acosh), :(asin), :(asinh), :(atan), :(atanh), + :(sin), :(sinh), :(cos), :(cosh), :(tan), :(tanh), + :(exp), :(exp2), :(expm1), :(log), :(log10), :(log1p), + :(log2), :(exponent), :(sqrt)) + @eval $(f)(::Missing) = missing +end +clamp(::Missing, lo, hi) = missing + +end # module diff --git a/base/mathconstants.jl b/base/mathconstants.jl new file mode 100644 index 0000000..a3d1be9 --- /dev/null +++ b/base/mathconstants.jl @@ -0,0 +1,98 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" + Base.MathConstants + +Module containing the mathematical constants. +See [`π`](@ref), [`ℯ`](@ref), [`γ`](@ref), [`φ`](@ref) and [`catalan`](@ref). +""" +module MathConstants + +export π, pi, ℯ, e, γ, eulergamma, catalan, φ, golden + +Base.@irrational π 3.14159265358979323846 pi +Base.@irrational ℯ 2.71828182845904523536 exp(big(1)) +Base.@irrational γ 0.57721566490153286061 euler +Base.@irrational φ 1.61803398874989484820 (1+sqrt(big(5)))/2 +Base.@irrational catalan 0.91596559417721901505 catalan + +# aliases +""" + π + pi + +The constant pi. + +# Examples +```jldoctest +julia> pi +π = 3.1415926535897... +``` +""" +π, const pi = π + +""" + ℯ + e + +The constant ℯ. + +# Examples +```jldoctest +julia> ℯ +ℯ = 2.7182818284590... +``` +""" +ℯ, const e = ℯ + +""" + γ + eulergamma + +Euler's constant. + +# Examples +```jldoctest +julia> Base.MathConstants.eulergamma +γ = 0.5772156649015... +``` +""" +γ, const eulergamma = γ + +""" + φ + golden + +The golden ratio. + +# Examples +```jldoctest +julia> Base.MathConstants.golden +φ = 1.6180339887498... +``` +""" +φ, const golden = φ + +""" + catalan + +Catalan's constant. + +# Examples +```jldoctest +julia> Base.MathConstants.catalan +catalan = 0.9159655941772... +``` +""" +catalan + +# loop over types to prevent ambiguities for ^(::Number, x) +for T in (AbstractIrrational, Rational, Integer, Number, Complex) + Base.:^(::Irrational{:ℯ}, x::T) = exp(x) +end +Base.literal_pow(::typeof(^), ::Irrational{:ℯ}, ::Val{p}) where {p} = exp(p) + +Base.log(::Irrational{:ℯ}) = 1 # use 1 to correctly promote expressions like log(x)/log(ℯ) +Base.log(::Irrational{:ℯ}, x::Number) = log(x) + +end # module diff --git a/base/meta.jl b/base/meta.jl new file mode 100644 index 0000000..ab1d3d0 --- /dev/null +++ b/base/meta.jl @@ -0,0 +1,353 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" +Convenience functions for metaprogramming. +""" +module Meta + +using ..CoreLogging + +export quot, + isexpr, + show_sexpr, + @dump + +""" + Meta.quot(ex)::Expr + +Quote expression `ex` to produce an expression with head `quote`. This can for instance be used to represent objects of type `Expr` in the AST. +See also the manual section about [QuoteNode](@ref man-quote-node). + +# Examples +```jldoctest +julia> eval(Meta.quot(:x)) +:x + +julia> dump(Meta.quot(:x)) +Expr + head: Symbol quote + args: Array{Any}((1,)) + 1: Symbol x + +julia> eval(Meta.quot(:(1+2))) +:(1 + 2) +``` +""" +quot(ex) = Expr(:quote, ex) + +""" + Meta.isexpr(ex, head[, n])::Bool + +Check if `ex` is an expression with head `head` and `n` arguments. + +# Examples +```jldoctest +julia> ex = :(f(x)) +:(f(x)) + +julia> Meta.isexpr(ex, :block) +false + +julia> Meta.isexpr(ex, :call) +true + +julia> Meta.isexpr(ex, [:block, :call]) # multiple possible heads +true + +julia> Meta.isexpr(ex, :call, 1) +false + +julia> Meta.isexpr(ex, :call, 2) +true +``` +""" +isexpr(@nospecialize(ex), head::Symbol) = isa(ex, Expr) && ex.head === head +isexpr(@nospecialize(ex), heads::Union{Set,Vector,Tuple}) = isa(ex, Expr) && in(ex.head, heads) +isexpr(@nospecialize(ex), heads, n::Int) = isexpr(ex, heads) && length(ex.args) == n + +""" + Meta.show_sexpr([io::IO,], ex) + +Show expression `ex` as a lisp style S-expression. + +# Examples +```jldoctest +julia> Meta.show_sexpr(:(f(x, g(y,z)))) +(:call, :f, :x, (:call, :g, :y, :z)) +``` +""" +show_sexpr(ex) = show_sexpr(stdout, ex) +show_sexpr(io::IO, ex) = show_sexpr(io, ex, 0) +show_sexpr(io::IO, ex, indent::Int) = show(io, ex) + +const sexpr_indent_width = 2 + +function show_sexpr(io::IO, ex::QuoteNode, indent::Int) + inner = indent + sexpr_indent_width + print(io, "(:quote, #QuoteNode\n", " "^inner) + show_sexpr(io, ex.value, inner) + print(io, '\n', " "^indent, ')') +end +function show_sexpr(io::IO, ex::Expr, indent::Int) + inner = indent + sexpr_indent_width + print(io, '(') + show_sexpr(io, ex.head, inner) + for arg in ex.args + print(io, ex.head === :block ? ",\n"*" "^inner : ", ") + show_sexpr(io, arg, inner) + end + if isempty(ex.args) + print(io, ",)") + else + print(io, (ex.head === :block ? "\n"*" "^indent : ""), ')') + end +end + +""" + @dump expr + +Show every part of the representation of the given expression. Equivalent to +[`dump(:(expr))`](@ref dump). +""" +macro dump(expr) + return :(dump($(QuoteNode(expr)))) +end + +""" + lower(m, x) + +Takes the expression `x` and returns an equivalent expression in lowered form +for executing in module `m`. +See also [`code_lowered`](@ref). +""" +lower(m::Module, @nospecialize(x)) = ccall(:jl_expand, Any, (Any, Any), x, m) + +""" + @lower [m] x + +Return lowered form of the expression `x` in module `m`. +By default `m` is the module in which the macro is called. +See also [`lower`](@ref). +""" +macro lower(code) + return :(lower($__module__, $(QuoteNode(code)))) +end +macro lower(mod, code) + return :(lower($(esc(mod)), $(QuoteNode(code)))) +end + + +## interface to parser ## + +""" + ParseError(msg) + +The expression passed to the [`parse`](@ref) function could not be interpreted as a valid Julia +expression. +""" +struct ParseError <: Exception + msg::AbstractString +end + +""" + parse(str, start; greedy=true, raise=true, depwarn=true) + +Parse the expression string and return an expression (which could later be passed to eval +for execution). `start` is the index of the first character to start parsing. If `greedy` is +`true` (default), `parse` will try to consume as much input as it can; otherwise, it will +stop as soon as it has parsed a valid expression. Incomplete but otherwise syntactically +valid expressions will return `Expr(:incomplete, "(error message)")`. If `raise` is `true` +(default), syntax errors other than incomplete expressions will raise an error. If `raise` +is `false`, `parse` will return an expression that will raise an error upon evaluation. If +`depwarn` is `false`, deprecation warnings will be suppressed. + +```jldoctest +julia> Meta.parse("x = 3, y = 5", 7) +(:(y = 5), 13) + +julia> Meta.parse("x = 3, y = 5", 5) +(:((3, y) = 5), 13) +``` +""" +function parse(str::AbstractString, pos::Integer; greedy::Bool=true, raise::Bool=true, + depwarn::Bool=true) + # pos is one based byte offset. + # returns (expr, end_pos). expr is () in case of parse error. + bstr = String(str) + # For now, assume all parser warnings are depwarns + ex, pos = with_logger(depwarn ? current_logger() : NullLogger()) do + ccall(:jl_parse_string, Any, + (Ptr{UInt8}, Csize_t, Int32, Int32), + bstr, sizeof(bstr), pos-1, greedy ? 1 : 0) + end + if raise && isa(ex,Expr) && ex.head === :error + throw(ParseError(ex.args[1])) + end + return ex, pos+1 # C is zero-based, Julia is 1-based +end + +""" + parse(str; raise=true, depwarn=true) + +Parse the expression string greedily, returning a single expression. An error is thrown if +there are additional characters after the first expression. If `raise` is `true` (default), +syntax errors will raise an error; otherwise, `parse` will return an expression that will +raise an error upon evaluation. If `depwarn` is `false`, deprecation warnings will be +suppressed. + +```jldoctest +julia> Meta.parse("x = 3") +:(x = 3) + +julia> Meta.parse("x = ") +:($(Expr(:incomplete, "incomplete: premature end of input"))) + +julia> Meta.parse("1.0.2") +ERROR: Base.Meta.ParseError("invalid numeric constant \\\"1.0.\\\"") +Stacktrace: +[...] + +julia> Meta.parse("1.0.2"; raise = false) +:($(Expr(:error, "invalid numeric constant \"1.0.\""))) +``` +""" +function parse(str::AbstractString; raise::Bool=true, depwarn::Bool=true) + ex, pos = parse(str, 1, greedy=true, raise=raise, depwarn=depwarn) + if isa(ex,Expr) && ex.head === :error + return ex + end + if pos <= ncodeunits(str) + raise && throw(ParseError("extra token after end of expression")) + return Expr(:error, "extra token after end of expression") + end + return ex +end + +""" + partially_inline!(code::Vector{Any}, slot_replacements::Vector{Any}, + type_signature::Type{<:Tuple}, static_param_values::Vector{Any}, + slot_offset::Int, statement_offset::Int, + boundscheck::Symbol) + +Return `code` after performing an in-place partial inlining pass on the Julia IR stored +within it. + +The kind of inlining transformations performed by this function are those that are generally +possible given only a runtime type signature for a method invocation and the corresponding +method's lowered IR. Thus, this function is mainly useful when preparing Julia IR to be +emitted from a `@generated` function. + +The performed transformations are: + +- replace slot numbers in the range `1:length(slot_replacements)` with the corresponding items in `slot_replacements` +- increment other slot numbers by `slot_offset` +- substitute static parameter placeholders (e.g. `Expr(:static_parameter, 1)`) with the corresponding +values in `static_param_values` +- increment any statement indices present in the IR (`GotoNode`s, `SSAValue`s, etc.) by `statement_offset` +(useful when the caller plans to prepend new statements to the IR) +- turn off boundschecking (if `boundscheck === :off`) or propagate boundschecking (if `boundscheck === :propagate`) + +This function is similar to `Core.Compiler.ssa_substitute!`, but works on pre-type-inference +IR instead of the optimizer's IR. +""" +function partially_inline!(code::Vector{Any}, slot_replacements::Vector{Any}, + @nospecialize(type_signature)#=::Type{<:Tuple}=#, + static_param_values::Vector{Any}, + slot_offset::Int, statement_offset::Int, + boundscheck::Symbol) + for i = 1:length(code) + isassigned(code, i) || continue + code[i] = _partially_inline!(code[i], slot_replacements, type_signature, + static_param_values, slot_offset, + statement_offset, boundscheck) + end + return code +end + +function _partially_inline!(@nospecialize(x), slot_replacements::Vector{Any}, + @nospecialize(type_signature), static_param_values::Vector{Any}, + slot_offset::Int, statement_offset::Int, + boundscheck::Symbol) + if isa(x, Core.SSAValue) + return Core.SSAValue(x.id + statement_offset) + end + if isa(x, Core.GotoNode) + return Core.GotoNode(x.label + statement_offset) + end + if isa(x, Core.SlotNumber) + id = x.id + if 1 <= id <= length(slot_replacements) + return slot_replacements[id] + end + return Core.SlotNumber(id + slot_offset) + end + if isa(x, Core.NewvarNode) + return Core.NewvarNode(_partially_inline!(x.slot, slot_replacements, type_signature, + static_param_values, slot_offset, + statement_offset, boundscheck)) + end + if isa(x, Core.PhiNode) + partially_inline!(x.values, slot_replacements, type_signature, static_param_values, + slot_offset, statement_offset, boundscheck) + x.edges .+= slot_offset + return x + end + if isa(x, Expr) + head = x.head + if head === :static_parameter + return QuoteNode(static_param_values[x.args[1]]) + elseif head === :cfunction + @assert !isa(type_signature, UnionAll) || !isempty(spvals) + if !isa(x.args[2], QuoteNode) # very common no-op + x.args[2] = _partially_inline!(x.args[2], slot_replacements, type_signature, + static_param_values, slot_offset, + statement_offset, boundscheck) + end + x.args[3] = _instantiate_type_in_env(x.args[3], type_signature, static_param_values) + x.args[4] = Core.svec(Any[_instantiate_type_in_env(argt, type_signature, static_param_values) for argt in x.args[4]]...) + elseif head === :foreigncall + @assert !isa(type_signature, UnionAll) || !isempty(static_param_values) + for i = 1:length(x.args) + if i == 2 + x.args[2] = _instantiate_type_in_env(x.args[2], type_signature, static_param_values) + elseif i == 3 + x.args[3] = Core.svec(Any[_instantiate_type_in_env(argt, type_signature, static_param_values) for argt in x.args[3]]...) + elseif i == 4 + @assert isa(x.args[4], Int) + elseif i == 5 + @assert isa((x.args[5]::QuoteNode).value, Symbol) + else + x.args[i] = _partially_inline!(x.args[i], slot_replacements, + type_signature, static_param_values, + slot_offset, statement_offset, + boundscheck) + end + end + elseif head === :boundscheck + if boundscheck === :propagate + return x + elseif boundscheck === :off + return false + else + return true + end + elseif head === :gotoifnot + x.args[1] = _partially_inline!(x.args[1], slot_replacements, type_signature, + static_param_values, slot_offset, + statement_offset, boundscheck) + x.args[2] += statement_offset + elseif head === :enter + x.args[1] += statement_offset + elseif !is_meta_expr_head(head) + partially_inline!(x.args, slot_replacements, type_signature, static_param_values, + slot_offset, statement_offset, boundscheck) + end + end + return x +end + +_instantiate_type_in_env(x, spsig, spvals) = ccall(:jl_instantiate_type_in_env, Any, (Any, Any, Ptr{Any}), x, spsig, spvals) + +is_meta_expr_head(head::Symbol) = (head === :inbounds || head === :boundscheck || head === :meta || head === :loopinfo) + +end # module diff --git a/base/methodshow.jl b/base/methodshow.jl new file mode 100644 index 0000000..f24a4e7 --- /dev/null +++ b/base/methodshow.jl @@ -0,0 +1,429 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Method and method table pretty-printing + +function argtype_decl(env, n, sig::DataType, i::Int, nargs, isva::Bool) # -> (argname, argtype) + t = sig.parameters[i] + if i == nargs && isva && !isvarargtype(t) + t = Vararg{t,length(sig.parameters)-nargs+1} + end + if isa(n,Expr) + n = n.args[1] # handle n::T in arg list + end + s = string(n) + i = findfirst(isequal('#'), s) + if i !== nothing + s = s[1:i-1] + end + if t === Any && !isempty(s) + return s, "" + end + if isvarargtype(t) + v1, v2 = nothing, nothing + if isa(t, UnionAll) + v1 = t.var + t = t.body + if isa(t, UnionAll) + v2 = t.var + t = t.body + end + end + ut = unwrap_unionall(t) + tt, tn = ut.parameters[1], ut.parameters[2] + if isa(tn, TypeVar) && (tn === v1 || tn === v2) + if tt === Any || (isa(tt, TypeVar) && (tt === v1 || tt === v2)) + return string(s, "..."), "" + else + return s, string_with_env(env, tt) * "..." + end + end + return s, string_with_env(env, "Vararg{", tt, ",", tn, "}") + end + return s, string_with_env(env, t) +end + +function method_argnames(m::Method) + argnames = ccall(:jl_uncompress_argnames, Vector{Any}, (Any,), m.slot_syms) + isempty(argnames) && return argnames + return argnames[1:m.nargs] +end + +function arg_decl_parts(m::Method) + tv = Any[] + sig = m.sig + while isa(sig, UnionAll) + push!(tv, sig.var) + sig = sig.body + end + file = m.file + line = m.line + argnames = method_argnames(m) + if length(argnames) >= m.nargs + show_env = ImmutableDict{Symbol, Any}() + for t in tv + show_env = ImmutableDict(show_env, :unionall_env => t) + end + decls = Tuple{String,String}[argtype_decl(show_env, argnames[i], sig, i, m.nargs, m.isva) + for i = 1:m.nargs] + else + decls = Tuple{String,String}[("", "") for i = 1:length(sig.parameters::SimpleVector)] + end + return tv, decls, file, line +end + +const empty_sym = Symbol("") + +function kwarg_decl(m::Method, kwtype::DataType) + sig = rewrap_unionall(Tuple{kwtype, Any, unwrap_unionall(m.sig).parameters...}, m.sig) + kwli = ccall(:jl_methtable_lookup, Any, (Any, Any, UInt), kwtype.name.mt, sig, get_world_counter()) + if kwli !== nothing + kwli = kwli::Method + slotnames = ccall(:jl_uncompress_argnames, Vector{Any}, (Any,), kwli.slot_syms) + kws = filter(x -> !(x === empty_sym || '#' in string(x)), slotnames[(kwli.nargs + 1):end]) + # ensure the kwarg... is always printed last. The order of the arguments are not + # necessarily the same as defined in the function + i = findfirst(x -> endswith(string(x), "..."), kws) + if i !== nothing + push!(kws, kws[i]) + deleteat!(kws, i) + end + return kws + end + return () +end + +function show_method_params(io::IO, tv) + if !isempty(tv) + print(io, " where ") + if length(tv) == 1 + show(io, tv[1]) + else + print(io, "{") + for i = 1:length(tv) + if i > 1 + print(io, ", ") + end + x = tv[i] + show(io, x) + io = IOContext(io, :unionall_env => x) + end + print(io, "}") + end + end +end + +# In case the line numbers in the source code have changed since the code was compiled, +# allow packages to set a callback function that corrects them. +# (Used by Revise and perhaps other packages.) +default_methodloc(method::Method) = method.file, method.line +const methodloc_callback = Ref{Function}(default_methodloc) + +functionloc(m::Core.MethodInstance) = functionloc(m.def) + +""" + functionloc(m::Method) + +Returns a tuple `(filename,line)` giving the location of a `Method` definition. +""" +function functionloc(m::Method) + file, ln = invokelatest(methodloc_callback[], m) + if ln <= 0 + error("could not determine location of method definition") + end + return (find_source_file(string(file)), ln) +end + +""" + functionloc(f::Function, types) + +Returns a tuple `(filename,line)` giving the location of a generic `Function` definition. +""" +functionloc(@nospecialize(f), @nospecialize(types)) = functionloc(which(f,types)) + +function functionloc(@nospecialize(f)) + mt = methods(f) + if isempty(mt) + if isa(f, Function) + error("function has no definitions") + else + error("object is not callable") + end + end + if length(mt) > 1 + error("function has multiple methods; please specify a type signature") + end + return functionloc(first(mt)) +end + +function show(io::IO, m::Method; kwtype::Union{DataType, Nothing}=nothing) + tv, decls, file, line = arg_decl_parts(m) + sig = unwrap_unionall(m.sig) + ft0 = sig.parameters[1] + ft = unwrap_unionall(ft0) + d1 = decls[1] + if sig === Tuple + # Builtin + print(io, m.name, "(...) in ", m.module) + return + end + if ft <: Function && isa(ft, DataType) && + isdefined(ft.name.module, ft.name.mt.name) && + # TODO: more accurate test? (tn.name === "#" name) + ft0 === typeof(getfield(ft.name.module, ft.name.mt.name)) + print(io, ft.name.mt.name) + elseif isa(ft, DataType) && ft.name === Type.body.name + f = ft.parameters[1] + if isa(f, DataType) && isempty(f.parameters) + print(io, f) + else + print(io, "(", d1[1], "::", d1[2], ")") + end + else + print(io, "(", d1[1], "::", d1[2], ")") + end + print(io, "(") + join(io, String[isempty(d[2]) ? d[1] : d[1]*"::"*d[2] for d in decls[2:end]], + ", ", ", ") + if kwtype !== nothing + kwargs = kwarg_decl(m, kwtype) + if !isempty(kwargs) + print(io, "; ") + join(io, kwargs, ", ", ", ") + end + end + print(io, ")") + show_method_params(io, tv) + print(io, " in ", m.module) + if line > 0 + try + file, line = invokelatest(methodloc_callback[], m) + catch + end + print(io, " at ", file, ":", line) + end +end + +function show_method_list_header(io::IO, ms::MethodList, namefmt::Function) + mt = ms.mt + name = mt.name + hasname = isdefined(mt.module, name) && + typeof(getfield(mt.module, name)) <: Function + n = length(ms) + if mt.module === Core && n == 0 && mt.defs === nothing && mt.cache !== nothing + # try to detect Builtin + print(io, "# built-in function; no methods") + else + m = n==1 ? "method" : "methods" + print(io, "# $n $m") + sname = string(name) + namedisplay = namefmt(sname) + if hasname + what = startswith(sname, '@') ? "macro" : "generic function" + print(io, " for ", what, " ", namedisplay) + elseif '#' in sname + print(io, " for anonymous function ", namedisplay) + elseif mt === _TYPE_NAME.mt + print(io, " for type constructor") + end + print(io, ":") + end +end + +function show_method_table(io::IO, ms::MethodList, max::Int=-1, header::Bool=true) + mt = ms.mt + name = mt.name + hasname = isdefined(mt.module, name) && + typeof(getfield(mt.module, name)) <: Function + if header + show_method_list_header(io, ms, str -> "\""*str*"\"") + end + kwtype = isdefined(mt, :kwsorter) ? typeof(mt.kwsorter) : nothing + n = rest = 0 + local last + + resize!(LAST_SHOWN_LINE_INFOS, 0) + for meth in ms + if max==-1 || n 0 + println(io) + if rest == 1 + show(io, last; kwtype=kwtype) + else + print(io, "... $rest methods not shown") + if hasname + print(io, " (use methods($name) to see them all)") + end + end + end +end + +show(io::IO, ms::MethodList) = show_method_table(io, ms) +show(io::IO, mt::Core.MethodTable) = show_method_table(io, MethodList(mt)) + +function inbase(m::Module) + if m == Base + true + else + parent = parentmodule(m) + parent === m ? false : inbase(parent) + end +end +fileurl(file) = let f = find_source_file(file); f === nothing ? "" : "file://"*f; end + +function url(m::Method) + M = m.module + (m.file === :null || m.file === :string) && return "" + file = string(m.file) + line = m.line + line <= 0 || occursin(r"In\[[0-9]+\]", file) && return "" + Sys.iswindows() && (file = replace(file, '\\' => '/')) + libgit2_id = PkgId(UUID((0x76f85450_5226_5b5a,0x8eaa_529ad045b433)), "LibGit2") + if inbase(M) + if isempty(Base.GIT_VERSION_INFO.commit) + # this url will only work if we're on a tagged release + return "https://github.com/JuliaLang/julia/tree/v$VERSION/base/$file#L$line" + else + return "https://github.com/JuliaLang/julia/tree/$(Base.GIT_VERSION_INFO.commit)/base/$file#L$line" + end + elseif root_module_exists(libgit2_id) + LibGit2 = root_module(libgit2_id) + try + d = dirname(file) + return LibGit2.with(LibGit2.GitRepoExt(d)) do repo + LibGit2.with(LibGit2.GitConfig(repo)) do cfg + u = LibGit2.get(cfg, "remote.origin.url", "") + u = match(LibGit2.GITHUB_REGEX,u).captures[1] + commit = string(LibGit2.head_oid(repo)) + root = LibGit2.path(repo) + if startswith(file, root) || startswith(realpath(file), root) + "https://github.com/$u/tree/$commit/"*file[length(root)+1:end]*"#L$line" + else + fileurl(file) + end + end + end + catch + return fileurl(file) + end + else + return fileurl(file) + end +end + +function show(io::IO, ::MIME"text/html", m::Method; kwtype::Union{DataType, Nothing}=nothing) + tv, decls, file, line = arg_decl_parts(m) + sig = unwrap_unionall(m.sig) + ft0 = sig.parameters[1] + ft = unwrap_unionall(ft0) + d1 = decls[1] + if sig === Tuple + # Builtin + print(io, m.name, "(...) in ", m.module) + return + end + if ft <: Function && isa(ft, DataType) && + isdefined(ft.name.module, ft.name.mt.name) && + ft0 === typeof(getfield(ft.name.module, ft.name.mt.name)) + print(io, ft.name.mt.name) + elseif isa(ft, DataType) && ft.name === Type.body.name + f = ft.parameters[1] + if isa(f, DataType) && isempty(f.parameters) + print(io, f) + else + print(io, "(", d1[1], "::", d1[2], ")") + end + else + print(io, "(", d1[1], "::", d1[2], ")") + end + print(io, "(") + join(io, String[isempty(d[2]) ? d[1] : d[1]*"::"*d[2]*"" + for d in decls[2:end]], ", ", ", ") + if kwtype !== nothing + kwargs = kwarg_decl(m, kwtype) + if !isempty(kwargs) + print(io, "; ") + join(io, kwargs, ", ", ", ") + print(io, "") + end + end + print(io, ")") + if !isempty(tv) + print(io,"") + show_method_params(io, tv) + print(io,"") + end + print(io, " in ", m.module) + if line > 0 + try + file, line = invokelatest(methodloc_callback[], m) + catch + end + u = url(m) + if isempty(u) + print(io, " at ", file, ":", line) + else + print(io, """ at """, + file, ":", line, "") + end + end +end + +function show(io::IO, mime::MIME"text/html", ms::MethodList) + mt = ms.mt + show_method_list_header(io, ms, str -> ""*str*"") + kwtype = isdefined(mt, :kwsorter) ? typeof(mt.kwsorter) : nothing + print(io, "
    ") + for meth in ms + print(io, "
  • ") + show(io, mime, meth; kwtype=kwtype) + print(io, "
  • ") + end + print(io, "
") +end + +show(io::IO, mime::MIME"text/html", mt::Core.MethodTable) = show(io, mime, MethodList(mt)) + +# pretty-printing of AbstractVector{Method} +function show(io::IO, mime::MIME"text/plain", mt::AbstractVector{Method}) + resize!(LAST_SHOWN_LINE_INFOS, 0) + first = true + for (i, m) in enumerate(mt) + first || println(io) + first = false + print(io, "[$(i)] ") + show(io, m) + file, line = m.file, m.line + try + file, line = invokelatest(methodloc_callback[], m) + catch + end + push!(LAST_SHOWN_LINE_INFOS, (string(file), line)) + end +end + +function show(io::IO, mime::MIME"text/html", mt::AbstractVector{Method}) + summary(io, mt) + if !isempty(mt) + print(io, ":
    ") + for d in mt + print(io, "
  • ") + show(io, mime, d) + end + print(io, "
") + end +end diff --git a/base/missing.jl b/base/missing.jl new file mode 100644 index 0000000..e34aaad --- /dev/null +++ b/base/missing.jl @@ -0,0 +1,405 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Missing, missing and ismissing are defined in essentials.jl + +show(io::IO, x::Missing) = print(io, "missing") + +""" + MissingException(msg) + +Exception thrown when a [`missing`](@ref) value is encountered in a situation +where it is not supported. The error message, in the `msg` field +may provide more specific details. +""" +struct MissingException <: Exception + msg::AbstractString +end + +showerror(io::IO, ex::MissingException) = + print(io, "MissingException: ", ex.msg) + +""" + nonmissingtype(T::Type) + +If `T` is a union of types containing `Missing`, return a new type with +`Missing` removed. + +# Examples +```jldoctest +julia> nonmissingtype(Union{Int64,Missing}) +Int64 + +julia> nonmissingtype(Any) +Any +``` + +!!! compat "Julia 1.3" + This function is exported as of Julia 1.3. +""" +nonmissingtype(::Type{T}) where {T} = Core.Compiler.typesubtract(T, Missing) + +function nonmissingtype_checked(T::Type) + R = nonmissingtype(T) + R >: T && error("could not compute non-missing type") + return R +end + +promote_rule(T::Type{Missing}, S::Type) = Union{S, Missing} +promote_rule(T::Type{Union{Nothing, Missing}}, S::Type) = Union{S, Nothing, Missing} +function promote_rule(T::Type{>:Union{Nothing, Missing}}, S::Type) + R = nonnothingtype(T) + R >: T && return Any + T = R + R = nonmissingtype(T) + R >: T && return Any + T = R + R = promote_type(T, S) + return Union{R, Nothing, Missing} +end +function promote_rule(T::Type{>:Missing}, S::Type) + R = nonmissingtype(T) + R >: T && return Any + T = R + R = promote_type(T, S) + return Union{R, Missing} +end + +convert(::Type{T}, x::T) where {T>:Missing} = x +convert(::Type{T}, x::T) where {T>:Union{Missing, Nothing}} = x +convert(::Type{T}, x) where {T>:Missing} = convert(nonmissingtype_checked(T), x) +convert(::Type{T}, x) where {T>:Union{Missing, Nothing}} = convert(nonmissingtype_checked(nonnothingtype_checked(T)), x) + + +# Comparison operators +==(::Missing, ::Missing) = missing +==(::Missing, ::Any) = missing +==(::Any, ::Missing) = missing +# To fix ambiguity +==(::Missing, ::WeakRef) = missing +==(::WeakRef, ::Missing) = missing +isequal(::Missing, ::Missing) = true +isequal(::Missing, ::Any) = false +isequal(::Any, ::Missing) = false +<(::Missing, ::Missing) = missing +<(::Missing, ::Any) = missing +<(::Any, ::Missing) = missing +isless(::Missing, ::Missing) = false +isless(::Missing, ::Any) = false +isless(::Any, ::Missing) = true +isapprox(::Missing, ::Missing; kwargs...) = missing +isapprox(::Missing, ::Any; kwargs...) = missing +isapprox(::Any, ::Missing; kwargs...) = missing + +# Unary operators/functions +for f in (:(!), :(~), :(+), :(-), :(zero), :(one), :(oneunit), + :(isfinite), :(isinf), :(isodd), + :(isinteger), :(isreal), :(isnan), + :(iszero), :(transpose), :(adjoint), :(float), :(conj), + :(abs), :(abs2), :(iseven), :(ispow2), + :(real), :(imag), :(sign), :(inv)) + @eval ($f)(::Missing) = missing +end +for f in (:(Base.zero), :(Base.one), :(Base.oneunit)) + @eval ($f)(::Type{Missing}) = missing + @eval function $(f)(::Type{Union{T, Missing}}) where T + T === Any && throw(MethodError($f, (Any,))) # To prevent StackOverflowError + $f(T) + end +end + +# Binary operators/functions +for f in (:(+), :(-), :(*), :(/), :(^), :(div), :(mod), :(fld), :(rem)) + @eval begin + # Scalar with missing + ($f)(::Missing, ::Missing) = missing + ($f)(::Missing, ::Number) = missing + ($f)(::Number, ::Missing) = missing + end +end + +min(::Missing, ::Missing) = missing +min(::Missing, ::Any) = missing +min(::Any, ::Missing) = missing +max(::Missing, ::Missing) = missing +max(::Missing, ::Any) = missing +max(::Any, ::Missing) = missing + +# Rounding and related functions +round(::Missing, ::RoundingMode=RoundNearest; sigdigits::Integer=0, digits::Integer=0, base::Integer=0) = missing +round(::Type{>:Missing}, ::Missing, ::RoundingMode=RoundNearest) = missing +round(::Type{T}, ::Missing, ::RoundingMode=RoundNearest) where {T} = + throw(MissingException("cannot convert a missing value to type $T: use Union{$T, Missing} instead")) +round(::Type{T}, x::Any, r::RoundingMode=RoundNearest) where {T>:Missing} = round(nonmissingtype_checked(T), x, r) +# to fix ambiguities +round(::Type{T}, x::Rational, r::RoundingMode=RoundNearest) where {T>:Missing} = round(nonmissingtype_checked(T), x, r) +round(::Type{T}, x::Rational{Bool}, r::RoundingMode=RoundNearest) where {T>:Missing} = round(nonmissingtype_checked(T), x, r) + +# Handle ceil, floor, and trunc separately as they have no RoundingMode argument +for f in (:(ceil), :(floor), :(trunc)) + @eval begin + ($f)(::Missing; sigdigits::Integer=0, digits::Integer=0, base::Integer=0) = missing + ($f)(::Type{>:Missing}, ::Missing) = missing + ($f)(::Type{T}, ::Missing) where {T} = + throw(MissingException("cannot convert a missing value to type $T: use Union{$T, Missing} instead")) + ($f)(::Type{T}, x::Any) where {T>:Missing} = $f(nonmissingtype_checked(T), x) + # to fix ambiguities + ($f)(::Type{T}, x::Rational) where {T>:Missing} = $f(nonmissingtype_checked(T), x) + end +end + +# to avoid ambiguity warnings +(^)(::Missing, ::Integer) = missing + +# Bit operators +(&)(::Missing, ::Missing) = missing +(&)(a::Missing, b::Bool) = ifelse(b, missing, false) +(&)(b::Bool, a::Missing) = ifelse(b, missing, false) +(&)(::Missing, ::Integer) = missing +(&)(::Integer, ::Missing) = missing +(|)(::Missing, ::Missing) = missing +(|)(a::Missing, b::Bool) = ifelse(b, true, missing) +(|)(b::Bool, a::Missing) = ifelse(b, true, missing) +(|)(::Missing, ::Integer) = missing +(|)(::Integer, ::Missing) = missing +xor(::Missing, ::Missing) = missing +xor(a::Missing, b::Bool) = missing +xor(b::Bool, a::Missing) = missing +xor(::Missing, ::Integer) = missing +xor(::Integer, ::Missing) = missing + +*(d::Missing, x::AbstractString) = missing +*(d::AbstractString, x::Missing) = missing + +function float(A::AbstractArray{Union{T, Missing}}) where {T} + U = typeof(float(zero(T))) + convert(AbstractArray{Union{U, Missing}}, A) +end +float(A::AbstractArray{Missing}) = A + +""" + skipmissing(itr) + +Return an iterator over the elements in `itr` skipping [`missing`](@ref) values. +The returned object can be indexed using indices of `itr` if the latter is indexable. +Indices corresponding to missing values are not valid: they are skipped by [`keys`](@ref) +and [`eachindex`](@ref), and a `MissingException` is thrown when trying to use them. + +Use [`collect`](@ref) to obtain an `Array` containing the non-`missing` values in +`itr`. Note that even if `itr` is a multidimensional array, the result will always +be a `Vector` since it is not possible to remove missings while preserving dimensions +of the input. + +# Examples +```jldoctest +julia> x = skipmissing([1, missing, 2]) +Base.SkipMissing{Array{Union{Missing, Int64},1}}(Union{Missing, Int64}[1, missing, 2]) + +julia> sum(x) +3 + +julia> x[1] +1 + +julia> x[2] +ERROR: MissingException: the value at index (2,) is missing +[...] + +julia> argmax(x) +3 + +julia> collect(keys(x)) +2-element Array{Int64,1}: + 1 + 3 + +julia> collect(skipmissing([1, missing, 2])) +2-element Array{Int64,1}: + 1 + 2 + +julia> collect(skipmissing([1 missing; 2 missing])) +2-element Array{Int64,1}: + 1 + 2 +``` +""" +skipmissing(itr) = SkipMissing(itr) + +struct SkipMissing{T} + x::T +end +IteratorSize(::Type{<:SkipMissing}) = SizeUnknown() +IteratorEltype(::Type{SkipMissing{T}}) where {T} = IteratorEltype(T) +eltype(::Type{SkipMissing{T}}) where {T} = nonmissingtype(eltype(T)) + +function iterate(itr::SkipMissing, state...) + y = iterate(itr.x, state...) + y === nothing && return nothing + item, state = y + while item === missing + y = iterate(itr.x, state) + y === nothing && return nothing + item, state = y + end + item, state +end + +IndexStyle(::Type{<:SkipMissing{T}}) where {T} = IndexStyle(T) +eachindex(itr::SkipMissing) = + Iterators.filter(i -> @inbounds(itr.x[i]) !== missing, eachindex(itr.x)) +keys(itr::SkipMissing) = + Iterators.filter(i -> @inbounds(itr.x[i]) !== missing, keys(itr.x)) +@propagate_inbounds function getindex(itr::SkipMissing, I...) + v = itr.x[I...] + v === missing && throw(MissingException("the value at index $I is missing")) + v +end + +# Optimized mapreduce implementation +# The generic method is faster when !(eltype(A) >: Missing) since it does not need +# additional loops to identify the two first non-missing values of each block +mapreduce(f, op, itr::SkipMissing{<:AbstractArray}) = + _mapreduce(f, op, IndexStyle(itr.x), eltype(itr.x) >: Missing ? itr : itr.x) + +function _mapreduce(f, op, ::IndexLinear, itr::SkipMissing{<:AbstractArray}) + A = itr.x + local ai + inds = LinearIndices(A) + i = first(inds) + ilast = last(inds) + while i <= ilast + @inbounds ai = A[i] + ai === missing || break + i += 1 + end + i > ilast && return mapreduce_empty(f, op, eltype(itr)) + a1 = ai + i += 1 + while i <= ilast + @inbounds ai = A[i] + ai === missing || break + i += 1 + end + i > ilast && return mapreduce_first(f, op, a1) + # We know A contains at least two non-missing entries: the result cannot be nothing + something(mapreduce_impl(f, op, itr, first(inds), last(inds))) +end + +_mapreduce(f, op, ::IndexCartesian, itr::SkipMissing) = mapfoldl(f, op, itr) + +mapreduce_impl(f, op, A::SkipMissing, ifirst::Integer, ilast::Integer) = + mapreduce_impl(f, op, A, ifirst, ilast, pairwise_blocksize(f, op)) + +# Returns nothing when the input contains only missing values, and Some(x) otherwise +@noinline function mapreduce_impl(f, op, itr::SkipMissing{<:AbstractArray}, + ifirst::Integer, ilast::Integer, blksize::Int) + A = itr.x + if ifirst == ilast + @inbounds a1 = A[ifirst] + if a1 === missing + return nothing + else + return Some(mapreduce_first(f, op, a1)) + end + elseif ifirst + blksize > ilast + # sequential portion + local ai + i = ifirst + while i <= ilast + @inbounds ai = A[i] + ai === missing || break + i += 1 + end + i > ilast && return nothing + a1 = ai::eltype(itr) + i += 1 + while i <= ilast + @inbounds ai = A[i] + ai === missing || break + i += 1 + end + i > ilast && return Some(mapreduce_first(f, op, a1)) + a2 = ai::eltype(itr) + i += 1 + v = op(f(a1), f(a2)) + @simd for i = i:ilast + @inbounds ai = A[i] + if ai !== missing + v = op(v, f(ai)) + end + end + return Some(v) + else + # pairwise portion + imid = (ifirst + ilast) >> 1 + v1 = mapreduce_impl(f, op, itr, ifirst, imid, blksize) + v2 = mapreduce_impl(f, op, itr, imid+1, ilast, blksize) + if v1 === nothing && v2 === nothing + return nothing + elseif v1 === nothing + return v2 + elseif v2 === nothing + return v1 + else + return Some(op(something(v1), something(v2))) + end + end +end + +""" + filter(f, itr::SkipMissing{<:AbstractArray}) + +Return a vector similar to the array wrapped by the given `SkipMissing` iterator +but with all missing elements and those for which `f` returns `false` removed. + +!!! compat "Julia 1.2" + This method requires Julia 1.2 or later. + +# Examples +```jldoctest +julia> x = [1 2; missing 4] +2×2 Array{Union{Missing, Int64},2}: + 1 2 + missing 4 + +julia> filter(isodd, skipmissing(x)) +1-element Array{Int64,1}: + 1 +``` +""" +function filter(f, itr::SkipMissing{<:AbstractArray}) + y = similar(itr.x, eltype(itr), 0) + for xi in itr.x + if xi !== missing && f(xi) + push!(y, xi) + end + end + y +end + +""" + coalesce(x, y...) + +Return the first value in the arguments which is not equal to [`missing`](@ref), +if any. Otherwise return `missing`. + +# Examples + +```jldoctest +julia> coalesce(missing, 1) +1 + +julia> coalesce(1, missing) +1 + +julia> coalesce(nothing, 1) # returns `nothing` + +julia> coalesce(missing, missing) +missing +``` +""" +function coalesce end + +coalesce() = missing +coalesce(x::Missing, y...) = coalesce(y...) +coalesce(x::Any, y...) = x diff --git a/base/mpfr.jl b/base/mpfr.jl new file mode 100644 index 0000000..063a2e6 --- /dev/null +++ b/base/mpfr.jl @@ -0,0 +1,1053 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +module MPFR + +export + BigFloat, + setprecision + +import + .Base: *, +, -, /, <, <=, ==, >, >=, ^, ceil, cmp, convert, copysign, div, + inv, exp, exp2, exponent, factorial, floor, fma, hypot, isinteger, + isfinite, isinf, isnan, ldexp, log, log2, log10, max, min, mod, modf, + nextfloat, prevfloat, promote_rule, rem, rem2pi, round, show, float, + sum, sqrt, string, print, trunc, precision, exp10, expm1, + log1p, + eps, signbit, sin, cos, sincos, tan, sec, csc, cot, acos, asin, atan, + cosh, sinh, tanh, sech, csch, coth, acosh, asinh, atanh, + cbrt, typemax, typemin, unsafe_trunc, floatmin, floatmax, rounding, + setrounding, maxintfloat, widen, significand, frexp, tryparse, iszero, + isone, big, _string_n + +import .Base.Rounding: rounding_raw, setrounding_raw + +import .Base.GMP: ClongMax, CulongMax, CdoubleMax, Limb + +import .Base.FastMath.sincos_fast + +version() = VersionNumber(unsafe_string(ccall((:mpfr_get_version,:libmpfr), Ptr{Cchar}, ()))) +patches() = split(unsafe_string(ccall((:mpfr_get_patches,:libmpfr), Ptr{Cchar}, ())),' ') + +function __init__() + try + # set exponent to full range by default + set_emin!(get_emin_min()) + set_emax!(get_emax_max()) + catch ex + Base.showerror_nostdio(ex, "WARNING: Error during initialization of module MPFR") + end + nothing +end + +""" + MPFR.MPFRRoundingMode + +Matches the `mpfr_rnd_t` enum provided by MPFR, see +https://www.mpfr.org/mpfr-current/mpfr.html#Rounding-Modes + +This is for internal use, and ensures that `ROUNDING_MODE[]` is type-stable. +""" +@enum MPFRRoundingMode begin + MPFRRoundNearest + MPFRRoundToZero + MPFRRoundUp + MPFRRoundDown + MPFRRoundFromZero + MPFRRoundFaithful +end + +convert(::Type{MPFRRoundingMode}, ::RoundingMode{:Nearest}) = MPFRRoundNearest +convert(::Type{MPFRRoundingMode}, ::RoundingMode{:ToZero}) = MPFRRoundToZero +convert(::Type{MPFRRoundingMode}, ::RoundingMode{:Up}) = MPFRRoundUp +convert(::Type{MPFRRoundingMode}, ::RoundingMode{:Down}) = MPFRRoundDown +convert(::Type{MPFRRoundingMode}, ::RoundingMode{:FromZero}) = MPFRRoundFromZero + +function convert(::Type{RoundingMode}, r::MPFRRoundingMode) + if r == MPFRRoundNearest + return RoundNearest + elseif r == MPFRRoundToZero + return RoundToZero + elseif r == MPFRRoundUp + return RoundUp + elseif r == MPFRRoundDown + return RoundDown + elseif r == MPFRRoundFromZero + return RoundFromZero + else + throw(ArgumentError("invalid MPFR rounding mode code: $r")) + end +end + +const ROUNDING_MODE = Ref{MPFRRoundingMode}(MPFRRoundNearest) +const DEFAULT_PRECISION = Ref{Clong}(256) + +# Basic type and initialization definitions + +""" + BigFloat <: AbstractFloat + +Arbitrary precision floating point number type. +""" +mutable struct BigFloat <: AbstractFloat + prec::Clong + sign::Cint + exp::Clong + d::Ptr{Limb} + # _d::Buffer{Limb} # Julia gc handle for memory @ d + _d::String # Julia gc handle for memory @ d (optimized) + + # Not recommended for general use: + # used internally by, e.g. deepcopy + global function _BigFloat(prec::Clong, sign::Cint, exp::Clong, d::String) + # ccall-based version, inlined below + #z = new(zero(Clong), zero(Cint), zero(Clong), C_NULL, d) + #ccall((:mpfr_custom_init,:libmpfr), Cvoid, (Ptr{Limb}, Clong), d, prec) # currently seems to be a no-op in mpfr + #NAN_KIND = Cint(0) + #ccall((:mpfr_custom_init_set,:libmpfr), Cvoid, (Ref{BigFloat}, Cint, Clong, Ptr{Limb}), z, NAN_KIND, prec, d) + #return z + return new(prec, sign, exp, pointer(d), d) + end + + function BigFloat(; precision::Integer=DEFAULT_PRECISION[]) + nb = ccall((:mpfr_custom_get_size,:libmpfr), Csize_t, (Clong,), precision) + nb = (nb + Core.sizeof(Limb) - 1) ÷ Core.sizeof(Limb) # align to number of Limb allocations required for this + #d = Vector{Limb}(undef, nb) + d = _string_n(nb * Core.sizeof(Limb)) + EXP_NAN = Clong(1) - Clong(typemax(Culong) >> 1) + return _BigFloat(Clong(precision), one(Cint), EXP_NAN, d) # +NAN + end +end + +rounding_raw(::Type{BigFloat}) = ROUNDING_MODE[] +setrounding_raw(::Type{BigFloat}, r::MPFRRoundingMode) = ROUNDING_MODE[]=r + +rounding(::Type{BigFloat}) = convert(RoundingMode, rounding_raw(BigFloat)) +setrounding(::Type{BigFloat}, r::RoundingMode) = setrounding_raw(BigFloat, convert(MPFRRoundingMode, r)) + + +# overload the definition of unsafe_convert to ensure that `x.d` is assigned +# it may have been dropped in the event that the BigFloat was serialized +Base.unsafe_convert(::Type{Ref{BigFloat}}, x::Ptr{BigFloat}) = x +@inline function Base.unsafe_convert(::Type{Ref{BigFloat}}, x::Ref{BigFloat}) + x = x[] + if x.d == C_NULL + x.d = pointer(x._d) + end + return convert(Ptr{BigFloat}, Base.pointer_from_objref(x)) +end + +""" + BigFloat(x::Union{Real, AbstractString} [, rounding::RoundingMode=rounding(BigFloat)]; [precision::Integer=precision(BigFloat)]) + +Create an arbitrary precision floating point number from `x`, with precision +`precision`. The `rounding` argument specifies the direction in which the result should be +rounded if the conversion cannot be done exactly. If not provided, these are set by the current global values. + +`BigFloat(x::Real)` is the same as `convert(BigFloat,x)`, except if `x` itself is already +`BigFloat`, in which case it will return a value with the precision set to the current +global precision; `convert` will always return `x`. + +`BigFloat(x::AbstractString)` is identical to [`parse`](@ref). This is provided for +convenience since decimal literals are converted to `Float64` when parsed, so +`BigFloat(2.1)` may not yield what you expect. + +!!! compat "Julia 1.1" + `precision` as a keyword argument requires at least Julia 1.1. + In Julia 1.0 `precision` is the second positional argument (`BigFloat(x, precision)`). + +# Examples +```jldoctest +julia> BigFloat(2.1) # 2.1 here is a Float64 +2.100000000000000088817841970012523233890533447265625 + +julia> BigFloat("2.1") # the closest BigFloat to 2.1 +2.099999999999999999999999999999999999999999999999999999999999999999999999999986 + +julia> BigFloat("2.1", RoundUp) +2.100000000000000000000000000000000000000000000000000000000000000000000000000021 + +julia> BigFloat("2.1", RoundUp, precision=128) +2.100000000000000000000000000000000000007 +``` + +# See also +- [`@big_str`](@ref) +- [`rounding`](@ref) and [`setrounding`](@ref) +- [`precision`](@ref) and [`setprecision`](@ref) +""" +BigFloat(x, r::RoundingMode) + +widen(::Type{Float64}) = BigFloat +widen(::Type{BigFloat}) = BigFloat + +function BigFloat(x::BigFloat, r::MPFRRoundingMode=ROUNDING_MODE[]; precision::Integer=DEFAULT_PRECISION[]) + if precision == MPFR.precision(x) + return x + else + z = BigFloat(;precision=precision) + ccall((:mpfr_set, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), + z, x, r) + return z + end +end + +function _duplicate(x::BigFloat) + z = BigFloat(;precision=precision(x)) + ccall((:mpfr_set, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Int32), z, x, 0) + return z +end + +# convert to BigFloat +for (fJ, fC) in ((:si,:Clong), (:ui,:Culong)) + @eval begin + function BigFloat(x::($fC), r::MPFRRoundingMode=ROUNDING_MODE[]; precision::Integer=DEFAULT_PRECISION[]) + z = BigFloat(;precision=precision) + ccall(($(string(:mpfr_set_,fJ)), :libmpfr), Int32, (Ref{BigFloat}, $fC, MPFRRoundingMode), z, x, r) + return z + end + end +end + +function BigFloat(x::Float64, r::MPFRRoundingMode=ROUNDING_MODE[]; precision::Integer=DEFAULT_PRECISION[]) + z = BigFloat(;precision=precision) + ccall((:mpfr_set_d, :libmpfr), Int32, (Ref{BigFloat}, Float64, MPFRRoundingMode), z, x, r) + if isnan(x) && signbit(x) != signbit(z) + z.sign = -z.sign + end + return z +end + +function BigFloat(x::BigInt, r::MPFRRoundingMode=ROUNDING_MODE[]; precision::Integer=DEFAULT_PRECISION[]) + z = BigFloat(;precision=precision) + ccall((:mpfr_set_z, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigInt}, MPFRRoundingMode), z, x, r) + return z +end + +BigFloat(x::Integer, r::MPFRRoundingMode=ROUNDING_MODE[]; precision::Integer=DEFAULT_PRECISION[]) = + BigFloat(BigInt(x), r; precision=precision) + +BigFloat(x::Union{Bool,Int8,Int16,Int32}, r::MPFRRoundingMode=ROUNDING_MODE[]; precision::Integer=DEFAULT_PRECISION[]) = + BigFloat(convert(Clong, x), r; precision=precision) +BigFloat(x::Union{UInt8,UInt16,UInt32}, r::MPFRRoundingMode=ROUNDING_MODE[]; precision::Integer=DEFAULT_PRECISION[]) = + BigFloat(convert(Culong, x), r; precision=precision) + +BigFloat(x::Union{Float16,Float32}, r::MPFRRoundingMode=ROUNDING_MODE[]; precision::Integer=DEFAULT_PRECISION[]) = + BigFloat(Float64(x), r; precision=precision) + +function BigFloat(x::Rational, r::MPFRRoundingMode=ROUNDING_MODE[]; precision::Integer=DEFAULT_PRECISION[]) + setprecision(BigFloat, precision) do + setrounding_raw(BigFloat, r) do + BigFloat(numerator(x)) / BigFloat(denominator(x)) + end + end +end + +function tryparse(::Type{BigFloat}, s::AbstractString; base::Integer=0, precision::Integer=DEFAULT_PRECISION[], rounding::MPFRRoundingMode=ROUNDING_MODE[]) + !isempty(s) && isspace(s[end]) && return tryparse(BigFloat, rstrip(s), base = base) + z = BigFloat(precision=precision) + err = ccall((:mpfr_set_str, :libmpfr), Int32, (Ref{BigFloat}, Cstring, Int32, MPFRRoundingMode), z, s, base, rounding) + err == 0 ? z : nothing +end + +BigFloat(x::AbstractString, r::MPFRRoundingMode=ROUNDING_MODE[]; precision::Integer=DEFAULT_PRECISION[]) = + parse(BigFloat, x; precision=precision, rounding=r) + +Rational(x::BigFloat) = convert(Rational{BigInt}, x) +AbstractFloat(x::BigInt) = BigFloat(x) + +float(::Type{BigInt}) = BigFloat + +BigFloat(x::Real, r::RoundingMode; precision::Integer=DEFAULT_PRECISION[]) = + BigFloat(x, convert(MPFRRoundingMode, r); precision=precision) +BigFloat(x::AbstractString, r::RoundingMode; precision::Integer=DEFAULT_PRECISION[]) = + BigFloat(x, convert(MPFRRoundingMode, r); precision=precision) + +## BigFloat -> Integer +""" + MPFR.unsafe_cast(T, x::BigFloat, r::RoundingMode) + +Convert `x` to integer type `T`, rounding the direction of `r`. If the value is not +representable by T, an arbitrary value will be returned. +""" +unsafe_cast(T, x::BigFloat, r::RoundingMode) = unsafe_cast(T, x, convert(MPFRRoundingMode, r)) + +function unsafe_cast(::Type{Int64}, x::BigFloat, r::MPFRRoundingMode) + ccall((:__gmpfr_mpfr_get_sj,:libmpfr), Cintmax_t, (Ref{BigFloat}, MPFRRoundingMode), x, r) +end +function unsafe_cast(::Type{UInt64}, x::BigFloat, r::MPFRRoundingMode) + ccall((:__gmpfr_mpfr_get_uj,:libmpfr), Cuintmax_t, (Ref{BigFloat}, MPFRRoundingMode), x, r) +end + +function unsafe_cast(::Type{T}, x::BigFloat, r::MPFRRoundingMode) where T<:Signed + unsafe_cast(Int64, x, r) % T +end +function unsafe_cast(::Type{T}, x::BigFloat, r::MPFRRoundingMode) where T<:Unsigned + unsafe_cast(UInt64, x, r) % T +end + +function unsafe_cast(::Type{BigInt}, x::BigFloat, r::MPFRRoundingMode) + # actually safe, just keep naming consistent + z = BigInt() + ccall((:mpfr_get_z, :libmpfr), Int32, (Ref{BigInt}, Ref{BigFloat}, MPFRRoundingMode), z, x, r) + return z +end +unsafe_cast(::Type{Int128}, x::BigFloat, r::MPFRRoundingMode) = Int128(unsafe_cast(BigInt, x, r)) +unsafe_cast(::Type{UInt128}, x::BigFloat, r::MPFRRoundingMode) = UInt128(unsafe_cast(BigInt, x, r)) + +unsafe_trunc(::Type{T}, x::BigFloat) where {T<:Integer} = unsafe_cast(T, x, RoundToZero) + +function trunc(::Type{T}, x::BigFloat) where T<:Union{Signed,Unsigned} + (typemin(T) <= x <= typemax(T)) || throw(InexactError(:trunc, T, x)) + unsafe_cast(T, x, RoundToZero) +end +function floor(::Type{T}, x::BigFloat) where T<:Union{Signed,Unsigned} + (typemin(T) <= x <= typemax(T)) || throw(InexactError(:floor, T, x)) + unsafe_cast(T, x, RoundDown) +end +function ceil(::Type{T}, x::BigFloat) where T<:Union{Signed,Unsigned} + (typemin(T) <= x <= typemax(T)) || throw(InexactError(:ceil, T, x)) + unsafe_cast(T, x, RoundUp) +end + +function round(::Type{T}, x::BigFloat) where T<:Union{Signed,Unsigned} + (typemin(T) <= x <= typemax(T)) || throw(InexactError(:round, T, x)) + unsafe_cast(T, x, ROUNDING_MODE[]) +end + +trunc(::Type{BigInt}, x::BigFloat) = unsafe_cast(BigInt, x, RoundToZero) +floor(::Type{BigInt}, x::BigFloat) = unsafe_cast(BigInt, x, RoundDown) +ceil(::Type{BigInt}, x::BigFloat) = unsafe_cast(BigInt, x, RoundUp) +round(::Type{BigInt}, x::BigFloat) = unsafe_cast(BigInt, x, ROUNDING_MODE[]) + +# convert/round/trunc/floor/ceil(Integer, x) should return a BigInt +trunc(::Type{Integer}, x::BigFloat) = trunc(BigInt, x) +floor(::Type{Integer}, x::BigFloat) = floor(BigInt, x) +ceil(::Type{Integer}, x::BigFloat) = ceil(BigInt, x) +round(::Type{Integer}, x::BigFloat) = round(BigInt, x) + +function Bool(x::BigFloat) + iszero(x) && return false + isone(x) && return true + throw(InexactError(:Bool, Bool, x)) +end +function BigInt(x::BigFloat) + isinteger(x) || throw(InexactError(:BigInt, BigInt, x)) + trunc(BigInt, x) +end + +function (::Type{T})(x::BigFloat) where T<:Integer + isinteger(x) || throw(InexactError(nameof(T), T, x)) + trunc(T,x) +end + +## BigFloat -> AbstractFloat +_cpynansgn(x::AbstractFloat, y::BigFloat) = isnan(x) && signbit(x) != signbit(y) ? -x : x + +Float64(x::BigFloat, r::MPFRRoundingMode=ROUNDING_MODE[]) = + _cpynansgn(ccall((:mpfr_get_d,:libmpfr), Float64, (Ref{BigFloat}, MPFRRoundingMode), x, r), x) +Float64(x::BigFloat, r::RoundingMode) = Float64(x, convert(MPFRRoundingMode, r)) + +Float32(x::BigFloat, r::MPFRRoundingMode=ROUNDING_MODE[]) = + _cpynansgn(ccall((:mpfr_get_flt,:libmpfr), Float32, (Ref{BigFloat}, MPFRRoundingMode), x, r), x) +Float32(x::BigFloat, r::RoundingMode) = Float32(x, convert(MPFRRoundingMode, r)) + +# TODO: avoid double rounding +Float16(x::BigFloat) = Float16(Float32(x)) + +promote_rule(::Type{BigFloat}, ::Type{<:Real}) = BigFloat +promote_rule(::Type{BigInt}, ::Type{<:AbstractFloat}) = BigFloat +promote_rule(::Type{BigFloat}, ::Type{<:AbstractFloat}) = BigFloat + +big(::Type{<:AbstractFloat}) = BigFloat + +big(x::AbstractFloat) = convert(BigFloat, x) + +function (::Type{Rational{BigInt}})(x::AbstractFloat) + isnan(x) && return zero(BigInt) // zero(BigInt) + isinf(x) && return copysign(one(BigInt),x) // zero(BigInt) + iszero(x) && return zero(BigInt) // one(BigInt) + s = max(precision(x) - exponent(x), 0) + BigInt(ldexp(x,s)) // (BigInt(1) << s) +end + +# Basic arithmetic without promotion +for (fJ, fC) in ((:+,:add), (:*,:mul)) + @eval begin + # BigFloat + function ($fJ)(x::BigFloat, y::BigFloat) + z = BigFloat() + ccall(($(string(:mpfr_,fC)),:libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, x, y, ROUNDING_MODE[]) + return z + end + + # Unsigned Integer + function ($fJ)(x::BigFloat, c::CulongMax) + z = BigFloat() + ccall(($(string(:mpfr_,fC,:_ui)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Culong, MPFRRoundingMode), z, x, c, ROUNDING_MODE[]) + return z + end + ($fJ)(c::CulongMax, x::BigFloat) = ($fJ)(x,c) + + # Signed Integer + function ($fJ)(x::BigFloat, c::ClongMax) + z = BigFloat() + ccall(($(string(:mpfr_,fC,:_si)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Clong, MPFRRoundingMode), z, x, c, ROUNDING_MODE[]) + return z + end + ($fJ)(c::ClongMax, x::BigFloat) = ($fJ)(x,c) + + # Float32/Float64 + function ($fJ)(x::BigFloat, c::CdoubleMax) + z = BigFloat() + ccall(($(string(:mpfr_,fC,:_d)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Cdouble, MPFRRoundingMode), z, x, c, ROUNDING_MODE[]) + return z + end + ($fJ)(c::CdoubleMax, x::BigFloat) = ($fJ)(x,c) + + # BigInt + function ($fJ)(x::BigFloat, c::BigInt) + z = BigFloat() + ccall(($(string(:mpfr_,fC,:_z)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigInt}, MPFRRoundingMode), z, x, c, ROUNDING_MODE[]) + return z + end + ($fJ)(c::BigInt, x::BigFloat) = ($fJ)(x,c) + end +end + +for (fJ, fC) in ((:-,:sub), (:/,:div)) + @eval begin + # BigFloat + function ($fJ)(x::BigFloat, y::BigFloat) + z = BigFloat() + ccall(($(string(:mpfr_,fC)),:libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, x, y, ROUNDING_MODE[]) + return z + end + + # Unsigned Int + function ($fJ)(x::BigFloat, c::CulongMax) + z = BigFloat() + ccall(($(string(:mpfr_,fC,:_ui)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Culong, MPFRRoundingMode), z, x, c, ROUNDING_MODE[]) + return z + end + function ($fJ)(c::CulongMax, x::BigFloat) + z = BigFloat() + ccall(($(string(:mpfr_,:ui_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Culong, Ref{BigFloat}, MPFRRoundingMode), z, c, x, ROUNDING_MODE[]) + return z + end + + # Signed Integer + function ($fJ)(x::BigFloat, c::ClongMax) + z = BigFloat() + ccall(($(string(:mpfr_,fC,:_si)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Clong, MPFRRoundingMode), z, x, c, ROUNDING_MODE[]) + return z + end + function ($fJ)(c::ClongMax, x::BigFloat) + z = BigFloat() + ccall(($(string(:mpfr_,:si_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Clong, Ref{BigFloat}, MPFRRoundingMode), z, c, x, ROUNDING_MODE[]) + return z + end + + # Float32/Float64 + function ($fJ)(x::BigFloat, c::CdoubleMax) + z = BigFloat() + ccall(($(string(:mpfr_,fC,:_d)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Cdouble, MPFRRoundingMode), z, x, c, ROUNDING_MODE[]) + return z + end + function ($fJ)(c::CdoubleMax, x::BigFloat) + z = BigFloat() + ccall(($(string(:mpfr_,:d_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Cdouble, Ref{BigFloat}, MPFRRoundingMode), z, c, x, ROUNDING_MODE[]) + return z + end + + # BigInt + function ($fJ)(x::BigFloat, c::BigInt) + z = BigFloat() + ccall(($(string(:mpfr_,fC,:_z)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigInt}, MPFRRoundingMode), z, x, c, ROUNDING_MODE[]) + return z + end + # no :mpfr_z_div function + end +end + +function -(c::BigInt, x::BigFloat) + z = BigFloat() + ccall((:mpfr_z_sub, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigInt}, Ref{BigFloat}, MPFRRoundingMode), z, c, x, ROUNDING_MODE[]) + return z +end + +inv(x::BigFloat) = one(Clong) / x # faster than fallback one(x)/x + +function fma(x::BigFloat, y::BigFloat, z::BigFloat) + r = BigFloat() + ccall(("mpfr_fma",:libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), r, x, y, z, ROUNDING_MODE[]) + return r +end + +# div +# BigFloat +function div(x::BigFloat, y::BigFloat) + z = BigFloat() + ccall((:mpfr_div,:libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, x, y, RoundToZero) + ccall((:mpfr_trunc, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), z, z) + return z +end + +# Unsigned Int +function div(x::BigFloat, c::CulongMax) + z = BigFloat() + ccall((:mpfr_div_ui, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Culong, MPFRRoundingMode), z, x, c, RoundToZero) + ccall((:mpfr_trunc, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), z, z) + return z +end +function div(c::CulongMax, x::BigFloat) + z = BigFloat() + ccall((:mpfr_ui_div, :libmpfr), Int32, (Ref{BigFloat}, Culong, Ref{BigFloat}, MPFRRoundingMode), z, c, x, RoundToZero) + ccall((:mpfr_trunc, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), z, z) + return z +end + +# Signed Integer +function div(x::BigFloat, c::ClongMax) + z = BigFloat() + ccall((:mpfr_div_si, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Clong, MPFRRoundingMode), z, x, c, RoundToZero) + ccall((:mpfr_trunc, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), z, z) + return z +end +function div(c::ClongMax, x::BigFloat) + z = BigFloat() + ccall((:mpfr_si_div, :libmpfr), Int32, (Ref{BigFloat}, Clong, Ref{BigFloat}, MPFRRoundingMode), z, c, x, RoundToZero) + ccall((:mpfr_trunc, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), z, z) + return z +end + +# Float32/Float64 +function div(x::BigFloat, c::CdoubleMax) + z = BigFloat() + ccall((:mpfr_div_d, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Cdouble, MPFRRoundingMode), z, x, c, RoundToZero) + ccall((:mpfr_trunc, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), z, z) + return z +end +function div(c::CdoubleMax, x::BigFloat) + z = BigFloat() + ccall((:mpfr_d_div, :libmpfr), Int32, (Ref{BigFloat}, Cdouble, Ref{BigFloat}, MPFRRoundingMode), z, c, x, RoundToZero) + ccall((:mpfr_trunc, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), z, z) + return z +end + +# BigInt +function div(x::BigFloat, c::BigInt) + z = BigFloat() + ccall((:mpfr_div_z, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigInt}, MPFRRoundingMode), z, x, c, RoundToZero) + ccall((:mpfr_trunc, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), z, z) + return z +end + + +# More efficient commutative operations +for (fJ, fC, fI) in ((:+, :add, 0), (:*, :mul, 1)) + @eval begin + function ($fJ)(a::BigFloat, b::BigFloat, c::BigFloat) + z = BigFloat() + ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, a, b, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, z, c, ROUNDING_MODE[]) + return z + end + function ($fJ)(a::BigFloat, b::BigFloat, c::BigFloat, d::BigFloat) + z = BigFloat() + ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, a, b, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, z, c, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, z, d, ROUNDING_MODE[]) + return z + end + function ($fJ)(a::BigFloat, b::BigFloat, c::BigFloat, d::BigFloat, e::BigFloat) + z = BigFloat() + ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, a, b, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, z, c, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, z, d, ROUNDING_MODE[]) + ccall(($(string(:mpfr_,fC)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, z, e, ROUNDING_MODE[]) + return z + end + end +end + +function -(x::BigFloat) + z = BigFloat() + ccall((:mpfr_neg, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, x, ROUNDING_MODE[]) + return z +end + +function sqrt(x::BigFloat) + isnan(x) && return x + z = BigFloat() + ccall((:mpfr_sqrt, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, x, ROUNDING_MODE[]) + isnan(z) && throw(DomainError(x, "NaN result for non-NaN input.")) + return z +end + +sqrt(x::BigInt) = sqrt(BigFloat(x)) + +function ^(x::BigFloat, y::BigFloat) + z = BigFloat() + ccall((:mpfr_pow, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, x, y, ROUNDING_MODE[]) + return z +end + +function ^(x::BigFloat, y::CulongMax) + z = BigFloat() + ccall((:mpfr_pow_ui, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Culong, MPFRRoundingMode), z, x, y, ROUNDING_MODE[]) + return z +end + +function ^(x::BigFloat, y::ClongMax) + z = BigFloat() + ccall((:mpfr_pow_si, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Clong, MPFRRoundingMode), z, x, y, ROUNDING_MODE[]) + return z +end + +function ^(x::BigFloat, y::BigInt) + z = BigFloat() + ccall((:mpfr_pow_z, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigInt}, MPFRRoundingMode), z, x, y, ROUNDING_MODE[]) + return z +end + +^(x::BigFloat, y::Integer) = typemin(Clong) <= y <= typemax(Clong) ? x^Clong(y) : x^BigInt(y) +^(x::BigFloat, y::Unsigned) = typemin(Culong) <= y <= typemax(Culong) ? x^Culong(y) : x^BigInt(y) + +for f in (:exp, :exp2, :exp10, :expm1, :cosh, :sinh, :tanh, :sech, :csch, :coth, :cbrt) + @eval function $f(x::BigFloat) + z = BigFloat() + ccall(($(string(:mpfr_,f)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, x, ROUNDING_MODE[]) + return z + end +end + +function sincos_fast(v::BigFloat) + s = BigFloat() + c = BigFloat() + ccall((:mpfr_sin_cos, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), s, c, v, ROUNDING_MODE[]) + return (s, c) +end +sincos(v::BigFloat) = sincos_fast(v) + +# return log(2) +function big_ln2() + c = BigFloat() + ccall((:mpfr_const_log2, :libmpfr), Cint, (Ref{BigFloat}, MPFRRoundingMode), c, MPFR.ROUNDING_MODE[]) + return c +end + +function ldexp(x::BigFloat, n::Clong) + z = BigFloat() + ccall((:mpfr_mul_2si, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Clong, MPFRRoundingMode), z, x, n, ROUNDING_MODE[]) + return z +end +function ldexp(x::BigFloat, n::Culong) + z = BigFloat() + ccall((:mpfr_mul_2ui, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Culong, MPFRRoundingMode), z, x, n, ROUNDING_MODE[]) + return z +end +ldexp(x::BigFloat, n::ClongMax) = ldexp(x, convert(Clong, n)) +ldexp(x::BigFloat, n::CulongMax) = ldexp(x, convert(Culong, n)) +ldexp(x::BigFloat, n::Integer) = x * exp2(BigFloat(n)) + +function factorial(x::BigFloat) + if x < 0 || !isinteger(x) + throw(DomainError(x, "Must be a non-negative integer.")) + end + ui = convert(Culong, x) + z = BigFloat() + ccall((:mpfr_fac_ui, :libmpfr), Int32, (Ref{BigFloat}, Culong, MPFRRoundingMode), z, ui, ROUNDING_MODE[]) + return z +end + +function hypot(x::BigFloat, y::BigFloat) + z = BigFloat() + ccall((:mpfr_hypot, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, x, y, ROUNDING_MODE[]) + return z +end + +for f in (:log, :log2, :log10) + @eval function $f(x::BigFloat) + if x < 0 + throw(DomainError(x, string($f, " will only return a complex result if called ", + "with a complex argument. Try ", $f, "(complex(x))."))) + end + z = BigFloat() + ccall(($(string(:mpfr_,f)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, x, ROUNDING_MODE[]) + return z + end +end + +function log1p(x::BigFloat) + if x < -1 + throw(DomainError(x, string("log1p will only return a complex result if called ", + "with a complex argument. Try log1p(complex(x))."))) + end + z = BigFloat() + ccall((:mpfr_log1p, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, x, ROUNDING_MODE[]) + return z +end + +function max(x::BigFloat, y::BigFloat) + isnan(x) && return x + isnan(y) && return y + z = BigFloat() + ccall((:mpfr_max, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, x, y, ROUNDING_MODE[]) + return z +end + +function min(x::BigFloat, y::BigFloat) + isnan(x) && return x + isnan(y) && return y + z = BigFloat() + ccall((:mpfr_min, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, x, y, ROUNDING_MODE[]) + return z +end + +function modf(x::BigFloat) + isinf(x) && return (BigFloat(NaN), x) + zint = BigFloat() + zfloat = BigFloat() + ccall((:mpfr_modf, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), zint, zfloat, x, ROUNDING_MODE[]) + return (zfloat, zint) +end + +function rem(x::BigFloat, y::BigFloat) + z = BigFloat() + ccall((:mpfr_fmod, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, x, y, ROUNDING_MODE[]) + return z +end + +function rem(x::BigFloat, y::BigFloat, ::RoundingMode{:Nearest}) + z = BigFloat() + ccall((:mpfr_remainder, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, x, y, ROUNDING_MODE[]) + return z +end + +# TODO: use a higher-precision BigFloat(pi) here? +rem2pi(x::BigFloat, r::RoundingMode) = rem(x, 2*BigFloat(pi), r) + +function sum(arr::AbstractArray{BigFloat}) + z = BigFloat(0) + for i in arr + ccall((:mpfr_add, :libmpfr), Int32, + (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, z, i, ROUNDING_MODE[]) + end + return z +end + +# Functions for which NaN results are converted to DomainError, following Base +for f in (:sin, :cos, :tan, :sec, :csc, :acos, :asin, :atan, :acosh, :asinh, :atanh) + @eval begin + function ($f)(x::BigFloat) + isnan(x) && return x + z = BigFloat() + ccall(($(string(:mpfr_,f)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, x, ROUNDING_MODE[]) + isnan(z) && throw(DomainError(x, "NaN result for non-NaN input.")) + return z + end + end +end + +function atan(y::BigFloat, x::BigFloat) + z = BigFloat() + ccall((:mpfr_atan2, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, y, x, ROUNDING_MODE[]) + return z +end + +# Utility functions +==(x::BigFloat, y::BigFloat) = ccall((:mpfr_equal_p, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), x, y) != 0 +<=(x::BigFloat, y::BigFloat) = ccall((:mpfr_lessequal_p, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), x, y) != 0 +>=(x::BigFloat, y::BigFloat) = ccall((:mpfr_greaterequal_p, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), x, y) != 0 +<(x::BigFloat, y::BigFloat) = ccall((:mpfr_less_p, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), x, y) != 0 +>(x::BigFloat, y::BigFloat) = ccall((:mpfr_greater_p, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), x, y) != 0 + +function cmp(x::BigFloat, y::BigInt) + isnan(x) && return 1 + ccall((:mpfr_cmp_z, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigInt}), x, y) +end +function cmp(x::BigFloat, y::ClongMax) + isnan(x) && return 1 + ccall((:mpfr_cmp_si, :libmpfr), Int32, (Ref{BigFloat}, Clong), x, y) +end +function cmp(x::BigFloat, y::CulongMax) + isnan(x) && return 1 + ccall((:mpfr_cmp_ui, :libmpfr), Int32, (Ref{BigFloat}, Culong), x, y) +end +cmp(x::BigFloat, y::Integer) = cmp(x,big(y)) +cmp(x::Integer, y::BigFloat) = -cmp(y,x) + +function cmp(x::BigFloat, y::CdoubleMax) + isnan(x) && return isnan(y) ? 0 : 1 + isnan(y) && return -1 + ccall((:mpfr_cmp_d, :libmpfr), Int32, (Ref{BigFloat}, Cdouble), x, y) +end +cmp(x::CdoubleMax, y::BigFloat) = -cmp(y,x) + +==(x::BigFloat, y::Integer) = !isnan(x) && cmp(x,y) == 0 +==(x::Integer, y::BigFloat) = y == x +==(x::BigFloat, y::CdoubleMax) = !isnan(x) && !isnan(y) && cmp(x,y) == 0 +==(x::CdoubleMax, y::BigFloat) = y == x + +<(x::BigFloat, y::Integer) = !isnan(x) && cmp(x,y) < 0 +<(x::Integer, y::BigFloat) = !isnan(y) && cmp(y,x) > 0 +<(x::BigFloat, y::CdoubleMax) = !isnan(x) && !isnan(y) && cmp(x,y) < 0 +<(x::CdoubleMax, y::BigFloat) = !isnan(x) && !isnan(y) && cmp(y,x) > 0 + +<=(x::BigFloat, y::Integer) = !isnan(x) && cmp(x,y) <= 0 +<=(x::Integer, y::BigFloat) = !isnan(y) && cmp(y,x) >= 0 +<=(x::BigFloat, y::CdoubleMax) = !isnan(x) && !isnan(y) && cmp(x,y) <= 0 +<=(x::CdoubleMax, y::BigFloat) = !isnan(x) && !isnan(y) && cmp(y,x) >= 0 + +signbit(x::BigFloat) = ccall((:mpfr_signbit, :libmpfr), Int32, (Ref{BigFloat},), x) != 0 + +function precision(x::BigFloat) # precision of an object of type BigFloat + return ccall((:mpfr_get_prec, :libmpfr), Clong, (Ref{BigFloat},), x) +end + +""" + precision(BigFloat) + +Get the precision (in bits) currently used for [`BigFloat`](@ref) arithmetic. +""" +precision(::Type{BigFloat}) = Int(DEFAULT_PRECISION[]) # precision of the type BigFloat itself + +""" + setprecision([T=BigFloat,] precision::Int) + +Set the precision (in bits) to be used for `T` arithmetic. + +!!! warning + + This function is not thread-safe. It will affect code running on all threads, but + its behavior is undefined if called concurrently with computations that use the + setting. +""" +function setprecision(::Type{BigFloat}, precision::Integer) + if precision < 2 + throw(DomainError(precision, "`precision` cannot be less than 2.")) + end + DEFAULT_PRECISION[] = precision + return precision +end + +setprecision(precision::Integer) = setprecision(BigFloat, precision) + +maxintfloat(x::BigFloat) = BigFloat(2)^precision(x) +maxintfloat(::Type{BigFloat}) = BigFloat(2)^precision(BigFloat) + +function copysign(x::BigFloat, y::BigFloat) + z = BigFloat() + ccall((:mpfr_copysign, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), z, x, y, ROUNDING_MODE[]) + return z +end + +function exponent(x::BigFloat) + if iszero(x) || !isfinite(x) + throw(DomainError(x, "`x` must be non-zero and finite.")) + end + # The '- 1' is to make it work as Base.exponent + return ccall((:mpfr_get_exp, :libmpfr), Clong, (Ref{BigFloat},), x) - 1 +end + +function frexp(x::BigFloat) + z = BigFloat() + c = Ref{Clong}() + ccall((:mpfr_frexp, :libmpfr), Int32, (Ptr{Clong}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), c, z, x, ROUNDING_MODE[]) + return (z, c[]) +end + +function significand(x::BigFloat) + z = BigFloat() + c = Ref{Clong}() + ccall((:mpfr_frexp, :libmpfr), Int32, (Ptr{Clong}, Ref{BigFloat}, Ref{BigFloat}, MPFRRoundingMode), c, z, x, ROUNDING_MODE[]) + # Double the significand to make it work as Base.significand + ccall((:mpfr_mul_si, :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}, Clong, MPFRRoundingMode), z, z, 2, ROUNDING_MODE[]) + return z +end + +function isinteger(x::BigFloat) + return ccall((:mpfr_integer_p, :libmpfr), Int32, (Ref{BigFloat},), x) != 0 +end + +for (f,R) in ((:roundeven, :Nearest), + (:ceil, :Up), + (:floor, :Down), + (:trunc, :ToZero), + (:round, :NearestTiesAway)) + @eval begin + function round(x::BigFloat, ::RoundingMode{$(QuoteNode(R))}) + z = BigFloat() + ccall(($(string(:mpfr_,f)), :libmpfr), Int32, (Ref{BigFloat}, Ref{BigFloat}), z, x) + return z + end + end +end + +function isinf(x::BigFloat) + return ccall((:mpfr_inf_p, :libmpfr), Int32, (Ref{BigFloat},), x) != 0 +end + +function isnan(x::BigFloat) + return ccall((:mpfr_nan_p, :libmpfr), Int32, (Ref{BigFloat},), x) != 0 +end + +isfinite(x::BigFloat) = !isinf(x) && !isnan(x) + +iszero(x::BigFloat) = x == Clong(0) +isone(x::BigFloat) = x == Clong(1) + +@eval typemax(::Type{BigFloat}) = $(BigFloat(Inf)) +@eval typemin(::Type{BigFloat}) = $(BigFloat(-Inf)) + +function nextfloat!(x::BigFloat, n::Integer=1) + signbit(n) && return prevfloat!(x, abs(n)) + for i = 1:n + ccall((:mpfr_nextabove, :libmpfr), Int32, (Ref{BigFloat},), x) + end + return x +end + +function prevfloat!(x::BigFloat, n::Integer=1) + signbit(n) && return nextfloat!(x, abs(n)) + for i = 1:n + ccall((:mpfr_nextbelow, :libmpfr), Int32, (Ref{BigFloat},), x) + end + return x +end + +nextfloat(x::BigFloat, n::Integer=1) = n == 0 ? x : nextfloat!(_duplicate(x), n) +prevfloat(x::BigFloat, n::Integer=1) = n == 0 ? x : prevfloat!(_duplicate(x), n) + +eps(::Type{BigFloat}) = nextfloat(BigFloat(1)) - BigFloat(1) + +floatmin(::Type{BigFloat}) = nextfloat(zero(BigFloat)) +floatmax(::Type{BigFloat}) = prevfloat(BigFloat(Inf)) + +""" + setprecision(f::Function, [T=BigFloat,] precision::Integer) + +Change the `T` arithmetic precision (in bits) for the duration of `f`. +It is logically equivalent to: + + old = precision(BigFloat) + setprecision(BigFloat, precision) + f() + setprecision(BigFloat, old) + +Often used as `setprecision(T, precision) do ... end` + +Note: `nextfloat()`, `prevfloat()` do not use the precision mentioned by +`setprecision` +""" +function setprecision(f::Function, ::Type{T}, prec::Integer) where T + old_prec = precision(T) + setprecision(T, prec) + try + return f() + finally + setprecision(T, old_prec) + end +end + +setprecision(f::Function, prec::Integer) = setprecision(f, BigFloat, prec) + +function string_mpfr(x::BigFloat, fmt::String) + buf = Base.StringVector(0) + s = _calculate_buffer_size!(buf, fmt, x) + resize!(buf, s) + _fill_buffer!(buf, fmt, x) + String(buf) +end + +function _calculate_buffer_size!(buf, fmt, x::BigFloat) + ccall((:mpfr_snprintf,:libmpfr), + Int32, (Ptr{UInt8}, Culong, Ptr{UInt8}, Ref{BigFloat}...), + buf, 0, fmt, x) +end + +function _fill_buffer!(buf, fmt, x::BigFloat) + s = length(buf) + # we temporarily need one more item in buffer to capture null termination + resize!(buf, s + 1) + n = ccall((:mpfr_sprintf,:libmpfr), Int32, (Ptr{UInt8}, Ptr{UInt8}, Ref{BigFloat}...), buf, fmt, x) + @assert n + 1 == length(buf) + @assert last(buf) == 0x00 + resize!(buf, s) +end + +function _prettify_bigfloat(s::String)::String + mantissa, exponent = split(s, 'e') + if !occursin('.', mantissa) + mantissa = string(mantissa, '.') + end + mantissa = rstrip(mantissa, '0') + if endswith(mantissa, '.') + mantissa = string(mantissa, '0') + end + expo = parse(Int, exponent) + if -5 < expo < 6 + expo == 0 && return mantissa + int, frac = split(mantissa, '.') + if expo > 0 + expo < length(frac) ? + string(int, frac[1:expo], '.', frac[expo+1:end]) : + string(int, frac, '0'^(expo-length(frac)), '.', '0') + else + neg = startswith(int, '-') + neg == true && (int = lstrip(int, '-')) + @assert length(int) == 1 + string(neg ? '-' : "", '0', '.', '0'^(-expo-1), int, frac) + end + else + string(mantissa, 'e', exponent) + end +end + +function _string(x::BigFloat, fmt::String)::String + isfinite(x) || return string(Float64(x)) + _prettify_bigfloat(string_mpfr(x, fmt)) +end +_string(x::BigFloat) = _string(x, "%.Re") +_string(x::BigFloat, k::Integer) = _string(x, "%.$(k)Re") + +string(b::BigFloat) = _string(b) + +print(io::IO, b::BigFloat) = print(io, string(b)) +function show(io::IO, b::BigFloat) + if get(io, :compact, false) + print(io, _string(b, 5)) + else + print(io, _string(b)) + end +end + +# get/set exponent min/max +get_emax() = ccall((:mpfr_get_emax, :libmpfr), Clong, ()) +get_emax_min() = ccall((:mpfr_get_emax_min, :libmpfr), Clong, ()) +get_emax_max() = ccall((:mpfr_get_emax_max, :libmpfr), Clong, ()) + +get_emin() = ccall((:mpfr_get_emin, :libmpfr), Clong, ()) +get_emin_min() = ccall((:mpfr_get_emin_min, :libmpfr), Clong, ()) +get_emin_max() = ccall((:mpfr_get_emin_max, :libmpfr), Clong, ()) + +set_emax!(x) = ccall((:mpfr_set_emax, :libmpfr), Cvoid, (Clong,), x) +set_emin!(x) = ccall((:mpfr_set_emin, :libmpfr), Cvoid, (Clong,), x) + +function Base.deepcopy_internal(x::BigFloat, stackdict::IdDict) + haskey(stackdict, x) && return stackdict[x] + # d = copy(x._d) + d = x._d + d′ = GC.@preserve d unsafe_string(pointer(d), sizeof(d)) # creates a definitely-new String + y = _BigFloat(x.prec, x.sign, x.exp, d′) + #ccall((:mpfr_custom_move,:libmpfr), Cvoid, (Ref{BigFloat}, Ptr{Limb}), y, d) # unnecessary + stackdict[x] = y + return y +end + +function Base.lerpi(j::Integer, d::Integer, a::BigFloat, b::BigFloat) + t = BigFloat(j)/d + fma(t, b, fma(-t, a, a)) +end + +end #module diff --git a/base/multidimensional.jl b/base/multidimensional.jl new file mode 100644 index 0000000..cee61bc --- /dev/null +++ b/base/multidimensional.jl @@ -0,0 +1,1748 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +### Multidimensional iterators +module IteratorsMD + import .Base: eltype, length, size, first, last, in, getindex, + setindex!, IndexStyle, min, max, zero, oneunit, isless, eachindex, + ndims, IteratorSize, convert, show, iterate, promote_rule + + import .Base: +, -, *, (:) + import .Base: simd_outer_range, simd_inner_length, simd_index + using .Base: IndexLinear, IndexCartesian, AbstractCartesianIndex, fill_to_length, tail + using .Base.Iterators: Reverse + + export CartesianIndex, CartesianIndices + + """ + CartesianIndex(i, j, k...) -> I + CartesianIndex((i, j, k...)) -> I + + Create a multidimensional index `I`, which can be used for + indexing a multidimensional array `A`. In particular, `A[I]` is + equivalent to `A[i,j,k...]`. One can freely mix integer and + `CartesianIndex` indices; for example, `A[Ipre, i, Ipost]` (where + `Ipre` and `Ipost` are `CartesianIndex` indices and `i` is an + `Int`) can be a useful expression when writing algorithms that + work along a single dimension of an array of arbitrary + dimensionality. + + A `CartesianIndex` is sometimes produced by [`eachindex`](@ref), and + always when iterating with an explicit [`CartesianIndices`](@ref). + + # Examples + ```jldoctest + julia> A = reshape(Vector(1:16), (2, 2, 2, 2)) + 2×2×2×2 Array{Int64,4}: + [:, :, 1, 1] = + 1 3 + 2 4 + + [:, :, 2, 1] = + 5 7 + 6 8 + + [:, :, 1, 2] = + 9 11 + 10 12 + + [:, :, 2, 2] = + 13 15 + 14 16 + + julia> A[CartesianIndex((1, 1, 1, 1))] + 1 + + julia> A[CartesianIndex((1, 1, 1, 2))] + 9 + + julia> A[CartesianIndex((1, 1, 2, 1))] + 5 + ``` + """ + struct CartesianIndex{N} <: AbstractCartesianIndex{N} + I::NTuple{N,Int} + CartesianIndex{N}(index::NTuple{N,Integer}) where {N} = new(index) + end + + CartesianIndex(index::NTuple{N,Integer}) where {N} = CartesianIndex{N}(index) + CartesianIndex(index::Integer...) = CartesianIndex(index) + CartesianIndex{N}(index::Vararg{Integer,N}) where {N} = CartesianIndex{N}(index) + # Allow passing tuples smaller than N + CartesianIndex{N}(index::Tuple) where {N} = CartesianIndex{N}(fill_to_length(index, 1, Val(N))) + CartesianIndex{N}(index::Integer...) where {N} = CartesianIndex{N}(index) + CartesianIndex{N}() where {N} = CartesianIndex{N}(()) + # Un-nest passed CartesianIndexes + CartesianIndex(index::Union{Integer, CartesianIndex}...) = CartesianIndex(flatten(index)) + flatten(I::Tuple{}) = I + flatten(I::Tuple{Any}) = I + flatten(I::Tuple{<:CartesianIndex}) = I[1].I + @inline flatten(I) = _flatten(I...) + @inline _flatten() = () + @inline _flatten(i, I...) = (i, _flatten(I...)...) + @inline _flatten(i::CartesianIndex, I...) = (i.I..., _flatten(I...)...) + CartesianIndex(index::Tuple{Vararg{Union{Integer, CartesianIndex}}}) = CartesianIndex(index...) + show(io::IO, i::CartesianIndex) = (print(io, "CartesianIndex"); show(io, i.I)) + + # length + length(::CartesianIndex{N}) where {N} = N + length(::Type{CartesianIndex{N}}) where {N} = N + + # indexing + getindex(index::CartesianIndex, i::Integer) = index.I[i] + Base.get(A::AbstractArray, I::CartesianIndex, default) = get(A, I.I, default) + eltype(::Type{T}) where {T<:CartesianIndex} = eltype(fieldtype(T, :I)) + + # access to index tuple + Tuple(index::CartesianIndex) = index.I + + # equality + Base.:(==)(a::CartesianIndex{N}, b::CartesianIndex{N}) where N = a.I == b.I + + # zeros and ones + zero(::CartesianIndex{N}) where {N} = zero(CartesianIndex{N}) + zero(::Type{CartesianIndex{N}}) where {N} = CartesianIndex(ntuple(x -> 0, Val(N))) + oneunit(::CartesianIndex{N}) where {N} = oneunit(CartesianIndex{N}) + oneunit(::Type{CartesianIndex{N}}) where {N} = CartesianIndex(ntuple(x -> 1, Val(N))) + + # arithmetic, min/max + @inline (-)(index::CartesianIndex{N}) where {N} = + CartesianIndex{N}(map(-, index.I)) + @inline (+)(index1::CartesianIndex{N}, index2::CartesianIndex{N}) where {N} = + CartesianIndex{N}(map(+, index1.I, index2.I)) + @inline (-)(index1::CartesianIndex{N}, index2::CartesianIndex{N}) where {N} = + CartesianIndex{N}(map(-, index1.I, index2.I)) + @inline min(index1::CartesianIndex{N}, index2::CartesianIndex{N}) where {N} = + CartesianIndex{N}(map(min, index1.I, index2.I)) + @inline max(index1::CartesianIndex{N}, index2::CartesianIndex{N}) where {N} = + CartesianIndex{N}(map(max, index1.I, index2.I)) + + @inline (*)(a::Integer, index::CartesianIndex{N}) where {N} = CartesianIndex{N}(map(x->a*x, index.I)) + @inline (*)(index::CartesianIndex, a::Integer) = *(a,index) + + # comparison + @inline isless(I1::CartesianIndex{N}, I2::CartesianIndex{N}) where {N} = _isless(0, I1.I, I2.I) + @inline function _isless(ret, I1::NTuple{N,Int}, I2::NTuple{N,Int}) where N + newret = ifelse(ret==0, icmp(I1[N], I2[N]), ret) + _isless(newret, Base.front(I1), Base.front(I2)) + end + _isless(ret, ::Tuple{}, ::Tuple{}) = ifelse(ret==1, true, false) + icmp(a, b) = ifelse(isless(a,b), 1, ifelse(a==b, 0, -1)) + + # conversions + convert(::Type{T}, index::CartesianIndex{1}) where {T<:Number} = convert(T, index[1]) + convert(::Type{T}, index::CartesianIndex) where {T<:Tuple} = convert(T, index.I) + + # hashing + const cartindexhash_seed = UInt == UInt64 ? 0xd60ca92f8284b8b0 : 0xf2ea7c2e + function Base.hash(ci::CartesianIndex, h::UInt) + h += cartindexhash_seed + for i in ci.I + h = hash(i, h) + end + return h + end + + # nextind and prevind with CartesianIndex + function Base.nextind(a::AbstractArray{<:Any,N}, i::CartesianIndex{N}) where {N} + iter = CartesianIndices(axes(a)) + # might overflow + I = inc(i.I, first(iter).I, last(iter).I) + return I + end + function Base.prevind(a::AbstractArray{<:Any,N}, i::CartesianIndex{N}) where {N} + iter = CartesianIndices(axes(a)) + # might underflow + I = dec(i.I, last(iter).I, first(iter).I) + return I + end + + # Iteration over the elements of CartesianIndex cannot be supported until its length can be inferred, + # see #23719 + Base.iterate(::CartesianIndex) = + error("iteration is deliberately unsupported for CartesianIndex. Use `I` rather than `I...`, or use `Tuple(I)...`") + + # Iteration + """ + CartesianIndices(sz::Dims) -> R + CartesianIndices((istart:istop, jstart:jstop, ...)) -> R + + Define a region `R` spanning a multidimensional rectangular range + of integer indices. These are most commonly encountered in the + context of iteration, where `for I in R ... end` will return + [`CartesianIndex`](@ref) indices `I` equivalent to the nested loops + + for j = jstart:jstop + for i = istart:istop + ... + end + end + + Consequently these can be useful for writing algorithms that + work in arbitrary dimensions. + + CartesianIndices(A::AbstractArray) -> R + + As a convenience, constructing a `CartesianIndices` from an array makes a + range of its indices. + + # Examples + ```jldoctest + julia> foreach(println, CartesianIndices((2, 2, 2))) + CartesianIndex(1, 1, 1) + CartesianIndex(2, 1, 1) + CartesianIndex(1, 2, 1) + CartesianIndex(2, 2, 1) + CartesianIndex(1, 1, 2) + CartesianIndex(2, 1, 2) + CartesianIndex(1, 2, 2) + CartesianIndex(2, 2, 2) + + julia> CartesianIndices(fill(1, (2,3))) + 2×3 CartesianIndices{2,Tuple{Base.OneTo{Int64},Base.OneTo{Int64}}}: + CartesianIndex(1, 1) CartesianIndex(1, 2) CartesianIndex(1, 3) + CartesianIndex(2, 1) CartesianIndex(2, 2) CartesianIndex(2, 3) + ``` + + ## Conversion between linear and cartesian indices + + Linear index to cartesian index conversion exploits the fact that a + `CartesianIndices` is an `AbstractArray` and can be indexed linearly: + + ```jldoctest + julia> cartesian = CartesianIndices((1:3, 1:2)) + 3×2 CartesianIndices{2,Tuple{UnitRange{Int64},UnitRange{Int64}}}: + CartesianIndex(1, 1) CartesianIndex(1, 2) + CartesianIndex(2, 1) CartesianIndex(2, 2) + CartesianIndex(3, 1) CartesianIndex(3, 2) + + julia> cartesian[4] + CartesianIndex(1, 2) + ``` + + ## Broadcasting + + `CartesianIndices` support broadcasting arithmetic (+ and -) with a `CartesianIndex`. + + !!! compat "Julia 1.1" + Broadcasting of CartesianIndices requires at least Julia 1.1. + + ```jldoctest + julia> CIs = CartesianIndices((2:3, 5:6)) + 2×2 CartesianIndices{2,Tuple{UnitRange{Int64},UnitRange{Int64}}}: + CartesianIndex(2, 5) CartesianIndex(2, 6) + CartesianIndex(3, 5) CartesianIndex(3, 6) + + julia> CI = CartesianIndex(3, 4) + CartesianIndex(3, 4) + + julia> CIs .+ CI + 2×2 CartesianIndices{2,Tuple{UnitRange{Int64},UnitRange{Int64}}}: + CartesianIndex(5, 9) CartesianIndex(5, 10) + CartesianIndex(6, 9) CartesianIndex(6, 10) + ``` + + For cartesian to linear index conversion, see [`LinearIndices`](@ref). + """ + struct CartesianIndices{N,R<:NTuple{N,AbstractUnitRange{Int}}} <: AbstractArray{CartesianIndex{N},N} + indices::R + end + + CartesianIndices(::Tuple{}) = CartesianIndices{0,typeof(())}(()) + CartesianIndices(inds::NTuple{N,AbstractUnitRange{<:Integer}}) where {N} = + CartesianIndices(map(r->convert(AbstractUnitRange{Int}, r), inds)) + + CartesianIndices(index::CartesianIndex) = CartesianIndices(index.I) + CartesianIndices(sz::NTuple{N,<:Integer}) where {N} = CartesianIndices(map(Base.OneTo, sz)) + CartesianIndices(inds::NTuple{N,Union{<:Integer,AbstractUnitRange{<:Integer}}}) where {N} = + CartesianIndices(map(i->first(i):last(i), inds)) + + CartesianIndices(A::AbstractArray) = CartesianIndices(axes(A)) + + """ + (:)(I::CartesianIndex, J::CartesianIndex) + + Construct [`CartesianIndices`](@ref) from two `CartesianIndex`. + + !!! compat "Julia 1.1" + This method requires at least Julia 1.1. + + # Examples + ```jldoctest + julia> I = CartesianIndex(2,1); + + julia> J = CartesianIndex(3,3); + + julia> I:J + 2×3 CartesianIndices{2,Tuple{UnitRange{Int64},UnitRange{Int64}}}: + CartesianIndex(2, 1) CartesianIndex(2, 2) CartesianIndex(2, 3) + CartesianIndex(3, 1) CartesianIndex(3, 2) CartesianIndex(3, 3) + ``` + """ + (:)(I::CartesianIndex{N}, J::CartesianIndex{N}) where N = + CartesianIndices(map((i,j) -> i:j, Tuple(I), Tuple(J))) + + promote_rule(::Type{CartesianIndices{N,R1}}, ::Type{CartesianIndices{N,R2}}) where {N,R1,R2} = + CartesianIndices{N,Base.indices_promote_type(R1,R2)} + + convert(::Type{Tuple{}}, R::CartesianIndices{0}) = () + convert(::Type{NTuple{N,AbstractUnitRange{Int}}}, R::CartesianIndices{N}) where {N} = + R.indices + convert(::Type{NTuple{N,AbstractUnitRange}}, R::CartesianIndices{N}) where {N} = + convert(NTuple{N,AbstractUnitRange{Int}}, R) + convert(::Type{NTuple{N,UnitRange{Int}}}, R::CartesianIndices{N}) where {N} = + UnitRange{Int}.(convert(NTuple{N,AbstractUnitRange}, R)) + convert(::Type{NTuple{N,UnitRange}}, R::CartesianIndices{N}) where {N} = + UnitRange.(convert(NTuple{N,AbstractUnitRange}, R)) + convert(::Type{Tuple{Vararg{AbstractUnitRange{Int}}}}, R::CartesianIndices{N}) where {N} = + convert(NTuple{N,AbstractUnitRange{Int}}, R) + convert(::Type{Tuple{Vararg{AbstractUnitRange}}}, R::CartesianIndices) = + convert(Tuple{Vararg{AbstractUnitRange{Int}}}, R) + convert(::Type{Tuple{Vararg{UnitRange{Int}}}}, R::CartesianIndices{N}) where {N} = + convert(NTuple{N,UnitRange{Int}}, R) + convert(::Type{Tuple{Vararg{UnitRange}}}, R::CartesianIndices) = + convert(Tuple{Vararg{UnitRange{Int}}}, R) + + convert(::Type{CartesianIndices{N,R}}, inds::CartesianIndices{N}) where {N,R} = + CartesianIndices(convert(R, inds.indices)) + + # AbstractArray implementation + Base.axes(iter::CartesianIndices{N,R}) where {N,R} = map(Base.axes1, iter.indices) + Base.IndexStyle(::Type{CartesianIndices{N,R}}) where {N,R} = IndexCartesian() + @inline function Base.getindex(iter::CartesianIndices{N,<:NTuple{N,Base.OneTo}}, I::Vararg{Int, N}) where {N} + @boundscheck checkbounds(iter, I...) + CartesianIndex(I) + end + @inline function Base.getindex(iter::CartesianIndices{N,R}, I::Vararg{Int, N}) where {N,R} + @boundscheck checkbounds(iter, I...) + CartesianIndex(I .- first.(Base.axes1.(iter.indices)) .+ first.(iter.indices)) + end + + ndims(R::CartesianIndices) = ndims(typeof(R)) + ndims(::Type{CartesianIndices{N}}) where {N} = N + ndims(::Type{CartesianIndices{N,TT}}) where {N,TT} = N + + eachindex(::IndexCartesian, A::AbstractArray) = CartesianIndices(axes(A)) + + @inline function eachindex(::IndexCartesian, A::AbstractArray, B::AbstractArray...) + axsA = axes(A) + Base._all_match_first(axes, axsA, B...) || Base.throw_eachindex_mismatch(IndexCartesian(), A, B...) + CartesianIndices(axsA) + end + + eltype(::Type{CartesianIndices{N}}) where {N} = CartesianIndex{N} + eltype(::Type{CartesianIndices{N,TT}}) where {N,TT} = CartesianIndex{N} + IteratorSize(::Type{<:CartesianIndices{N}}) where {N} = Base.HasShape{N}() + + @inline function iterate(iter::CartesianIndices) + iterfirst, iterlast = first(iter), last(iter) + if any(map(>, iterfirst.I, iterlast.I)) + return nothing + end + iterfirst, iterfirst + end + @inline function iterate(iter::CartesianIndices, state) + valid, I = __inc(state.I, first(iter).I, last(iter).I) + valid || return nothing + return CartesianIndex(I...), CartesianIndex(I...) + end + + # increment & carry + @inline function inc(state, start, stop) + _, I = __inc(state, start, stop) + return CartesianIndex(I...) + end + + # increment post check to avoid integer overflow + @inline __inc(::Tuple{}, ::Tuple{}, ::Tuple{}) = false, () + @inline function __inc(state::Tuple{Int}, start::Tuple{Int}, stop::Tuple{Int}) + valid = state[1] < stop[1] + return valid, (state[1]+1,) + end + + @inline function __inc(state, start, stop) + if state[1] < stop[1] + return true, (state[1]+1, tail(state)...) + end + valid, I = __inc(tail(state), tail(start), tail(stop)) + return valid, (start[1], I...) + end + + # 0-d cartesian ranges are special-cased to iterate once and only once + iterate(iter::CartesianIndices{0}, done=false) = done ? nothing : (CartesianIndex(), true) + + size(iter::CartesianIndices) = map(dimlength, first(iter).I, last(iter).I) + dimlength(start, stop) = stop-start+1 + + length(iter::CartesianIndices) = prod(size(iter)) + + first(iter::CartesianIndices) = CartesianIndex(map(first, iter.indices)) + last(iter::CartesianIndices) = CartesianIndex(map(last, iter.indices)) + + @inline function in(i::CartesianIndex{N}, r::CartesianIndices{N}) where {N} + _in(true, i.I, first(r).I, last(r).I) + end + _in(b, ::Tuple{}, ::Tuple{}, ::Tuple{}) = b + @inline _in(b, i, start, stop) = _in(b & (start[1] <= i[1] <= stop[1]), tail(i), tail(start), tail(stop)) + + simd_outer_range(iter::CartesianIndices{0}) = iter + function simd_outer_range(iter::CartesianIndices) + CartesianIndices(tail(iter.indices)) + end + + simd_inner_length(iter::CartesianIndices{0}, ::CartesianIndex) = 1 + simd_inner_length(iter::CartesianIndices, I::CartesianIndex) = Base.length(iter.indices[1]) + + simd_index(iter::CartesianIndices{0}, ::CartesianIndex, I1::Int) = first(iter) + @inline function simd_index(iter::CartesianIndices, Ilast::CartesianIndex, I1::Int) + CartesianIndex((I1+first(iter.indices[1]), Ilast.I...)) + end + + # Split out the first N elements of a tuple + @inline function split(t, V::Val) + ref = ntuple(d->true, V) # create a reference tuple of length N + _split1(t, ref), _splitrest(t, ref) + end + @inline _split1(t, ref) = (t[1], _split1(tail(t), tail(ref))...) + @inline _splitrest(t, ref) = _splitrest(tail(t), tail(ref)) + # exit either when we've exhausted the input or reference tuple + _split1(::Tuple{}, ::Tuple{}) = () + _split1(::Tuple{}, ref) = () + _split1(t, ::Tuple{}) = () + _splitrest(::Tuple{}, ::Tuple{}) = () + _splitrest(t, ::Tuple{}) = t + _splitrest(::Tuple{}, ref) = () + + @inline function split(I::CartesianIndex, V::Val) + i, j = split(I.I, V) + CartesianIndex(i), CartesianIndex(j) + end + function split(R::CartesianIndices, V::Val) + i, j = split(R.indices, V) + CartesianIndices(i), CartesianIndices(j) + end + + # reversed CartesianIndices iteration + + @inline function iterate(r::Reverse{<:CartesianIndices}) + iterfirst, iterlast = last(r.itr), first(r.itr) + if any(map(<, iterfirst.I, iterlast.I)) + return nothing + end + iterfirst, iterfirst + end + @inline function iterate(r::Reverse{<:CartesianIndices}, state) + valid, I = __dec(state.I, last(r.itr).I, first(r.itr).I) + valid || return nothing + return CartesianIndex(I...), CartesianIndex(I...) + end + + # decrement & carry + @inline function dec(state, start, stop) + _, I = __dec(state, start, stop) + return CartesianIndex(I...) + end + + # decrement post check to avoid integer overflow + @inline __dec(::Tuple{}, ::Tuple{}, ::Tuple{}) = false, () + @inline function __dec(state::Tuple{Int}, start::Tuple{Int}, stop::Tuple{Int}) + valid = state[1] > stop[1] + return valid, (state[1]-1,) + end + + @inline function __dec(state, start, stop) + if state[1] > stop[1] + return true, (state[1]-1, tail(state)...) + end + valid, I = __dec(tail(state), tail(start), tail(stop)) + return valid, (start[1], I...) + end + + # 0-d cartesian ranges are special-cased to iterate once and only once + iterate(iter::Reverse{<:CartesianIndices{0}}, state=false) = state ? nothing : (CartesianIndex(), true) + + Base.LinearIndices(inds::CartesianIndices{N,R}) where {N,R} = LinearIndices{N,R}(inds.indices) +end # IteratorsMD + + +using .IteratorsMD + +## Bounds-checking with CartesianIndex +# Disallow linear indexing with CartesianIndex +function checkbounds(::Type{Bool}, A::AbstractArray, i::Union{CartesianIndex, AbstractArray{<:CartesianIndex}}) + @_inline_meta + checkbounds_indices(Bool, axes(A), (i,)) +end + +@inline checkbounds_indices(::Type{Bool}, ::Tuple{}, I::Tuple{CartesianIndex,Vararg{Any}}) = + checkbounds_indices(Bool, (), (I[1].I..., tail(I)...)) +@inline checkbounds_indices(::Type{Bool}, IA::Tuple{Any}, I::Tuple{CartesianIndex,Vararg{Any}}) = + checkbounds_indices(Bool, IA, (I[1].I..., tail(I)...)) +@inline checkbounds_indices(::Type{Bool}, IA::Tuple, I::Tuple{CartesianIndex,Vararg{Any}}) = + checkbounds_indices(Bool, IA, (I[1].I..., tail(I)...)) + +# Indexing into Array with mixtures of Integers and CartesianIndices is +# extremely performance-sensitive. While the abstract fallbacks support this, +# codegen has extra support for SIMDification that sub2ind doesn't (yet) support +@propagate_inbounds getindex(A::Array, i1::Union{Integer, CartesianIndex}, I::Union{Integer, CartesianIndex}...) = + A[to_indices(A, (i1, I...))...] +@propagate_inbounds setindex!(A::Array, v, i1::Union{Integer, CartesianIndex}, I::Union{Integer, CartesianIndex}...) = + (A[to_indices(A, (i1, I...))...] = v; A) + +# Support indexing with an array of CartesianIndex{N}s +# Here we try to consume N of the indices (if there are that many available) +# The first two simply handle ambiguities +@inline function checkbounds_indices(::Type{Bool}, ::Tuple{}, + I::Tuple{AbstractArray{CartesianIndex{N}},Vararg{Any}}) where N + checkindex(Bool, (), I[1]) & checkbounds_indices(Bool, (), tail(I)) +end +@inline function checkbounds_indices(::Type{Bool}, IA::Tuple{Any}, + I::Tuple{AbstractArray{CartesianIndex{0}},Vararg{Any}}) + checkbounds_indices(Bool, IA, tail(I)) +end +@inline function checkbounds_indices(::Type{Bool}, IA::Tuple{Any}, + I::Tuple{AbstractArray{CartesianIndex{N}},Vararg{Any}}) where N + checkindex(Bool, IA, I[1]) & checkbounds_indices(Bool, (), tail(I)) +end +@inline function checkbounds_indices(::Type{Bool}, IA::Tuple, + I::Tuple{AbstractArray{CartesianIndex{N}},Vararg{Any}}) where N + IA1, IArest = IteratorsMD.split(IA, Val(N)) + checkindex(Bool, IA1, I[1]) & checkbounds_indices(Bool, IArest, tail(I)) +end + +function checkindex(::Type{Bool}, inds::Tuple, I::AbstractArray{<:CartesianIndex}) + b = true + for i in I + b &= checkbounds_indices(Bool, inds, (i,)) + end + b +end + +# combined count of all indices, including CartesianIndex and +# AbstractArray{CartesianIndex} +# rather than returning N, it returns an NTuple{N,Bool} so the result is inferrable +@inline index_ndims(i1, I...) = (true, index_ndims(I...)...) +@inline function index_ndims(i1::CartesianIndex, I...) + (map(x->true, i1.I)..., index_ndims(I...)...) +end +@inline function index_ndims(i1::AbstractArray{CartesianIndex{N}}, I...) where N + (ntuple(x->true, Val(N))..., index_ndims(I...)...) +end +index_ndims() = () + +# combined dimensionality of all indices +# rather than returning N, it returns an NTuple{N,Bool} so the result is inferrable +@inline index_dimsum(i1, I...) = (index_dimsum(I...)...,) +@inline index_dimsum(::Colon, I...) = (true, index_dimsum(I...)...) +@inline index_dimsum(::AbstractArray{Bool}, I...) = (true, index_dimsum(I...)...) +@inline function index_dimsum(::AbstractArray{<:Any,N}, I...) where N + (ntuple(x->true, Val(N))..., index_dimsum(I...)...) +end +index_dimsum() = () + +# Recursively compute the lengths of a list of indices, without dropping scalars +index_lengths() = () +@inline index_lengths(::Real, rest...) = (1, index_lengths(rest...)...) +@inline index_lengths(A::AbstractArray, rest...) = (length(A), index_lengths(rest...)...) + +# shape of array to create for getindex() with indices I, dropping scalars +# returns a Tuple{Vararg{AbstractUnitRange}} of indices +index_shape() = () +@inline index_shape(::Real, rest...) = index_shape(rest...) +@inline index_shape(A::AbstractArray, rest...) = (axes(A)..., index_shape(rest...)...) + +""" + LogicalIndex(mask) + +The `LogicalIndex` type is a special vector that simply contains all indices I +where `mask[I]` is true. This specialized type does not support indexing +directly as doing so would require O(n) lookup time. `AbstractArray{Bool}` are +wrapped with `LogicalIndex` upon calling [`to_indices`](@ref). +""" +struct LogicalIndex{T, A<:AbstractArray{Bool}} <: AbstractVector{T} + mask::A + sum::Int + LogicalIndex{T,A}(mask::A) where {T,A<:AbstractArray{Bool}} = new(mask, count(mask)) +end +LogicalIndex(mask::AbstractVector{Bool}) = LogicalIndex{Int, typeof(mask)}(mask) +LogicalIndex(mask::AbstractArray{Bool, N}) where {N} = LogicalIndex{CartesianIndex{N}, typeof(mask)}(mask) +(::Type{LogicalIndex{Int}})(mask::AbstractArray) = LogicalIndex{Int, typeof(mask)}(mask) +size(L::LogicalIndex) = (L.sum,) +length(L::LogicalIndex) = L.sum +collect(L::LogicalIndex) = [i for i in L] +show(io::IO, r::LogicalIndex) = print(io, "Base.LogicalIndex(", r.mask, ")") +# Iteration over LogicalIndex is very performance-critical, but it also must +# support arbitrary AbstractArray{Bool}s with both Int and CartesianIndex. +# Thus the iteration state contains an index iterator and its state. We also +# keep track of the count of elements since we already know how many there +# should be -- this way we don't need to look at future indices to check done. +@inline function iterate(L::LogicalIndex{Int}) + r = LinearIndices(L.mask) + iterate(L, (1, r)) +end +@inline function iterate(L::LogicalIndex{<:CartesianIndex}) + r = CartesianIndices(axes(L.mask)) + iterate(L, (1, r)) +end +@propagate_inbounds function iterate(L::LogicalIndex, s) + # We're looking for the n-th true element, using iterator r at state i + n = s[1] + n > length(L) && return nothing + #unroll once to help inference, cf issue #29418 + idx, i = iterate(tail(s)...) + s = (n+1, s[2], i) + L.mask[idx] && return (idx, s) + while true + idx, i = iterate(tail(s)...) + s = (n+1, s[2], i) + L.mask[idx] && return (idx, s) + end +end +# When wrapping a BitArray, lean heavily upon its internals. +@inline function iterate(L::Base.LogicalIndex{Int,<:BitArray}) + L.sum == 0 && return nothing + Bc = L.mask.chunks + return iterate(L, (1, @inbounds Bc[1])) +end +@inline function iterate(L::Base.LogicalIndex{Int,<:BitArray}, s) + Bc = L.mask.chunks + i1, c = s + while c==0 + i1 % UInt >= length(Bc) % UInt && return nothing + i1 += 1 + @inbounds c = Bc[i1] + end + tz = trailing_zeros(c) + 1 + c = _blsr(c) + return ((i1-1)<<6 + tz, (i1, c)) +end + +@inline checkbounds(::Type{Bool}, A::AbstractArray, I::LogicalIndex{<:Any,<:AbstractArray{Bool,1}}) = + eachindex(IndexLinear(), A) == eachindex(IndexLinear(), I.mask) +@inline checkbounds(::Type{Bool}, A::AbstractArray, I::LogicalIndex) = axes(A) == axes(I.mask) +@inline checkindex(::Type{Bool}, indx::AbstractUnitRange, I::LogicalIndex) = (indx,) == axes(I.mask) +checkindex(::Type{Bool}, inds::Tuple, I::LogicalIndex) = false + +ensure_indexable(I::Tuple{}) = () +@inline ensure_indexable(I::Tuple{Any, Vararg{Any}}) = (I[1], ensure_indexable(tail(I))...) +@inline ensure_indexable(I::Tuple{LogicalIndex, Vararg{Any}}) = (collect(I[1]), ensure_indexable(tail(I))...) + +# In simple cases, we know that we don't need to use axes(A). Optimize those +# until Julia gets smart enough to elide the call on its own: +to_indices(A, I::Tuple{}) = () +@inline to_indices(A, I::Tuple{Vararg{Union{Integer, CartesianIndex}}}) = to_indices(A, (), I) +# But some index types require more context spanning multiple indices +# CartesianIndexes are simple; they just splat out +@inline to_indices(A, inds, I::Tuple{CartesianIndex, Vararg{Any}}) = + to_indices(A, inds, (I[1].I..., tail(I)...)) +# But for arrays of CartesianIndex, we just skip the appropriate number of inds +@inline function to_indices(A, inds, I::Tuple{AbstractArray{CartesianIndex{N}}, Vararg{Any}}) where N + _, indstail = IteratorsMD.split(inds, Val(N)) + (to_index(A, I[1]), to_indices(A, indstail, tail(I))...) +end +# And boolean arrays behave similarly; they also skip their number of dimensions +@inline function to_indices(A, inds, I::Tuple{AbstractArray{Bool, N}, Vararg{Any}}) where N + _, indstail = IteratorsMD.split(inds, Val(N)) + (to_index(A, I[1]), to_indices(A, indstail, tail(I))...) +end +# As an optimization, we allow trailing Array{Bool} and BitArray to be linear over trailing dimensions +@inline to_indices(A, inds, I::Tuple{Union{Array{Bool,N}, BitArray{N}}}) where {N} = + (_maybe_linear_logical_index(IndexStyle(A), A, I[1]),) +_maybe_linear_logical_index(::IndexStyle, A, i) = to_index(A, i) +_maybe_linear_logical_index(::IndexLinear, A, i) = LogicalIndex{Int}(i) + +# Colons get converted to slices by `uncolon` +@inline to_indices(A, inds, I::Tuple{Colon, Vararg{Any}}) = + (uncolon(inds, I), to_indices(A, _maybetail(inds), tail(I))...) + +const CI0 = Union{CartesianIndex{0}, AbstractArray{CartesianIndex{0}}} +uncolon(inds::Tuple{}, I::Tuple{Colon, Vararg{Any}}) = Slice(OneTo(1)) +uncolon(inds::Tuple, I::Tuple{Colon, Vararg{Any}}) = Slice(inds[1]) + +### From abstractarray.jl: Internal multidimensional indexing definitions ### +getindex(x::Number, i::CartesianIndex{0}) = x +getindex(t::Tuple, i::CartesianIndex{1}) = getindex(t, i.I[1]) + +# These are not defined on directly on getindex to avoid +# ambiguities for AbstractArray subtypes. See the note in abstractarray.jl + +@inline function _getindex(l::IndexStyle, A::AbstractArray, I::Union{Real, AbstractArray}...) + @boundscheck checkbounds(A, I...) + return _unsafe_getindex(l, _maybe_reshape(l, A, I...), I...) +end +# But we can speed up IndexCartesian arrays by reshaping them to the appropriate dimensionality: +_maybe_reshape(::IndexLinear, A::AbstractArray, I...) = A +_maybe_reshape(::IndexCartesian, A::AbstractVector, I...) = A +@inline _maybe_reshape(::IndexCartesian, A::AbstractArray, I...) = __maybe_reshape(A, index_ndims(I...)) +@inline __maybe_reshape(A::AbstractArray{T,N}, ::NTuple{N,Any}) where {T,N} = A +@inline __maybe_reshape(A::AbstractArray, ::NTuple{N,Any}) where {N} = reshape(A, Val(N)) + +function _unsafe_getindex(::IndexStyle, A::AbstractArray, I::Vararg{Union{Real, AbstractArray}, N}) where N + # This is specifically not inlined to prevent excessive allocations in type unstable code + shape = index_shape(I...) + dest = similar(A, shape) + map(unsafe_length, axes(dest)) == map(unsafe_length, shape) || throw_checksize_error(dest, shape) + _unsafe_getindex!(dest, A, I...) # usually a generated function, don't allow it to impact inference result + return dest +end + +# Always index with the exactly indices provided. +@generated function _unsafe_getindex!(dest::AbstractArray, src::AbstractArray, I::Vararg{Union{Real, AbstractArray}, N}) where N + quote + @_inline_meta + D = eachindex(dest) + Dy = iterate(D) + @inbounds @nloops $N j d->I[d] begin + # This condition is never hit, but at the moment + # the optimizer is not clever enough to split the union without it + Dy === nothing && return dest + (idx, state) = Dy + dest[idx] = @ncall $N getindex src j + Dy = iterate(D, state) + end + return dest + end +end + +@noinline throw_checksize_error(A, sz) = throw(DimensionMismatch("output array is the wrong size; expected $sz, got $(size(A))")) + +## setindex! ## +function _setindex!(l::IndexStyle, A::AbstractArray, x, I::Union{Real, AbstractArray}...) + @_inline_meta + @boundscheck checkbounds(A, I...) + _unsafe_setindex!(l, _maybe_reshape(l, A, I...), x, I...) + A +end + +@generated function _unsafe_setindex!(::IndexStyle, A::AbstractArray, x, I::Union{Real,AbstractArray}...) + N = length(I) + quote + x′ = unalias(A, x) + @nexprs $N d->(I_d = unalias(A, I[d])) + idxlens = @ncall $N index_lengths I + @ncall $N setindex_shape_check x′ (d->idxlens[d]) + Xy = iterate(x′) + @inbounds @nloops $N i d->I_d begin + # This is never reached, but serves as an assumption for + # the optimizer that it does not need to emit error paths + Xy === nothing && break + (val, state) = Xy + @ncall $N setindex! A val i + Xy = iterate(x′, state) + end + A + end +end + +diff(a::AbstractVector) = diff(a, dims=1) + +""" + diff(A::AbstractVector) + diff(A::AbstractArray; dims::Integer) + +Finite difference operator on a vector or a multidimensional array `A`. In the +latter case the dimension to operate on needs to be specified with the `dims` +keyword argument. + +!!! compat "Julia 1.1" + `diff` for arrays with dimension higher than 2 requires at least Julia 1.1. + +# Examples +```jldoctest +julia> a = [2 4; 6 16] +2×2 Array{Int64,2}: + 2 4 + 6 16 + +julia> diff(a, dims=2) +2×1 Array{Int64,2}: + 2 + 10 + +julia> diff(vec(a)) +3-element Array{Int64,1}: + 4 + -2 + 12 +``` +""" +function diff(a::AbstractArray{T,N}; dims::Integer) where {T,N} + require_one_based_indexing(a) + 1 <= dims <= N || throw(ArgumentError("dimension $dims out of range (1:$N)")) + + r = axes(a) + r0 = ntuple(i -> i == dims ? UnitRange(1, last(r[i]) - 1) : UnitRange(r[i]), N) + r1 = ntuple(i -> i == dims ? UnitRange(2, last(r[i])) : UnitRange(r[i]), N) + + return view(a, r1...) .- view(a, r0...) +end + +### from abstractarray.jl + +# In the common case where we have two views into the same parent, aliasing checks +# are _much_ easier and more important to get right +function mightalias(A::SubArray{T,<:Any,P}, B::SubArray{T,<:Any,P}) where {T,P} + if !_parentsmatch(A.parent, B.parent) + # We cannot do any better than the usual dataids check + return !_isdisjoint(dataids(A), dataids(B)) + end + # Now we know that A.parent === B.parent. This means that the indices of A + # and B are the same length and indexing into the same dimensions. We can + # just walk through them and check for overlaps: O(ndims(A)). We must finally + # ensure that the indices don't alias with either parent + return _indicesmightoverlap(A.indices, B.indices) || + !_isdisjoint(dataids(A.parent), _splatmap(dataids, B.indices)) || + !_isdisjoint(dataids(B.parent), _splatmap(dataids, A.indices)) +end +_parentsmatch(A::AbstractArray, B::AbstractArray) = A === B +# Two reshape(::Array)s of the same size aren't `===` because they have different headers +_parentsmatch(A::Array, B::Array) = pointer(A) == pointer(B) && size(A) == size(B) + +_indicesmightoverlap(A::Tuple{}, B::Tuple{}) = true +_indicesmightoverlap(A::Tuple{}, B::Tuple) = error("malformed subarray") +_indicesmightoverlap(A::Tuple, B::Tuple{}) = error("malformed subarray") +# For ranges, it's relatively cheap to construct the intersection +@inline function _indicesmightoverlap(A::Tuple{AbstractRange, Vararg{Any}}, B::Tuple{AbstractRange, Vararg{Any}}) + !isempty(intersect(A[1], B[1])) ? _indicesmightoverlap(tail(A), tail(B)) : false +end +# But in the common AbstractUnitRange case, there's an even faster shortcut +@inline function _indicesmightoverlap(A::Tuple{AbstractUnitRange, Vararg{Any}}, B::Tuple{AbstractUnitRange, Vararg{Any}}) + max(first(A[1]),first(B[1])) <= min(last(A[1]),last(B[1])) ? _indicesmightoverlap(tail(A), tail(B)) : false +end +# And we can check scalars against each other and scalars against arrays quite easily +@inline _indicesmightoverlap(A::Tuple{Real, Vararg{Any}}, B::Tuple{Real, Vararg{Any}}) = + A[1] == B[1] ? _indicesmightoverlap(tail(A), tail(B)) : false +@inline _indicesmightoverlap(A::Tuple{Real, Vararg{Any}}, B::Tuple{AbstractArray, Vararg{Any}}) = + A[1] in B[1] ? _indicesmightoverlap(tail(A), tail(B)) : false +@inline _indicesmightoverlap(A::Tuple{AbstractArray, Vararg{Any}}, B::Tuple{Real, Vararg{Any}}) = + B[1] in A[1] ? _indicesmightoverlap(tail(A), tail(B)) : false +# And small arrays are quick, too +@inline function _indicesmightoverlap(A::Tuple{AbstractArray, Vararg{Any}}, B::Tuple{AbstractArray, Vararg{Any}}) + if length(A[1]) == 1 + return A[1][1] in B[1] ? _indicesmightoverlap(tail(A), tail(B)) : false + elseif length(B[1]) == 1 + return B[1][1] in A[1] ? _indicesmightoverlap(tail(A), tail(B)) : false + else + # But checking larger arrays requires O(m*n) and is too much work + return true + end +end +# And in general, checking the intersection is too much work +_indicesmightoverlap(A::Tuple{Any, Vararg{Any}}, B::Tuple{Any, Vararg{Any}}) = true + +""" + fill!(A, x) + +Fill array `A` with the value `x`. If `x` is an object reference, all elements will refer to +the same object. `fill!(A, Foo())` will return `A` filled with the result of evaluating +`Foo()` once. + +# Examples +```jldoctest +julia> A = zeros(2,3) +2×3 Array{Float64,2}: + 0.0 0.0 0.0 + 0.0 0.0 0.0 + +julia> fill!(A, 2.) +2×3 Array{Float64,2}: + 2.0 2.0 2.0 + 2.0 2.0 2.0 + +julia> a = [1, 1, 1]; A = fill!(Vector{Vector{Int}}(undef, 3), a); a[1] = 2; A +3-element Array{Array{Int64,1},1}: + [2, 1, 1] + [2, 1, 1] + [2, 1, 1] + +julia> x = 0; f() = (global x += 1; x); fill!(Vector{Int}(undef, 3), f()) +3-element Array{Int64,1}: + 1 + 1 + 1 +``` +""" +function fill!(A::AbstractArray{T}, x) where T + xT = convert(T, x) + for I in eachindex(A) + @inbounds A[I] = xT + end + A +end + +""" + copyto!(dest::AbstractArray, src) -> dest + + +Copy all elements from collection `src` to array `dest`, whose length must be greater than +or equal to the length `n` of `src`. The first `n` elements of `dest` are overwritten, +the other elements are left untouched. + +# Examples +```jldoctest +julia> x = [1., 0., 3., 0., 5.]; + +julia> y = zeros(7); + +julia> copyto!(y, x); + +julia> y +7-element Array{Float64,1}: + 1.0 + 0.0 + 3.0 + 0.0 + 5.0 + 0.0 + 0.0 +``` +""" +copyto!(dest, src) + +function copyto!(dest::AbstractArray{T,N}, src::AbstractArray{T,N}) where {T,N} + checkbounds(dest, axes(src)...) + src′ = unalias(dest, src) + for I in eachindex(IndexStyle(src′,dest), src′) + @inbounds dest[I] = src′[I] + end + dest +end + +function copyto!(dest::AbstractArray{T1,N}, Rdest::CartesianIndices{N}, + src::AbstractArray{T2,N}, Rsrc::CartesianIndices{N}) where {T1,T2,N} + isempty(Rdest) && return dest + if size(Rdest) != size(Rsrc) + throw(ArgumentError("source and destination must have same size (got $(size(Rsrc)) and $(size(Rdest)))")) + end + checkbounds(dest, first(Rdest)) + checkbounds(dest, last(Rdest)) + checkbounds(src, first(Rsrc)) + checkbounds(src, last(Rsrc)) + src′ = unalias(dest, src) + ΔI = first(Rdest) - first(Rsrc) + if @generated + quote + @nloops $N i (n->Rsrc.indices[n]) begin + @inbounds @nref($N,dest,n->i_n+ΔI[n]) = @nref($N,src′,i) + end + end + else + for I in Rsrc + @inbounds dest[I + ΔI] = src′[I] + end + end + dest +end + +""" + copyto!(dest, Rdest::CartesianIndices, src, Rsrc::CartesianIndices) -> dest + +Copy the block of `src` in the range of `Rsrc` to the block of `dest` +in the range of `Rdest`. The sizes of the two regions must match. +""" +copyto!(::AbstractArray, ::CartesianIndices, ::AbstractArray, ::CartesianIndices) + +# circshift! +circshift!(dest::AbstractArray, src, ::Tuple{}) = copyto!(dest, src) +""" + circshift!(dest, src, shifts) + +Circularly shift, i.e. rotate, the data in `src`, storing the result in +`dest`. `shifts` specifies the amount to shift in each dimension. + +The `dest` array must be distinct from the `src` array (they cannot +alias each other). + +See also [`circshift`](@ref). +""" +@noinline function circshift!(dest::AbstractArray{T,N}, src, shiftamt::DimsInteger) where {T,N} + dest === src && throw(ArgumentError("dest and src must be separate arrays")) + inds = axes(src) + axes(dest) == inds || throw(ArgumentError("indices of src and dest must match (got $inds and $(axes(dest)))")) + _circshift!(dest, (), src, (), inds, fill_to_length(shiftamt, 0, Val(N))) +end +circshift!(dest::AbstractArray, src, shiftamt) = circshift!(dest, src, (shiftamt...,)) + +# For each dimension, we copy the first half of src to the second half +# of dest, and the second half of src to the first half of dest. This +# uses a recursive bifurcation strategy so that these splits can be +# encoded by ranges, which means that we need only one call to `mod` +# per dimension rather than one call per index. +# `rdest` and `rsrc` are tuples-of-ranges that grow one dimension at a +# time; when all the dimensions have been filled in, you call `copyto!` +# for that block. In other words, in two dimensions schematically we +# have the following call sequence (--> means a call): +# circshift!(dest, src, shiftamt) --> +# _circshift!(dest, src, ("first half of dim1",)) --> +# _circshift!(dest, src, ("first half of dim1", "first half of dim2")) --> copyto! +# _circshift!(dest, src, ("first half of dim1", "second half of dim2")) --> copyto! +# _circshift!(dest, src, ("second half of dim1",)) --> +# _circshift!(dest, src, ("second half of dim1", "first half of dim2")) --> copyto! +# _circshift!(dest, src, ("second half of dim1", "second half of dim2")) --> copyto! +@inline function _circshift!(dest, rdest, src, rsrc, + inds::Tuple{AbstractUnitRange,Vararg{Any}}, + shiftamt::Tuple{Integer,Vararg{Any}}) + ind1, d = inds[1], shiftamt[1] + s = mod(d, length(ind1)) + sf, sl = first(ind1)+s, last(ind1)-s + r1, r2 = first(ind1):sf-1, sf:last(ind1) + r3, r4 = first(ind1):sl, sl+1:last(ind1) + tinds, tshiftamt = tail(inds), tail(shiftamt) + _circshift!(dest, (rdest..., r1), src, (rsrc..., r4), tinds, tshiftamt) + _circshift!(dest, (rdest..., r2), src, (rsrc..., r3), tinds, tshiftamt) +end +# At least one of inds, shiftamt is empty +function _circshift!(dest, rdest, src, rsrc, inds, shiftamt) + copyto!(dest, CartesianIndices(rdest), src, CartesianIndices(rsrc)) +end + +# circcopy! +""" + circcopy!(dest, src) + +Copy `src` to `dest`, indexing each dimension modulo its length. +`src` and `dest` must have the same size, but can be offset in +their indices; any offset results in a (circular) wraparound. If the +arrays have overlapping indices, then on the domain of the overlap +`dest` agrees with `src`. + +# Examples +```julia-repl +julia> src = reshape(Vector(1:16), (4,4)) +4×4 Array{Int64,2}: + 1 5 9 13 + 2 6 10 14 + 3 7 11 15 + 4 8 12 16 + +julia> dest = OffsetArray{Int}(undef, (0:3,2:5)) + +julia> circcopy!(dest, src) +OffsetArrays.OffsetArray{Int64,2,Array{Int64,2}} with indices 0:3×2:5: + 8 12 16 4 + 5 9 13 1 + 6 10 14 2 + 7 11 15 3 + +julia> dest[1:3,2:4] == src[1:3,2:4] +true +``` +""" +function circcopy!(dest, src) + dest === src && throw(ArgumentError("dest and src must be separate arrays")) + indssrc, indsdest = axes(src), axes(dest) + if (szsrc = map(length, indssrc)) != (szdest = map(length, indsdest)) + throw(DimensionMismatch("src and dest must have the same sizes (got $szsrc and $szdest)")) + end + shift = map((isrc, idest)->first(isrc)-first(idest), indssrc, indsdest) + all(x->x==0, shift) && return copyto!(dest, src) + _circcopy!(dest, (), indsdest, src, (), indssrc) +end + +# This uses the same strategy described above for _circshift! +@inline function _circcopy!(dest, rdest, indsdest::Tuple{AbstractUnitRange,Vararg{Any}}, + src, rsrc, indssrc::Tuple{AbstractUnitRange,Vararg{Any}}) + indd1, inds1 = indsdest[1], indssrc[1] + l = length(indd1) + s = mod(first(inds1)-first(indd1), l) + sdf = first(indd1)+s + rd1, rd2 = first(indd1):sdf-1, sdf:last(indd1) + ssf = last(inds1)-s + rs1, rs2 = first(inds1):ssf, ssf+1:last(inds1) + tindsd, tindss = tail(indsdest), tail(indssrc) + _circcopy!(dest, (rdest..., rd1), tindsd, src, (rsrc..., rs2), tindss) + _circcopy!(dest, (rdest..., rd2), tindsd, src, (rsrc..., rs1), tindss) +end + +# At least one of indsdest, indssrc are empty (and both should be, since we've checked) +function _circcopy!(dest, rdest, indsdest, src, rsrc, indssrc) + copyto!(dest, CartesianIndices(rdest), src, CartesianIndices(rsrc)) +end + +### BitArrays + +## getindex + +# contiguous multidimensional indexing: if the first dimension is a range, +# we can get some performance from using copy_chunks! +@inline function _unsafe_getindex!(X::BitArray, B::BitArray, I0::Union{UnitRange{Int},Slice}) + copy_chunks!(X.chunks, 1, B.chunks, indexoffset(I0)+1, length(I0)) + return X +end + +# Optimization where the inner dimension is contiguous improves perf dramatically +@generated function _unsafe_getindex!(X::BitArray, B::BitArray, + I0::Union{Slice,UnitRange{Int}}, I::Union{Int,UnitRange{Int},Slice}...) + N = length(I) + quote + $(Expr(:meta, :inline)) + @nexprs $N d->(I_d = I[d]) + + idxlens = @ncall $N index_lengths I0 I + + f0 = indexoffset(I0)+1 + l0 = idxlens[1] + + gap_lst_1 = 0 + @nexprs $N d->(gap_lst_{d+1} = idxlens[d+1]) + stride = 1 + ind = f0 + @nexprs $N d->begin + stride *= size(B, d) + stride_lst_d = stride + ind += stride * indexoffset(I_d) + gap_lst_{d+1} *= stride + end + + storeind = 1 + Xc, Bc = X.chunks, B.chunks + @nloops($N, i, d->(1:idxlens[d+1]), + d->nothing, # PRE + d->(ind += stride_lst_d - gap_lst_d), # POST + begin # BODY + copy_chunks!(Xc, storeind, Bc, ind, l0) + storeind += l0 + end) + return X + end +end + +# in the general multidimensional non-scalar case, can we do about 10% better +# in most cases by manually hoisting the bitarray chunks access out of the loop +# (This should really be handled by the compiler or with an immutable BitArray) +@generated function _unsafe_getindex!(X::BitArray, B::BitArray, I::Union{Int,AbstractArray{Int}}...) + N = length(I) + quote + $(Expr(:meta, :inline)) + stride_1 = 1 + @nexprs $N d->(stride_{d+1} = stride_d*size(B, d)) + $(Symbol(:offset_, N)) = 1 + ind = 0 + Xc, Bc = X.chunks, B.chunks + @nloops $N i d->I[d] d->(@inbounds offset_{d-1} = offset_d + (i_d-1)*stride_d) begin + ind += 1 + unsafe_bitsetindex!(Xc, unsafe_bitgetindex(Bc, offset_0), ind) + end + return X + end +end + +## setindex! + +function copy_to_bitarray_chunks!(Bc::Vector{UInt64}, pos_d::Int, C::StridedArray, pos_s::Int, numbits::Int) + bind = pos_d + cind = pos_s + lastind = pos_d + numbits - 1 + @inbounds while bind ≤ lastind + unsafe_bitsetindex!(Bc, Bool(C[cind]), bind) + bind += 1 + cind += 1 + end +end + +# Note: the next two functions rely on the following definition of the conversion to Bool: +# convert(::Type{Bool}, x::Real) = x==0 ? false : x==1 ? true : throw(InexactError(...)) +# they're used to pre-emptively check in bulk when possible, which is much faster. +# Also, the functions can be overloaded for custom types T<:Real : +# a) in the unlikely eventuality that they use a different logic for Bool conversion +# b) to skip the check if not necessary +@inline try_bool_conversion(x::Real) = + x == 0 || x == 1 || throw(InexactError(:try_bool_conversion, Bool, x)) +@inline unchecked_bool_convert(x::Real) = x == 1 + +function copy_to_bitarray_chunks!(Bc::Vector{UInt64}, pos_d::Int, C::StridedArray{<:Real}, pos_s::Int, numbits::Int) + @inbounds for i = (1:numbits) .+ (pos_s - 1) + try_bool_conversion(C[i]) + end + + kd0, ld0 = get_chunks_id(pos_d) + kd1, ld1 = get_chunks_id(pos_d + numbits - 1) + + delta_kd = kd1 - kd0 + + u = _msk64 + if delta_kd == 0 + msk_d0 = msk_d1 = ~(u << ld0) | (u << (ld1+1)) + lt0 = ld1 + else + msk_d0 = ~(u << ld0) + msk_d1 = (u << (ld1+1)) + lt0 = 63 + end + + bind = kd0 + ind = pos_s + @inbounds if ld0 > 0 + c = UInt64(0) + for j = ld0:lt0 + c |= (UInt64(unchecked_bool_convert(C[ind])) << j) + ind += 1 + end + Bc[kd0] = (Bc[kd0] & msk_d0) | (c & ~msk_d0) + bind += 1 + end + + nc = _div64(numbits - ind + pos_s) + @inbounds for i = 1:nc + c = UInt64(0) + for j = 0:63 + c |= (UInt64(unchecked_bool_convert(C[ind])) << j) + ind += 1 + end + Bc[bind] = c + bind += 1 + end + + @inbounds if bind ≤ kd1 + @assert bind == kd1 + c = UInt64(0) + for j = 0:ld1 + c |= (UInt64(unchecked_bool_convert(C[ind])) << j) + ind += 1 + end + Bc[kd1] = (Bc[kd1] & msk_d1) | (c & ~msk_d1) + end +end + +# contiguous multidimensional indexing: if the first dimension is a range, +# we can get some performance from using copy_chunks! + +@inline function setindex!(B::BitArray, X::Union{StridedArray,BitArray}, J0::Union{Colon,UnitRange{Int}}) + I0 = to_indices(B, (J0,))[1] + @boundscheck checkbounds(B, I0) + l0 = length(I0) + setindex_shape_check(X, l0) + l0 == 0 && return B + f0 = indexoffset(I0)+1 + copy_to_bitarray_chunks!(B.chunks, f0, X, 1, l0) + return B +end + +@inline function setindex!(B::BitArray, X::Union{StridedArray,BitArray}, + I0::Union{Colon,UnitRange{Int}}, I::Union{Int,UnitRange{Int},Colon}...) + J = to_indices(B, (I0, I...)) + @boundscheck checkbounds(B, J...) + _unsafe_setindex!(B, X, J...) +end +@generated function _unsafe_setindex!(B::BitArray, X::Union{StridedArray,BitArray}, + I0::Union{Slice,UnitRange{Int}}, I::Union{Int,UnitRange{Int},Slice}...) + N = length(I) + quote + idxlens = @ncall $N index_lengths I0 d->I[d] + @ncall $N setindex_shape_check X idxlens[1] d->idxlens[d+1] + isempty(X) && return B + f0 = indexoffset(I0)+1 + l0 = idxlens[1] + + gap_lst_1 = 0 + @nexprs $N d->(gap_lst_{d+1} = idxlens[d+1]) + stride = 1 + ind = f0 + @nexprs $N d->begin + stride *= size(B, d) + stride_lst_d = stride + ind += stride * indexoffset(I[d]) + gap_lst_{d+1} *= stride + end + + refind = 1 + Bc = B.chunks + @nloops($N, i, d->I[d], + d->nothing, # PRE + d->(ind += stride_lst_d - gap_lst_d), # POST + begin # BODY + copy_to_bitarray_chunks!(Bc, ind, X, refind, l0) + refind += l0 + end) + + return B + end +end + +@propagate_inbounds function setindex!(B::BitArray, X::AbstractArray, + I0::Union{Colon,UnitRange{Int}}, I::Union{Int,UnitRange{Int},Colon}...) + _setindex!(IndexStyle(B), B, X, to_indices(B, (I0, I...))...) +end + +## fill! contiguous views of BitArrays with a single value +function fill!(V::SubArray{Bool, <:Any, <:BitArray, Tuple{AbstractUnitRange{Int}}}, x) + B = V.parent + I0 = V.indices[1] + l0 = length(I0) + l0 == 0 && return V + fill_chunks!(B.chunks, Bool(x), first(I0), l0) + return V +end + +fill!(V::SubArray{Bool, <:Any, <:BitArray, Tuple{AbstractUnitRange{Int}, Vararg{Union{Int,AbstractUnitRange{Int}}}}}, x) = + _unsafe_fill_indices!(V.parent, x, V.indices...) + +@generated function _unsafe_fill_indices!(B::BitArray, x, + I0::AbstractUnitRange{Int}, I::Union{Int,AbstractUnitRange{Int}}...) + N = length(I) + quote + y = Bool(x) + idxlens = @ncall $N index_lengths I0 d->I[d] + + f0 = indexoffset(I0)+1 + l0 = idxlens[1] + l0 == 0 && return B + @nexprs $N d->(isempty(I[d]) && return B) + + gap_lst_1 = 0 + @nexprs $N d->(gap_lst_{d+1} = idxlens[d+1]) + stride = 1 + ind = f0 + @nexprs $N d->begin + stride *= size(B, d) + stride_lst_d = stride + ind += stride * indexoffset(I[d]) + gap_lst_{d+1} *= stride + end + + @nloops($N, i, d->I[d], + d->nothing, # PRE + d->(ind += stride_lst_d - gap_lst_d), # POST + fill_chunks!(B.chunks, y, ind, l0) # BODY + ) + + return B + end +end + +## isassigned + +@generated function isassigned(B::BitArray, I_0::Int, I::Int...) + N = length(I) + quote + @nexprs $N d->(I_d = I[d]) + stride = 1 + index = I_0 + @nexprs $N d->begin + l = size(B,d) + stride *= l + @boundscheck 1 <= I_{d-1} <= l || return false + index += (I_d - 1) * stride + end + return isassigned(B, index) + end +end + +## permutedims + +## Permute array dims ## + +function permutedims(B::StridedArray, perm) + dimsB = size(B) + ndimsB = length(dimsB) + (ndimsB == length(perm) && isperm(perm)) || throw(ArgumentError("no valid permutation of dimensions")) + dimsP = ntuple(i->dimsB[perm[i]], ndimsB)::typeof(dimsB) + P = similar(B, dimsP) + permutedims!(P, B, perm) +end + +function checkdims_perm(P::AbstractArray{TP,N}, B::AbstractArray{TB,N}, perm) where {TP,TB,N} + indsB = axes(B) + length(perm) == N || throw(ArgumentError("expected permutation of size $N, but length(perm)=$(length(perm))")) + isperm(perm) || throw(ArgumentError("input is not a permutation")) + indsP = axes(P) + for i = 1:length(perm) + indsP[i] == indsB[perm[i]] || throw(DimensionMismatch("destination tensor of incorrect size")) + end + nothing +end + +for (V, PT, BT) in [((:N,), BitArray, BitArray), ((:T,:N), Array, StridedArray)] + @eval @generated function permutedims!(P::$PT{$(V...)}, B::$BT{$(V...)}, perm) where $(V...) + quote + checkdims_perm(P, B, perm) + + #calculates all the strides + native_strides = size_to_strides(1, size(B)...) + strides_1 = 0 + @nexprs $N d->(strides_{d+1} = native_strides[perm[d]]) + + #Creates offset, because indexing starts at 1 + offset = 1 - sum(@ntuple $N d->strides_{d+1}) + + ind = 1 + @nexprs 1 d->(counts_{$N+1} = strides_{$N+1}) # a trick to set counts_($N+1) + @nloops($N, i, P, + d->(counts_d = strides_d), # PRE + d->(counts_{d+1} += strides_{d+1}), # POST + begin # BODY + sumc = sum(@ntuple $N d->counts_{d+1}) + @inbounds P[ind] = B[sumc+offset] + ind += 1 + end) + + return P + end + end +end + +## unique across dim + +# TODO: this doesn't fit into the new hashing scheme in any obvious way + +struct Prehashed + hash::UInt +end +hash(x::Prehashed) = x.hash + +""" + unique(A::AbstractArray; dims::Int) + +Return unique regions of `A` along dimension `dims`. + +# Examples +```jldoctest +julia> A = map(isodd, reshape(Vector(1:8), (2,2,2))) +2×2×2 Array{Bool,3}: +[:, :, 1] = + 1 1 + 0 0 + +[:, :, 2] = + 1 1 + 0 0 + +julia> unique(A) +2-element Array{Bool,1}: + 1 + 0 + +julia> unique(A, dims=2) +2×1×2 Array{Bool,3}: +[:, :, 1] = + 1 + 0 + +[:, :, 2] = + 1 + 0 + +julia> unique(A, dims=3) +2×2×1 Array{Bool,3}: +[:, :, 1] = + 1 1 + 0 0 +``` +""" +unique(A::AbstractArray; dims::Union{Colon,Integer} = :) = _unique_dims(A, dims) + +_unique_dims(A::AbstractArray, dims::Colon) = invoke(unique, Tuple{Any}, A) + +@generated function _unique_dims(A::AbstractArray{T,N}, dim::Integer) where {T,N} + quote + 1 <= dim <= $N || return copy(A) + hashes = zeros(UInt, axes(A, dim)) + + # Compute hash for each row + k = 0 + @nloops $N i A d->(if d == dim; k = i_d; end) begin + @inbounds hashes[k] = hash(hashes[k], hash((@nref $N A i))) + end + + # Collect index of first row for each hash + uniquerow = similar(Array{Int}, axes(A, dim)) + firstrow = Dict{Prehashed,Int}() + for k = axes(A, dim) + uniquerow[k] = get!(firstrow, Prehashed(hashes[k]), k) + end + uniquerows = collect(values(firstrow)) + + # Check for collisions + collided = falses(axes(A, dim)) + @inbounds begin + @nloops $N i A d->(if d == dim + k = i_d + j_d = uniquerow[k] + else + j_d = i_d + end) begin + if (@nref $N A j) != (@nref $N A i) + collided[k] = true + end + end + end + + if any(collided) + nowcollided = similar(BitArray, axes(A, dim)) + while any(collided) + # Collect index of first row for each collided hash + empty!(firstrow) + for j = axes(A, dim) + collided[j] || continue + uniquerow[j] = get!(firstrow, Prehashed(hashes[j]), j) + end + for v in values(firstrow) + push!(uniquerows, v) + end + + # Check for collisions + fill!(nowcollided, false) + @nloops $N i A d->begin + if d == dim + k = i_d + j_d = uniquerow[k] + (!collided[k] || j_d == k) && continue + else + j_d = i_d + end + end begin + if (@nref $N A j) != (@nref $N A i) + nowcollided[k] = true + end + end + (collided, nowcollided) = (nowcollided, collided) + end + end + + @nref $N A d->d == dim ? sort!(uniquerows) : (axes(A, d)) + end +end + +""" + extrema(A::AbstractArray; dims) -> Array{Tuple} + +Compute the minimum and maximum elements of an array over the given dimensions. + +# Examples +```jldoctest +julia> A = reshape(Vector(1:2:16), (2,2,2)) +2×2×2 Array{Int64,3}: +[:, :, 1] = + 1 5 + 3 7 + +[:, :, 2] = + 9 13 + 11 15 + +julia> extrema(A, dims = (1,2)) +1×1×2 Array{Tuple{Int64,Int64},3}: +[:, :, 1] = + (1, 7) + +[:, :, 2] = + (9, 15) +``` +""" +extrema(A::AbstractArray; dims = :) = _extrema_dims(identity, A, dims) + +""" + extrema(f, A::AbstractArray; dims) -> Array{Tuple} + +Compute the minimum and maximum of `f` applied to each element in the given dimensions +of `A`. + +!!! compat "Julia 1.2" + This method requires Julia 1.2 or later. +""" +extrema(f, A::AbstractArray; dims=:) = _extrema_dims(f, A, dims) + +_extrema_dims(f, A::AbstractArray, ::Colon) = _extrema_itr(f, A) + +function _extrema_dims(f, A::AbstractArray, dims) + sz = [size(A)...] + for d in dims + sz[d] = 1 + end + T = promote_op(f, eltype(A)) + B = Array{Tuple{T,T}}(undef, sz...) + return extrema!(f, B, A) +end + +@noinline function extrema!(f, B, A) + require_one_based_indexing(B, A) + sA = size(A) + sB = size(B) + for I in CartesianIndices(sB) + fAI = f(A[I]) + B[I] = (fAI, fAI) + end + Bmax = CartesianIndex(sB) + @inbounds @simd for I in CartesianIndices(sA) + J = min(Bmax,I) + BJ = B[J] + fAI = f(A[I]) + if fAI < BJ[1] + B[J] = (fAI, BJ[2]) + elseif fAI > BJ[2] + B[J] = (BJ[1], fAI) + end + end + return B +end +extrema!(B, A) = extrema!(identity, B, A) + +# Show for pairs() with Cartesian indices. Needs to be here rather than show.jl for bootstrap order +function Base.showarg(io::IO, r::Iterators.Pairs{<:Integer, <:Any, <:Any, T}, toplevel) where T <: Union{AbstractVector, Tuple} + print(io, "pairs(::$T)") +end +function Base.showarg(io::IO, r::Iterators.Pairs{<:CartesianIndex, <:Any, <:Any, T}, toplevel) where T <: AbstractArray + print(io, "pairs(::$T)") +end + +function Base.showarg(io::IO, r::Iterators.Pairs{<:CartesianIndex, <:Any, <:Any, T}, toplevel) where T<:AbstractVector + print(io, "pairs(IndexCartesian(), ::$T)") +end + +## sortslices + +""" + sortslices(A; dims, alg::Algorithm=DEFAULT_UNSTABLE, lt=isless, by=identity, rev::Bool=false, order::Ordering=Forward) + +Sort slices of an array `A`. The required keyword argument `dims` must +be either an integer or a tuple of integers. It specifies the +dimension(s) over which the slices are sorted. + +E.g., if `A` is a matrix, `dims=1` will sort rows, `dims=2` will sort columns. +Note that the default comparison function on one dimensional slices sorts +lexicographically. + +For the remaining keyword arguments, see the documentation of [`sort!`](@ref). + +# Examples +```jldoctest +julia> sortslices([7 3 5; -1 6 4; 9 -2 8], dims=1) # Sort rows +3×3 Array{Int64,2}: + -1 6 4 + 7 3 5 + 9 -2 8 + +julia> sortslices([7 3 5; -1 6 4; 9 -2 8], dims=1, lt=(x,y)->isless(x[2],y[2])) +3×3 Array{Int64,2}: + 9 -2 8 + 7 3 5 + -1 6 4 + +julia> sortslices([7 3 5; -1 6 4; 9 -2 8], dims=1, rev=true) +3×3 Array{Int64,2}: + 9 -2 8 + 7 3 5 + -1 6 4 + +julia> sortslices([7 3 5; 6 -1 -4; 9 -2 8], dims=2) # Sort columns +3×3 Array{Int64,2}: + 3 5 7 + -1 -4 6 + -2 8 9 + +julia> sortslices([7 3 5; 6 -1 -4; 9 -2 8], dims=2, alg=InsertionSort, lt=(x,y)->isless(x[2],y[2])) +3×3 Array{Int64,2}: + 5 3 7 + -4 -1 6 + 8 -2 9 + +julia> sortslices([7 3 5; 6 -1 -4; 9 -2 8], dims=2, rev=true) +3×3 Array{Int64,2}: + 7 5 3 + 6 -4 -1 + 9 8 -2 +``` + +# Higher dimensions + +`sortslices` extends naturally to higher dimensions. E.g., if `A` is a +a 2x2x2 array, `sortslices(A, dims=3)` will sort slices within the 3rd dimension, +passing the 2x2 slices `A[:, :, 1]` and `A[:, :, 2]` to the comparison function. +Note that while there is no default order on higher-dimensional slices, you may +use the `by` or `lt` keyword argument to specify such an order. + +If `dims` is a tuple, the order of the dimensions in `dims` is +relevant and specifies the linear order of the slices. E.g., if `A` is three +dimensional and `dims` is `(1, 2)`, the orderings of the first two dimensions +are re-arranged such such that the slices (of the remaining third dimension) are sorted. +If `dims` is `(2, 1)` instead, the same slices will be taken, +but the result order will be row-major instead. + +# Higher dimensional examples +``` +julia> A = permutedims(reshape([4 3; 2 1; 'A' 'B'; 'C' 'D'], (2, 2, 2)), (1, 3, 2)) +2×2×2 Array{Any,3}: +[:, :, 1] = + 4 3 + 2 1 + +[:, :, 2] = + 'A' 'B' + 'C' 'D' + +julia> sortslices(A, dims=(1,2)) +2×2×2 Array{Any,3}: +[:, :, 1] = + 1 3 + 2 4 + +[:, :, 2] = + 'D' 'B' + 'C' 'A' + +julia> sortslices(A, dims=(2,1)) +2×2×2 Array{Any,3}: +[:, :, 1] = + 1 2 + 3 4 + +[:, :, 2] = + 'D' 'C' + 'B' 'A' + +julia> sortslices(reshape([5; 4; 3; 2; 1], (1,1,5)), dims=3, by=x->x[1,1]) +1×1×5 Array{Int64,3}: +[:, :, 1] = + 1 + +[:, :, 2] = + 2 + +[:, :, 3] = + 3 + +[:, :, 4] = + 4 + +[:, :, 5] = + 5 +``` +""" +function sortslices(A::AbstractArray; dims::Union{Integer, Tuple{Vararg{Integer}}}, kws...) + _sortslices(A, Val{dims}(); kws...) +end + +# Works around inference's lack of ability to recognize partial constness +struct DimSelector{dims, T} + A::T +end +DimSelector{dims}(x::T) where {dims, T} = DimSelector{dims, T}(x) +(ds::DimSelector{dims, T})(i) where {dims, T} = i in dims ? axes(ds.A, i) : (:,) + +_negdims(n, dims) = filter(i->!(i in dims), 1:n) + +function compute_itspace(A, ::Val{dims}) where {dims} + negdims = _negdims(ndims(A), dims) + axs = Iterators.product(ntuple(DimSelector{dims}(A), ndims(A))...) + vec(permutedims(collect(axs), (dims..., negdims...))) +end + +function _sortslices(A::AbstractArray, d::Val{dims}; kws...) where dims + itspace = compute_itspace(A, d) + vecs = map(its->view(A, its...), itspace) + p = sortperm(vecs; kws...) + if ndims(A) == 2 && isa(dims, Integer) && isa(A, Array) + # At the moment, the performance of the generic version is subpar + # (about 5x slower). Hardcode a fast-path until we're able to + # optimize this. + return dims == 1 ? A[p, :] : A[:, p] + else + B = similar(A) + for (x, its) in zip(p, itspace) + B[its...] = vecs[x] + end + B + end +end + +getindex(b::Ref, ::CartesianIndex{0}) = getindex(b) +setindex!(b::Ref, x, ::CartesianIndex{0}) = setindex!(b, x) diff --git a/base/multimedia.jl b/base/multimedia.jl new file mode 100644 index 0000000..d34aa18 --- /dev/null +++ b/base/multimedia.jl @@ -0,0 +1,412 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +module Multimedia + +import .Base: show, print, convert, repr + +export AbstractDisplay, display, pushdisplay, popdisplay, displayable, redisplay, + MIME, @MIME_str, istextmime, + showable, TextDisplay + +########################################################################### +# We define a singleton type MIME{mime symbol} for each MIME type, so +# that Julia's dispatch and overloading mechanisms can be used to +# dispatch show and to add conversions for new types. + +""" + MIME + +A type representing a standard internet data format. "MIME" stands for +"Multipurpose Internet Mail Extensions", since the standard was originally +used to describe multimedia attachments to email messages. + +A `MIME` object can be passed as the second argument to [`show`](@ref) to +request output in that format. + +# Examples +```jldoctest +julia> show(stdout, MIME("text/plain"), "hi") +"hi" +``` +""" +struct MIME{mime} end + +""" + @MIME_str + +A convenience macro for writing [`MIME`](@ref) types, typically used when +adding methods to [`show`](@ref). +For example the syntax `show(io::IO, ::MIME"text/html", x::MyType) = ...` +could be used to define how to write an HTML representation of `MyType`. +""" +macro MIME_str(s) + :(MIME{$(Expr(:quote, Symbol(s)))}) +end + +# fallback text/plain representation of any type: +show(io::IO, ::MIME"text/plain", x) = show(io, x) + +MIME(s) = MIME{Symbol(s)}() +show(io::IO, ::MIME{mime}) where {mime} = print(io, "MIME type ", string(mime)) +print(io::IO, ::MIME{mime}) where {mime} = print(io, mime) + +########################################################################### +# For any type T one can define show(io, ::MIME"type", x::T) = ... +# in order to provide a way to export T as a given mime type. + +""" + showable(mime, x) + +Returns a boolean value indicating whether or not the object `x` can be written +as the given `mime` type. + +(By default, this is determined automatically by the existence of the +corresponding [`show`](@ref) method for `typeof(x)`. Some types provide custom `showable` +methods; for example, if the available MIME formats depend on the *value* of `x`.) + +# Examples +```jldoctest +julia> showable(MIME("text/plain"), rand(5)) +true + +julia> showable("img/png", rand(5)) +false +``` +""" +showable(::MIME{mime}, @nospecialize x) where {mime} = hasmethod(show, Tuple{IO, MIME{mime}, typeof(x)}) +showable(m::AbstractString, @nospecialize x) = showable(MIME(m), x) + +""" + show(io, mime, x) + +The [`display`](@ref) functions ultimately call `show` in order to write an object `x` as a +given `mime` type to a given I/O stream `io` (usually a memory buffer), if possible. In order +to provide a rich multimedia representation of a user-defined type `T`, it is only necessary +to define a new `show` method for `T`, via: `show(io, ::MIME"mime", x::T) = ...`, +where `mime` is a MIME-type string and the function body calls [`write`](@ref) (or similar) to write +that representation of `x` to `io`. (Note that the `MIME""` notation only supports +literal strings; to construct `MIME` types in a more flexible manner use +`MIME{Symbol("")}`.) + +For example, if you define a `MyImage` type and know how to write it to a PNG file, you +could define a function `show(io, ::MIME"image/png", x::MyImage) = ...` to allow +your images to be displayed on any PNG-capable `AbstractDisplay` (such as IJulia). As usual, be sure +to `import Base.show` in order to add new methods to the built-in Julia function +`show`. + +The default MIME type is `MIME"text/plain"`. There is a fallback definition for `text/plain` +output that calls `show` with 2 arguments. Therefore, this case should be handled by +defining a 2-argument `show(io::IO, x::MyType)` method. + +Technically, the `MIME"mime"` macro defines a singleton type for the given `mime` string, +which allows us to exploit Julia's dispatch mechanisms in determining how to display objects +of any given type. + +The first argument to `show` can be an [`IOContext`](@ref) specifying output format properties. +See [`IOContext`](@ref) for details. +""" +show(stream, mime, x) +show(io::IO, m::AbstractString, x) = show(io, MIME(m), x) + +""" + repr(mime, x; context=nothing) + +Returns an `AbstractString` or `Vector{UInt8}` containing the representation of +`x` in the requested `mime` type, as written by [`show(io, mime, x)`](@ref) (throwing a +[`MethodError`](@ref) if no appropriate `show` is available). An `AbstractString` is +returned for MIME types with textual representations (such as `"text/html"` or +`"application/postscript"`), whereas binary data is returned as +`Vector{UInt8}`. (The function `istextmime(mime)` returns whether or not Julia +treats a given `mime` type as text.) + +The optional keyword argument `context` can be set to `:key=>value` pair +or an `IO` or [`IOContext`](@ref) object whose attributes are used for the I/O +stream passed to `show`. + +As a special case, if `x` is an `AbstractString` (for textual MIME types) or a +`Vector{UInt8}` (for binary MIME types), the `repr` function assumes that +`x` is already in the requested `mime` format and simply returns `x`. This +special case does not apply to the `"text/plain"` MIME type. This is useful so +that raw data can be passed to `display(m::MIME, x)`. + +In particular, `repr("text/plain", x)` is typically a "pretty-printed" version +of `x` designed for human consumption. See also [`repr(x)`](@ref) to instead +return a string corresponding to [`show(x)`](@ref) that may be closer to how +the value of `x` would be entered in Julia. + +# Examples +```jldoctest +julia> A = [1 2; 3 4]; + +julia> repr("text/plain", A) +"2×2 Array{Int64,2}:\\n 1 2\\n 3 4" +``` +""" +repr(m::MIME, x; context=nothing) = istextmime(m) ? _textrepr(m, x, context) : _binrepr(m, x, context) +repr(m::AbstractString, x; context=nothing) = repr(MIME(m), x; context=context) + +# strings are shown escaped for text/plain +_textrepr(m::MIME, x, context) = String(__binrepr(m, x, context)) +_textrepr(::MIME, x::AbstractString, context) = x +_textrepr(m::MIME"text/plain", x::AbstractString, context) = String(__binrepr(m, x, context)) + + +function __binrepr(m::MIME, x, context) + s = IOBuffer() + if context === nothing + show(s, m, x) + else + show(IOContext(s, context), m, x) + end + take!(s) +end +_binrepr(m::MIME, x, context) = __binrepr(m, x, context) +_binrepr(m::MIME, x::Vector{UInt8}, context) = x + +""" + istextmime(m::MIME) + +Determine whether a MIME type is text data. MIME types are assumed to be binary +data except for a set of types known to be text data (possibly Unicode). + +# Examples +```jldoctest +julia> istextmime(MIME("text/plain")) +true + +julia> istextmime(MIME("img/png")) +false +``` +""" +istextmime(m::MIME) = startswith(string(m), "text/") +istextmime(m::AbstractString) = istextmime(MIME(m)) + +for mime in ["application/atom+xml", "application/ecmascript", + "application/javascript", "application/julia", + "application/json", "application/postscript", + "application/rdf+xml", "application/rss+xml", + "application/x-latex", "application/xhtml+xml", "application/xml", + "application/xml-dtd", "image/svg+xml", "model/vrml", + "model/x3d+vrml", "model/x3d+xml"] + global istextmime(::MIME{Symbol(mime)}) = true +end + +########################################################################### +# We have an abstract AbstractDisplay class that can be subclassed in order to +# define new rich-display output devices. A typical subclass should +# overload display(d::AbstractDisplay, m::MIME, x) for supported MIME types m, +# (typically using show, repr, ..., to get the MIME +# representation of x) and should also overload display(d::AbstractDisplay, x) +# to display x in whatever MIME type is preferred by the AbstractDisplay and +# is writable by x. display(..., x) should throw a MethodError if x +# cannot be displayed. The return value of display(...) is up to the +# AbstractDisplay type. + +""" + AbstractDisplay + +Abstract supertype for rich display output devices. [`TextDisplay`](@ref) is a subtype +of this. +""" +abstract type AbstractDisplay end + +# it is convenient to accept strings instead of ::MIME +display(d::AbstractDisplay, mime::AbstractString, @nospecialize x) = display(d, MIME(mime), x) +display(mime::AbstractString, @nospecialize x) = display(MIME(mime), x) + +""" + displayable(mime) -> Bool + displayable(d::AbstractDisplay, mime) -> Bool + +Returns a boolean value indicating whether the given `mime` type (string) is displayable by +any of the displays in the current display stack, or specifically by the display `d` in the +second variant. +""" +displayable(d::AbstractDisplay, mime::AbstractString) = displayable(d, MIME(mime)) +displayable(mime::AbstractString) = displayable(MIME(mime)) + +# simplest display, which only knows how to display text/plain + +""" + TextDisplay(io::IO) + +Returns a `TextDisplay <: AbstractDisplay`, which displays any object as the text/plain MIME type +(by default), writing the text representation to the given I/O stream. (This is how +objects are printed in the Julia REPL.) +""" +struct TextDisplay <: AbstractDisplay + io::IO +end +display(d::TextDisplay, M::MIME"text/plain", @nospecialize x) = show(d.io, M, x) +display(d::TextDisplay, @nospecialize x) = display(d, MIME"text/plain"(), x) + +# if you explicitly call display("text/foo", x), it should work on a TextDisplay: +displayable(d::TextDisplay, M::MIME) = istextmime(M) +function display(d::TextDisplay, M::MIME, @nospecialize x) + displayable(d, M) || throw(MethodError(display, (d, M, x))) + show(d.io, M, x) +end + +import Base: close, flush +flush(d::TextDisplay) = flush(d.io) +close(d::TextDisplay) = close(d.io) + +########################################################################### +# We keep a stack of Displays, and calling display(x) uses the topmost +# AbstractDisplay that is capable of displaying x (doesn't throw an error) + +const displays = AbstractDisplay[] + +""" + pushdisplay(d::AbstractDisplay) + +Pushes a new display `d` on top of the global display-backend stack. Calling `display(x)` or +`display(mime, x)` will display `x` on the topmost compatible backend in the stack (i.e., +the topmost backend that does not throw a [`MethodError`](@ref)). +""" +function pushdisplay(d::AbstractDisplay) + global displays + push!(displays, d) +end + +""" + popdisplay() + popdisplay(d::AbstractDisplay) + +Pop the topmost backend off of the display-backend stack, or the topmost copy of `d` in the +second variant. +""" +popdisplay() = pop!(displays) +function popdisplay(d::AbstractDisplay) + for i = length(displays):-1:1 + if d == displays[i] + return splice!(displays, i) + end + end + throw(KeyError(d)) +end +function reinit_displays() + empty!(displays) + pushdisplay(TextDisplay(stdout)) +end + +xdisplayable(D::AbstractDisplay, @nospecialize args...) = applicable(display, D, args...) + +""" + display(x) + display(d::AbstractDisplay, x) + display(mime, x) + display(d::AbstractDisplay, mime, x) + +AbstractDisplay `x` using the topmost applicable display in the display stack, typically using the +richest supported multimedia output for `x`, with plain-text [`stdout`](@ref) output as a fallback. +The `display(d, x)` variant attempts to display `x` on the given display `d` only, throwing +a [`MethodError`](@ref) if `d` cannot display objects of this type. + +In general, you cannot assume that `display` output goes to `stdout` (unlike [`print(x)`](@ref) or +[`show(x)`](@ref)). For example, `display(x)` may open up a separate window with an image. +`display(x)` means "show `x` in the best way you can for the current output device(s)." +If you want REPL-like text output that is guaranteed to go to `stdout`, use +[`show(stdout, "text/plain", x)`](@ref) instead. + +There are also two variants with a `mime` argument (a MIME type string, such as +`"image/png"`), which attempt to display `x` using the requested MIME type *only*, throwing +a `MethodError` if this type is not supported by either the display(s) or by `x`. With these +variants, one can also supply the "raw" data in the requested MIME type by passing +`x::AbstractString` (for MIME types with text-based storage, such as text/html or +application/postscript) or `x::Vector{UInt8}` (for binary MIME types). +""" +function display(@nospecialize x) + for i = length(displays):-1:1 + if xdisplayable(displays[i], x) + try + return display(displays[i], x) + catch e + isa(e, MethodError) && e.f in (display, show) || + rethrow() + end + end + end + throw(MethodError(display, (x,))) +end + +function display(m::MIME, @nospecialize x) + for i = length(displays):-1:1 + if xdisplayable(displays[i], m, x) + try + return display(displays[i], m, x) + catch e + isa(e, MethodError) && e.f == display || + rethrow() + end + end + end + throw(MethodError(display, (m, x))) +end + +displayable(d::D, ::MIME{mime}) where {D<:AbstractDisplay,mime} = + hasmethod(display, Tuple{D,MIME{mime},Any}) + +function displayable(m::MIME) + for d in displays + displayable(d, m) && return true + end + return false +end + +########################################################################### +# The redisplay method can be overridden by a AbstractDisplay in order to +# update an existing display (instead of, for example, opening a new +# window), and is used by the IJulia interface to defer display +# until the next interactive prompt. This is especially useful +# for Matlab/Pylab-like stateful plotting interfaces, where +# a plot is created and then modified many times (xlabel, title, etc.). + +""" + redisplay(x) + redisplay(d::AbstractDisplay, x) + redisplay(mime, x) + redisplay(d::AbstractDisplay, mime, x) + +By default, the `redisplay` functions simply call [`display`](@ref). +However, some display backends may override `redisplay` to modify an existing +display of `x` (if any). +Using `redisplay` is also a hint to the backend that `x` may be redisplayed +several times, and the backend may choose to defer the display until +(for example) the next interactive prompt. +""" +function redisplay(@nospecialize x) + for i = length(displays):-1:1 + if xdisplayable(displays[i], x) + try + return redisplay(displays[i], x) + catch e + isa(e, MethodError) && e.f in (redisplay, display, show) || + rethrow() + end + end + end + throw(MethodError(redisplay, (x,))) +end + +function redisplay(m::Union{MIME,AbstractString}, @nospecialize x) + for i = length(displays):-1:1 + if xdisplayable(displays[i], m, x) + try + return redisplay(displays[i], m, x) + catch e + isa(e, MethodError) && e.f in (redisplay, display) || + rethrow() + end + end + end + throw(MethodError(redisplay, (m, x))) +end + +# default redisplay is simply to call display +redisplay(d::AbstractDisplay, @nospecialize x) = display(d, x) +redisplay(d::AbstractDisplay, m::Union{MIME,AbstractString}, @nospecialize x) = display(d, m, x) + +########################################################################### + +end # module diff --git a/base/multinverses.jl b/base/multinverses.jl new file mode 100644 index 0000000..319072e --- /dev/null +++ b/base/multinverses.jl @@ -0,0 +1,159 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +module MultiplicativeInverses + +import Base: div, divrem, rem, unsigned +using Base: IndexLinear, IndexCartesian, tail +export multiplicativeinverse + +unsigned(::Type{Int8}) = UInt8 +unsigned(::Type{Int16}) = UInt16 +unsigned(::Type{Int32}) = UInt32 +unsigned(::Type{Int64}) = UInt64 +unsigned(::Type{Int128}) = UInt128 +unsigned(::Type{T}) where {T<:Unsigned} = T + +abstract type MultiplicativeInverse{T} end + +# Computes integer division by a constant using multiply, add, and bitshift. + +# The idea here is to compute floor(n/d) as floor(m*n/2^p) and then +# implement division by 2^p as a right bitshift. The trick is finding +# m (the "magic number") and p. Roughly speaking, one can think of this as +# floor(n/d) = floor((n/2^p) * (2^p/d)) +# so that m is effectively 2^p/d. +# +# A few examples are illustrative: +# Division of Int32 by 3: +# floor((2^32+2)/3 * n/2^32) = floor(n/3 + 2n/(3*2^32)) +# The correction term, 2n/(3*2^32), is strictly less than 1/3 for any +# nonnegative n::Int32, so this divides any nonnegative Int32 by 3. +# (When n < 0, we add 1, and one can show that this computes +# ceil(n/d) = -floor(abs(n)/d).) +# +# Division of Int32 by 5 uses a magic number (2^33+3)/5 and then +# right-shifts by 33 rather than 32. Consequently, the size of the +# shift depends on the specific denominator. +# +# Division of Int32 by 7 would be problematic, because a viable magic +# number of (2^34+5)/7 is too big to represent as an Int32 (the +# unsigned representation needs 32 bits). We can exploit wrap-around +# and use (2^34+5)/7 - 2^32 (an Int32 < 0), and then correct the +# 64-bit product with an add (the `addmul` field below). +# +# Further details can be found in Hacker's Delight, Chapter 10. + +struct SignedMultiplicativeInverse{T<:Signed} <: MultiplicativeInverse{T} + divisor::T + multiplier::T + addmul::Int8 + shift::UInt8 + + function SignedMultiplicativeInverse{T}(d::T) where T<:Signed + d == 0 && throw(ArgumentError("cannot compute magic for d == $d")) + signedmin = unsigned(typemin(T)) + UT = unsigned(T) + + # Algorithm from Hacker's Delight, section 10-4 + ad = unsigned(abs(d)) + t = signedmin + signbit(d) + anc = t - one(UT) - rem(t, ad) # absolute value of nc + p = sizeof(d)*8 - 1 + q1, r1 = divrem(signedmin, anc) + q2, r2 = divrem(signedmin, ad) + while true + p += 1 # loop until we find a satisfactory p + # update q1, r1 = divrem(2^p, abs(nc)) + q1 = q1<<1 + r1 = r1<<1 + if r1 >= anc # must be unsigned comparison + q1 += one(UT) + r1 -= anc + end + # update q2, r2 = divrem(2^p, abs(d)) + q2 = q2<<1 + r2 = r2<<1 + if r2 >= ad + q2 += one(UT) + r2 -= ad + end + delta = ad - r2 + (q1 < delta || (q1 == delta && r1 == 0)) || break + end + + m = flipsign((q2 + one(UT)) % T, d) # resulting magic number + s = p - sizeof(d)*8 # resulting shift + new(d, m, d > 0 && m < 0 ? Int8(1) : d < 0 && m > 0 ? Int8(-1) : Int8(0), UInt8(s)) + end +end +SignedMultiplicativeInverse(x::Signed) = SignedMultiplicativeInverse{typeof(x)}(x) + +struct UnsignedMultiplicativeInverse{T<:Unsigned} <: MultiplicativeInverse{T} + divisor::T + multiplier::T + add::Bool + shift::UInt8 + + function UnsignedMultiplicativeInverse{T}(d::T) where T<:Unsigned + d == 0 && throw(ArgumentError("cannot compute magic for d == $d")) + u2 = convert(T, 2) + add = false + signedmin = one(d) << (sizeof(d)*8-1) + signedmax = signedmin - one(T) + allones = (zero(d) - 1) % T + + nc = allones - rem(convert(T, allones - d), d) + p = 8*sizeof(d) - 1 + q1, r1 = divrem(signedmin, nc) + q2, r2 = divrem(signedmax, d) + while true + p += 1 + if r1 >= convert(T, nc - r1) + q1 = q1 + q1 + one(T) + r1 = r1 + r1 - nc + else + q1 = q1 + q1 + r1 = r1 + r1 + end + if convert(T, r2 + one(T)) >= convert(T, d - r2) + add |= q2 >= signedmax + q2 = q2 + q2 + one(T) + r2 = r2 + r2 + one(T) - d + else + add |= q2 >= signedmin + q2 = q2 + q2 + r2 = r2 + r2 + one(T) + end + delta = d - one(T) - r2 + (p < sizeof(d)*16 && (q1 < delta || (q1 == delta && r1 == 0))) || break + end + m = q2 + one(T) # resulting magic number + s = p - sizeof(d)*8 - add # resulting shift + new(d, m, add, s % UInt8) + end +end +UnsignedMultiplicativeInverse(x::Unsigned) = UnsignedMultiplicativeInverse{typeof(x)}(x) + +function div(a::T, b::SignedMultiplicativeInverse{T}) where T + x = ((widen(a)*b.multiplier) >>> (sizeof(a)*8)) % T + x += (a*b.addmul) % T + ifelse(abs(b.divisor) == 1, a*b.divisor, (signbit(x) + (x >> b.shift)) % T) +end +function div(a::T, b::UnsignedMultiplicativeInverse{T}) where T + x = ((widen(a)*b.multiplier) >>> (sizeof(a)*8)) % T + x = ifelse(b.add, convert(T, convert(T, (convert(T, a - x) >>> 1)) + x), x) + ifelse(b.divisor == 1, a, x >>> b.shift) +end + +rem(a::T, b::MultiplicativeInverse{T}) where {T} = + a - div(a, b)*b.divisor + +function divrem(a::T, b::MultiplicativeInverse{T}) where T + d = div(a, b) + (d, a - d*b.divisor) +end + +multiplicativeinverse(x::Signed) = SignedMultiplicativeInverse(x) +multiplicativeinverse(x::Unsigned) = UnsignedMultiplicativeInverse(x) + +end diff --git a/base/namedtuple.jl b/base/namedtuple.jl new file mode 100644 index 0000000..9291a1a --- /dev/null +++ b/base/namedtuple.jl @@ -0,0 +1,314 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" + NamedTuple + +`NamedTuple`s are, as their name suggests, named [`Tuple`](@ref)s. That is, they're a +tuple-like collection of values, where each entry has a unique name, represented as a +[`Symbol`](@ref). Like `Tuple`s, `NamedTuple`s are immutable; neither the names nor the values +can be modified in place after construction. + +Accessing the value associated with a name in a named tuple can be done using field +access syntax, e.g. `x.a`, or using [`getindex`](@ref), e.g. `x[:a]`. A tuple of the +names can be obtained using [`keys`](@ref), and a tuple of the values can be obtained +using [`values`](@ref). + +!!! note + Iteration over `NamedTuple`s produces the *values* without the names. (See example + below.) To iterate over the name-value pairs, use the [`pairs`](@ref) function. + +# Examples +```jldoctest +julia> x = (a=1, b=2) +(a = 1, b = 2) + +julia> x.a +1 + +julia> x[:a] +1 + +julia> keys(x) +(:a, :b) + +julia> values(x) +(1, 2) + +julia> collect(x) +2-element Array{Int64,1}: + 1 + 2 + +julia> collect(pairs(x)) +2-element Array{Pair{Symbol,Int64},1}: + :a => 1 + :b => 2 +``` + +In a similar fashion as to how one can define keyword arguments programmatically, +a named tuple can be created by giving a pair `name::Symbol => value` or splatting +an iterator yielding such pairs after a semicolon inside a tuple literal: + +```jldoctest +julia> (; :a => 1) +(a = 1,) + +julia> keys = (:a, :b, :c); values = (1, 2, 3); + +julia> (; zip(keys, values)...) +(a = 1, b = 2, c = 3) +``` +""" +Core.NamedTuple + +if nameof(@__MODULE__) === :Base + +""" + NamedTuple{names,T}(args::Tuple) + +Construct a named tuple with the given `names` (a tuple of Symbols) and field types `T` +(a `Tuple` type) from a tuple of values. +""" +@eval function NamedTuple{names,T}(args::Tuple) where {names, T <: Tuple} + if length(args) != length(names::Tuple) + throw(ArgumentError("Wrong number of arguments to named tuple constructor.")) + end + # Note T(args) might not return something of type T; e.g. + # Tuple{Type{Float64}}((Float64,)) returns a Tuple{DataType} + $(Expr(:splatnew, :(NamedTuple{names,T}), :(T(args)))) +end + +""" + NamedTuple{names}(nt::NamedTuple) + +Construct a named tuple by selecting fields in `names` (a tuple of Symbols) from +another named tuple. +""" +function NamedTuple{names}(nt::NamedTuple) where {names} + if @generated + types = Tuple{(fieldtype(nt, n) for n in names)...} + Expr(:new, :(NamedTuple{names, $types}), Any[ :(getfield(nt, $(QuoteNode(n)))) for n in names ]...) + else + types = Tuple{(fieldtype(typeof(nt), n) for n in names)...} + NamedTuple{names, types}(Tuple(getfield(nt, n) for n in names)) + end +end + +NamedTuple{names, T}(itr) where {names, T <: Tuple} = NamedTuple{names, T}(T(itr)) +NamedTuple{names}(itr) where {names} = NamedTuple{names}(Tuple(itr)) + +end # if Base + +length(t::NamedTuple) = nfields(t) +iterate(t::NamedTuple, iter=1) = iter > nfields(t) ? nothing : (getfield(t, iter), iter + 1) +firstindex(t::NamedTuple) = 1 +lastindex(t::NamedTuple) = nfields(t) +getindex(t::NamedTuple, i::Int) = getfield(t, i) +getindex(t::NamedTuple, i::Symbol) = getfield(t, i) +indexed_iterate(t::NamedTuple, i::Int, state=1) = (getfield(t, i), i+1) +isempty(::NamedTuple{()}) = true +isempty(::NamedTuple) = false +empty(::NamedTuple) = NamedTuple() + +convert(::Type{NamedTuple{names,T}}, nt::NamedTuple{names,T}) where {names,T<:Tuple} = nt +convert(::Type{NamedTuple{names}}, nt::NamedTuple{names}) where {names} = nt + +function convert(::Type{NamedTuple{names,T}}, nt::NamedTuple{names}) where {names,T<:Tuple} + NamedTuple{names,T}(T(nt)) +end + +if nameof(@__MODULE__) === :Base + Tuple(nt::NamedTuple) = (nt...,) + (::Type{T})(nt::NamedTuple) where {T <: Tuple} = convert(T, Tuple(nt)) +end + +function show(io::IO, t::NamedTuple) + n = nfields(t) + for i = 1:n + # if field types aren't concrete, show full type + if typeof(getfield(t, i)) !== fieldtype(typeof(t), i) + show(io, typeof(t)) + print(io, "(") + show(io, Tuple(t)) + print(io, ")") + return + end + end + if n == 0 + print(io, "NamedTuple()") + else + typeinfo = get(io, :typeinfo, Any) + print(io, "(") + for i = 1:n + print(io, fieldname(typeof(t),i), " = ") + show(IOContext(io, :typeinfo => + t isa typeinfo <: NamedTuple ? fieldtype(typeinfo, i) : Any), + getfield(t, i)) + if n == 1 + print(io, ",") + elseif i < n + print(io, ", ") + end + end + print(io, ")") + end +end + +eltype(::Type{NamedTuple{names,T}}) where {names,T} = eltype(T) + +==(a::NamedTuple{n}, b::NamedTuple{n}) where {n} = Tuple(a) == Tuple(b) +==(a::NamedTuple, b::NamedTuple) = false + +isequal(a::NamedTuple{n}, b::NamedTuple{n}) where {n} = isequal(Tuple(a), Tuple(b)) +isequal(a::NamedTuple, b::NamedTuple) = false + +_nt_names(::NamedTuple{names}) where {names} = names +_nt_names(::Type{T}) where {names,T<:NamedTuple{names}} = names + +hash(x::NamedTuple, h::UInt) = xor(objectid(_nt_names(x)), hash(Tuple(x), h)) + +isless(a::NamedTuple{n}, b::NamedTuple{n}) where {n} = isless(Tuple(a), Tuple(b)) +# TODO: case where one argument's names are a prefix of the other's + +same_names(::NamedTuple{names}...) where {names} = true +same_names(::NamedTuple...) = false + +# NOTE: this method signature makes sure we don't define map(f) +function map(f, nt::NamedTuple{names}, nts::NamedTuple...) where names + if !same_names(nt, nts...) + throw(ArgumentError("Named tuple names do not match.")) + end + NamedTuple{names}(map(f, map(Tuple, (nt, nts...))...)) +end + +# a version of `in` for the older world these generated functions run in +@pure function sym_in(x::Symbol, itr::Tuple{Vararg{Symbol}}) + for y in itr + y === x && return true + end + return false +end + +@pure function merge_names(an::Tuple{Vararg{Symbol}}, bn::Tuple{Vararg{Symbol}}) + names = Symbol[an...] + for n in bn + if !sym_in(n, an) + push!(names, n) + end + end + (names...,) +end + +@pure function merge_types(names::Tuple{Vararg{Symbol}}, a::Type{<:NamedTuple}, b::Type{<:NamedTuple}) + bn = _nt_names(b) + Tuple{Any[ fieldtype(sym_in(n, bn) ? b : a, n) for n in names ]...} +end + +""" + merge(a::NamedTuple, bs::NamedTuple...) + +Construct a new named tuple by merging two or more existing ones, in a left-associative +manner. Merging proceeds left-to-right, between pairs of named tuples, and so the order of fields +present in both the leftmost and rightmost named tuples take the same position as they are found in the +leftmost named tuple. However, values are taken from matching fields in the rightmost named tuple that +contains that field. Fields present in only the rightmost named tuple of a pair are appended at the end. +A fallback is implemented for when only a single named tuple is supplied, +with signature `merge(a::NamedTuple)`. + +!!! compat "Julia 1.1" + Merging 3 or more `NamedTuple` requires at least Julia 1.1. + +# Examples +```jldoctest +julia> merge((a=1, b=2, c=3), (b=4, d=5)) +(a = 1, b = 4, c = 3, d = 5) +``` + +```jldoctest +julia> merge((a=1, b=2), (b=3, c=(d=1,)), (c=(d=2,),)) +(a = 1, b = 3, c = (d = 2,)) +``` +""" +function merge(a::NamedTuple{an}, b::NamedTuple{bn}) where {an, bn} + if @generated + names = merge_names(an, bn) + types = merge_types(names, a, b) + vals = Any[ :(getfield($(sym_in(n, bn) ? :b : :a), $(QuoteNode(n)))) for n in names ] + :( NamedTuple{$names,$types}(($(vals...),)) ) + else + names = merge_names(an, bn) + types = merge_types(names, typeof(a), typeof(b)) + NamedTuple{names,types}(map(n->getfield(sym_in(n, bn) ? b : a, n), names)) + end +end + +merge(a::NamedTuple{()}, b::NamedTuple) = b + +merge(a::NamedTuple, b::Iterators.Pairs{<:Any,<:Any,<:Any,<:NamedTuple}) = merge(a, b.data) + +merge(a::NamedTuple, b::NamedTuple, cs::NamedTuple...) = merge(merge(a, b), cs...) + +merge(a::NamedTuple) = a + +""" + merge(a::NamedTuple, iterable) + +Interpret an iterable of key-value pairs as a named tuple, and perform a merge. + +```jldoctest +julia> merge((a=1, b=2, c=3), [:b=>4, :d=>5]) +(a = 1, b = 4, c = 3, d = 5) +``` +""" +function merge(a::NamedTuple, itr) + names = Symbol[] + vals = Any[] + inds = IdDict{Symbol,Int}() + for (k::Symbol, v) in itr + oldind = get(inds, k, 0) + if oldind > 0 + vals[oldind] = v + else + push!(names, k) + push!(vals, v) + inds[k] = length(names) + end + end + merge(a, NamedTuple{(names...,)}((vals...,))) +end + +keys(nt::NamedTuple{names}) where {names} = names +values(nt::NamedTuple) = Tuple(nt) +haskey(nt::NamedTuple, key::Union{Integer, Symbol}) = isdefined(nt, key) +get(nt::NamedTuple, key::Union{Integer, Symbol}, default) = haskey(nt, key) ? getfield(nt, key) : default +get(f::Callable, nt::NamedTuple, key::Union{Integer, Symbol}) = haskey(nt, key) ? getfield(nt, key) : f() +tail(t::NamedTuple{names}) where names = NamedTuple{tail(names)}(t) + +@pure function diff_names(an::Tuple{Vararg{Symbol}}, bn::Tuple{Vararg{Symbol}}) + names = Symbol[] + for n in an + if !sym_in(n, bn) + push!(names, n) + end + end + (names...,) +end + +""" + structdiff(a::NamedTuple{an}, b::Union{NamedTuple{bn},Type{NamedTuple{bn}}}) where {an,bn} + +Construct a copy of named tuple `a`, except with fields that exist in `b` removed. +`b` can be a named tuple, or a type of the form `NamedTuple{field_names}`. +""" +function structdiff(a::NamedTuple{an}, b::Union{NamedTuple{bn}, Type{NamedTuple{bn}}}) where {an, bn} + if @generated + names = diff_names(an, bn) + types = Tuple{Any[ fieldtype(a, n) for n in names ]...} + vals = Any[ :(getfield(a, $(QuoteNode(n)))) for n in names ] + :( NamedTuple{$names,$types}(($(vals...),)) ) + else + names = diff_names(an, bn) + types = Tuple{Any[ fieldtype(typeof(a), n) for n in names ]...} + NamedTuple{names,types}(map(n->getfield(a, n), names)) + end +end diff --git a/base/ntuple.jl b/base/ntuple.jl new file mode 100644 index 0000000..1fdbc01 --- /dev/null +++ b/base/ntuple.jl @@ -0,0 +1,69 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# `ntuple`, for constructing tuples of a given length + +""" + ntuple(f::Function, n::Integer) + +Create a tuple of length `n`, computing each element as `f(i)`, +where `i` is the index of the element. + +# Examples +```jldoctest +julia> ntuple(i -> 2*i, 4) +(2, 4, 6, 8) +``` +""" +function ntuple(f::F, n::Integer) where F + t = n == 0 ? () : + n == 1 ? (f(1),) : + n == 2 ? (f(1), f(2)) : + n == 3 ? (f(1), f(2), f(3)) : + n == 4 ? (f(1), f(2), f(3), f(4)) : + n == 5 ? (f(1), f(2), f(3), f(4), f(5)) : + n == 6 ? (f(1), f(2), f(3), f(4), f(5), f(6)) : + n == 7 ? (f(1), f(2), f(3), f(4), f(5), f(6), f(7)) : + n == 8 ? (f(1), f(2), f(3), f(4), f(5), f(6), f(7), f(8)) : + n == 9 ? (f(1), f(2), f(3), f(4), f(5), f(6), f(7), f(8), f(9)) : + n == 10 ? (f(1), f(2), f(3), f(4), f(5), f(6), f(7), f(8), f(9), f(10)) : + _ntuple(f, n) + return t +end + +function _ntuple(f, n) + @_noinline_meta + (n >= 0) || throw(ArgumentError(string("tuple length should be ≥ 0, got ", n))) + ([f(i) for i = 1:n]...,) +end + +# inferrable ntuple (enough for bootstrapping) +ntuple(f, ::Val{0}) = () +ntuple(f, ::Val{1}) = (@_inline_meta; (f(1),)) +ntuple(f, ::Val{2}) = (@_inline_meta; (f(1), f(2))) +ntuple(f, ::Val{3}) = (@_inline_meta; (f(1), f(2), f(3))) + +@inline function ntuple(f::F, ::Val{N}) where {F,N} + N::Int + (N >= 0) || throw(ArgumentError(string("tuple length should be ≥ 0, got ", N))) + if @generated + quote + @nexprs $N i -> t_i = f(i) + @ncall $N tuple t + end + else + Tuple(f(i) for i = 1:N) + end +end + +@inline function fill_to_length(t::Tuple, val, ::Val{_N}) where {_N} + M = length(t) + N = _N::Int + M > N && throw(ArgumentError("input tuple of length $M, requested $N")) + if @generated + quote + (t..., $(fill(:val, (_N::Int) - length(t.parameters))...)) + end + else + (t..., fill(val, N-M)...) + end +end diff --git a/base/number.jl b/base/number.jl new file mode 100644 index 0000000..9ad5f2a --- /dev/null +++ b/base/number.jl @@ -0,0 +1,344 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## generic operations on numbers ## + +# Numbers are convertible +convert(::Type{T}, x::T) where {T<:Number} = x +convert(::Type{T}, x::Number) where {T<:Number} = T(x) + +""" + isinteger(x) -> Bool + +Test whether `x` is numerically equal to some integer. + +# Examples +```jldoctest +julia> isinteger(4.0) +true +``` +""" +isinteger(x::Integer) = true + +""" + iszero(x) + +Return `true` if `x == zero(x)`; if `x` is an array, this checks whether +all of the elements of `x` are zero. + +# Examples +```jldoctest +julia> iszero(0.0) +true + +julia> iszero([1, 9, 0]) +false + +julia> iszero([false, 0, 0]) +true +``` +""" +iszero(x) = x == zero(x) # fallback method + +""" + isone(x) + +Return `true` if `x == one(x)`; if `x` is an array, this checks whether +`x` is an identity matrix. + +# Examples +```jldoctest +julia> isone(1.0) +true + +julia> isone([1 0; 0 2]) +false + +julia> isone([1 0; 0 true]) +true +``` +""" +isone(x) = x == one(x) # fallback method + +size(x::Number) = () +size(x::Number, d::Integer) = d < 1 ? throw(BoundsError()) : 1 +axes(x::Number) = () +axes(x::Number, d::Integer) = d < 1 ? throw(BoundsError()) : OneTo(1) +eltype(::Type{T}) where {T<:Number} = T +ndims(x::Number) = 0 +ndims(::Type{<:Number}) = 0 +length(x::Number) = 1 +firstindex(x::Number) = 1 +lastindex(x::Number) = 1 +IteratorSize(::Type{<:Number}) = HasShape{0}() +keys(::Number) = OneTo(1) + +getindex(x::Number) = x +function getindex(x::Number, i::Integer) + @_inline_meta + @boundscheck i == 1 || throw(BoundsError()) + x +end +function getindex(x::Number, I::Integer...) + @_inline_meta + @boundscheck all(isone, I) || throw(BoundsError()) + x +end +first(x::Number) = x +last(x::Number) = x +copy(x::Number) = x # some code treats numbers as collection-like + +""" + divrem(x, y) + +The quotient and remainder from Euclidean division. Equivalent to `(div(x,y), rem(x,y))` or +`(x÷y, x%y)`. + +# Examples +```jldoctest +julia> divrem(3,7) +(0, 3) + +julia> divrem(7,3) +(2, 1) +``` +""" +divrem(x,y) = (div(x,y),rem(x,y)) + +""" + fldmod(x, y) + +The floored quotient and modulus after division. Equivalent to `(fld(x,y), mod(x,y))`. +""" +fldmod(x,y) = (fld(x,y),mod(x,y)) + +""" + signbit(x) + +Returns `true` if the value of the sign of `x` is negative, otherwise `false`. + +# Examples +```jldoctest +julia> signbit(-4) +true + +julia> signbit(5) +false + +julia> signbit(5.5) +false + +julia> signbit(-4.1) +true +``` +""" +signbit(x::Real) = x < 0 + +""" + sign(x) + +Return zero if `x==0` and ``x/|x|`` otherwise (i.e., ±1 for real `x`). +""" +sign(x::Number) = x == 0 ? x/abs(oneunit(x)) : x/abs(x) +sign(x::Real) = ifelse(x < 0, oftype(one(x),-1), ifelse(x > 0, one(x), typeof(one(x))(x))) +sign(x::Unsigned) = ifelse(x > 0, one(x), oftype(one(x),0)) +abs(x::Real) = ifelse(signbit(x), -x, x) + +""" + abs2(x) + +Squared absolute value of `x`. + +# Examples +```jldoctest +julia> abs2(-3) +9 +``` +""" +abs2(x::Real) = x*x + +""" + flipsign(x, y) + +Return `x` with its sign flipped if `y` is negative. For example `abs(x) = flipsign(x,x)`. + +# Examples +```jldoctest +julia> flipsign(5, 3) +5 + +julia> flipsign(5, -3) +-5 +``` +""" +flipsign(x::Real, y::Real) = ifelse(signbit(y), -x, +x) # the + is for type-stability on Bool + +""" + copysign(x, y) -> z + +Return `z` which has the magnitude of `x` and the same sign as `y`. + +# Examples +```jldoctest +julia> copysign(1, -2) +-1 + +julia> copysign(-1, 2) +1 +``` +""" +copysign(x::Real, y::Real) = ifelse(signbit(x)!=signbit(y), -x, +x) + +conj(x::Real) = x +transpose(x::Number) = x +adjoint(x::Number) = conj(x) +angle(z::Real) = atan(zero(z), z) + +""" + inv(x) + +Return the multiplicative inverse of `x`, such that `x*inv(x)` or `inv(x)*x` +yields [`one(x)`](@ref) (the multiplicative identity) up to roundoff errors. + +If `x` is a number, this is essentially the same as `one(x)/x`, but for +some types `inv(x)` may be slightly more efficient. + +# Examples +```jldoctest +julia> inv(2) +0.5 + +julia> inv(1 + 2im) +0.2 - 0.4im + +julia> inv(1 + 2im) * (1 + 2im) +1.0 + 0.0im + +julia> inv(2//3) +3//2 +``` + +!!! compat "Julia 1.2" + `inv(::Missing)` requires at least Julia 1.2. +""" +inv(x::Number) = one(x)/x + + +""" + widemul(x, y) + +Multiply `x` and `y`, giving the result as a larger type. + +# Examples +```jldoctest +julia> widemul(Float32(3.), 4.) +12.0 +``` +""" +widemul(x::Number, y::Number) = widen(x)*widen(y) + +iterate(x::Number) = (x, nothing) +iterate(x::Number, ::Any) = nothing +isempty(x::Number) = false +in(x::Number, y::Number) = x == y + +map(f, x::Number, ys::Number...) = f(x, ys...) + +""" + zero(x) + +Get the additive identity element for the type of `x` (`x` can also specify the type itself). + +# Examples +```jldoctest +julia> zero(1) +0 + +julia> zero(big"2.0") +0.0 + +julia> zero(rand(2,2)) +2×2 Array{Float64,2}: + 0.0 0.0 + 0.0 0.0 +``` +""" +zero(x::Number) = oftype(x,0) +zero(::Type{T}) where {T<:Number} = convert(T,0) + +""" + one(x) + one(T::type) + +Return a multiplicative identity for `x`: a value such that +`one(x)*x == x*one(x) == x`. Alternatively `one(T)` can +take a type `T`, in which case `one` returns a multiplicative +identity for any `x` of type `T`. + +If possible, `one(x)` returns a value of the same type as `x`, +and `one(T)` returns a value of type `T`. However, this may +not be the case for types representing dimensionful quantities +(e.g. time in days), since the multiplicative +identity must be dimensionless. In that case, `one(x)` +should return an identity value of the same precision +(and shape, for matrices) as `x`. + +If you want a quantity that is of the same type as `x`, or of type `T`, +even if `x` is dimensionful, use [`oneunit`](@ref) instead. + +# Examples +```jldoctest +julia> one(3.7) +1.0 + +julia> one(Int) +1 + +julia> import Dates; one(Dates.Day(1)) +1 +``` +""" +one(::Type{T}) where {T<:Number} = convert(T,1) +one(x::T) where {T<:Number} = one(T) +# note that convert(T, 1) should throw an error if T is dimensionful, +# so this fallback definition should be okay. + +""" + oneunit(x::T) + oneunit(T::Type) + +Returns `T(one(x))`, where `T` is either the type of the argument or +(if a type is passed) the argument. This differs from [`one`](@ref) for +dimensionful quantities: `one` is dimensionless (a multiplicative identity) +while `oneunit` is dimensionful (of the same type as `x`, or of type `T`). + +# Examples +```jldoctest +julia> oneunit(3.7) +1.0 + +julia> import Dates; oneunit(Dates.Day) +1 day +``` +""" +oneunit(x::T) where {T} = T(one(x)) +oneunit(::Type{T}) where {T} = T(one(T)) + +""" + big(T::Type) + +Compute the type that represents the numeric type `T` with arbitrary precision. +Equivalent to `typeof(big(zero(T)))`. + +# Examples +```jldoctest +julia> big(Rational) +Rational{BigInt} + +julia> big(Float64) +BigFloat + +julia> big(Complex{Int}) +Complex{BigInt} +``` +""" +big(::Type{T}) where {T<:Number} = typeof(big(zero(T))) diff --git a/base/operators.jl b/base/operators.jl new file mode 100644 index 0000000..e156c61 --- /dev/null +++ b/base/operators.jl @@ -0,0 +1,1131 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## types ## + +""" + <:(T1, T2) + +Subtype operator: returns `true` if and only if all values of type `T1` are +also of type `T2`. + +# Examples +```jldoctest +julia> Float64 <: AbstractFloat +true + +julia> Vector{Int} <: AbstractArray +true + +julia> Matrix{Float64} <: Matrix{AbstractFloat} +false +``` +""" +(<:) + +""" + >:(T1, T2) + +Supertype operator, equivalent to `T2 <: T1`. +""" +const (>:)(@nospecialize(a), @nospecialize(b)) = (b <: a) + +""" + supertype(T::DataType) + +Return the supertype of DataType `T`. + +# Examples +```jldoctest +julia> supertype(Int32) +Signed +``` +""" +function supertype(T::DataType) + @_pure_meta + T.super +end + +function supertype(T::UnionAll) + @_pure_meta + UnionAll(T.var, supertype(T.body)) +end + +## generic comparison ## + +""" + ==(x, y) + +Generic equality operator. Falls back to [`===`](@ref). +Should be implemented for all types with a notion of equality, based on the abstract value +that an instance represents. For example, all numeric types are compared by numeric value, +ignoring type. Strings are compared as sequences of characters, ignoring encoding. +For collections, `==` is generally called recursively on all contents, +though other properties (like the shape for arrays) may also be taken into account. + +This operator follows IEEE semantics for floating-point numbers: `0.0 == -0.0` and +`NaN != NaN`. + +The result is of type `Bool`, except when one of the operands is [`missing`](@ref), +in which case `missing` is returned +([three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic)). +For collections, `missing` is returned if at least one of the operands contains +a `missing` value and all non-missing values are equal. +Use [`isequal`](@ref) or [`===`](@ref) to always get a `Bool` result. + +# Implementation +New numeric types should implement this function for two arguments of the new type, and +handle comparison to other types via promotion rules where possible. + +[`isequal`](@ref) falls back to `==`, so new methods of `==` will be used by the +[`Dict`](@ref) type to compare keys. If your type will be used as a dictionary key, it +should therefore also implement [`hash`](@ref). +""" +==(x, y) = x === y + +""" + isequal(x, y) + +Similar to [`==`](@ref), except for the treatment of floating point numbers +and of missing values. `isequal` treats all floating-point `NaN` values as equal +to each other, treats `-0.0` as unequal to `0.0`, and [`missing`](@ref) as equal +to `missing`. Always returns a `Bool` value. + +# Implementation +The default implementation of `isequal` calls `==`, so a type that does not involve +floating-point values generally only needs to define `==`. + +`isequal` is the comparison function used by hash tables (`Dict`). `isequal(x,y)` must imply +that `hash(x) == hash(y)`. + +This typically means that types for which a custom `==` or `isequal` method exists must +implement a corresponding `hash` method (and vice versa). Collections typically implement +`isequal` by calling `isequal` recursively on all contents. + +Scalar types generally do not need to implement `isequal` separate from `==`, unless they +represent floating-point numbers amenable to a more efficient implementation than that +provided as a generic fallback (based on `isnan`, `signbit`, and `==`). + +# Examples +```jldoctest +julia> isequal([1., NaN], [1., NaN]) +true + +julia> [1., NaN] == [1., NaN] +false + +julia> 0.0 == -0.0 +true + +julia> isequal(0.0, -0.0) +false +``` +""" +isequal(x, y) = x == y + +signequal(x, y) = signbit(x)::Bool == signbit(y)::Bool +signless(x, y) = signbit(x)::Bool & !signbit(y)::Bool + +isequal(x::AbstractFloat, y::AbstractFloat) = (isnan(x) & isnan(y)) | signequal(x, y) & (x == y) +isequal(x::Real, y::AbstractFloat) = (isnan(x) & isnan(y)) | signequal(x, y) & (x == y) +isequal(x::AbstractFloat, y::Real ) = (isnan(x) & isnan(y)) | signequal(x, y) & (x == y) + +""" + isless(x, y) + +Test whether `x` is less than `y`, according to a fixed total order. +`isless` is not defined on all pairs of values `(x, y)`. However, if it +is defined, it is expected to satisfy the following: +- If `isless(x, y)` is defined, then so is `isless(y, x)` and `isequal(x, y)`, + and exactly one of those three yields `true`. +- The relation defined by `isless` is transitive, i.e., + `isless(x, y) && isless(y, z)` implies `isless(x, z)`. + +Values that are normally unordered, such as `NaN`, +are ordered in an arbitrary but consistent fashion. +[`missing`](@ref) values are ordered last. + +This is the default comparison used by [`sort`](@ref). + +# Implementation +Non-numeric types with a total order should implement this function. +Numeric types only need to implement it if they have special values such as `NaN`. +Types with a partial order should implement [`<`](@ref). +""" +function isless end + +isless(x::AbstractFloat, y::AbstractFloat) = (!isnan(x) & (isnan(y) | signless(x, y))) | (x < y) +isless(x::Real, y::AbstractFloat) = (!isnan(x) & (isnan(y) | signless(x, y))) | (x < y) +isless(x::AbstractFloat, y::Real ) = (!isnan(x) & (isnan(y) | signless(x, y))) | (x < y) + + +function ==(T::Type, S::Type) + @_pure_meta + return ccall(:jl_types_equal, Cint, (Any, Any), T, S) != 0 +end +function !=(T::Type, S::Type) + @_pure_meta + return !(T == S) +end +==(T::TypeVar, S::Type) = false +==(T::Type, S::TypeVar) = false + +## comparison fallbacks ## + +""" + !=(x, y) + ≠(x,y) + +Not-equals comparison operator. Always gives the opposite answer as [`==`](@ref). + +# Implementation +New types should generally not implement this, and rely on the fallback definition +`!=(x,y) = !(x==y)` instead. + +# Examples +```jldoctest +julia> 3 != 2 +true + +julia> "foo" ≠ "foo" +false +``` +""" +!=(x, y) = !(x == y) +const ≠ = != + +""" + ===(x,y) -> Bool + ≡(x,y) -> Bool + +Determine whether `x` and `y` are identical, in the sense that no program could distinguish +them. First the types of `x` and `y` are compared. If those are identical, mutable objects +are compared by address in memory and immutable objects (such as numbers) are compared by +contents at the bit level. This function is sometimes called "egal". +It always returns a `Bool` value. + +# Examples +```jldoctest +julia> a = [1, 2]; b = [1, 2]; + +julia> a == b +true + +julia> a === b +false + +julia> a === a +true +``` +""" +=== +const ≡ = === + +""" + !==(x, y) + ≢(x,y) + +Always gives the opposite answer as [`===`](@ref). + +# Examples +```jldoctest +julia> a = [1, 2]; b = [1, 2]; + +julia> a ≢ b +true + +julia> a ≢ a +false +``` +""" +!==(@nospecialize(x), @nospecialize(y)) = !(x === y) +const ≢ = !== + +""" + <(x, y) + +Less-than comparison operator. Falls back to [`isless`](@ref). +Because of the behavior of floating-point NaN values, this operator implements +a partial order. + +# Implementation +New numeric types with a canonical partial order should implement this function for +two arguments of the new type. +Types with a canonical total order should implement [`isless`](@ref) instead. +(x < y) | (x == y) + +# Examples +```jldoctest +julia> 'a' < 'b' +true + +julia> "abc" < "abd" +true + +julia> 5 < 3 +false +``` +""" +<(x, y) = isless(x, y) + +""" + >(x, y) + +Greater-than comparison operator. Falls back to `y < x`. + +# Implementation +Generally, new types should implement [`<`](@ref) instead of this function, +and rely on the fallback definition `>(x, y) = y < x`. + +# Examples +```jldoctest +julia> 'a' > 'b' +false + +julia> 7 > 3 > 1 +true + +julia> "abc" > "abd" +false + +julia> 5 > 3 +true +``` +""" +>(x, y) = y < x + +""" + <=(x, y) + ≤(x,y) + +Less-than-or-equals comparison operator. Falls back to `(x < y) | (x == y)`. + +# Examples +```jldoctest +julia> 'a' <= 'b' +true + +julia> 7 ≤ 7 ≤ 9 +true + +julia> "abc" ≤ "abc" +true + +julia> 5 <= 3 +false +``` +""" +<=(x, y) = (x < y) | (x == y) +const ≤ = <= + +""" + >=(x, y) + ≥(x,y) + +Greater-than-or-equals comparison operator. Falls back to `y <= x`. + +# Examples +```jldoctest +julia> 'a' >= 'b' +false + +julia> 7 ≥ 7 ≥ 3 +true + +julia> "abc" ≥ "abc" +true + +julia> 5 >= 3 +true +``` +""" +>=(x, y) = (y <= x) +const ≥ = >= + +# this definition allows Number types to implement < instead of isless, +# which is more idiomatic: +isless(x::Real, y::Real) = x ifelse(1 > 2, 1, 2) +2 +``` +""" +ifelse + +""" + cmp(x,y) + +Return -1, 0, or 1 depending on whether `x` is less than, equal to, or greater than `y`, +respectively. Uses the total order implemented by `isless`. + +# Examples +```jldoctest +julia> cmp(1, 2) +-1 + +julia> cmp(2, 1) +1 + +julia> cmp(2+im, 3-im) +ERROR: MethodError: no method matching isless(::Complex{Int64}, ::Complex{Int64}) +[...] +``` +""" +cmp(x, y) = isless(x, y) ? -1 : ifelse(isless(y, x), 1, 0) + +""" + cmp(<, x, y) + +Return -1, 0, or 1 depending on whether `x` is less than, equal to, or greater than `y`, +respectively. The first argument specifies a less-than comparison function to use. +""" +cmp(<, x, y) = (x < y) ? -1 : ifelse(y < x, 1, 0) + +# cmp returns -1, 0, +1 indicating ordering +cmp(x::Integer, y::Integer) = ifelse(isless(x, y), -1, ifelse(isless(y, x), 1, 0)) + +""" + max(x, y, ...) + +Return the maximum of the arguments. See also the [`maximum`](@ref) function +to take the maximum element from a collection. + +# Examples +```jldoctest +julia> max(2, 5, 1) +5 +``` +""" +max(x, y) = ifelse(isless(y, x), x, y) + +""" + min(x, y, ...) + +Return the minimum of the arguments. See also the [`minimum`](@ref) function +to take the minimum element from a collection. + +# Examples +```jldoctest +julia> min(2, 5, 1) +1 +``` +""" +min(x,y) = ifelse(isless(y, x), y, x) + +""" + minmax(x, y) + +Return `(min(x,y), max(x,y))`. See also: [`extrema`](@ref) that returns `(minimum(x), maximum(x))`. + +# Examples +```jldoctest +julia> minmax('c','b') +('b', 'c') +``` +""" +minmax(x,y) = isless(y, x) ? (y, x) : (x, y) + +""" + extrema(itr) -> Tuple + +Compute both the minimum and maximum element in a single pass, and return them as a 2-tuple. + +# Examples +```jldoctest +julia> extrema(2:10) +(2, 10) + +julia> extrema([9,pi,4.5]) +(3.141592653589793, 9.0) +``` +""" +extrema(itr) = _extrema_itr(identity, itr) + +""" + extrema(f, itr) -> Tuple + +Compute both the minimum and maximum of `f` applied to each element in `itr` and return +them as a 2-tuple. Only one pass is made over `itr`. + +!!! compat "Julia 1.2" + This method requires Julia 1.2 or later. + +# Examples +```jldoctest +julia> extrema(sin, 0:π) +(0.0, 0.9092974268256817) +``` +""" +extrema(f, itr) = _extrema_itr(f, itr) + +function _extrema_itr(f, itr) + y = iterate(itr) + y === nothing && throw(ArgumentError("collection must be non-empty")) + (v, s) = y + vmin = vmax = f(v) + while true + y = iterate(itr, s) + y === nothing && break + (x, s) = y + fx = f(x) + vmax = max(fx, vmax) + vmin = min(fx, vmin) + end + return (vmin, vmax) +end + +extrema(x::Real) = (x, x) +extrema(f, x::Real) = (y = f(x); (y, y)) + +## definitions providing basic traits of arithmetic operators ## + +""" + identity(x) + +The identity function. Returns its argument. + +# Examples +```jldoctest +julia> identity("Well, what did you expect?") +"Well, what did you expect?" +``` +""" +identity(x) = x + ++(x::Number) = x +*(x::Number) = x +(&)(x::Integer) = x +(|)(x::Integer) = x +xor(x::Integer) = x + +const ⊻ = xor + +# foldl for argument lists. expand recursively up to a point, then +# switch to a loop. this allows small cases like `a+b+c+d` to be inlined +# efficiently, without a major slowdown for `+(x...)` when `x` is big. +afoldl(op,a) = a +afoldl(op,a,b) = op(a,b) +afoldl(op,a,b,c...) = afoldl(op, op(a,b), c...) +function afoldl(op,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,qs...) + y = op(op(op(op(op(op(op(op(op(op(op(op(op(op(op(a,b),c),d),e),f),g),h),i),j),k),l),m),n),o),p) + for x in qs; y = op(y,x); end + y +end + +for op in (:+, :*, :&, :|, :xor, :min, :max, :kron) + @eval begin + # note: these definitions must not cause a dispatch loop when +(a,b) is + # not defined, and must only try to call 2-argument definitions, so + # that defining +(a,b) is sufficient for full functionality. + ($op)(a, b, c, xs...) = afoldl($op, ($op)(($op)(a,b),c), xs...) + # a further concern is that it's easy for a type like (Int,Int...) + # to match many definitions, so we need to keep the number of + # definitions down to avoid losing type information. + end +end + +""" + \\(x, y) + +Left division operator: multiplication of `y` by the inverse of `x` on the left. Gives +floating-point results for integer arguments. + +# Examples +```jldoctest +julia> 3 \\ 6 +2.0 + +julia> inv(3) * 6 +2.0 + +julia> A = [4 3; 2 1]; x = [5, 6]; + +julia> A \\ x +2-element Array{Float64,1}: + 6.5 + -7.0 + +julia> inv(A) * x +2-element Array{Float64,1}: + 6.5 + -7.0 +``` +""" +\(x,y) = adjoint(adjoint(y)/adjoint(x)) + +# Core <<, >>, and >>> take either Int or UInt as second arg. Signed shift +# counts can shift in either direction, and are translated here to unsigned +# counts. Integer datatypes only need to implement the unsigned version. + +""" + <<(x, n) + +Left bit shift operator, `x << n`. For `n >= 0`, the result is `x` shifted left +by `n` bits, filling with `0`s. This is equivalent to `x * 2^n`. For `n < 0`, +this is equivalent to `x >> -n`. + +# Examples +```jldoctest +julia> Int8(3) << 2 +12 + +julia> bitstring(Int8(3)) +"00000011" + +julia> bitstring(Int8(12)) +"00001100" +``` +See also [`>>`](@ref), [`>>>`](@ref). +""" +function <<(x::Integer, c::Integer) + @_inline_meta + typemin(Int) <= c <= typemax(Int) && return x << (c % Int) + (x >= 0 || c >= 0) && return zero(x) << 0 # for type stability + oftype(x, -1) +end +function <<(x::Integer, c::Unsigned) + @_inline_meta + if c isa UInt + throw(MethodError(<<, (x, c))) + end + c <= typemax(UInt) ? x << (c % UInt) : zero(x) << UInt(0) +end +<<(x::Integer, c::Int) = c >= 0 ? x << unsigned(c) : x >> unsigned(-c) + +""" + >>(x, n) + +Right bit shift operator, `x >> n`. For `n >= 0`, the result is `x` shifted +right by `n` bits, where `n >= 0`, filling with `0`s if `x >= 0`, `1`s if `x < +0`, preserving the sign of `x`. This is equivalent to `fld(x, 2^n)`. For `n < +0`, this is equivalent to `x << -n`. + +# Examples +```jldoctest +julia> Int8(13) >> 2 +3 + +julia> bitstring(Int8(13)) +"00001101" + +julia> bitstring(Int8(3)) +"00000011" + +julia> Int8(-14) >> 2 +-4 + +julia> bitstring(Int8(-14)) +"11110010" + +julia> bitstring(Int8(-4)) +"11111100" +``` +See also [`>>>`](@ref), [`<<`](@ref). +""" +function >>(x::Integer, c::Integer) + @_inline_meta + if c isa UInt + throw(MethodError(>>, (x, c))) + end + typemin(Int) <= c <= typemax(Int) && return x >> (c % Int) + (x >= 0 || c < 0) && return zero(x) >> 0 + oftype(x, -1) +end +>>(x::Integer, c::Int) = c >= 0 ? x >> unsigned(c) : x << unsigned(-c) + +""" + >>>(x, n) + +Unsigned right bit shift operator, `x >>> n`. For `n >= 0`, the result is `x` +shifted right by `n` bits, where `n >= 0`, filling with `0`s. For `n < 0`, this +is equivalent to `x << -n`. + +For [`Unsigned`](@ref) integer types, this is equivalent to [`>>`](@ref). For +[`Signed`](@ref) integer types, this is equivalent to `signed(unsigned(x) >> n)`. + +# Examples +```jldoctest +julia> Int8(-14) >>> 2 +60 + +julia> bitstring(Int8(-14)) +"11110010" + +julia> bitstring(Int8(60)) +"00111100" +``` + +[`BigInt`](@ref)s are treated as if having infinite size, so no filling is required and this +is equivalent to [`>>`](@ref). + +See also [`>>`](@ref), [`<<`](@ref). +""" +function >>>(x::Integer, c::Integer) + @_inline_meta + typemin(Int) <= c <= typemax(Int) ? x >>> (c % Int) : zero(x) >>> 0 +end +function >>>(x::Integer, c::Unsigned) + @_inline_meta + if c isa UInt + throw(MethodError(>>>, (x, c))) + end + c <= typemax(UInt) ? x >>> (c % UInt) : zero(x) >>> 0 +end +>>>(x::Integer, c::Int) = c >= 0 ? x >>> unsigned(c) : x << unsigned(-c) + +# fallback div, fld, and cld implementations +# NOTE: C89 fmod() and x87 FPREM implicitly provide truncating float division, +# so it is used here as the basis of float div(). +div(x::T, y::T) where {T<:Real} = convert(T,round((x-rem(x,y))/y)) + +""" + fld(x, y) + +Largest integer less than or equal to `x/y`. + +# Examples +```jldoctest +julia> fld(7.3,5.5) +1.0 +``` +""" +fld(x::T, y::T) where {T<:Real} = convert(T,round((x-mod(x,y))/y)) + +""" + cld(x, y) + +Smallest integer larger than or equal to `x/y`. + +# Examples +```jldoctest +julia> cld(5.5,2.2) +3.0 +``` +""" +cld(x::T, y::T) where {T<:Real} = convert(T,round((x-modCeil(x,y))/y)) +#rem(x::T, y::T) where {T<:Real} = convert(T,x-y*trunc(x/y)) +#mod(x::T, y::T) where {T<:Real} = convert(T,x-y*floor(x/y)) +modCeil(x::T, y::T) where {T<:Real} = convert(T,x-y*ceil(x/y)) + +# operator alias + +""" + rem(x, y) + %(x, y) + +Remainder from Euclidean division, returning a value of the same sign as `x`, and smaller in +magnitude than `y`. This value is always exact. + +# Examples +```jldoctest +julia> x = 15; y = 4; + +julia> x % y +3 + +julia> x == div(x, y) * y + rem(x, y) +true +``` +""" +rem +const % = rem + +""" + div(x, y) + ÷(x, y) + +The quotient from Euclidean division. Computes `x/y`, truncated to an integer. + +# Examples +```jldoctest +julia> 9 ÷ 4 +2 + +julia> -5 ÷ 3 +-1 +``` +""" +div +const ÷ = div + +""" + mod1(x, y) + +Modulus after flooring division, returning a value `r` such that `mod(r, y) == mod(x, y)` +in the range ``(0, y]`` for positive `y` and in the range ``[y,0)`` for negative `y`. + +See also: [`fld1`](@ref), [`fldmod1`](@ref). + +# Examples +```jldoctest +julia> mod1(4, 2) +2 + +julia> mod1(4, 3) +1 +``` +""" +mod1(x::T, y::T) where {T<:Real} = (m = mod(x, y); ifelse(m == 0, y, m)) + + +""" + fld1(x, y) + +Flooring division, returning a value consistent with `mod1(x,y)` + +See also: [`mod1`](@ref), [`fldmod1`](@ref). + +# Examples +```jldoctest +julia> x = 15; y = 4; + +julia> fld1(x, y) +4 + +julia> x == fld(x, y) * y + mod(x, y) +true + +julia> x == (fld1(x, y) - 1) * y + mod1(x, y) +true +``` +""" +fld1(x::T, y::T) where {T<:Real} = (m = mod1(x, y); fld(x + y - m, y)) +function fld1(x::T, y::T) where T<:Integer + d = div(x, y) + return d + (!signbit(x ⊻ y) & (d * y != x)) +end + +""" + fldmod1(x, y) + +Return `(fld1(x,y), mod1(x,y))`. + +See also: [`fld1`](@ref), [`mod1`](@ref). +""" +fldmod1(x, y) = (fld1(x, y), mod1(x, y)) + + +""" + widen(x) + +If `x` is a type, return a "larger" type, defined so that arithmetic operations +`+` and `-` are guaranteed not to overflow nor lose precision for any combination +of values that type `x` can hold. + +For fixed-size integer types less than 128 bits, `widen` will return a type with +twice the number of bits. + +If `x` is a value, it is converted to `widen(typeof(x))`. + +# Examples +```jldoctest +julia> widen(Int32) +Int64 + +julia> widen(1.5f0) +1.5 +``` +""" +widen(x::T) where {T} = convert(widen(T), x) +widen(x::Type{T}) where {T} = throw(MethodError(widen, (T,))) + +# function pipelining + +""" + |>(x, f) + +Applies a function to the preceding argument. This allows for easy function chaining. + +# Examples +```jldoctest +julia> [1:5;] |> x->x.^2 |> sum |> inv +0.01818181818181818 +``` +""" +|>(x, f) = f(x) + +# function composition + +""" + f ∘ g + +Compose functions: i.e. `(f ∘ g)(args...)` means `f(g(args...))`. The `∘` symbol can be +entered in the Julia REPL (and most editors, appropriately configured) by typing `\\circ`. + +# Examples +```jldoctest +julia> map(uppercase∘first, ["apple", "banana", "carrot"]) +3-element Array{Char,1}: + 'A' + 'B' + 'C' +``` +""" +∘(f, g) = (x...)->f(g(x...)) + + +""" + !f::Function + +Predicate function negation: when the argument of `!` is a function, it returns a +function which computes the boolean negation of `f`. + +# Examples +```jldoctest +julia> str = "∀ ε > 0, ∃ δ > 0: |x-y| < δ ⇒ |f(x)-f(y)| < ε" +"∀ ε > 0, ∃ δ > 0: |x-y| < δ ⇒ |f(x)-f(y)| < ε" + +julia> filter(isletter, str) +"εδxyδfxfyε" + +julia> filter(!isletter, str) +"∀ > 0, ∃ > 0: |-| < ⇒ |()-()| < " +``` +""" +!(f::Function) = (x...)->!f(x...) + +""" + Fix1(f, x) + +A type representing a partially-applied version of the two-argument function +`f`, with the first argument fixed to the value "x". In other words, +`Fix1(f, x)` behaves similarly to `y->f(x, y)`. +""" +struct Fix1{F,T} <: Function + f::F + x::T + + Fix1(f::F, x::T) where {F,T} = new{F,T}(f, x) + Fix1(f::Type{F}, x::T) where {F,T} = new{Type{F},T}(f, x) +end + +(f::Fix1)(y) = f.f(f.x, y) + +""" + Fix2(f, x) + +A type representing a partially-applied version of the two-argument function +`f`, with the second argument fixed to the value "x". In other words, +`Fix2(f, x)` behaves similarly to `y->f(y, x)`. +""" +struct Fix2{F,T} <: Function + f::F + x::T + + Fix2(f::F, x::T) where {F,T} = new{F,T}(f, x) + Fix2(f::Type{F}, x::T) where {F,T} = new{Type{F},T}(f, x) +end + +(f::Fix2)(y) = f.f(y, f.x) + +""" + isequal(x) + +Create a function that compares its argument to `x` using [`isequal`](@ref), i.e. +a function equivalent to `y -> isequal(y, x)`. + +The returned function is of type `Base.Fix2{typeof(isequal)}`, which can be +used to implement specialized methods. +""" +isequal(x) = Fix2(isequal, x) + +""" + ==(x) + +Create a function that compares its argument to `x` using [`==`](@ref), i.e. +a function equivalent to `y -> y == x`. + +The returned function is of type `Base.Fix2{typeof(==)}`, which can be +used to implement specialized methods. +""" +==(x) = Fix2(==, x) + +""" + !=(x) + +Create a function that compares its argument to `x` using [`!=`](@ref), i.e. +a function equivalent to `y -> y != x`. +The returned function is of type `Base.Fix2{typeof(!=)}`, which can be +used to implement specialized methods. + +!!! compat "Julia 1.2" + This functionality requires at least Julia 1.2. +""" +!=(x) = Fix2(!=, x) + +""" + >=(x) + +Create a function that compares its argument to `x` using [`>=`](@ref), i.e. +a function equivalent to `y -> y >= x`. +The returned function is of type `Base.Fix2{typeof(>=)}`, which can be +used to implement specialized methods. + +!!! compat "Julia 1.2" + This functionality requires at least Julia 1.2. +""" +>=(x) = Fix2(>=, x) + +""" + <=(x) + +Create a function that compares its argument to `x` using [`<=`](@ref), i.e. +a function equivalent to `y -> y <= x`. +The returned function is of type `Base.Fix2{typeof(<=)}`, which can be +used to implement specialized methods. + +!!! compat "Julia 1.2" + This functionality requires at least Julia 1.2. +""" +<=(x) = Fix2(<=, x) + +""" + >(x) + +Create a function that compares its argument to `x` using [`>`](@ref), i.e. +a function equivalent to `y -> y > x`. +The returned function is of type `Base.Fix2{typeof(>)}`, which can be +used to implement specialized methods. + +!!! compat "Julia 1.2" + This functionality requires at least Julia 1.2. +""" +>(x) = Fix2(>, x) + +""" + <(x) + +Create a function that compares its argument to `x` using [`<`](@ref), i.e. +a function equivalent to `y -> y < x`. +The returned function is of type `Base.Fix2{typeof(<)}`, which can be +used to implement specialized methods. + +!!! compat "Julia 1.2" + This functionality requires at least Julia 1.2. +""" +<(x) = Fix2(<, x) + +""" + splat(f) + +Defined as +```julia + splat(f) = args->f(args...) +``` +i.e. given a function returns a new function that takes one argument and splats +its argument into the original function. This is useful as an adaptor to pass +a multi-argument function in a context that expects a single argument, but +passes a tuple as that single argument. + +# Example usage: +```jldoctest +julia> map(Base.splat(+), zip(1:3,4:6)) +3-element Array{Int64,1}: + 5 + 7 + 9 +``` +""" +splat(f) = args->f(args...) + +## in & contains + +""" + in(x) + +Create a function that checks whether its argument is [`in`](@ref) `x`, i.e. +a function equivalent to `y -> y in x`. + +The returned function is of type `Base.Fix2{typeof(in)}`, which can be +used to implement specialized methods. +""" +in(x) = Fix2(in, x) + +function in(x, itr) + anymissing = false + for y in itr + v = (y == x) + if ismissing(v) + anymissing = true + elseif v + return true + end + end + return anymissing ? missing : false +end + +const ∈ = in +∋(itr, x) = ∈(x, itr) +∉(x, itr) = !∈(x, itr) +∌(itr, x) = !∋(itr, x) + +""" + in(item, collection) -> Bool + ∈(item, collection) -> Bool + ∋(collection, item) -> Bool + +Determine whether an item is in the given collection, in the sense that it is +[`==`](@ref) to one of the values generated by iterating over the collection. +Returns a `Bool` value, except if `item` is [`missing`](@ref) or `collection` +contains `missing` but not `item`, in which case `missing` is returned +([three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic), +matching the behavior of [`any`](@ref) and [`==`](@ref)). + +Some collections follow a slightly different definition. For example, +[`Set`](@ref)s check whether the item [`isequal`](@ref) to one of the elements. +[`Dict`](@ref)s look for `key=>value` pairs, and the key is compared using +[`isequal`](@ref). To test for the presence of a key in a dictionary, +use [`haskey`](@ref) or `k in keys(dict)`. For these collections, the result +is always a `Bool` and never `missing`. + +# Examples +```jldoctest +julia> a = 1:3:20 +1:3:19 + +julia> 4 in a +true + +julia> 5 in a +false + +julia> missing in [1, 2] +missing + +julia> 1 in [2, missing] +missing + +julia> 1 in [1, missing] +true + +julia> missing in Set([1, 2]) +false +``` +""" +in, ∋ + +""" + ∉(item, collection) -> Bool + ∌(collection, item) -> Bool + +Negation of `∈` and `∋`, i.e. checks that `item` is not in `collection`. + +# Examples +```jldoctest +julia> 1 ∉ 2:4 +true + +julia> 1 ∉ 1:3 +false +``` +""" +∉, ∌ diff --git a/base/options.jl b/base/options.jl new file mode 100644 index 0000000..f7dec64 --- /dev/null +++ b/base/options.jl @@ -0,0 +1,82 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# NOTE: This type needs to be kept in sync with jl_options in src/julia.h +struct JLOptions + quiet::Int8 + banner::Int8 + julia_bindir::Ptr{UInt8} + julia_bin::Ptr{UInt8} + commands::Ptr{Ptr{UInt8}} # (e)eval, (E)print, (L)load + image_file::Ptr{UInt8} + cpu_target::Ptr{UInt8} + nprocs::Int32 + machine_file::Ptr{UInt8} + project::Ptr{UInt8} + isinteractive::Int8 + color::Int8 + historyfile::Int8 + startupfile::Int8 + compile_enabled::Int8 + code_coverage::Int8 + malloc_log::Int8 + opt_level::Int8 + debug_level::Int8 + check_bounds::Int8 + depwarn::Int8 + warn_overwrite::Int8 + can_inline::Int8 + polly::Int8 + trace_compile::Ptr{UInt8} + fast_math::Int8 + worker::Int8 + cookie::Ptr{UInt8} + handle_signals::Int8 + use_sysimage_native_code::Int8 + use_compiled_modules::Int8 + bindto::Ptr{UInt8} + outputbc::Ptr{UInt8} + outputunoptbc::Ptr{UInt8} + outputjitbc::Ptr{UInt8} + outputo::Ptr{UInt8} + outputji::Ptr{UInt8} + output_code_coverage::Ptr{UInt8} + incremental::Int8 +end + +# This runs early in the sysimage != is not defined yet +if sizeof(JLOptions) === ccall(:jl_sizeof_jl_options, Int, ()) +else + ccall(:jl_throw, Cvoid, (Any,), "Option structure mismatch") +end + +JLOptions() = unsafe_load(cglobal(:jl_options, JLOptions)) + +function show(io::IO, opt::JLOptions) + print(io, "JLOptions(") + fields = fieldnames(JLOptions) + nfields = length(fields) + for (i, f) in enumerate(fields) + v = getfield(opt, i) + if isa(v, Ptr{UInt8}) + v = (v != C_NULL) ? unsafe_string(v) : "" + elseif isa(v, Ptr{Ptr{UInt8}}) + v = unsafe_load_commands(v) + end + print(io, f, " = ", repr(v), i < nfields ? ", " : "") + end + print(io, ")") +end + +function unsafe_load_commands(v::Ptr{Ptr{UInt8}}) + cmds = Pair{Char, String}[] + v == C_NULL && return cmds + i = 1 + while true + s = unsafe_load(v, i) + s == C_NULL && break + e = Char(unsafe_load(s)) + push!(cmds, e => unsafe_string(s + 1)) + i += 1 + end + return cmds +end diff --git a/base/ordering.jl b/base/ordering.jl new file mode 100644 index 0000000..52320ac --- /dev/null +++ b/base/ordering.jl @@ -0,0 +1,78 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +module Order + + +import ..@__MODULE__, ..parentmodule +const Base = parentmodule(@__MODULE__) +import .Base: + AbstractVector, @propagate_inbounds, isless, identity, getindex, + +, -, !, &, <, | + +## notions of element ordering ## + +export # not exported by Base + Ordering, Forward, Reverse, + By, Lt, Perm, + ReverseOrdering, ForwardOrdering, + DirectOrdering, + lt, ord, ordtype + +abstract type Ordering end + +struct ForwardOrdering <: Ordering end +struct ReverseOrdering{Fwd<:Ordering} <: Ordering + fwd::Fwd +end + +ReverseOrdering(rev::ReverseOrdering) = rev.fwd +ReverseOrdering(fwd::Fwd) where {Fwd} = ReverseOrdering{Fwd}(fwd) + +const DirectOrdering = Union{ForwardOrdering,ReverseOrdering{ForwardOrdering}} + +const Forward = ForwardOrdering() +const Reverse = ReverseOrdering(Forward) + +struct By{T} <: Ordering + by::T +end + +struct Lt{T} <: Ordering + lt::T +end + +struct Perm{O<:Ordering,V<:AbstractVector} <: Ordering + order::O + data::V +end + +lt(o::ForwardOrdering, a, b) = isless(a,b) +lt(o::ReverseOrdering, a, b) = lt(o.fwd,b,a) +lt(o::By, a, b) = isless(o.by(a),o.by(b)) +lt(o::Lt, a, b) = o.lt(a,b) + +@propagate_inbounds function lt(p::Perm, a::Integer, b::Integer) + da = p.data[a] + db = p.data[b] + lt(p.order, da, db) | (!lt(p.order, db, da) & (a < b)) +end + +ordtype(o::ReverseOrdering, vs::AbstractArray) = ordtype(o.fwd, vs) +ordtype(o::Perm, vs::AbstractArray) = ordtype(o.order, o.data) +# TODO: here, we really want the return type of o.by, without calling it +ordtype(o::By, vs::AbstractArray) = try typeof(o.by(vs[1])) catch; Any end +ordtype(o::Ordering, vs::AbstractArray) = eltype(vs) + +_ord(lt::typeof(isless), by::typeof(identity), order::Ordering) = order +_ord(lt::typeof(isless), by, order::Ordering) = By(by) +_ord(lt, by::typeof(identity), order::Ordering) = Lt(lt) +_ord(lt, by, order::Ordering) = Lt((x,y)->lt(by(x),by(y))) + +ord(lt, by, rev::Nothing, order::Ordering=Forward) = _ord(lt, by, order) + +function ord(lt, by, rev::Bool, order::Ordering=Forward) + o = _ord(lt, by, order) + return rev ? ReverseOrdering(o) : o +end + +end diff --git a/base/osutils.jl b/base/osutils.jl new file mode 100644 index 0000000..1f5a708 --- /dev/null +++ b/base/osutils.jl @@ -0,0 +1,38 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" + @static + +Partially evaluate an expression at parse time. + +For example, `@static Sys.iswindows() ? foo : bar` will evaluate `Sys.iswindows()` and insert +either `foo` or `bar` into the expression. +This is useful in cases where a construct would be invalid on other platforms, +such as a `ccall` to a non-existent function. +`@static if Sys.isapple() foo end` and `@static foo <&&,||> bar` are also valid syntax. +""" +macro static(ex) + if isa(ex, Expr) + @label loop + hd = ex.head + if hd ∈ (:if, :elseif, :&&, :||) + cond = Core.eval(__module__, ex.args[1]) + if xor(cond, hd === :||) + return esc(ex.args[2]) + elseif length(ex.args) == 3 + br = ex.args[3] + if br isa Expr && br.head === :elseif + ex = br + @goto loop + else + return esc(ex.args[3]) + end + elseif hd ∈ (:if, :elseif) + return nothing + else + return cond + end + end + end + throw(ArgumentError("invalid @static macro")) +end diff --git a/base/pair.jl b/base/pair.jl new file mode 100644 index 0000000..3ce8817 --- /dev/null +++ b/base/pair.jl @@ -0,0 +1,75 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +struct Pair{A, B} + first::A + second::B + function Pair{A, B}(@nospecialize(a), @nospecialize(b)) where {A, B} + @_inline_meta + # if we didn't inline this, it's probably because the callsite was actually dynamic + # to avoid potentially compiling many copies of this, we mark the arguments with `@nospecialize` + # but also mark the whole function with `@inline` to ensure we will inline it whenever possible + # (even if `convert(::Type{A}, a::A)` for some reason was expensive) + return new(a, b) + end +end +Pair(a, b) = Pair{typeof(a), typeof(b)}(a, b) +const => = Pair + +""" + Pair(x, y) + x => y + +Construct a `Pair` object with type `Pair{typeof(x), typeof(y)}`. The elements +are stored in the fields `first` and `second`. They can also be accessed via +iteration (but a `Pair` is treated as a single "scalar" for broadcasting operations). + +See also: [`Dict`](@ref) + +# Examples +```jldoctest +julia> p = "foo" => 7 +"foo" => 7 + +julia> typeof(p) +Pair{String,Int64} + +julia> p.first +"foo" + +julia> for x in p + println(x) + end +foo +7 +``` +""" +Pair, => + +eltype(p::Type{Pair{A, B}}) where {A, B} = Union{A, B} +iterate(p::Pair, i=1) = i > 2 ? nothing : (getfield(p, i), i + 1) +indexed_iterate(p::Pair, i::Int, state=1) = (getfield(p, i), i + 1) + +hash(p::Pair, h::UInt) = hash(p.second, hash(p.first, h)) + +==(p::Pair, q::Pair) = (p.first==q.first) & (p.second==q.second) +isequal(p::Pair, q::Pair) = isequal(p.first,q.first) & isequal(p.second,q.second) + +isless(p::Pair, q::Pair) = ifelse(!isequal(p.first,q.first), isless(p.first,q.first), + isless(p.second,q.second)) +getindex(p::Pair,i::Int) = getfield(p,i) +getindex(p::Pair,i::Real) = getfield(p, convert(Int, i)) +reverse(p::Pair{A,B}) where {A,B} = Pair{B,A}(p.second, p.first) + +firstindex(p::Pair) = 1 +lastindex(p::Pair) = 2 +length(p::Pair) = 2 +first(p::Pair) = p.first +last(p::Pair) = p.second + +convert(::Type{Pair{A,B}}, x::Pair{A,B}) where {A,B} = x +function convert(::Type{Pair{A,B}}, x::Pair) where {A,B} + Pair{A,B}(convert(A, x[1]), convert(B, x[2])) +end + +promote_rule(::Type{Pair{A1,B1}}, ::Type{Pair{A2,B2}}) where {A1,B1,A2,B2} = + Pair{promote_type(A1, A2), promote_type(B1, B2)} diff --git a/base/parse.jl b/base/parse.jl new file mode 100644 index 0000000..bfbcafb --- /dev/null +++ b/base/parse.jl @@ -0,0 +1,382 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +import Base.Checked: add_with_overflow, mul_with_overflow + +## string to integer functions ## + +""" + parse(type, str; base) + +Parse a string as a number. For `Integer` types, a base can be specified +(the default is 10). For floating-point types, the string is parsed as a decimal +floating-point number. `Complex` types are parsed from decimal strings +of the form `"R±Iim"` as a `Complex(R,I)` of the requested type; `"i"` or `"j"` can also be +used instead of `"im"`, and `"R"` or `"Iim"` are also permitted. +If the string does not contain a valid number, an error is raised. + +!!! compat "Julia 1.1" + `parse(Bool, str)` requires at least Julia 1.1. + +# Examples +```jldoctest +julia> parse(Int, "1234") +1234 + +julia> parse(Int, "1234", base = 5) +194 + +julia> parse(Int, "afc", base = 16) +2812 + +julia> parse(Float64, "1.2e-3") +0.0012 + +julia> parse(Complex{Float64}, "3.2e-1 + 4.5im") +0.32 + 4.5im +``` +""" +parse(T::Type, str; base = Int) + +function parse(::Type{T}, c::AbstractChar; base::Integer = 10) where T<:Integer + a::Int = (base <= 36 ? 10 : 36) + 2 <= base <= 62 || throw(ArgumentError("invalid base: base must be 2 ≤ base ≤ 62, got $base")) + d = '0' <= c <= '9' ? c-'0' : + 'A' <= c <= 'Z' ? c-'A'+10 : + 'a' <= c <= 'z' ? c-'a'+a : throw(ArgumentError("invalid digit: $(repr(c))")) + d < base || throw(ArgumentError("invalid base $base digit $(repr(c))")) + convert(T, d) +end + +function parseint_iterate(s::AbstractString, startpos::Int, endpos::Int) + (0 < startpos <= endpos) || (return Char(0), 0, 0) + j = startpos + c, startpos = iterate(s,startpos)::Tuple{Char, Int} + c, startpos, j +end + +function parseint_preamble(signed::Bool, base::Int, s::AbstractString, startpos::Int, endpos::Int) + c, i, j = parseint_iterate(s, startpos, endpos) + + while isspace(c) + c, i, j = parseint_iterate(s,i,endpos) + end + (j == 0) && (return 0, 0, 0) + + sgn = 1 + if signed + if c == '-' || c == '+' + (c == '-') && (sgn = -1) + c, i, j = parseint_iterate(s,i,endpos) + end + end + + while isspace(c) + c, i, j = parseint_iterate(s,i,endpos) + end + (j == 0) && (return 0, 0, 0) + + if base == 0 + if c == '0' && i <= endpos + c, i = iterate(s,i)::Tuple{Char, Int} + base = c=='b' ? 2 : c=='o' ? 8 : c=='x' ? 16 : 10 + if base != 10 + c, i, j = parseint_iterate(s,i,endpos) + end + else + base = 10 + end + end + return sgn, base, j +end + +function tryparse_internal(::Type{T}, s::AbstractString, startpos::Int, endpos::Int, base_::Integer, raise::Bool) where T<:Integer + sgn, base, i = parseint_preamble(T<:Signed, Int(base_), s, startpos, endpos) + if sgn == 0 && base == 0 && i == 0 + raise && throw(ArgumentError("input string is empty or only contains whitespace")) + return nothing + end + if !(2 <= base <= 62) + raise && throw(ArgumentError("invalid base: base must be 2 ≤ base ≤ 62, got $base")) + return nothing + end + if i == 0 + raise && throw(ArgumentError("premature end of integer: $(repr(SubString(s,startpos,endpos)))")) + return nothing + end + c, i = parseint_iterate(s,i,endpos) + if i == 0 + raise && throw(ArgumentError("premature end of integer: $(repr(SubString(s,startpos,endpos)))")) + return nothing + end + + base = convert(T, base) + # Special case the common cases of base being 10 or 16 to avoid expensive runtime div + m::T = base == 10 ? div(typemax(T) - T(9), T(10)) : + base == 16 ? div(typemax(T) - T(15), T(16)) : + div(typemax(T) - base + 1, base) + n::T = 0 + a::Int = base <= 36 ? 10 : 36 + _0 = UInt32('0') + _9 = UInt32('9') + _A = UInt32('A') + _a = UInt32('a') + _Z = UInt32('Z') + _z = UInt32('z') + while n <= m + # Fast path from `UInt32(::Char)`; non-ascii will be >= 0x80 + _c = reinterpret(UInt32, c) >> 24 + d::T = _0 <= _c <= _9 ? _c-_0 : + _A <= _c <= _Z ? _c-_A+ UInt32(10) : + _a <= _c <= _z ? _c-_a+a : base + if d >= base + raise && throw(ArgumentError("invalid base $base digit $(repr(c)) in $(repr(SubString(s,startpos,endpos)))")) + return nothing + end + n *= base + n += d + if i > endpos + n *= sgn + return n + end + c, i = iterate(s,i)::Tuple{Char, Int} + isspace(c) && break + end + (T <: Signed) && (n *= sgn) + while !isspace(c) + # Fast path from `UInt32(::Char)`; non-ascii will be >= 0x80 + _c = reinterpret(UInt32, c) >> 24 + d::T = _0 <= _c <= _9 ? _c-_0 : + _A <= _c <= _Z ? _c-_A+ UInt32(10) : + _a <= _c <= _z ? _c-_a+a : base + if d >= base + raise && throw(ArgumentError("invalid base $base digit $(repr(c)) in $(repr(SubString(s,startpos,endpos)))")) + return nothing + end + (T <: Signed) && (d *= sgn) + + n, ov_mul = mul_with_overflow(n, base) + n, ov_add = add_with_overflow(n, d) + if ov_mul | ov_add + raise && throw(OverflowError("overflow parsing $(repr(SubString(s,startpos,endpos)))")) + return nothing + end + (i > endpos) && return n + c, i = iterate(s,i)::Tuple{Char, Int} + end + while i <= endpos + c, i = iterate(s,i)::Tuple{Char, Int} + if !isspace(c) + raise && throw(ArgumentError("extra characters after whitespace in $(repr(SubString(s,startpos,endpos)))")) + return nothing + end + end + return n +end + +function tryparse_internal(::Type{Bool}, sbuff::Union{String,SubString{String}}, + startpos::Int, endpos::Int, base::Integer, raise::Bool) + if isempty(sbuff) + raise && throw(ArgumentError("input string is empty")) + return nothing + end + + if isnumeric(sbuff[1]) + intres = tryparse_internal(UInt8, sbuff, startpos, endpos, base, false) + (intres == 1) && return true + (intres == 0) && return false + raise && throw(ArgumentError("invalid Bool representation: $(repr(sbuff))")) + end + + orig_start = startpos + orig_end = endpos + + # Ignore leading and trailing whitespace + while isspace(sbuff[startpos]) && startpos <= endpos + startpos = nextind(sbuff, startpos) + end + while isspace(sbuff[endpos]) && endpos >= startpos + endpos = prevind(sbuff, endpos) + end + + len = endpos - startpos + 1 + p = pointer(sbuff) + startpos - 1 + GC.@preserve sbuff begin + (len == 4) && (0 == _memcmp(p, "true", 4)) && (return true) + (len == 5) && (0 == _memcmp(p, "false", 5)) && (return false) + end + + if raise + substr = SubString(sbuff, orig_start, orig_end) # show input string in the error to avoid confusion + if all(isspace, substr) + throw(ArgumentError("input string only contains whitespace")) + else + throw(ArgumentError("invalid Bool representation: $(repr(substr))")) + end + end + return nothing +end + +@inline function check_valid_base(base) + if 2 <= base <= 62 + return base + end + throw(ArgumentError("invalid base: base must be 2 ≤ base ≤ 62, got $base")) +end + +""" + tryparse(type, str; base) + +Like [`parse`](@ref), but returns either a value of the requested type, +or [`nothing`](@ref) if the string does not contain a valid number. +""" +function tryparse(::Type{T}, s::AbstractString; base::Union{Nothing,Integer} = nothing) where {T<:Integer} + # Zero base means, "figure it out" + tryparse_internal(T, s, firstindex(s), lastindex(s), base===nothing ? 0 : check_valid_base(base), false) +end + +function parse(::Type{T}, s::AbstractString; base::Union{Nothing,Integer} = nothing) where {T<:Integer} + convert(T, tryparse_internal(T, s, firstindex(s), lastindex(s), + base===nothing ? 0 : check_valid_base(base), true)) +end + +## string to float functions ## + +function tryparse(::Type{Float64}, s::String) + hasvalue, val = ccall(:jl_try_substrtod, Tuple{Bool, Float64}, + (Ptr{UInt8},Csize_t,Csize_t), s, 0, sizeof(s)) + hasvalue ? val : nothing +end +function tryparse(::Type{Float64}, s::SubString{String}) + hasvalue, val = ccall(:jl_try_substrtod, Tuple{Bool, Float64}, + (Ptr{UInt8},Csize_t,Csize_t), s.string, s.offset, s.ncodeunits) + hasvalue ? val : nothing +end +function tryparse_internal(::Type{Float64}, s::String, startpos::Int, endpos::Int) + hasvalue, val = ccall(:jl_try_substrtod, Tuple{Bool, Float64}, + (Ptr{UInt8},Csize_t,Csize_t), s, startpos-1, endpos-startpos+1) + hasvalue ? val : nothing +end +function tryparse_internal(::Type{Float64}, s::SubString{String}, startpos::Int, endpos::Int) + hasvalue, val = ccall(:jl_try_substrtod, Tuple{Bool, Float64}, + (Ptr{UInt8},Csize_t,Csize_t), s.string, s.offset+startpos-1, endpos-startpos+1) + hasvalue ? val : nothing +end +function tryparse(::Type{Float32}, s::String) + hasvalue, val = ccall(:jl_try_substrtof, Tuple{Bool, Float32}, + (Ptr{UInt8},Csize_t,Csize_t), s, 0, sizeof(s)) + hasvalue ? val : nothing +end +function tryparse(::Type{Float32}, s::SubString{String}) + hasvalue, val = ccall(:jl_try_substrtof, Tuple{Bool, Float32}, + (Ptr{UInt8},Csize_t,Csize_t), s.string, s.offset, s.ncodeunits) + hasvalue ? val : nothing +end +function tryparse_internal(::Type{Float32}, s::String, startpos::Int, endpos::Int) + hasvalue, val = ccall(:jl_try_substrtof, Tuple{Bool, Float32}, + (Ptr{UInt8},Csize_t,Csize_t), s, startpos-1, endpos-startpos+1) + hasvalue ? val : nothing +end +function tryparse_internal(::Type{Float32}, s::SubString{String}, startpos::Int, endpos::Int) + hasvalue, val = ccall(:jl_try_substrtof, Tuple{Bool, Float32}, + (Ptr{UInt8},Csize_t,Csize_t), s.string, s.offset+startpos-1, endpos-startpos+1) + hasvalue ? val : nothing +end +tryparse(::Type{T}, s::AbstractString) where {T<:Union{Float32,Float64}} = tryparse(T, String(s)) +tryparse(::Type{Float16}, s::AbstractString) = + convert(Union{Float16, Nothing}, tryparse(Float32, s)) +tryparse_internal(::Type{Float16}, s::AbstractString, startpos::Int, endpos::Int) = + convert(Union{Float16, Nothing}, tryparse_internal(Float32, s, startpos, endpos)) + +## string to complex functions ## + +function tryparse_internal(::Type{Complex{T}}, s::Union{String,SubString{String}}, i::Int, e::Int, raise::Bool) where {T<:Real} + # skip initial whitespace + while i ≤ e && isspace(s[i]) + i = nextind(s, i) + end + if i > e + raise && throw(ArgumentError("input string is empty or only contains whitespace")) + return nothing + end + + # find index of ± separating real/imaginary parts (if any) + i₊ = something(findnext(in(('+','-')), s, i), 0) + if i₊ == i # leading ± sign + i₊ = something(findnext(in(('+','-')), s, i₊+1), 0) + end + if i₊ != 0 && s[i₊-1] in ('e','E') # exponent sign + i₊ = something(findnext(in(('+','-')), s, i₊+1), 0) + end + + # find trailing im/i/j + iᵢ = something(findprev(in(('m','i','j')), s, e), 0) + if iᵢ > 0 && s[iᵢ] == 'm' # im + iᵢ -= 1 + if s[iᵢ] != 'i' + raise && throw(ArgumentError("expected trailing \"im\", found only \"m\"")) + return nothing + end + end + + if i₊ == 0 # purely real or imaginary value + if iᵢ > i && !(iᵢ == i+1 && s[i] in ('+','-')) # purely imaginary (not "±inf") + x = tryparse_internal(T, s, i, iᵢ-1, raise) + x === nothing && return nothing + return Complex{T}(zero(x),x) + else # purely real + x = tryparse_internal(T, s, i, e, raise) + x === nothing && return nothing + return Complex{T}(x) + end + end + + if iᵢ < i₊ + raise && throw(ArgumentError("missing imaginary unit")) + return nothing # no imaginary part + end + + # parse real part + re = tryparse_internal(T, s, i, i₊-1, raise) + re === nothing && return nothing + + # parse imaginary part + im = tryparse_internal(T, s, i₊+1, iᵢ-1, raise) + im === nothing && return nothing + + return Complex{T}(re, s[i₊]=='-' ? -im : im) +end + +# the ±1 indexing above for ascii chars is specific to String, so convert: +tryparse_internal(T::Type{Complex{S}}, s::AbstractString, i::Int, e::Int, raise::Bool) where S<:Real = + tryparse_internal(T, String(s), i, e, raise) + +# fallback methods for tryparse_internal +tryparse_internal(::Type{T}, s::AbstractString, startpos::Int, endpos::Int) where T<:Real = + startpos == firstindex(s) && endpos == lastindex(s) ? tryparse(T, s) : tryparse(T, SubString(s, startpos, endpos)) +function tryparse_internal(::Type{T}, s::AbstractString, startpos::Int, endpos::Int, raise::Bool) where T<:Real + result = tryparse_internal(T, s, startpos, endpos) + if raise && result === nothing + _parse_failure(T, s, startpos, endpos) + end + return result +end +function tryparse_internal(::Type{T}, s::AbstractString, raise::Bool; kwargs...) where T<:Real + result = tryparse(T, s; kwargs...) + if raise && result === nothing + _parse_failure(T, s) + end + return result +end +@noinline _parse_failure(T, s::AbstractString, startpos = firstindex(s), endpos = lastindex(s)) = + throw(ArgumentError("cannot parse $(repr(s[startpos:endpos])) as $T")) + +tryparse_internal(::Type{T}, s::AbstractString, startpos::Int, endpos::Int, raise::Bool) where T<:Integer = + tryparse_internal(T, s, startpos, endpos, 10, raise) + +parse(::Type{T}, s::AbstractString; kwargs...) where T<:Real = + convert(T, tryparse_internal(T, s, true; kwargs...)) +parse(::Type{T}, s::AbstractString) where T<:Complex = + convert(T, tryparse_internal(T, s, firstindex(s), lastindex(s), true)) + +tryparse(T::Type{Complex{S}}, s::AbstractString) where S<:Real = + tryparse_internal(T, s, firstindex(s), lastindex(s), false) diff --git a/base/path.jl b/base/path.jl new file mode 100644 index 0000000..80cfda5 --- /dev/null +++ b/base/path.jl @@ -0,0 +1,467 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +export + abspath, + basename, + dirname, + expanduser, + contractuser, + homedir, + isabspath, + isdirpath, + joinpath, + normpath, + realpath, + relpath, + splitdir, + splitdrive, + splitext, + splitpath + +if Sys.isunix() + const path_separator = "/" + const path_separator_re = r"/+" + const path_directory_re = r"(?:^|/)\.{0,2}$" + const path_dir_splitter = r"^(.*?)(/+)([^/]*)$" + const path_ext_splitter = r"^((?:.*/)?(?:\.|[^/\.])[^/]*?)(\.[^/\.]*|)$" + + splitdrive(path::String) = ("",path) +elseif Sys.iswindows() + const path_separator = "\\" + const path_separator_re = r"[/\\]+" + const path_absolute_re = r"^(?:[A-Za-z]+:)?[/\\]" + const path_directory_re = r"(?:^|[/\\])\.{0,2}$" + const path_dir_splitter = r"^(.*?)([/\\]+)([^/\\]*)$" + const path_ext_splitter = r"^((?:.*[/\\])?(?:\.|[^/\\\.])[^/\\]*?)(\.[^/\\\.]*|)$" + + function splitdrive(path::String) + m = match(r"^([^\\]+:|\\\\[^\\]+\\[^\\]+|\\\\\?\\UNC\\[^\\]+\\[^\\]+|\\\\\?\\[^\\]+:|)(.*)$"s, path) + String(m.captures[1]), String(m.captures[2]) + end +else + error("path primitives for this OS need to be defined") +end + + +""" + splitdrive(path::AbstractString) -> (AbstractString, AbstractString) + +On Windows, split a path into the drive letter part and the path part. On Unix systems, the +first component is always the empty string. +""" +splitdrive(path::AbstractString) + +""" + homedir() -> AbstractString + +Return the current user's home directory. + +!!! note + `homedir` determines the home directory via `libuv`'s `uv_os_homedir`. For details + (for example on how to specify the home directory via environment variables), see the + [`uv_os_homedir` documentation](http://docs.libuv.org/en/v1.x/misc.html#c.uv_os_homedir). +""" +function homedir() + buf = Base.StringVector(AVG_PATH - 1) # space for null-terminator implied by StringVector + sz = RefValue{Csize_t}(length(buf) + 1) # total buffer size including null + while true + rc = ccall(:uv_os_homedir, Cint, (Ptr{UInt8}, Ptr{Csize_t}), buf, sz) + if rc == 0 + resize!(buf, sz[]) + return String(buf) + elseif rc == Base.UV_ENOBUFS + resize!(buf, sz[] - 1) # space for null-terminator implied by StringVector + else + uv_error(:homedir, rc) + end + end +end + + +if Sys.iswindows() + isabspath(path::String) = occursin(path_absolute_re, path) +else + isabspath(path::String) = startswith(path, '/') +end + +""" + isabspath(path::AbstractString) -> Bool + +Determine whether a path is absolute (begins at the root directory). + +# Examples +```jldoctest +julia> isabspath("/home") +true + +julia> isabspath("home") +false +``` +""" +isabspath(path::AbstractString) + +""" + isdirpath(path::AbstractString) -> Bool + +Determine whether a path refers to a directory (for example, ends with a path separator). + +# Examples +```jldoctest +julia> isdirpath("/home") +false + +julia> isdirpath("/home/") +true +``` +""" +isdirpath(path::String) = occursin(path_directory_re, splitdrive(path)[2]) + +""" + splitdir(path::AbstractString) -> (AbstractString, AbstractString) + +Split a path into a tuple of the directory name and file name. + +# Examples +```jldoctest +julia> splitdir("/home/myuser") +("/home", "myuser") +``` +""" +function splitdir(path::String) + a, b = splitdrive(path) + _splitdir_nodrive(a,b) +end + +# Common splitdir functionality without splitdrive, needed for splitpath. +_splitdir_nodrive(path::String) = _splitdir_nodrive("", path) +function _splitdir_nodrive(a::String, b::String) + m = match(path_dir_splitter,b) + m === nothing && return (a,b) + a = string(a, isempty(m.captures[1]) ? m.captures[2][1] : m.captures[1]) + a, String(m.captures[3]) +end + +""" + dirname(path::AbstractString) -> AbstractString + +Get the directory part of a path. Trailing characters ('/' or '\\') in the path are +counted as part of the path. + +# Examples +```jldoctest +julia> dirname("/home/myuser") +"/home" + +julia> dirname("/home/myuser/") +"/home/myuser" +``` + +See also: [`basename`](@ref) +""" + dirname(path::AbstractString) = splitdir(path)[1] + +""" + basename(path::AbstractString) -> AbstractString + +Get the file name part of a path. + +# Examples +```jldoctest +julia> basename("/home/myuser/example.jl") +"example.jl" +``` + +See also: [`dirname`](@ref) +""" +basename(path::AbstractString) = splitdir(path)[2] + +""" + splitext(path::AbstractString) -> (AbstractString, AbstractString) + +If the last component of a path contains a dot, split the path into everything before the +dot and everything including and after the dot. Otherwise, return a tuple of the argument +unmodified and the empty string. + +# Examples +```jldoctest +julia> splitext("/home/myuser/example.jl") +("/home/myuser/example", ".jl") + +julia> splitext("/home/myuser/example") +("/home/myuser/example", "") +``` +""" +function splitext(path::String) + a, b = splitdrive(path) + m = match(path_ext_splitter, b) + m === nothing && return (path,"") + a*m.captures[1], String(m.captures[2]) +end + +function pathsep(paths::AbstractString...) + for path in paths + m = match(path_separator_re, String(path)) + m !== nothing && return m.match[1:1] + end + return path_separator +end + +""" + splitpath(path::AbstractString) -> Vector{String} + +Split a file path into all its path components. This is the opposite of +`joinpath`. Returns an array of substrings, one for each directory or file in +the path, including the root directory if present. + +!!! compat "Julia 1.1" + This function requires at least Julia 1.1. + +# Examples +```jldoctest +julia> splitpath("/home/myuser/example.jl") +4-element Array{String,1}: + "/" + "home" + "myuser" + "example.jl" +``` +""" +function splitpath(p::String) + drive, p = splitdrive(p) + out = String[] + isempty(p) && (pushfirst!(out,p)) # "" means the current directory. + while !isempty(p) + dir, base = _splitdir_nodrive(p) + dir == p && (pushfirst!(out, dir); break) # Reached root node. + if !isempty(base) # Skip trailing '/' in basename + pushfirst!(out, base) + end + p = dir + end + if !isempty(drive) # Tack the drive back on to the first element. + out[1] = drive*out[1] # Note that length(out) is always >= 1. + end + return out +end + +joinpath(a::AbstractString) = a + +""" + joinpath(parts...) -> AbstractString + +Join path components into a full path. If some argument is an absolute path or +(on Windows) has a drive specification that doesn't match the drive computed for +the join of the preceding paths, then prior components are dropped. + +# Examples +```jldoctest +julia> joinpath("/home/myuser", "example.jl") +"/home/myuser/example.jl" +``` +""" +joinpath(a::AbstractString, b::AbstractString, c::AbstractString...) = joinpath(joinpath(a,b), c...) + +function joinpath(a::String, b::String) + isabspath(b) && return b + A, a = splitdrive(a) + B, b = splitdrive(b) + !isempty(B) && A != B && return string(B,b) + C = isempty(B) ? A : B + isempty(a) ? string(C,b) : + occursin(path_separator_re, a[end:end]) ? string(C,a,b) : + string(C,a,pathsep(a,b),b) +end +joinpath(a::AbstractString, b::AbstractString) = joinpath(String(a), String(b)) + +""" + normpath(path::AbstractString) -> AbstractString + +Normalize a path, removing "." and ".." entries. + +# Examples +```jldoctest +julia> normpath("/home/myuser/../example.jl") +"/home/example.jl" +``` +""" +function normpath(path::String) + isabs = isabspath(path) + isdir = isdirpath(path) + drive, path = splitdrive(path) + parts = split(path, path_separator_re) + filter!(x->!isempty(x) && x!=".", parts) + while true + clean = true + for j = 1:length(parts)-1 + if parts[j] != ".." && parts[j+1] == ".." + deleteat!(parts, j:j+1) + clean = false + break + end + end + clean && break + end + if isabs + while !isempty(parts) && parts[1] == ".." + popfirst!(parts) + end + elseif isempty(parts) + push!(parts, ".") + end + path = join(parts, path_separator) + if isabs + path = path_separator*path + end + if isdir && !isdirpath(path) + path *= path_separator + end + string(drive,path) +end +normpath(a::AbstractString, b::AbstractString...) = normpath(joinpath(a,b...)) + +""" + abspath(path::AbstractString) -> AbstractString + +Convert a path to an absolute path by adding the current directory if necessary. +Also normalizes the path as in [`normpath`](@ref). +""" +abspath(a::String) = normpath(isabspath(a) ? a : joinpath(pwd(),a)) + +""" + abspath(path::AbstractString, paths::AbstractString...) -> AbstractString + +Convert a set of paths to an absolute path by joining them together and adding the +current directory if necessary. Equivalent to `abspath(joinpath(path, paths...))`. +""" +abspath(a::AbstractString, b::AbstractString...) = abspath(joinpath(a,b...)) + +if Sys.iswindows() + +function longpath(path::AbstractString) + p = cwstring(path) + buf = zeros(UInt16, length(p)) + while true + n = ccall((:GetLongPathNameW, "kernel32"), stdcall, + UInt32, (Ptr{UInt16}, Ptr{UInt16}, UInt32), + p, buf, length(buf)) + windowserror(:longpath, n == 0) + x = n < length(buf) # is the buffer big enough? + resize!(buf, n) # shrink if x, grow if !x + x && return transcode(String, buf) + end +end + +end # os-test + + +""" + realpath(path::AbstractString) -> AbstractString + +Canonicalize a path by expanding symbolic links and removing "." and ".." entries. +On case-insensitive case-preserving filesystems (typically Mac and Windows), the +filesystem's stored case for the path is returned. + +(This function throws an exception if `path` does not exist in the filesystem.) +""" +function realpath(path::AbstractString) + req = Libc.malloc(_sizeof_uv_fs) + try + ret = ccall(:uv_fs_realpath, Cint, + (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Ptr{Cvoid}), + C_NULL, req, path, C_NULL) + if ret < 0 + ccall(:uv_fs_req_cleanup, Cvoid, (Ptr{Cvoid},), req) + uv_error("realpath", ret) + end + path = unsafe_string(ccall(:jl_uv_fs_t_ptr, Cstring, (Ptr{Cvoid},), req)) + ccall(:uv_fs_req_cleanup, Cvoid, (Ptr{Cvoid},), req) + return path + finally + Libc.free(req) + end +end + +if Sys.iswindows() +# on windows, ~ means "temporary file" +expanduser(path::AbstractString) = path +contractuser(path::AbstractString) = path +else +function expanduser(path::AbstractString) + y = iterate(path) + y === nothing && return path + c, i = y + c != '~' && return path + y = iterate(path, i) + y === nothing && return homedir() + y[1] == '/' && return homedir() * path[i:end] + throw(ArgumentError("~user tilde expansion not yet implemented")) +end +function contractuser(path::AbstractString) + home = homedir() + if path == home + return "~" + elseif startswith(path, home) + return joinpath("~", relpath(path, home)) + else + return path + end +end +end + + +""" + expanduser(path::AbstractString) -> AbstractString + +On Unix systems, replace a tilde character at the start of a path with the current user's home directory. +""" +expanduser(path::AbstractString) + +""" + contractuser(path::AbstractString) -> AbstractString + +On Unix systems, if the path starts with `homedir()`, replace it with a tilde character. +""" +contractuser(path::AbstractString) + + +""" + relpath(path::AbstractString, startpath::AbstractString = ".") -> AbstractString + +Return a relative filepath to `path` either from the current directory or from an optional +start directory. This is a path computation: the filesystem is not accessed to confirm the +existence or nature of `path` or `startpath`. +""" +function relpath(path::String, startpath::String = ".") + isempty(path) && throw(ArgumentError("`path` must be specified")) + isempty(startpath) && throw(ArgumentError("`startpath` must be specified")) + curdir = "." + pardir = ".." + path == startpath && return curdir + path_arr = split(abspath(path), path_separator_re) + start_arr = split(abspath(startpath), path_separator_re) + i = 0 + while i < min(length(path_arr), length(start_arr)) + i += 1 + if path_arr[i] != start_arr[i] + i -= 1 + break + end + end + pathpart = join(path_arr[i+1:something(findlast(x -> !isempty(x), path_arr), 0)], path_separator) + prefix_num = something(findlast(x -> !isempty(x), start_arr), 0) - i - 1 + if prefix_num >= 0 + prefix = pardir * path_separator + relpath_ = isempty(pathpart) ? + (prefix^prefix_num) * pardir : + (prefix^prefix_num) * pardir * path_separator * pathpart + else + relpath_ = pathpart + end + return isempty(relpath_) ? curdir : relpath_ +end +relpath(path::AbstractString, startpath::AbstractString) = + relpath(String(path), String(startpath)) + +for f in (:isabspath, :isdirpath, :splitdir, :splitdrive, :splitext, :normpath, :abspath) + @eval $f(path::AbstractString) = $f(String(path)) +end diff --git a/base/pcre.jl b/base/pcre.jl new file mode 100644 index 0000000..b0d96b9 --- /dev/null +++ b/base/pcre.jl @@ -0,0 +1,222 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## low-level pcre2 interface ## + +module PCRE + +import ..RefValue + +include(string(length(Core.ARGS) >= 2 ? Core.ARGS[2] : "", "pcre_h.jl")) # include($BUILDROOT/base/pcre_h.jl) + +const PCRE_LIB = "libpcre2-8" + +function create_match_context() + JIT_STACK_START_SIZE = 32768 + JIT_STACK_MAX_SIZE = 1048576 + jit_stack = ccall((:pcre2_jit_stack_create_8, PCRE_LIB), Ptr{Cvoid}, + (Cint, Cint, Ptr{Cvoid}), + JIT_STACK_START_SIZE, JIT_STACK_MAX_SIZE, C_NULL) + ctx = ccall((:pcre2_match_context_create_8, PCRE_LIB), + Ptr{Cvoid}, (Ptr{Cvoid},), C_NULL) + ccall((:pcre2_jit_stack_assign_8, PCRE_LIB), Cvoid, + (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}), ctx, C_NULL, jit_stack) + return ctx +end + +const THREAD_MATCH_CONTEXTS = Ptr{Cvoid}[C_NULL] + +PCRE_COMPILE_LOCK = nothing + +_tid() = Int(ccall(:jl_threadid, Int16, ())+1) +_nth() = Int(unsafe_load(cglobal(:jl_n_threads, Cint))) + +function get_local_match_context() + tid = _tid() + ctx = @inbounds THREAD_MATCH_CONTEXTS[tid] + if ctx == C_NULL + @inbounds THREAD_MATCH_CONTEXTS[tid] = ctx = create_match_context() + end + return ctx +end + +function __init__() + resize!(THREAD_MATCH_CONTEXTS, _nth()) + fill!(THREAD_MATCH_CONTEXTS, C_NULL) + global PCRE_COMPILE_LOCK = Threads.SpinLock() +end + +# supported options for different use cases + +const COMPILE_MASK = + ANCHORED | + CASELESS | + DOLLAR_ENDONLY | + DOTALL | + ENDANCHORED | + EXTENDED | + FIRSTLINE | + MULTILINE | + NEWLINE_ANY | + NEWLINE_ANYCRLF | + NEWLINE_CR | + NEWLINE_CRLF | + NEWLINE_LF | + NO_AUTO_CAPTURE | + NO_START_OPTIMIZE | + NO_UTF_CHECK | + UNGREEDY | + UTF | + UCP + +const EXECUTE_MASK = + NEWLINE_ANY | + NEWLINE_ANYCRLF | + NEWLINE_CR | + NEWLINE_CRLF | + NEWLINE_LF | + NOTBOL | + NOTEMPTY | + NOTEMPTY_ATSTART | + NOTEOL | + NO_START_OPTIMIZE | + NO_UTF_CHECK | + PARTIAL_HARD | + PARTIAL_SOFT + + +const OPTIONS_MASK = COMPILE_MASK | EXECUTE_MASK + +const UNSET = ~Csize_t(0) # Indicates that an output vector element is unset + +function info(regex::Ptr{Cvoid}, what::Integer, ::Type{T}) where T + buf = RefValue{T}() + ret = ccall((:pcre2_pattern_info_8, PCRE_LIB), Int32, + (Ptr{Cvoid}, Int32, Ptr{Cvoid}), + regex, what, buf) % UInt32 + if ret != 0 + error(ret == ERROR_NULL ? "NULL regex object" : + ret == ERROR_BADMAGIC ? "invalid regex object" : + ret == ERROR_BADOPTION ? "invalid option flags" : + "unknown error $ret") + end + buf[] +end + +function ovec_length(match_data) + n = ccall((:pcre2_get_ovector_count_8, PCRE_LIB), UInt32, + (Ptr{Cvoid},), match_data) + return 2n +end + +function ovec_ptr(match_data) + ptr = ccall((:pcre2_get_ovector_pointer_8, PCRE_LIB), Ptr{Csize_t}, + (Ptr{Cvoid},), match_data) + return ptr +end + +function compile(pattern::AbstractString, options::Integer) + errno = RefValue{Cint}(0) + erroff = RefValue{Csize_t}(0) + re_ptr = ccall((:pcre2_compile_8, PCRE_LIB), Ptr{Cvoid}, + (Ptr{UInt8}, Csize_t, UInt32, Ref{Cint}, Ref{Csize_t}, Ptr{Cvoid}), + pattern, sizeof(pattern), options, errno, erroff, C_NULL) + re_ptr == C_NULL && error("PCRE compilation error: $(err_message(errno[])) at offset $(erroff[])") + re_ptr +end + +function jit_compile(regex::Ptr{Cvoid}) + errno = ccall((:pcre2_jit_compile_8, PCRE_LIB), Cint, + (Ptr{Cvoid}, UInt32), regex, JIT_COMPLETE) % UInt32 + errno == 0 && return true + errno == ERROR_JIT_BADOPTION && return false + error("PCRE JIT error: $(err_message(errno))") +end + +free_match_data(match_data) = + ccall((:pcre2_match_data_free_8, PCRE_LIB), Cvoid, (Ptr{Cvoid},), match_data) + +free_re(re) = + ccall((:pcre2_code_free_8, PCRE_LIB), Cvoid, (Ptr{Cvoid},), re) + +free_jit_stack(stack) = + ccall((:pcre2_jit_stack_free_8, PCRE_LIB), Cvoid, (Ptr{Cvoid},), stack) + +free_match_context(context) = + ccall((:pcre2_match_context_free_8, PCRE_LIB), Cvoid, (Ptr{Cvoid},), context) + +function err_message(errno) + buffer = Vector{UInt8}(undef, 256) + ccall((:pcre2_get_error_message_8, PCRE_LIB), Cvoid, + (Int32, Ptr{UInt8}, Csize_t), errno, buffer, sizeof(buffer)) + GC.@preserve buffer unsafe_string(pointer(buffer)) +end + +function exec(re, subject, offset, options, match_data) + rc = ccall((:pcre2_match_8, PCRE_LIB), Cint, + (Ptr{Cvoid}, Ptr{UInt8}, Csize_t, Csize_t, Cuint, Ptr{Cvoid}, Ptr{Cvoid}), + re, subject, sizeof(subject), offset, options, match_data, get_local_match_context()) + # rc == -1 means no match, -2 means partial match. + rc < -2 && error("PCRE.exec error: $(err_message(rc))") + rc >= 0 +end + +function exec_r(re, subject, offset, options) + match_data = create_match_data(re) + ans = exec(re, subject, offset, options, match_data) + free_match_data(match_data) + return ans +end + +function exec_r_data(re, subject, offset, options) + match_data = create_match_data(re) + ans = exec(re, subject, offset, options, match_data) + return ans, match_data +end + +function create_match_data(re) + ccall((:pcre2_match_data_create_from_pattern_8, PCRE_LIB), + Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}), re, C_NULL) +end + +function substring_number_from_name(re, name) + ccall((:pcre2_substring_number_from_name_8, PCRE_LIB), Cint, + (Ptr{Cvoid}, Cstring), re, name) +end + +function substring_length_bynumber(match_data, number) + s = RefValue{Csize_t}() + rc = ccall((:pcre2_substring_length_bynumber_8, PCRE_LIB), Cint, + (Ptr{Cvoid}, UInt32, Ref{Csize_t}), match_data, number, s) + rc < 0 && error("PCRE error: $(err_message(rc))") + convert(Int, s[]) +end + +function substring_copy_bynumber(match_data, number, buf, buf_size) + s = RefValue{Csize_t}(buf_size) + rc = ccall((:pcre2_substring_copy_bynumber_8, PCRE_LIB), Cint, + (Ptr{Cvoid}, UInt32, Ptr{UInt8}, Ref{Csize_t}), + match_data, number, buf, s) + rc < 0 && error("PCRE error: $(err_message(rc))") + convert(Int, s[]) +end + +function capture_names(re) + name_count = info(re, INFO_NAMECOUNT, UInt32) + name_entry_size = info(re, INFO_NAMEENTRYSIZE, UInt32) + nametable_ptr = info(re, INFO_NAMETABLE, Ptr{UInt8}) + names = Dict{Int, String}() + for i=1:name_count + offset = (i-1)*name_entry_size + 1 + # The capture group index corresponding to name 'i' is stored as a + # big-endian 16-bit value. + high_byte = UInt16(unsafe_load(nametable_ptr, offset)) + low_byte = UInt16(unsafe_load(nametable_ptr, offset+1)) + idx = (high_byte << 8) | low_byte + # The capture group name is a null-terminated string located directly + # after the index. + names[idx] = unsafe_string(nametable_ptr+offset+1) + end + names +end + +end # module diff --git a/base/permuteddimsarray.jl b/base/permuteddimsarray.jl new file mode 100644 index 0000000..d50cc11 --- /dev/null +++ b/base/permuteddimsarray.jl @@ -0,0 +1,260 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +module PermutedDimsArrays + +import Base: permutedims, permutedims! +export PermutedDimsArray + +# Some day we will want storage-order-aware iteration, so put perm in the parameters +struct PermutedDimsArray{T,N,perm,iperm,AA<:AbstractArray} <: AbstractArray{T,N} + parent::AA + + function PermutedDimsArray{T,N,perm,iperm,AA}(data::AA) where {T,N,perm,iperm,AA<:AbstractArray} + (isa(perm, NTuple{N,Int}) && isa(iperm, NTuple{N,Int})) || error("perm and iperm must both be NTuple{$N,Int}") + isperm(perm) || throw(ArgumentError(string(perm, " is not a valid permutation of dimensions 1:", N))) + all(map(d->iperm[perm[d]]==d, 1:N)) || throw(ArgumentError(string(perm, " and ", iperm, " must be inverses"))) + new(data) + end +end + +""" + PermutedDimsArray(A, perm) -> B + +Given an AbstractArray `A`, create a view `B` such that the +dimensions appear to be permuted. Similar to `permutedims`, except +that no copying occurs (`B` shares storage with `A`). + +See also: [`permutedims`](@ref). + +# Examples +```jldoctest +julia> A = rand(3,5,4); + +julia> B = PermutedDimsArray(A, (3,1,2)); + +julia> size(B) +(4, 3, 5) + +julia> B[3,1,2] == A[1,2,3] +true +``` +""" +function PermutedDimsArray(data::AbstractArray{T,N}, perm) where {T,N} + length(perm) == N || throw(ArgumentError(string(perm, " is not a valid permutation of dimensions 1:", N))) + iperm = invperm(perm) + PermutedDimsArray{T,N,(perm...,),(iperm...,),typeof(data)}(data) +end + +Base.parent(A::PermutedDimsArray) = A.parent +Base.size(A::PermutedDimsArray{T,N,perm}) where {T,N,perm} = genperm(size(parent(A)), perm) +Base.axes(A::PermutedDimsArray{T,N,perm}) where {T,N,perm} = genperm(axes(parent(A)), perm) + +Base.unsafe_convert(::Type{Ptr{T}}, A::PermutedDimsArray{T}) where {T} = Base.unsafe_convert(Ptr{T}, parent(A)) + +# It's OK to return a pointer to the first element, and indeed quite +# useful for wrapping C routines that require a different storage +# order than used by Julia. But for an array with unconventional +# storage order, a linear offset is ambiguous---is it a memory offset +# or a linear index? +Base.pointer(A::PermutedDimsArray, i::Integer) = throw(ArgumentError("pointer(A, i) is deliberately unsupported for PermutedDimsArray")) + +function Base.strides(A::PermutedDimsArray{T,N,perm}) where {T,N,perm} + s = strides(parent(A)) + ntuple(d->s[perm[d]], Val(N)) +end + +@inline function Base.getindex(A::PermutedDimsArray{T,N,perm,iperm}, I::Vararg{Int,N}) where {T,N,perm,iperm} + @boundscheck checkbounds(A, I...) + @inbounds val = getindex(A.parent, genperm(I, iperm)...) + val +end +@inline function Base.setindex!(A::PermutedDimsArray{T,N,perm,iperm}, val, I::Vararg{Int,N}) where {T,N,perm,iperm} + @boundscheck checkbounds(A, I...) + @inbounds setindex!(A.parent, val, genperm(I, iperm)...) + val +end + +@inline genperm(I::NTuple{N,Any}, perm::Dims{N}) where {N} = ntuple(d -> I[perm[d]], Val(N)) +@inline genperm(I, perm::AbstractVector{Int}) = genperm(I, (perm...,)) + +""" + permutedims(A::AbstractArray, perm) + +Permute the dimensions of array `A`. `perm` is a vector specifying a permutation of length +`ndims(A)`. + +See also: [`PermutedDimsArray`](@ref). + +# Examples +```jldoctest +julia> A = reshape(Vector(1:8), (2,2,2)) +2×2×2 Array{Int64,3}: +[:, :, 1] = + 1 3 + 2 4 + +[:, :, 2] = + 5 7 + 6 8 + +julia> permutedims(A, [3, 2, 1]) +2×2×2 Array{Int64,3}: +[:, :, 1] = + 1 3 + 5 7 + +[:, :, 2] = + 2 4 + 6 8 +``` +""" +function permutedims(A::AbstractArray, perm) + dest = similar(A, genperm(axes(A), perm)) + permutedims!(dest, A, perm) +end + +""" + permutedims(m::AbstractMatrix) + +Permute the dimensions of the matrix `m`, by flipping the elements across the diagonal of +the matrix. Differs from `LinearAlgebra`'s [`transpose`](@ref) in that the +operation is not recursive. + +# Examples +```jldoctest; setup = :(using LinearAlgebra) +julia> a = [1 2; 3 4]; + +julia> b = [5 6; 7 8]; + +julia> c = [9 10; 11 12]; + +julia> d = [13 14; 15 16]; + +julia> X = [[a] [b]; [c] [d]] +2×2 Array{Array{Int64,2},2}: + [1 2; 3 4] [5 6; 7 8] + [9 10; 11 12] [13 14; 15 16] + +julia> permutedims(X) +2×2 Array{Array{Int64,2},2}: + [1 2; 3 4] [9 10; 11 12] + [5 6; 7 8] [13 14; 15 16] + +julia> transpose(X) +2×2 Transpose{Transpose{Int64,Array{Int64,2}},Array{Array{Int64,2},2}}: + [1 3; 2 4] [9 11; 10 12] + [5 7; 6 8] [13 15; 14 16] +``` +""" +permutedims(A::AbstractMatrix) = permutedims(A, (2,1)) + +""" + permutedims(v::AbstractVector) + +Reshape vector `v` into a `1 × length(v)` row matrix. +Differs from `LinearAlgebra`'s [`transpose`](@ref) in that +the operation is not recursive. + +# Examples +```jldoctest; setup = :(using LinearAlgebra) +julia> permutedims([1, 2, 3, 4]) +1×4 Array{Int64,2}: + 1 2 3 4 + +julia> V = [[[1 2; 3 4]]; [[5 6; 7 8]]] +2-element Array{Array{Int64,2},1}: + [1 2; 3 4] + [5 6; 7 8] + +julia> permutedims(V) +1×2 Array{Array{Int64,2},2}: + [1 2; 3 4] [5 6; 7 8] + +julia> transpose(V) +1×2 Transpose{Transpose{Int64,Array{Int64,2}},Array{Array{Int64,2},1}}: + [1 3; 2 4] [5 7; 6 8] +``` +""" +permutedims(v::AbstractVector) = reshape(v, (1, length(v))) + +""" + permutedims!(dest, src, perm) + +Permute the dimensions of array `src` and store the result in the array `dest`. `perm` is a +vector specifying a permutation of length `ndims(src)`. The preallocated array `dest` should +have `size(dest) == size(src)[perm]` and is completely overwritten. No in-place permutation +is supported and unexpected results will happen if `src` and `dest` have overlapping memory +regions. + +See also [`permutedims`](@ref). +""" +function permutedims!(dest, src::AbstractArray, perm) + Base.checkdims_perm(dest, src, perm) + P = PermutedDimsArray(dest, invperm(perm)) + _copy!(P, src) + return dest +end + +function Base.copyto!(dest::PermutedDimsArray{T,N}, src::AbstractArray{T,N}) where {T,N} + checkbounds(dest, axes(src)...) + _copy!(dest, src) +end +Base.copyto!(dest::PermutedDimsArray, src::AbstractArray) = _copy!(dest, src) + +function _copy!(P::PermutedDimsArray{T,N,perm}, src) where {T,N,perm} + # If dest/src are "close to dense," then it pays to be cache-friendly. + # Determine the first permuted dimension + d = 0 # d+1 will hold the first permuted dimension of src + while d < ndims(src) && perm[d+1] == d+1 + d += 1 + end + if d == ndims(src) + copyto!(parent(P), src) # it's not permuted + else + R1 = CartesianIndices(axes(src)[1:d]) + d1 = findfirst(isequal(d+1), perm)::Int # first permuted dim of dest + R2 = CartesianIndices(axes(src)[d+2:d1-1]) + R3 = CartesianIndices(axes(src)[d1+1:end]) + _permutedims!(P, src, R1, R2, R3, d+1, d1) + end + return P +end + +@noinline function _permutedims!(P::PermutedDimsArray, src, R1::CartesianIndices{0}, R2, R3, ds, dp) + ip, is = axes(src, dp), axes(src, ds) + for jo in first(ip):8:last(ip), io in first(is):8:last(is) + for I3 in R3, I2 in R2 + for j in jo:min(jo+7, last(ip)) + for i in io:min(io+7, last(is)) + @inbounds P[i, I2, j, I3] = src[i, I2, j, I3] + end + end + end + end + P +end + +@noinline function _permutedims!(P::PermutedDimsArray, src, R1, R2, R3, ds, dp) + ip, is = axes(src, dp), axes(src, ds) + for jo in first(ip):8:last(ip), io in first(is):8:last(is) + for I3 in R3, I2 in R2 + for j in jo:min(jo+7, last(ip)) + for i in io:min(io+7, last(is)) + for I1 in R1 + @inbounds P[I1, i, I2, j, I3] = src[I1, i, I2, j, I3] + end + end + end + end + end + P +end + +function Base.showarg(io::IO, A::PermutedDimsArray{T,N,perm}, toplevel) where {T,N,perm} + print(io, "PermutedDimsArray(") + Base.showarg(io, parent(A), false) + print(io, ", ", perm, ')') + toplevel && print(io, " with eltype ", eltype(A)) +end + +end diff --git a/base/pointer.jl b/base/pointer.jl new file mode 100644 index 0000000..965139e --- /dev/null +++ b/base/pointer.jl @@ -0,0 +1,161 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" + Ptr{T} + +A memory address referring to data of type `T`. However, there is no guarantee that the +memory is actually valid, or that it actually represents data of the specified type. +""" +Ptr + +## converting pointers to an appropriate unsigned ## + +""" + C_NULL + +The C null pointer constant, sometimes used when calling external code. +""" +const C_NULL = bitcast(Ptr{Cvoid}, 0) + +# TODO: deprecate these conversions. C doesn't even allow them. + +# pointer to integer +convert(::Type{T}, x::Ptr) where {T<:Integer} = T(UInt(x)) + +# integer to pointer +convert(::Type{Ptr{T}}, x::Union{Int,UInt}) where {T} = Ptr{T}(x) + +# pointer to pointer +convert(::Type{Ptr{T}}, p::Ptr{T}) where {T} = p +convert(::Type{Ptr{T}}, p::Ptr) where {T} = bitcast(Ptr{T}, p) + +# object to pointer (when used with ccall) + +""" + unsafe_convert(T, x) + +Convert `x` to a C argument of type `T` +where the input `x` must be the return value of `cconvert(T, ...)`. + +In cases where [`convert`](@ref) would need to take a Julia object +and turn it into a `Ptr`, this function should be used to define and perform +that conversion. + +Be careful to ensure that a Julia reference to `x` exists as long as the result of this +function will be used. Accordingly, the argument `x` to this function should never be an +expression, only a variable name or field reference. For example, `x=a.b.c` is acceptable, +but `x=[a,b,c]` is not. + +The `unsafe` prefix on this function indicates that using the result of this function after +the `x` argument to this function is no longer accessible to the program may cause undefined +behavior, including program corruption or segfaults, at any later time. + +See also [`cconvert`](@ref) +""" +function unsafe_convert end + +unsafe_convert(::Type{Ptr{UInt8}}, x::Symbol) = ccall(:jl_symbol_name, Ptr{UInt8}, (Any,), x) +unsafe_convert(::Type{Ptr{Int8}}, x::Symbol) = ccall(:jl_symbol_name, Ptr{Int8}, (Any,), x) +unsafe_convert(::Type{Ptr{UInt8}}, s::String) = convert(Ptr{UInt8}, pointer_from_objref(s)+sizeof(Int)) +unsafe_convert(::Type{Ptr{Int8}}, s::String) = convert(Ptr{Int8}, pointer_from_objref(s)+sizeof(Int)) +# convert strings to String etc. to pass as pointers +cconvert(::Type{Ptr{UInt8}}, s::AbstractString) = String(s) +cconvert(::Type{Ptr{Int8}}, s::AbstractString) = String(s) + +unsafe_convert(::Type{Ptr{T}}, a::Array{T}) where {T} = ccall(:jl_array_ptr, Ptr{T}, (Any,), a) +unsafe_convert(::Type{Ptr{S}}, a::AbstractArray{T}) where {S,T} = convert(Ptr{S}, unsafe_convert(Ptr{T}, a)) +unsafe_convert(::Type{Ptr{T}}, a::AbstractArray{T}) where {T} = error("conversion to pointer not defined for $(typeof(a))") + +# unsafe pointer to array conversions +""" + unsafe_wrap(Array, pointer::Ptr{T}, dims; own = false) + +Wrap a Julia `Array` object around the data at the address given by `pointer`, +without making a copy. The pointer element type `T` determines the array +element type. `dims` is either an integer (for a 1d array) or a tuple of the array dimensions. +`own` optionally specifies whether Julia should take ownership of the memory, +calling `free` on the pointer when the array is no longer referenced. + +This function is labeled "unsafe" because it will crash if `pointer` is not +a valid memory address to data of the requested length. +""" +function unsafe_wrap(::Union{Type{Array},Type{Array{T}},Type{Array{T,N}}}, + p::Ptr{T}, dims::NTuple{N,Int}; own::Bool = false) where {T,N} + ccall(:jl_ptr_to_array, Array{T,N}, (Any, Ptr{Cvoid}, Any, Int32), + Array{T,N}, p, dims, own) +end +function unsafe_wrap(::Union{Type{Array},Type{Array{T}},Type{Array{T,1}}}, + p::Ptr{T}, d::Integer; own::Bool = false) where {T} + ccall(:jl_ptr_to_array_1d, Array{T,1}, + (Any, Ptr{Cvoid}, Csize_t, Cint), Array{T,1}, p, d, own) +end +unsafe_wrap(Atype::Type, p::Ptr, dims::NTuple{N,<:Integer}; own::Bool = false) where {N} = + unsafe_wrap(Atype, p, convert(Tuple{Vararg{Int}}, dims), own = own) + +""" + unsafe_load(p::Ptr{T}, i::Integer=1) + +Load a value of type `T` from the address of the `i`th element (1-indexed) starting at `p`. +This is equivalent to the C expression `p[i-1]`. + +The `unsafe` prefix on this function indicates that no validation is performed on the +pointer `p` to ensure that it is valid. Incorrect usage may segfault your program or return +garbage answers, in the same manner as C. +""" +unsafe_load(p::Ptr, i::Integer=1) = pointerref(p, Int(i), 1) + +""" + unsafe_store!(p::Ptr{T}, x, i::Integer=1) + +Store a value of type `T` to the address of the `i`th element (1-indexed) starting at `p`. +This is equivalent to the C expression `p[i-1] = x`. + +The `unsafe` prefix on this function indicates that no validation is performed on the +pointer `p` to ensure that it is valid. Incorrect usage may corrupt or segfault your +program, in the same manner as C. +""" +unsafe_store!(p::Ptr{Any}, @nospecialize(x), i::Integer=1) = pointerset(p, x, Int(i), 1) +unsafe_store!(p::Ptr{T}, x, i::Integer=1) where {T} = pointerset(p, convert(T,x), Int(i), 1) + +# convert a raw Ptr to an object reference, and vice-versa +""" + unsafe_pointer_to_objref(p::Ptr) + +Convert a `Ptr` to an object reference. Assumes the pointer refers to a valid heap-allocated +Julia object. If this is not the case, undefined behavior results, hence this function is +considered "unsafe" and should be used with care. + +See also: [`pointer_from_objref`](@ref). +""" +unsafe_pointer_to_objref(x::Ptr) = ccall(:jl_value_ptr, Any, (Ptr{Cvoid},), x) + +""" + pointer_from_objref(x) + +Get the memory address of a Julia object as a `Ptr`. The existence of the resulting `Ptr` +will not protect the object from garbage collection, so you must ensure that the object +remains referenced for the whole time that the `Ptr` will be used. + +This function may not be called on immutable objects, since they do not have +stable memory addresses. + +See also: [`unsafe_pointer_to_objref`](@ref). +""" +function pointer_from_objref(@nospecialize(x)) + @_inline_meta + typeof(x).mutable || error("pointer_from_objref cannot be used on immutable objects") + ccall(:jl_value_ptr, Ptr{Cvoid}, (Any,), x) +end + +## limited pointer arithmetic & comparison ## + +isequal(x::Ptr, y::Ptr) = (x === y) +isless(x::Ptr{T}, y::Ptr{T}) where {T} = x < y + +==(x::Ptr, y::Ptr) = UInt(x) == UInt(y) +<(x::Ptr, y::Ptr) = UInt(x) < UInt(y) +-(x::Ptr, y::Ptr) = UInt(x) - UInt(y) + ++(x::Ptr, y::Integer) = oftype(x, add_ptr(UInt(x), (y % UInt) % UInt)) +-(x::Ptr, y::Integer) = oftype(x, sub_ptr(UInt(x), (y % UInt) % UInt)) ++(x::Integer, y::Ptr) = y + x diff --git a/base/printf.jl b/base/printf.jl new file mode 100644 index 0000000..42eb1cb --- /dev/null +++ b/base/printf.jl @@ -0,0 +1,1254 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +module Printf +using .Base.Grisu +using .Base.GMP + +### printf formatter generation ### +const SmallFloatingPoint = Union{Float64,Float32,Float16} +const SmallNumber = Union{SmallFloatingPoint,Base.BitInteger} + +function gen(s::AbstractString) + args = [] + blk = Expr(:block, :(local neg, pt, len, exp, do_out, args, buf)) + gotbuf = false + for x in parse(s) + if isa(x,AbstractString) + push!(blk.args, :(print(out, $(length(x)==1 ? x[1] : x)))) + else + c = lowercase(x[end]) + f = c=='f' ? gen_f : + c=='e' ? gen_e : + c=='a' ? gen_a : + c=='g' ? gen_g : + c=='c' ? gen_c : + c=='s' ? gen_s : + c=='p' ? gen_p : + gen_d + if !gotbuf && c != 'c' && c != 's' && c != 'p' + push!(blk.args, :(buf = $Grisu.getbuf())) + gotbuf = true + end + arg, ex = f(x...) + push!(args, arg) + push!(blk.args, ex) + end + end + push!(blk.args, :nothing) + return args, blk +end + +### printf format string parsing ### + +function parse(s::AbstractString) + # parse format string into strings and format tuples + list = [] + a = Iterators.Stateful(pairs(s)) + lastparse = firstindex(s) + lastidx = 0 # invariant: lastidx == prevind(s, idx) + for (idx, c) in a + if c == '%' + lastparse > lastidx || push!(list, s[lastparse:lastidx]) + flags, width, precision, conversion = parse1!(s, a) + '\'' in flags && error("printf format flag ' not yet supported") + conversion == 'n' && error("printf feature %n not supported") + push!(list, conversion == '%' ? "%" : (flags,width,precision,conversion)) + lastparse = isempty(a) ? lastindex(s)+1 : Base.peek(a)[1] + end + lastidx = idx + end + lastparse > lastindex(s) || push!(list, s[lastparse:end]) + # coalesce adjacent strings + i = j = 1 + while i < length(list) + if isa(list[i],AbstractString) + for outer j = i+1:length(list) + if !isa(list[j],AbstractString) + j -= 1 + break + end + list[i] *= list[j] + end + deleteat!(list,i+1:j) + end + i += 1 + end + return list +end + +## parse a single printf specifier ## + +# printf specifiers: +# % # start +# (\d+\$)? # arg (not supported) +# [\-\+#0' ]* # flags +# (\d+)? # width +# (\.\d*)? # precision +# (h|hh|l|ll|L|j|t|z|q)? # modifier (ignored) +# [diouxXeEfFgGaAcCsSp%] # conversion + +pop_or_die!(s, a) = !isempty(a) ? popfirst!(a) : + throw(ArgumentError("invalid printf format string: $(repr(s))")) + +function parse1!(s, a) + width = 0 + precision = -1 + k, c = pop_or_die!(s, a) + j = k + # handle %% + if c == '%' + return "", width, precision, c + end + # parse flags + while c in "#0- + '" + k, c = pop_or_die!(s, a) + end + flags = String(s[j:k-1]) # All flags are 1 byte + # parse width + while '0' <= c <= '9' + width = 10*width + c-'0' + _, c = pop_or_die!(s, a) + end + # parse precision + if c == '.' + _, c = pop_or_die!(s, a) + if '0' <= c <= '9' + precision = 0 + while '0' <= c <= '9' + precision = 10*precision + c-'0' + _, c = pop_or_die!(s, a) + end + end + end + # parse length modifer (ignored) + if c == 'h' || c == 'l' + prev = c + _, c = pop_or_die!(s, a) + if c == prev + _, c = pop_or_die!(s, a) + end + elseif c in "Ljqtz" + _, c = pop_or_die!(s, a) + end + # validate conversion + if !(c in "diouxXDOUeEfFgGaAcCsSpn") + throw(ArgumentError("invalid printf format string: $(repr(s))")) + end + # TODO: warn about silly flag/conversion combinations + flags, width, precision, c +end + +### printf formatter generation ### + +function special_handler(flags::String, width::Int) + @gensym x + blk = Expr(:block) + pad = '-' in flags ? rpad : lpad + pos = '+' in flags ? "+" : + ' ' in flags ? " " : "" + abn = quote + isnan($x) ? $(pad("NaN", width)) : + $x < 0 ? $(pad("-Inf", width)) : + $(pad("$(pos)Inf", width)) + end + ex = :(isfinite($x) ? $blk : print(out, $abn)) + x, ex, blk +end + +function pad(m::Int, n, c::Char) + if m <= 1 + :($n > 0 && print(out,$c)) + else + @gensym i + quote + $i = $n + while $i > 0 + print(out,$c) + $i -= 1 + end + end + end +end + +function dynamic_pad(m, val, c::Char) + @gensym i + quote + if $m <= 1 + $val > 0 && print(out,$c) + else + $i = $val + while $i > 0 + print(out,$c) + $i -= 1 + end + end + end +end + +# returns the number of (ASCII) chars output by print_fixed +function print_fixed_width(precision, pt, ndigits, trailingzeros=true) + count = 0 + if pt <= 0 + # 0.0dddd0 + count += 2 + precision += pt + if pt < 0 + count -= pt + end + count += ndigits + precision -= ndigits + elseif ndigits <= pt + # dddd000.000000 + count += ndigits + if ndigits < pt + count += pt - ndigits + end + count += trailingzeros + else # 0 < pt < ndigits + # dd.dd0000 + ndigits -= pt + count += pt + 1 + ndigits + precision -= ndigits + end + if trailingzeros && precision > 0 + count += precision + end + return count +end + +# note: if print_fixed is changed, print_fixed_width should be changed accordingly +function print_fixed(out, precision, pt, ndigits, trailingzeros=true, buf = Grisu.getbuf()) + pdigits = pointer(buf) + if pt <= 0 + # 0.0dddd0 + print(out, '0') + print(out, '.') + precision += pt + while pt < 0 + print(out, '0') + pt += 1 + end + unsafe_write(out, pdigits, ndigits) + precision -= ndigits + elseif ndigits <= pt + # dddd000.000000 + unsafe_write(out, pdigits, ndigits) + while ndigits < pt + print(out, '0') + ndigits += 1 + end + if trailingzeros + print(out, '.') + end + else # 0 < pt < ndigits + # dd.dd0000 + ndigits -= pt + unsafe_write(out, pdigits, pt) + print(out, '.') + unsafe_write(out, pdigits+pt, ndigits) + precision -= ndigits + end + if trailingzeros + while precision > 0 + print(out, '0') + precision -= 1 + end + end +end + +function print_exp_e(out, exp::Integer) + print(out, exp < 0 ? '-' : '+') + exp = abs(exp) + d = div(exp,100) + if d > 0 + if d >= 10 + print(out, exp) + return + end + print(out, Char('0'+d)) + end + exp = rem(exp,100) + print(out, Char('0'+div(exp,10))) + print(out, Char('0'+rem(exp,10))) +end + +function print_exp_a(out, exp::Integer) + print(out, exp < 0 ? '-' : '+') + exp = abs(exp) + print(out, exp) +end + + +function gen_d(flags::String, width::Int, precision::Int, c::Char) + # print integer: + # [dDiu]: print decimal digits + # [o]: print octal digits + # [x]: print hex digits, lowercase + # [X]: print hex digits, uppercase + # + # flags: + # (#): prefix hex with 0x/0X; octal leads with 0 + # (0): pad left with zeros + # (-): left justify + # ( ): precede non-negative values with " " + # (+): precede non-negative values with "+" + # + x, ex, blk = special_handler(flags,width) + # interpret the number + prefix = "" + if lowercase(c)=='o' + fn = '#' in flags ? :decode_0ct : :decode_oct + elseif c=='x' + '#' in flags && (prefix = "0x") + fn = :decode_hex + elseif c=='X' + '#' in flags && (prefix = "0X") + fn = :decode_HEX + else + fn = :decode_dec + end + push!(blk.args, :((do_out, args) = $fn(out, $x, $flags, $width, $precision, $c, buf))) + ifblk = Expr(:if, :do_out, Expr(:block)) + push!(blk.args, ifblk) + blk = ifblk.args[2] + push!(blk.args, :((len, pt, neg) = args)) + # calculate padding + width -= length(prefix) + space_pad = width > max(1,precision) && '-' in flags || + precision < 0 && width > 1 && !('0' in flags) || + precision >= 0 && width > precision + padding = nothing + if precision < 1; precision = 1; end + if space_pad + if '+' in flags || ' ' in flags + width -= 1 + if width > precision + padding = :($width-(pt > $precision ? pt : $precision)) + end + else + if width > precision + padding = :($width-neg-(pt > $precision ? pt : $precision)) + end + end + end + # print space padding + if padding !== nothing && !('-' in flags) + push!(blk.args, pad(width-precision, padding, ' ')) + end + # print sign + '+' in flags ? push!(blk.args, :(print(out, neg ? '-' : '+'))) : + ' ' in flags ? push!(blk.args, :(print(out, neg ? '-' : ' '))) : + push!(blk.args, :(neg && print(out, '-'))) + # print prefix + for ch in prefix + push!(blk.args, :(print(out, $ch))) + end + # print zero padding & leading zeros + if space_pad && precision > 1 + push!(blk.args, pad(precision-1, :($precision-pt), '0')) + elseif !space_pad && width > 1 + zeros = '+' in flags || ' ' in flags ? :($(width-1)-pt) : :($width-neg-pt) + push!(blk.args, pad(width-1, zeros, '0')) + end + # print integer + push!(blk.args, :(unsafe_write(out, pointer(buf), pt))) + # print padding + if padding !== nothing && '-' in flags + push!(blk.args, pad(width-precision, padding, ' ')) + end + # return arg, expr + :(($x)::Real), ex +end + +function gen_f(flags::String, width::Int, precision::Int, c::Char) + # print to fixed trailing precision + # [fF]: the only choice + # + # flags + # (#): always print a decimal point + # (0): pad left with zeros + # (-): left justify + # ( ): precede non-negative values with " " + # (+): precede non-negative values with "+" + # + x, ex, blk = special_handler(flags,width) + # interpret the number + if precision < 0; precision = 6; end + push!(blk.args, :((do_out, args) = fix_dec(out, $x, $flags, $width, $precision, $c, buf))) + ifblk = Expr(:if, :do_out, Expr(:block)) + push!(blk.args, ifblk) + blk = ifblk.args[2] + push!(blk.args, :((len, pt, neg) = args)) + # calculate padding + padding = nothing + if precision > 0 || '#' in flags + width -= precision+1 + end + if '+' in flags || ' ' in flags + width -= 1 + if width > 1 + padding = :($width-(pt > 0 ? pt : 1)) + end + else + if width > 1 + padding = :($width-(pt > 0 ? pt : 1)-neg) + end + end + # print space padding + if padding !== nothing && !('-' in flags) && !('0' in flags) + push!(blk.args, pad(width-1, padding, ' ')) + end + # print sign + '+' in flags ? push!(blk.args, :(print(out, neg ? '-' : '+'))) : + ' ' in flags ? push!(blk.args, :(print(out, neg ? '-' : ' '))) : + push!(blk.args, :(neg && print(out, '-'))) + # print zero padding + if padding !== nothing && !('-' in flags) && '0' in flags + push!(blk.args, pad(width-1, padding, '0')) + end + # print digits + if precision > 0 + push!(blk.args, :(print_fixed(out,$precision,pt,len,true,buf))) + else + push!(blk.args, :(unsafe_write(out, pointer(buf), len))) + push!(blk.args, :(while pt >= (len+=1) print(out,'0') end)) + '#' in flags && push!(blk.args, :(print(out, '.'))) + end + # print space padding + if padding !== nothing && '-' in flags + push!(blk.args, pad(width-1, padding, ' ')) + end + # return arg, expr + :(($x)::Real), ex +end + +function gen_e(flags::String, width::Int, precision::Int, c::Char, inside_g::Bool=false) + # print float in scientific form: + # [e]: use 'e' to introduce exponent + # [E]: use 'E' to introduce exponent + # + # flags: + # (#): always print a decimal point + # (0): pad left with zeros + # (-): left justify + # ( ): precede non-negative values with " " + # (+): precede non-negative values with "+" + # + x, ex, blk = if inside_g + @gensym x + blk = Expr(:block) + x, blk, blk + else + special_handler(flags,width) + end + # interpret the number + if precision < 0; precision = 6; end + ndigits = min(precision+1,length(Grisu.getbuf())-1) + push!(blk.args, :((do_out, args) = ini_dec(out,$x,$ndigits, $flags, $width, $precision, $c, buf))) + push!(blk.args, :(digits = buf)) + ifblk = Expr(:if, :do_out, Expr(:block)) + push!(blk.args, ifblk) + blk = ifblk.args[2] + push!(blk.args, :((len, pt, neg) = args)) + push!(blk.args, :(exp = pt-1)) + expmark = isuppercase(c) ? "E" : "e" + if precision==0 && '#' in flags + expmark = string(".",expmark) + end + # calculate padding + padding = nothing + width -= precision+length(expmark)+(precision>0)+4 + # 4 = leading + expsign + 2 exp digits + if '+' in flags || ' ' in flags + width -= 1 # for the sign indicator + if width > 0 + padding = quote + padn=$width + if (exp<=-100)|(100<=exp) + if isa($x,SmallNumber) + padn -= 1 + else + padn -= Base.ndigits0z(exp) - 2 + end + end + padn + end + end + else + if width > 0 + padding = quote + padn=$width-neg + if (exp<=-100)|(100<=exp) + if isa($x,SmallNumber) + padn -= 1 + else + padn -= Base.ndigits0z(exp) - 2 + end + end + padn + end + end + end + # print space padding + if padding !== nothing && !('-' in flags) && !('0' in flags) + push!(blk.args, pad(width, padding, ' ')) + end + # print sign + '+' in flags ? push!(blk.args, :(print(out, neg ? '-' : '+'))) : + ' ' in flags ? push!(blk.args, :(print(out, neg ? '-' : ' '))) : + push!(blk.args, :(neg && print(out, '-'))) + # print zero padding + if padding !== nothing && !('-' in flags) && '0' in flags + push!(blk.args, pad(width, padding, '0')) + end + # print digits + push!(blk.args, :(write(out, digits[1]))) + if precision > 0 + if inside_g && !('#' in flags) + push!(blk.args, :(endidx = $ndigits; + while endidx > 1 && digits[endidx] == UInt8('0') + endidx -= 1 + end; + if endidx > 1 + print(out, '.') + unsafe_write(out, pointer(digits)+1, endidx-1) + end + )) + else + push!(blk.args, :(print(out, '.'))) + push!(blk.args, :(unsafe_write(out, pointer(digits)+1, $(ndigits-1)))) + if ndigits < precision+1 + n = precision+1-ndigits + push!(blk.args, pad(n, n, '0')) + end + end + end + for ch in expmark + push!(blk.args, :(print(out, $ch))) + end + push!(blk.args, :(print_exp_e(out, exp))) + # print space padding + if padding !== nothing && '-' in flags + push!(blk.args, pad(width, padding, ' ')) + end + # return arg, expr + :(($x)::Real), ex +end + +function gen_a(flags::String, width::Int, precision::Int, c::Char) + # print float in hexadecimal format + # [a]: lowercase hex float, e.g. -0x1.cfp-2 + # [A]: uppercase hex float, e.g. -0X1.CFP-2 + # + # flags: + # (#): always print a decimal point + # (0): pad left with zeros + # (-): left justify + # ( ): precede non-negative values with " " + # (+): precede non-negative values with "+" + # + x, ex, blk = special_handler(flags,width) + if c == 'A' + hexmark, expmark = "0X", "P" + fn = :ini_HEX + else + hexmark, expmark = "0x", "p" + fn = :ini_hex + end + # if no precision, print max non-zero + if precision < 0 + push!(blk.args, :((do_out, args) = $fn(out,$x, $flags, $width, $precision, $c, buf))) + else + ndigits = min(precision+1,length(Grisu.getbuf())-1) + push!(blk.args, :((do_out, args) = $fn(out,$x,$ndigits, $flags, $width, $precision, $c, buf))) + end + push!(blk.args, :(digits = buf)) + ifblk = Expr(:if, :do_out, Expr(:block)) + push!(blk.args, ifblk) + blk = ifblk.args[2] + push!(blk.args, :((len, exp, neg) = args)) + if precision==0 && '#' in flags + expmark = string(".",expmark) + end + # calculate padding + padding = nothing + if precision > 0 + width -= precision+length(hexmark)+length(expmark)+4 + # 4 = leading + expsign + 1 exp digit + decimal + else + width -= length(hexmark)+length(expmark)+3+(precision<0 && '#' in flags) + # 3 = leading + expsign + 1 exp digit + end + if '+' in flags || ' ' in flags + width -= 1 # for the sign indicator + if width > 0 + padding = :($(width+1) - Base.ndigits(exp)) + end + else + if width > 0 + padding = :($(width+1) - neg - Base.ndigits(exp)) + end + end + if precision < 0 && width > 0 + if '#' in flags + padding = :($padding - (len-1)) + else + padding = :($padding - (len>1 ? len : 0)) + end + end + # print space padding + if padding !== nothing && !('-' in flags) && !('0' in flags) + push!(blk.args, pad(width, padding, ' ')) + end + # print sign + '+' in flags ? push!(blk.args, :(print(out, neg ? '-' : '+'))) : + ' ' in flags ? push!(blk.args, :(print(out, neg ? '-' : ' '))) : + push!(blk.args, :(neg && print(out, '-'))) + # hex prefix + for ch in hexmark + push!(blk.args, :(print(out, $ch))) + end + # print zero padding + if padding !== nothing && !('-' in flags) && '0' in flags + push!(blk.args, pad(width, padding, '0')) + end + # print digits: assumes ASCII/UTF8 encoding of digits is okay for `out` + push!(blk.args, :(write(out, digits[1]))) + if precision > 0 + push!(blk.args, :(print(out, '.'))) + push!(blk.args, :(unsafe_write(out, pointer(digits)+1, $(ndigits-1)))) + if ndigits < precision+1 + n = precision+1-ndigits + push!(blk.args, pad(n, n, '0')) + end + elseif precision < 0 + ifvpblk = Expr(:if, :(len > 1), Expr(:block)) + vpblk = ifvpblk.args[2] + if '#' in flags + push!(blk.args, :(print(out, '.'))) + else + push!(vpblk.args, :(print(out, '.'))) + end + push!(vpblk.args, :(unsafe_write(out, pointer(digits)+1, len-1))) + push!(blk.args, ifvpblk) + end + for ch in expmark + push!(blk.args, :(print(out, $ch))) + end + push!(blk.args, :(print_exp_a(out, exp))) + # print space padding + if padding !== nothing && '-' in flags + push!(blk.args, pad(width, padding, ' ')) + end + # return arg, expr + :(($x)::Real), ex +end + +function gen_c(flags::String, width::Int, precision::Int, c::Char) + # print a character: + # [cC]: both the same for us (Unicode) + # + # flags: + # (0): pad left with zeros + # (-): left justify + # + @gensym x + blk = Expr(:block, :($x = Char($x))) + if width > 1 && !('-' in flags) + p = '0' in flags ? '0' : ' ' + push!(blk.args, pad(width-1, :($width-textwidth($x)), p)) + end + push!(blk.args, :(print(out, $x))) + if width > 1 && '-' in flags + push!(blk.args, pad(width-1, :($width-textwidth($x)), ' ')) + end + :(($x)::Integer), blk +end + +function _limit(s, prec) + prec >= sizeof(s) && return s + p = prevind(s, prec+1) + n = nextind(s, p)-1 + s[1:(prec>=n ? n : prevind(s,p))] +end + +function gen_s(flags::String, width::Int, precision::Int, c::Char) + # print a string: + # [sS]: both the same for us (Unicode) + # + # flags: + # (-): left justify + # (#): use `show`/`repr` instead of `print`/`string` + # + @gensym x + blk = Expr(:block) + if width > 0 + if !('#' in flags) + push!(blk.args, :($x = string($x))) + else + push!(blk.args, :($x = repr($x))) + end + if precision!=-1 + push!(blk.args, :($x = _limit($x, $precision))) + end + if !('-' in flags) + push!(blk.args, pad(width, :($width-textwidth($x)), ' ')) + end + push!(blk.args, :(print(out, $x))) + if '-' in flags + push!(blk.args, pad(width, :($width-textwidth($x)), ' ')) + end + else + if precision!=-1 + push!(blk.args, :(io = IOBuffer())) + else + push!(blk.args, :(io = out)) + end + if !('#' in flags) + push!(blk.args, :(print(io, $x))) + else + push!(blk.args, :(show(io, $x))) + end + if precision!=-1 + push!(blk.args, :(print(out, _limit(String(take!(io)), $precision)))) + end + end + :(($x)::Any), blk +end + +# TODO: faster pointer printing. + +function gen_p(flags::String, width::Int, precision::Int, c::Char) + # print pointer: + # [p]: the only option + # + # flags: + # (-): left justify + # + @gensym x + blk = Expr(:block) + ptrwidth = Sys.WORD_SIZE>>2 + width -= ptrwidth+2 + if width > 0 && !('-' in flags) + push!(blk.args, pad(width, width, ' ')) + end + push!(blk.args, :(print(out, '0'))) + push!(blk.args, :(print(out, 'x'))) + push!(blk.args, :(print(out, String(string(unsigned($x), pad = $ptrwidth, base = 16))))) + if width > 0 && '-' in flags + push!(blk.args, pad(width, width, ' ')) + end + :(($x)::Ptr), blk +end + +function gen_g(flags::String, width::Int, precision::Int, c::Char) + # print to fixed trailing precision + # [g]: lower case e on scientific + # [G]: Upper case e on scientific + # + # flags + # (#): always print a decimal point + # (0): pad left with zeros + # (-): left justify + # ( ): precede non-negative values with " " + # (+): precede non-negative values with "+" + # + x, ex, blk = special_handler(flags,width) + if precision < 0; precision = 6; end + ndigits = min(precision+1,length(Grisu.getbuf())-1) + # See if anyone else wants to handle it + push!(blk.args, :((do_out, args) = ini_dec(out,$x,$ndigits, $flags, $width, $precision, $c, buf))) + ifblk = Expr(:if, :do_out, Expr(:block)) + push!(blk.args, ifblk) + blk = ifblk.args[2] + push!(blk.args, :((len, pt, neg) = args)) + push!(blk.args, :(exp = pt-1)) + push!(blk.args, :(do_f = $precision > exp >= -4)) # Should we interpret like %f or %e? + feblk = Expr(:if, :do_f, Expr(:block), Expr(:block)) + push!(blk.args, feblk) + fblk = feblk.args[2] + eblk = feblk.args[3] + + ### %f branch + # Follow the same logic as gen_f() but more work has to be deferred until runtime + # because precision is unknown until then. + push!(fblk.args, :(fprec = $precision - (exp+1))) + push!(fblk.args, :((do_out, args) = fix_dec(out, $x, $flags, $width, fprec, $c - 1, buf))) + fifblk = Expr(:if, :do_out, Expr(:block)) + push!(fblk.args, fifblk) + blk = fifblk.args[2] + push!(blk.args, :((len, pt, neg) = args)) + push!(blk.args, :(padding = 0)) + push!(blk.args, :(width = $width)) + # need to compute value before left-padding since trailing zeros are elided + push!(blk.args, :(width -= print_fixed_width(fprec,pt,len,$('#' in flags)))) + if '+' in flags || ' ' in flags + push!(blk.args, :(width -= 1)) + else + push!(blk.args, :(if neg width -= 1; end)) + end + push!(blk.args, :(if width >= 1 padding = width; end)) + # print space padding + if !('-' in flags) && !('0' in flags) + padexpr = dynamic_pad(:width, :padding, ' ') + push!(blk.args, :(if padding > 0 + $padexpr; end)) + end + # print sign + '+' in flags ? push!(blk.args, :(print(out, neg ? '-' : '+'))) : + ' ' in flags ? push!(blk.args, :(print(out, neg ? '-' : ' '))) : + push!(blk.args, :(neg && print(out, '-'))) + # print zero padding + if !('-' in flags) && '0' in flags + padexpr = dynamic_pad(:width, :padding, '0') + push!(blk.args, :(if padding > 0 + $padexpr; end)) + end + # finally print value + push!(blk.args, :(print_fixed(out,fprec,pt,len,$('#' in flags),buf))) + # print space padding + if '-' in flags + padexpr = dynamic_pad(:width, :padding, ' ') + push!(blk.args, :(if padding > 0 + $padexpr; end)) + end + + ### %e branch + # Here we can do all the work at macro expansion time + var, eex = gen_e(flags, width, precision-1, c, true) + push!(eblk.args, :($(var.args[1]) = $x)) + push!(eblk.args, eex) + + :(($x)::Real), ex +end + +### core unsigned integer decoding functions ### + +macro handle_zero(ex, digits) + quote + if $(esc(ex)) == 0 + $(esc(digits))[1] = '0' + return Int32(1), Int32(1), $(esc(:neg)) + end + end +end + +decode_oct(out, d, flags::String, width::Int, precision::Int, c::Char, digits) = (true, decode_oct(d, digits)) +decode_0ct(out, d, flags::String, width::Int, precision::Int, c::Char, digits) = (true, decode_0ct(d, digits)) +decode_dec(out, d, flags::String, width::Int, precision::Int, c::Char, digits) = (true, decode_dec(d, digits)) +decode_hex(out, d, flags::String, width::Int, precision::Int, c::Char, digits) = (true, decode_hex(d, digits)) +decode_HEX(out, d, flags::String, width::Int, precision::Int, c::Char, digits) = (true, decode_HEX(d, digits)) +fix_dec(out, d, flags::String, width::Int, precision::Int, c::Char, digits) = (true, fix_dec(d, precision, digits)) +ini_dec(out, d, ndigits::Int, flags::String, width::Int, precision::Int, c::Char, digits) = (true, ini_dec(d, ndigits, digits)) +ini_hex(out, d, ndigits::Int, flags::String, width::Int, precision::Int, c::Char, digits) = (true, ini_hex(d, ndigits, digits)) +ini_HEX(out, d, ndigits::Int, flags::String, width::Int, precision::Int, c::Char, digits) = (true, ini_HEX(d, ndigits, digits)) +ini_hex(out, d, flags::String, width::Int, precision::Int, c::Char, digits) = (true, ini_hex(d, digits)) +ini_HEX(out, d, flags::String, width::Int, precision::Int, c::Char, digits) = (true, ini_HEX(d, digits)) + + +# fallbacks for Real types without explicit decode_* implementation +decode_oct(d::Real, digits) = decode_oct(Integer(d), digits) +decode_0ct(d::Real, digits) = decode_0ct(Integer(d), digits) +decode_dec(d::Real, digits) = decode_dec(Integer(d), digits) +decode_hex(d::Real, digits) = decode_hex(Integer(d), digits) +decode_HEX(d::Real, digits) = decode_HEX(Integer(d), digits) + +handlenegative(d::Unsigned) = (false, d) +function handlenegative(d::Integer) + if d < 0 + return true, unsigned(oftype(d,-d)) + else + return false, unsigned(d) + end +end + +function decode_oct(d::Integer, digits) + neg, x = handlenegative(d) + @handle_zero x digits + pt = i = div((sizeof(x)<<3)-leading_zeros(x)+2,3) + while i > 0 + digits[i] = 48+(x&0x7) + x >>= 3 + i -= 1 + end + return Int32(pt), Int32(pt), neg +end + +function decode_0ct(d::Integer, digits) + neg, x = handlenegative(d) + # doesn't need special handling for zero + pt = i = div((sizeof(x)<<3)-leading_zeros(x)+5,3) + while i > 0 + digits[i] = 48+(x&0x7) + x >>= 3 + i -= 1 + end + return Int32(pt), Int32(pt), neg +end + +function decode_dec(d::Integer, digits) + neg, x = handlenegative(d) + @handle_zero x digits + pt = i = Base.ndigits0z(x) + while i > 0 + digits[i] = 48+rem(x,10) + x = div(x,10) + i -= 1 + end + return Int32(pt), Int32(pt), neg +end + +function decode_hex(d::Integer, symbols::AbstractArray{UInt8,1}, digits) + neg, x = handlenegative(d) + @handle_zero x digits + pt = i = (sizeof(x)<<1)-(leading_zeros(x)>>2) + while i > 0 + digits[i] = symbols[(x&0xf)+1] + x >>= 4 + i -= 1 + end + return Int32(pt), Int32(pt), neg +end + +const hex_symbols = b"0123456789abcdef" +const HEX_symbols = b"0123456789ABCDEF" + +decode_hex(x::Integer, digits) = decode_hex(x,hex_symbols,digits) +decode_HEX(x::Integer, digits) = decode_hex(x,HEX_symbols,digits) + +function decode(b::Int, x::BigInt, digits) + neg = x.size < 0 + pt = Base.ndigits(x, base=abs(b)) + length(digits) < pt+1 && resize!(digits, pt+1) + neg && (x.size = -x.size) + GMP.MPZ.get_str!(digits, b, x) + neg && (x.size = -x.size) + return Int32(pt), Int32(pt), neg +end +decode_oct(x::BigInt, digits) = decode(8, x, digits) +decode_dec(x::BigInt, digits) = decode(10, x, digits) +decode_hex(x::BigInt, digits) = decode(16, x, digits) +decode_HEX(x::BigInt, digits) = decode(-16, x, digits) + +function decode_0ct(x::BigInt, digits) + neg = x.size < 0 + digits[1] = '0' + if x.size == 0 + return Int32(1), Int32(1), neg + end + pt = Base.ndigits0z(x, 8) + 1 + length(digits) < pt+1 && resize!(digits, pt+1) + neg && (x.size = -x.size) + p = convert(Ptr{UInt8}, digits) + 1 + GMP.MPZ.get_str!(p, 8, x) + neg && (x.size = -x.size) + return neg, Int32(pt), Int32(pt) +end + +### decoding functions directly used by printf generated code ### + +# decode_*(x)=> fixed precision, to 0th place, filled out +# fix_*(x,n) => fixed precision, to nth place, not filled out +# ini_*(x,n) => n initial digits, filled out + +# alternate versions: +# *_0ct(x,n) => ensure that the first octal digits is zero +# *_HEX(x,n) => use uppercase digits for hexadecimal + +# - returns (len, point, neg) +# - implies len = point +# + +function decode_dec(x::SmallFloatingPoint, digits) + if x == 0.0 + digits[1] = '0' + return (Int32(1), Int32(1), false) + end + len,pt,neg = grisu(x,Grisu.FIXED,0,digits) + if len == 0 + digits[1] = '0' + return (Int32(1), Int32(1), false) + else + for i = len+1:pt + digits[i] = '0' + end + end + return Int32(len), Int32(pt), neg +end +# TODO: implement decode_oct, decode_0ct, decode_hex, decode_HEX for SmallFloatingPoint + +## fix decoding functions ## +# +# - returns (neg, point, len) +# - if len less than point, trailing zeros implied +# + +# fallback for Real types without explicit fix_dec implementation +fix_dec(x::Real, n::Int, digits) = fix_dec(float(x),n,digits) + +fix_dec(x::Integer, n::Int, digits) = decode_dec(x, digits) + +function fix_dec(x::SmallFloatingPoint, n::Int, digits) + if n > length(digits)-1; n = length(digits)-1; end + len,pt,neg = grisu(x,Grisu.FIXED,n,digits) + if len == 0 + digits[1] = '0' + return (Int32(1), Int32(1), neg) + end + return Int32(len), Int32(pt), neg +end + +## ini decoding functions ## +# +# - returns (neg, point, len) +# - implies len = n (requested digits) +# + +# fallback for Real types without explicit fix_dec implementation +ini_dec(x::Real, n::Int, digits) = ini_dec(float(x),n,digits) + +function ini_dec(d::Integer, n::Int, digits) + neg, x = handlenegative(d) + k = ndigits(x) + if k <= n + pt = k + for i = k:-1:1 + digits[i] = '0'+rem(x,10) + x = div(x,10) + end + for i = k+1:n + digits[i] = '0' + end + else + p = Base.powers_of_ten[k-n+1] + r = rem(x,p) + if r >= (p>>1) + x += p + if x >= Base.powers_of_ten[k+1] + p *= 10 + k += 1 + end + end + pt = k + x = div(x,p) + for i = n:-1:1 + digits[i] = '0'+rem(x,10) + x = div(x,10) + end + end + return n, pt, neg +end + +function ini_dec(x::SmallFloatingPoint, n::Int, digits) + if x == 0.0 + ccall(:memset, Ptr{Cvoid}, (Ptr{Cvoid}, Cint, Csize_t), digits, '0', n) + return Int32(1), Int32(1), signbit(x) + else + len,pt,neg = grisu(x,Grisu.PRECISION,n,digits) + end + return Int32(len), Int32(pt), neg +end + +function ini_dec(x::BigInt, n::Int, digits) + if x.size == 0 + ccall(:memset, Ptr{Cvoid}, (Ptr{Cvoid}, Cint, Csize_t), digits, '0', n) + return Int32(1), Int32(1), false + end + d = Base.ndigits0z(x) + if d <= n + info = decode_dec(x) + d == n && return info + p = convert(Ptr{Cvoid}, digits) + info[2] + ccall(:memset, Ptr{Cvoid}, (Ptr{Cvoid}, Cint, Csize_t), p, '0', n - info[2]) + return info + end + return (n, d, decode_dec(round(BigInt,x/big(10)^(d-n)))[3]) +end + + +ini_hex(x::Real, n::Int, digits) = ini_hex(x,n,hex_symbols,digits) +ini_HEX(x::Real, n::Int, digits) = ini_hex(x,n,HEX_symbols,digits) + +ini_hex(x::Real, digits) = ini_hex(x,hex_symbols,digits) +ini_HEX(x::Real, digits) = ini_hex(x,HEX_symbols,digits) + +ini_hex(x::Real, n::Int, symbols::AbstractArray{UInt8,1}, digits) = ini_hex(float(x), n, symbols, digits) +ini_hex(x::Real, symbols::AbstractArray{UInt8,1}, digits) = ini_hex(float(x), symbols, digits) + +function ini_hex(x::SmallFloatingPoint, n::Int, symbols::AbstractArray{UInt8,1}, digits) + x = Float64(x) + if x == 0.0 + ccall(:memset, Ptr{Cvoid}, (Ptr{Cvoid}, Cint, Csize_t), digits, '0', n) + return Int32(1), Int32(0), signbit(x) + else + s, p = frexp(x) + sigbits = 4*min(n-1,13) + s = 0.25*round(ldexp(s,1+sigbits)) + # ensure last 2 exponent bits either 01 or 10 + u = (reinterpret(UInt64,s) & 0x003f_ffff_ffff_ffff) >> (52-sigbits) + if n > 14 + ccall(:memset, Ptr{Cvoid}, (Ptr{Cvoid}, Cint, Csize_t), digits, '0', n) + end + i = (sizeof(u)<<1)-(leading_zeros(u)>>2) + while i > 0 + digits[i] = symbols[(u&0xf)+1] + u >>= 4 + i -= 1 + end + # pt is the binary exponent + return Int32(n), Int32(p-1), x < 0.0 + end +end + +function ini_hex(x::SmallFloatingPoint, symbols::AbstractArray{UInt8,1}, digits) + x = Float64(x) + if x == 0.0 + ccall(:memset, Ptr{Cvoid}, (Ptr{Cvoid}, Cint, Csize_t), digits, '0', 1) + return Int32(1), Int32(0), signbit(x) + else + s, p = frexp(x) + s *= 2.0 + u = (reinterpret(UInt64,s) & 0x001f_ffff_ffff_ffff) + t = (trailing_zeros(u) >> 2) + u >>= (t<<2) + n = 14-t + for i = n:-1:1 + digits[i] = symbols[(u&0xf)+1] + u >>= 4 + end + # pt is the binary exponent + return Int32(n), Int32(p-1), x < 0.0 + end +end + +function ini_hex(x::Integer, digits) + len,pt,neg = decode_hex(x, digits) + pt = (len-1)<<2 + len,pt,neg +end +function ini_HEX(x::Integer, digits) + len,pt,neg = decode_HEX(x, digits) + pt = (len-1)<<2 + len,pt,neg +end + +# not implemented +ini_hex(x::Integer,ndigits::Int,digits) = throw(MethodError(ini_hex,(x,ndigits,digits))) + +#BigFloat +fix_dec(out, d::BigFloat, flags::String, width::Int, precision::Int, c::Char, digits) = bigfloat_printf(out, d, flags, width, precision, c, digits) +ini_dec(out, d::BigFloat, ndigits::Int, flags::String, width::Int, precision::Int, c::Char, digits) = bigfloat_printf(out, d, flags, width, precision, c, digits) +ini_hex(out, d::BigFloat, ndigits::Int, flags::String, width::Int, precision::Int, c::Char, digits) = bigfloat_printf(out, d, flags, width, precision, c, digits) +ini_HEX(out, d::BigFloat, ndigits::Int, flags::String, width::Int, precision::Int, c::Char, digits) = bigfloat_printf(out, d, flags, width, precision, c, digits) +ini_hex(out, d::BigFloat, flags::String, width::Int, precision::Int, c::Char, digits) = bigfloat_printf(out, d, flags, width, precision, c, digits) +ini_HEX(out, d::BigFloat, flags::String, width::Int, precision::Int, c::Char, digits) = bigfloat_printf(out, d, flags, width, precision, c, digits) +function bigfloat_printf(out, d::BigFloat, flags::String, width::Int, precision::Int, c::Char, digits) + fmt_len = sizeof(flags)+4 + if width > 0 + fmt_len += ndigits(width) + end + if precision >= 0 + fmt_len += ndigits(precision)+1 + end + fmt = IOBuffer(maxsize=fmt_len) + print(fmt, '%') + print(fmt, flags) + if width > 0 + print(fmt, width) + end + if precision == 0 + print(fmt, '.') + print(fmt, '0') + elseif precision > 0 + print(fmt, '.') + print(fmt, precision) + end + print(fmt, 'R') + print(fmt, c) + write(fmt, UInt8(0)) + printf_fmt = take!(fmt) + @assert length(printf_fmt) == fmt_len + bufsiz = length(digits) + lng = ccall((:mpfr_snprintf,:libmpfr), Int32, + (Ptr{UInt8}, Culong, Ptr{UInt8}, Ref{BigFloat}...), + digits, bufsiz, printf_fmt, d) + lng > 0 || error("invalid printf formatting for BigFloat") + unsafe_write(out, pointer(digits), min(lng, bufsiz-1)) + return (false, ()) +end + +### external printf interface ### + +is_str_expr(ex) = + isa(ex,Expr) && (ex.head === :string || (ex.head === :macrocall && isa(ex.args[1],Symbol) && + endswith(string(ex.args[1]),"str"))) + +function _printf(macroname, io, fmt, args) + isa(fmt, AbstractString) || throw(ArgumentError("$macroname: format must be a plain static string (no interpolation or prefix)")) + sym_args, blk = gen(fmt) + + has_splatting = false + for arg in args + if isa(arg, Expr) && arg.head === :... + has_splatting = true + break + end + end + + # + # Immediately check for corresponding arguments if there is no splatting + # + if !has_splatting && length(sym_args) != length(args) + throw(ArgumentError("$macroname: wrong number of arguments ($(length(args))) should be ($(length(sym_args)))")) + end + + for i = length(sym_args):-1:1 + var = sym_args[i].args[1] + if has_splatting + pushfirst!(blk.args, :($var = G[$i])) + else + pushfirst!(blk.args, :($var = $(esc(args[i])))) + end + end + + # + # Delay generation of argument list and check until evaluation time instead of macro + # expansion time if there is splatting. + # + if has_splatting + x = Expr(:call,:tuple,args...) + pushfirst!(blk.args, + quote + G = $(esc(x)) + if length(G) != $(length(sym_args)) + throw(ArgumentError(string($macroname,": wrong number of arguments (",length(G),") should be (",$(length(sym_args)),")"))) + end + end + ) + end + + pushfirst!(blk.args, :(out = $io)) + Expr(:let, Expr(:block), blk) +end + +macro printf(args...) + isempty(args) && throw(ArgumentError("@printf: called with no arguments")) + if isa(args[1], AbstractString) || is_str_expr(args[1]) + _printf("@printf", :stdout, args[1], args[2:end]) + else + (length(args) >= 2 && (isa(args[2], AbstractString) || is_str_expr(args[2]))) || + throw(ArgumentError("@printf: first or second argument must be a format string")) + _printf("@printf", esc(args[1]), args[2], args[3:end]) + end +end + +macro sprintf(args...) + isempty(args) && throw(ArgumentError("@sprintf: called with zero arguments")) + isa(args[1], AbstractString) || is_str_expr(args[1]) || + throw(ArgumentError("@sprintf: first argument must be a format string")) + letexpr = _printf("@sprintf", :(IOBuffer()), args[1], args[2:end]) + push!(letexpr.args[2].args, :(String(take!(out)))) + letexpr +end + +end # module diff --git a/base/process.jl b/base/process.jl new file mode 100644 index 0000000..f9ac44c --- /dev/null +++ b/base/process.jl @@ -0,0 +1,644 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +mutable struct Process <: AbstractPipe + cmd::Cmd + handle::Ptr{Cvoid} + in::IO + out::IO + err::IO + exitcode::Int64 + termsignal::Int32 + exitnotify::ThreadSynchronizer + function Process(cmd::Cmd, handle::Ptr{Cvoid}) + this = new(cmd, handle, devnull, devnull, devnull, + typemin(fieldtype(Process, :exitcode)), + typemin(fieldtype(Process, :termsignal)), + ThreadSynchronizer()) + finalizer(uvfinalize, this) + return this + end +end +pipe_reader(p::Process) = p.out +pipe_writer(p::Process) = p.in + +# Represents a whole pipeline of any number of related processes +# so the entire pipeline can be treated as one entity +mutable struct ProcessChain <: AbstractPipe + processes::Vector{Process} + in::IO + out::IO + err::IO + function ProcessChain() + return new(Process[], devnull, devnull, devnull) + end +end +pipe_reader(p::ProcessChain) = p.out +pipe_writer(p::ProcessChain) = p.in + +# release ownership of the libuv handle +function uvfinalize(proc::Process) + if proc.handle != C_NULL + disassociate_julia_struct(proc.handle) + ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), proc.handle) + proc.handle = C_NULL + end + nothing +end + +# called when the process dies +function uv_return_spawn(p::Ptr{Cvoid}, exit_status::Int64, termsignal::Int32) + data = ccall(:jl_uv_process_data, Ptr{Cvoid}, (Ptr{Cvoid},), p) + data == C_NULL && return + proc = unsafe_pointer_to_objref(data)::Process + proc.exitcode = exit_status + proc.termsignal = termsignal + ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), proc.handle) + proc.handle = C_NULL + lock(proc.exitnotify) + try + notify(proc.exitnotify) + finally + unlock(proc.exitnotify) + end + nothing +end + +# called when the libuv handle is destroyed +function _uv_hook_close(proc::Process) + proc.handle = C_NULL + nothing +end + +const SpawnIOs = Vector{Any} # convenience name for readability + +# handle marshalling of `Cmd` arguments from Julia to C +@noinline function _spawn_primitive(file, cmd::Cmd, stdio::SpawnIOs) + loop = eventloop() + iohandles = Tuple{Cint, UInt}[ # assuming little-endian layout + let h = rawhandle(io) + h === C_NULL ? (0x00, UInt(0)) : + h isa OS_HANDLE ? (0x02, UInt(cconvert(@static(Sys.iswindows() ? Ptr{Cvoid} : Cint), h))) : + h isa Ptr{Cvoid} ? (0x04, UInt(h)) : + error("invalid spawn handle $h from $io") + end + for io in stdio] + handle = Libc.malloc(_sizeof_uv_process) + disassociate_julia_struct(handle) # ensure that data field is set to C_NULL + error = ccall(:jl_spawn, Int32, + (Cstring, Ptr{Cstring}, Ptr{Cvoid}, Ptr{Cvoid}, + Ptr{Tuple{Cint, UInt}}, Int, + UInt32, Ptr{Cstring}, Cstring, Ptr{Cvoid}), + file, cmd.exec, loop, handle, + iohandles, length(iohandles), + cmd.flags, + cmd.env === nothing ? C_NULL : cmd.env, + isempty(cmd.dir) ? C_NULL : cmd.dir, + uv_jl_return_spawn::Ptr{Cvoid}) + if error != 0 + ccall(:jl_forceclose_uv, Cvoid, (Ptr{Cvoid},), handle) # will call free on handle eventually + throw(_UVError("could not spawn " * repr(cmd), error)) + end + pp = Process(cmd, handle) + associate_julia_struct(handle, pp) + return pp +end + +_spawn(cmds::AbstractCmd) = _spawn(cmds, Any[]) + +# optimization: we can spawn `Cmd` directly without allocating the ProcessChain +function _spawn(cmd::Cmd, stdios::SpawnIOs) + isempty(cmd.exec) && throw(ArgumentError("cannot spawn empty command")) + pp = setup_stdios(stdios) do stdios + return _spawn_primitive(cmd.exec[1], cmd, stdios) + end + return pp +end + +# assume that having a ProcessChain means that the stdio are setup +function _spawn(cmds::AbstractCmd, stdios::SpawnIOs) + pp = setup_stdios(stdios) do stdios + return _spawn(cmds, stdios, ProcessChain()) + end + return pp +end + +# helper function for making a copy of a SpawnIOs, with replacement +function _stdio_copy(stdios::SpawnIOs, fd::Int, @nospecialize replace) + nio = max(fd, length(stdios)) + new = SpawnIOs(undef, nio) + copyto!(fill!(new, devnull), stdios) + new[fd] = replace + return new +end + +function _spawn(redirect::CmdRedirect, stdios::SpawnIOs, args...) + fdnum = redirect.stream_no + 1 + io, close_io = setup_stdio(redirect.handle, redirect.readable) + try + stdios = _stdio_copy(stdios, fdnum, io) + return _spawn(redirect.cmd, stdios, args...) + finally + close_io && close_stdio(io) + end +end + +function _spawn(cmds::OrCmds, stdios::SpawnIOs, chain::ProcessChain) + in_pipe, out_pipe = link_pipe(false, false) + try + stdios_left = _stdio_copy(stdios, 2, out_pipe) + _spawn(cmds.a, stdios_left, chain) + stdios_right = _stdio_copy(stdios, 1, in_pipe) + _spawn(cmds.b, stdios_right, chain) + finally + close_pipe_sync(out_pipe) + close_pipe_sync(in_pipe) + end + return chain +end + +function _spawn(cmds::ErrOrCmds, stdios::SpawnIOs, chain::ProcessChain) + in_pipe, out_pipe = link_pipe(false, false) + try + stdios_left = _stdio_copy(stdios, 3, out_pipe) + _spawn(cmds.a, stdios_left, chain) + stdios_right = _stdio_copy(stdios, 1, in_pipe) + _spawn(cmds.b, stdios_right, chain) + finally + close_pipe_sync(out_pipe) + close_pipe_sync(in_pipe) + end + return chain +end + +function _spawn(cmds::AndCmds, stdios::SpawnIOs, chain::ProcessChain) + _spawn(cmds.a, stdios, chain) + _spawn(cmds.b, stdios, chain) + return chain +end + +function _spawn(cmd::Cmd, stdios::SpawnIOs, chain::ProcessChain) + isempty(cmd.exec) && throw(ArgumentError("cannot spawn empty command")) + pp = _spawn_primitive(cmd.exec[1], cmd, stdios) + push!(chain.processes, pp) + return chain +end + + +# open the child end of each element of `stdios`, and initialize the parent end +function setup_stdios(f, stdios::SpawnIOs) + nstdio = length(stdios) + open_io = Vector{Any}(undef, nstdio) + close_io = falses(nstdio) + try + for i in 1:nstdio + open_io[i], close_io[i] = setup_stdio(stdios[i], i == 1) + end + pp = f(open_io) + return pp + finally + for i in 1:nstdio + close_io[i] && close_stdio(open_io[i]) + end + end +end + +function setup_stdio(stdio::PipeEndpoint, child_readable::Bool) + if stdio.status == StatusInit + # if the PipeEndpoint isn't open, set it to the parent end + # and pass the other end to the child + rd, wr = link_pipe(!child_readable, child_readable) + try + open_pipe!(stdio, child_readable ? wr : rd) + catch ex + close_pipe_sync(rd) + close_pipe_sync(wr) + rethrow(ex) + end + child = child_readable ? rd : wr + return (child, true) + end + # if it's already open, assume that it's already the child end + # (since we can't do anything else) + return (stdio, false) +end + +function setup_stdio(stdio::Pipe, child_readable::Bool) + if stdio.in.status == StatusInit && stdio.out.status == StatusInit + link_pipe!(stdio) + end + io = child_readable ? stdio.out : stdio.in + return (io, false) +end + +setup_stdio(stdio::AbstractPipe, readable::Bool) = + setup_stdio(readable ? pipe_reader(stdio) : pipe_writer(stdio), readable) + +function setup_stdio(stdio::IOStream, child_readable::Bool) + io = RawFD(fd(stdio)) + return (io, false) +end + +function setup_stdio(stdio::FileRedirect, child_readable::Bool) + if child_readable + attr = JL_O_RDONLY + perm = zero(S_IRUSR) + else + attr = JL_O_WRONLY | JL_O_CREAT + attr |= stdio.append ? JL_O_APPEND : JL_O_TRUNC + perm = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH + end + io = Filesystem.open(stdio.filename, attr, perm) + return (io, true) +end + +# incrementally move data between an IOBuffer and a system Pipe +# TODO: probably more efficient (when valid) to use `stdio` directly as the +# PipeEndpoint buffer field in some cases +function setup_stdio(stdio::Union{IOBuffer, BufferStream}, child_readable::Bool) + parent = PipeEndpoint() + rd, wr = link_pipe(!child_readable, child_readable) + try + open_pipe!(parent, child_readable ? wr : rd) + catch ex + close_pipe_sync(rd) + close_pipe_sync(wr) + rethrow(ex) + end + child = child_readable ? rd : wr + try + let in = (child_readable ? parent : stdio), + out = (child_readable ? stdio : parent) + @async try + write(in, out) + catch ex + @warn "Process error" exception=(ex, catch_backtrace()) + finally + close(parent) + end + end + catch ex + close_pipe_sync(child) + rethrow(ex) + end + return (child, true) +end + +function setup_stdio(io, child_readable::Bool) + # if there is no specialization, + # assume that rawhandle is defined for it + return (io, false) +end + +close_stdio(stdio::OS_HANDLE) = close_pipe_sync(stdio) +close_stdio(stdio) = close(stdio) + +# INTERNAL +# pad out stdio to have at least three elements, +# passing either `devnull` or the corresponding `stdio` +# A Redirectable can be any of: +# - A system IO handle, to be passed to the child +# - An uninitialized pipe, to be created +# - devnull (to pass /dev/null for 0-2, or to leave undefined for fd > 2) +# - An Filesystem.File or IOStream object to redirect the output to +# - A FileRedirect, containing a string specifying a filename to be opened for the child + +spawn_opts_swallow(stdios::StdIOSet) = Any[stdios...] +spawn_opts_inherit(stdios::StdIOSet) = Any[stdios...] +spawn_opts_swallow(in::Redirectable=devnull, out::Redirectable=devnull, err::Redirectable=devnull) = + Any[in, out, err] +# pass original descriptors to child processes by default, because we might +# have already exhausted and closed the libuv object for our standard streams. +# ref issue #8529 +spawn_opts_inherit(in::Redirectable=RawFD(0), out::Redirectable=RawFD(1), err::Redirectable=RawFD(2)) = + Any[in, out, err] + +function eachline(cmd::AbstractCmd; keep::Bool=false) + out = PipeEndpoint() + processes = _spawn(cmd, Any[devnull, out, stderr]) + # if the user consumes all the data, also check process exit status for success + ondone = () -> (success(processes) || pipeline_error(processes); nothing) + return EachLine(out, keep=keep, ondone=ondone)::EachLine +end + +""" + open(command, mode::AbstractString, stdio=devnull) + +Run `command` asynchronously. Like `open(command, stdio; read, write)` except specifying +the read and write flags via a mode string instead of keyword arguments. +Possible mode strings are: + +| Mode | Description | Keywords | +|:-----|:------------|:---------------------------------| +| `r` | read | none | +| `w` | write | `write = true` | +| `r+` | read, write | `read = true, write = true` | +| `w+` | read, write | `read = true, write = true` | +""" +function open(cmds::AbstractCmd, mode::AbstractString, stdio::Redirectable=devnull) + if mode == "r+" || mode == "w+" + return open(cmds, stdio, read = true, write = true) + elseif mode == "r" + return open(cmds, stdio) + elseif mode == "w" + return open(cmds, stdio, write = true) + else + throw(ArgumentError("mode must be \"r\", \"w\", \"r+\", or \"w+\", not $(repr(mode))")) + end +end + +# return a Process object to read-to/write-from the pipeline +""" + open(command, stdio=devnull; write::Bool = false, read::Bool = !write) + +Start running `command` asynchronously, and return a `process::IO` object. If `read` is +true, then reads from the process come from the process's standard output and `stdio` optionally +specifies the process's standard input stream. If `write` is true, then writes go to +the process's standard input and `stdio` optionally specifies the process's standard output +stream. +The process's standard error stream is connected to the current global `stderr`. +""" +function open(cmds::AbstractCmd, stdio::Redirectable=devnull; write::Bool=false, read::Bool=!write) + if read && write + stdio === devnull || throw(ArgumentError("no stream can be specified for `stdio` in read-write mode")) + in = PipeEndpoint() + out = PipeEndpoint() + processes = _spawn(cmds, Any[in, out, stderr]) + processes.in = in + processes.out = out + elseif read + out = PipeEndpoint() + processes = _spawn(cmds, Any[stdio, out, stderr]) + processes.out = out + elseif write + in = PipeEndpoint() + processes = _spawn(cmds, Any[in, stdio, stderr]) + processes.in = in + else + stdio === devnull || throw(ArgumentError("no stream can be specified for `stdio` in no-access mode")) + processes = _spawn(cmds, Any[devnull, devnull, stderr]) + end + return processes +end + +""" + open(f::Function, command, args...; kwargs...) + +Similar to `open(command, args...; kwargs...)`, but calls `f(stream)` on the resulting process +stream, then closes the input stream and waits for the process to complete. +Returns the value returned by `f`. +""" +function open(f::Function, cmds::AbstractCmd, args...; kwargs...) + P = open(cmds, args...; kwargs...) + ret = try + f(P) + catch + kill(P) + rethrow() + finally + close(P.in) + end + success(P) || pipeline_error(P) + return ret +end + +""" + read(command::Cmd) + +Run `command` and return the resulting output as an array of bytes. +""" +function read(cmd::AbstractCmd) + procs = open(cmd, "r", devnull) + bytes = read(procs.out) + success(procs) || pipeline_error(procs) + return bytes +end + +""" + read(command::Cmd, String) + +Run `command` and return the resulting output as a `String`. +""" +read(cmd::AbstractCmd, ::Type{String}) = String(read(cmd)) + +""" + run(command, args...; wait::Bool = true) + +Run a command object, constructed with backticks (see the [Running External Programs](@ref) +section in the manual). Throws an error if anything goes wrong, including the process +exiting with a non-zero status (when `wait` is true). + +If `wait` is false, the process runs asynchronously. You can later wait for it and check +its exit status by calling `success` on the returned process object. + +When `wait` is false, the process' I/O streams are directed to `devnull`. +When `wait` is true, I/O streams are shared with the parent process. +Use [`pipeline`](@ref) to control I/O redirection. +""" +function run(cmds::AbstractCmd, args...; wait::Bool = true) + if wait + ps = _spawn(cmds, spawn_opts_inherit(args...)) + success(ps) || pipeline_error(ps) + else + stdios = spawn_opts_swallow(args...) + ps = _spawn(cmds, stdios) + # for each stdio input argument, guess whether the user + # passed a `stdio` placeholder object as input, and thus + # might be able to use the return AbstractProcess as an IO object + # (this really only applies to PipeEndpoint, Pipe, TCPSocket, or an AbstractPipe wrapping one of those) + if length(stdios) > 0 + in = stdios[1] + isa(in, IO) && (ps.in = in) + if length(stdios) > 1 + out = stdios[2] + isa(out, IO) && (ps.out = out) + if length(stdios) > 2 + err = stdios[3] + isa(err, IO) && (ps.err = err) + end + end + end + end + return ps +end + +# some common signal numbers that are usually available on all platforms +# and might be useful as arguments to `kill` or testing against `Process.termsignal` +const SIGHUP = 1 +const SIGINT = 2 +const SIGQUIT = 3 # !windows +const SIGKILL = 9 +const SIGPIPE = 13 # !windows +const SIGTERM = 15 + +function test_success(proc::Process) + @assert process_exited(proc) + if proc.exitcode < 0 + #TODO: this codepath is not currently tested + throw(_UVError("could not start process " * repr(proc.cmd), proc.exitcode)) + end + return proc.exitcode == 0 && (proc.termsignal == 0 || proc.termsignal == SIGPIPE) +end + +function success(x::Process) + wait(x) + return test_success(x) +end +success(procs::Vector{Process}) = mapreduce(success, &, procs) +success(procs::ProcessChain) = success(procs.processes) + +""" + success(command) + +Run a command object, constructed with backticks (see the [Running External Programs](@ref) +section in the manual), and tell whether it was successful (exited with a code of 0). +An exception is raised if the process cannot be started. +""" +success(cmd::AbstractCmd) = success(_spawn(cmd)) + + +""" + ProcessFailedException + +Indicates problematic exit status of a process. +When running commands or pipelines, this is thrown to indicate +a nonzero exit code was returned (i.e. that the invoked process failed). +""" +struct ProcessFailedException <: Exception + procs::Vector{Process} +end +ProcessFailedException(proc::Process) = ProcessFailedException([proc]) + +function showerror(io::IO, err::ProcessFailedException) + if length(err.procs) == 1 + proc = err.procs[1] + println(io, "failed process: ", proc, " [", proc.exitcode, "]") + else + println(io, "failed processes:") + for proc in err.procs + println(io, " ", proc, " [", proc.exitcode, "]") + end + end +end + +function pipeline_error(proc::Process) + if !proc.cmd.ignorestatus + throw(ProcessFailedException(proc)) + end + nothing +end + +function pipeline_error(procs::ProcessChain) + failed = Process[] + for p = procs.processes + if !test_success(p) && !p.cmd.ignorestatus + push!(failed, p) + end + end + isempty(failed) && return nothing + throw(ProcessFailedException(failed)) +end + +""" + kill(p::Process, signum=SIGTERM) + +Send a signal to a process. The default is to terminate the process. +Returns successfully if the process has already exited, but throws an +error if killing the process failed for other reasons (e.g. insufficient +permissions). +""" +function kill(p::Process, signum::Integer) + iolock_begin() + if process_running(p) + @assert p.handle != C_NULL + err = ccall(:uv_process_kill, Int32, (Ptr{Cvoid}, Int32), p.handle, signum) + if err != 0 && err != UV_ESRCH + throw(_UVError("kill", err)) + end + end + iolock_end() + nothing +end +kill(ps::Vector{Process}) = foreach(kill, ps) +kill(ps::ProcessChain) = foreach(kill, ps.processes) +kill(p::Process) = kill(p, SIGTERM) + +""" + getpid(process) -> Int32 + +Get the child process ID, if it still exists. + +!!! compat "Julia 1.1" + This function requires at least Julia 1.1. +""" +function Libc.getpid(p::Process) + # TODO: due to threading, this method is no longer synchronized with the user application + iolock_begin() + ppid = Int32(0) + if p.handle != C_NULL + ppid = ccall(:jl_uv_process_pid, Int32, (Ptr{Cvoid},), p.handle) + end + iolock_end() + ppid <= 0 && throw(_UVError("getpid", UV_ESRCH)) + return ppid +end + +## process status ## + +""" + process_running(p::Process) + +Determine whether a process is currently running. +""" +process_running(s::Process) = s.handle != C_NULL +process_running(s::Vector{Process}) = any(process_running, s) +process_running(s::ProcessChain) = process_running(s.processes) + +""" + process_exited(p::Process) + +Determine whether a process has exited. +""" +process_exited(s::Process) = !process_running(s) +process_exited(s::Vector{Process}) = all(process_exited, s) +process_exited(s::ProcessChain) = process_exited(s.processes) + +process_signaled(s::Process) = (s.termsignal > 0) + +function process_status(s::Process) + return process_running(s) ? "ProcessRunning" : + process_signaled(s) ? "ProcessSignaled(" * string(s.termsignal) * ")" : + process_exited(s) ? "ProcessExited(" * string(s.exitcode) * ")" : + error("process status error") +end + +function wait(x::Process) + process_exited(x) && return + iolock_begin() + if !process_exited(x) + preserve_handle(x) + lock(x.exitnotify) + iolock_end() + try + wait(x.exitnotify) + finally + unlock(x.exitnotify) + unpreserve_handle(x) + end + else + iolock_end() + end + nothing +end + +wait(x::ProcessChain) = foreach(wait, x.processes) + +show(io::IO, p::Process) = print(io, "Process(", p.cmd, ", ", process_status(p), ")") + +# allow the elements of the Cmd to be accessed as an array or iterator +for f in (:length, :firstindex, :lastindex, :keys, :first, :last, :iterate) + @eval $f(cmd::Cmd) = $f(cmd.exec) +end +eltype(::Type{Cmd}) = eltype(fieldtype(Cmd, :exec)) +for f in (:iterate, :getindex) + @eval $f(cmd::Cmd, i) = $f(cmd.exec, i) +end diff --git a/base/promotion.jl b/base/promotion.jl new file mode 100644 index 0000000..e08128e --- /dev/null +++ b/base/promotion.jl @@ -0,0 +1,416 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## type join (closest common ancestor, or least upper bound) ## + +""" + typejoin(T, S) + + +Return the closest common ancestor of `T` and `S`, i.e. the narrowest type from which +they both inherit. +""" +typejoin() = (@_pure_meta; Bottom) +typejoin(@nospecialize(t)) = (@_pure_meta; t) +typejoin(@nospecialize(t), ts...) = (@_pure_meta; typejoin(t, typejoin(ts...))) +function typejoin(@nospecialize(a), @nospecialize(b)) + @_pure_meta + if isa(a, TypeVar) + return typejoin(a.ub, b) + elseif isa(b, TypeVar) + return typejoin(a, b.ub) + elseif a <: b + return b + elseif b <: a + return a + elseif isa(a, UnionAll) + return UnionAll(a.var, typejoin(a.body, b)) + elseif isa(b, UnionAll) + return UnionAll(b.var, typejoin(a, b.body)) + elseif isa(a, Union) + return typejoin(typejoin(a.a, a.b), b) + elseif isa(b, Union) + return typejoin(a, typejoin(b.a, b.b)) + elseif a <: Tuple + if !(b <: Tuple) + return Any + end + ap, bp = a.parameters, b.parameters + lar = length(ap)::Int + lbr = length(bp)::Int + if lar == 0 + return Tuple{Vararg{tailjoin(bp, 1)}} + end + if lbr == 0 + return Tuple{Vararg{tailjoin(ap, 1)}} + end + laf, afixed = full_va_len(ap) + lbf, bfixed = full_va_len(bp) + if laf < lbf + if isvarargtype(ap[lar]) && !afixed + c = Vector{Any}(undef, laf) + c[laf] = Vararg{typejoin(unwrapva(ap[lar]), tailjoin(bp, laf))} + n = laf-1 + else + c = Vector{Any}(undef, laf+1) + c[laf+1] = Vararg{tailjoin(bp, laf+1)} + n = laf + end + elseif lbf < laf + if isvarargtype(bp[lbr]) && !bfixed + c = Vector{Any}(undef, lbf) + c[lbf] = Vararg{typejoin(unwrapva(bp[lbr]), tailjoin(ap, lbf))} + n = lbf-1 + else + c = Vector{Any}(undef, lbf+1) + c[lbf+1] = Vararg{tailjoin(ap, lbf+1)} + n = lbf + end + else + c = Vector{Any}(undef, laf) + n = laf + end + for i = 1:n + ai = ap[min(i,lar)]; bi = bp[min(i,lbr)] + ci = typejoin(unwrapva(ai), unwrapva(bi)) + c[i] = i == length(c) && (isvarargtype(ai) || isvarargtype(bi)) ? Vararg{ci} : ci + end + return Tuple{c...} + elseif b <: Tuple + return Any + end + while b !== Any + if a <: b.name.wrapper + while a.name !== b.name + a = supertype(a) + end + if a.name === Type.body.name + ap = a.parameters[1] + bp = b.parameters[1] + if ((isa(ap,TypeVar) && ap.lb === Bottom && ap.ub === Any) || + (isa(bp,TypeVar) && bp.lb === Bottom && bp.ub === Any)) + # handle special Type{T} supertype + return Type + end + end + aprimary = a.name.wrapper + # join on parameters + n = length(a.parameters) + if n == 0 + return aprimary + end + vars = [] + for i = 1:n + ai, bi = a.parameters[i], b.parameters[i] + if ai === bi || (isa(ai,Type) && isa(bi,Type) && ai <: bi && bi <: ai) + aprimary = aprimary{ai} + else + # pushfirst!(vars, aprimary.var) + _growbeg!(vars, 1) + arrayset(false, vars, aprimary.var, 1) + aprimary = aprimary.body + end + end + for v in vars + aprimary = UnionAll(v, aprimary) + end + return aprimary + end + b = supertype(b) + end + return Any +end + +""" + promote_typejoin(T, S) + +Compute a type that contains both `T` and `S`, which could be +either a parent of both types, or a `Union` if appropriate. +Falls back to [`typejoin`](@ref). +""" +promote_typejoin(@nospecialize(a), @nospecialize(b)) = _promote_typejoin(a, b)::Type +_promote_typejoin(@nospecialize(a), @nospecialize(b)) = typejoin(a, b) +_promote_typejoin(::Type{Nothing}, ::Type{T}) where {T} = + isconcretetype(T) || T === Union{} ? Union{T, Nothing} : Any +_promote_typejoin(::Type{T}, ::Type{Nothing}) where {T} = + isconcretetype(T) || T === Union{} ? Union{T, Nothing} : Any +_promote_typejoin(::Type{Missing}, ::Type{T}) where {T} = + isconcretetype(T) || T === Union{} ? Union{T, Missing} : Any +_promote_typejoin(::Type{T}, ::Type{Missing}) where {T} = + isconcretetype(T) || T === Union{} ? Union{T, Missing} : Any +_promote_typejoin(::Type{Nothing}, ::Type{Missing}) = Union{Nothing, Missing} +_promote_typejoin(::Type{Missing}, ::Type{Nothing}) = Union{Nothing, Missing} +_promote_typejoin(::Type{Nothing}, ::Type{Nothing}) = Nothing +_promote_typejoin(::Type{Missing}, ::Type{Missing}) = Missing + +# Returns length, isfixed +function full_va_len(p) + isempty(p) && return 0, true + last = p[end] + if isvarargtype(last) + N = unwrap_unionall(last).parameters[2] + if isa(N, Integer) + return (length(p) + N - 1)::Int, true + end + return length(p)::Int, false + end + return length(p)::Int, true +end + +# reduce typejoin over A[i:end] +function tailjoin(A, i) + if i > length(A) + return unwrapva(A[end]) + end + t = Bottom + for j = i:length(A) + t = typejoin(t, unwrapva(A[j])) + end + return t +end + +## promotion mechanism ## + +""" + promote_type(type1, type2) + +Promotion refers to converting values of mixed types to a single common type. +`promote_type` represents the default promotion behavior in Julia when +operators (usually mathematical) are given arguments of differing types. +`promote_type` generally tries to return a type which can at least approximate +most values of either input type without excessively widening. Some loss is +tolerated; for example, `promote_type(Int64, Float64)` returns +[`Float64`](@ref) even though strictly, not all [`Int64`](@ref) values can be +represented exactly as `Float64` values. + +```jldoctest +julia> promote_type(Int64, Float64) +Float64 + +julia> promote_type(Int32, Int64) +Int64 + +julia> promote_type(Float32, BigInt) +BigFloat + +julia> promote_type(Int16, Float16) +Float16 + +julia> promote_type(Int64, Float16) +Float16 + +julia> promote_type(Int8, UInt16) +UInt16 +``` +""" +function promote_type end + +promote_type() = Bottom +promote_type(T) = T +promote_type(T, S, U, V...) = (@_inline_meta; promote_type(T, promote_type(S, U, V...))) + +promote_type(::Type{Bottom}, ::Type{Bottom}) = Bottom +promote_type(::Type{T}, ::Type{T}) where {T} = T +promote_type(::Type{T}, ::Type{Bottom}) where {T} = T +promote_type(::Type{Bottom}, ::Type{T}) where {T} = T + +function promote_type(::Type{T}, ::Type{S}) where {T,S} + @_inline_meta + # Try promote_rule in both orders. Typically only one is defined, + # and there is a fallback returning Bottom below, so the common case is + # promote_type(T, S) => + # promote_result(T, S, result, Bottom) => + # typejoin(result, Bottom) => result + promote_result(T, S, promote_rule(T,S), promote_rule(S,T)) +end + +""" + promote_rule(type1, type2) + +Specifies what type should be used by [`promote`](@ref) when given values of types `type1` and +`type2`. This function should not be called directly, but should have definitions added to +it for new types as appropriate. +""" +function promote_rule end + +promote_rule(::Type{<:Any}, ::Type{<:Any}) = Bottom + +promote_result(::Type{<:Any},::Type{<:Any},::Type{T},::Type{S}) where {T,S} = (@_inline_meta; promote_type(T,S)) +# If no promote_rule is defined, both directions give Bottom. In that +# case use typejoin on the original types instead. +promote_result(::Type{T},::Type{S},::Type{Bottom},::Type{Bottom}) where {T,S} = (@_inline_meta; typejoin(T, S)) + +""" + promote(xs...) + +Convert all arguments to a common type, and return them all (as a tuple). +If no arguments can be converted, an error is raised. + +# Examples +```jldoctest +julia> promote(Int8(1), Float16(4.5), Float32(4.1)) +(1.0f0, 4.5f0, 4.1f0) +``` +""" +function promote end + +function _promote(x::T, y::S) where {T,S} + @_inline_meta + R = promote_type(T, S) + return (convert(R, x), convert(R, y)) +end +promote_typeof(x) = typeof(x) +promote_typeof(x, xs...) = (@_inline_meta; promote_type(typeof(x), promote_typeof(xs...))) +function _promote(x, y, z) + @_inline_meta + R = promote_typeof(x, y, z) + return (convert(R, x), convert(R, y), convert(R, z)) +end +function _promote(x, y, zs...) + @_inline_meta + R = promote_typeof(x, y, zs...) + return (convert(R, x), convert(R, y), convert(Tuple{Vararg{R}}, zs)...) +end +# TODO: promote(x::T, ys::T...) where {T} here to catch all circularities? + +## promotions in arithmetic, etc. ## + +promote() = () +promote(x) = (x,) + +function promote(x, y) + @_inline_meta + px, py = _promote(x, y) + not_sametype((x,y), (px,py)) + px, py +end +function promote(x, y, z) + @_inline_meta + px, py, pz = _promote(x, y, z) + not_sametype((x,y,z), (px,py,pz)) + px, py, pz +end +function promote(x, y, z, a...) + p = _promote(x, y, z, a...) + not_sametype((x, y, z, a...), p) + p +end + +promote(x::T, y::T, zs::T...) where {T} = (x, y, zs...) + +not_sametype(x::T, y::T) where {T} = sametype_error(x) + +not_sametype(x, y) = nothing + +function sametype_error(input) + @_noinline_meta + error("promotion of types ", + join(map(x->string(typeof(x)), input), ", ", " and "), + " failed to change any arguments") +end + ++(x::Number, y::Number) = +(promote(x,y)...) +*(x::Number, y::Number) = *(promote(x,y)...) +-(x::Number, y::Number) = -(promote(x,y)...) +/(x::Number, y::Number) = /(promote(x,y)...) + +""" + ^(x, y) + +Exponentiation operator. If `x` is a matrix, computes matrix exponentiation. + +If `y` is an `Int` literal (e.g. `2` in `x^2` or `-3` in `x^-3`), the Julia code +`x^y` is transformed by the compiler to `Base.literal_pow(^, x, Val(y))`, to +enable compile-time specialization on the value of the exponent. +(As a default fallback we have `Base.literal_pow(^, x, Val(y)) = ^(x,y)`, +where usually `^ == Base.^` unless `^` has been defined in the calling +namespace.) + +```jldoctest +julia> 3^5 +243 + +julia> A = [1 2; 3 4] +2×2 Array{Int64,2}: + 1 2 + 3 4 + +julia> A^3 +2×2 Array{Int64,2}: + 37 54 + 81 118 +``` +""" +^(x::Number, y::Number) = ^(promote(x,y)...) + +fma(x::Number, y::Number, z::Number) = fma(promote(x,y,z)...) +muladd(x::Number, y::Number, z::Number) = muladd(promote(x,y,z)...) + +==(x::Number, y::Number) = (==)(promote(x,y)...) +<( x::Real, y::Real) = (< )(promote(x,y)...) +<=(x::Real, y::Real) = (<=)(promote(x,y)...) + +div(x::Real, y::Real) = div(promote(x,y)...) +fld(x::Real, y::Real) = fld(promote(x,y)...) +cld(x::Real, y::Real) = cld(promote(x,y)...) +rem(x::Real, y::Real) = rem(promote(x,y)...) +mod(x::Real, y::Real) = mod(promote(x,y)...) + +mod1(x::Real, y::Real) = mod1(promote(x,y)...) +fld1(x::Real, y::Real) = fld1(promote(x,y)...) + +max(x::Real, y::Real) = max(promote(x,y)...) +min(x::Real, y::Real) = min(promote(x,y)...) +minmax(x::Real, y::Real) = minmax(promote(x, y)...) + +if isdefined(Core, :Compiler) + const _return_type = Core.Compiler.return_type +else + _return_type(@nospecialize(f), @nospecialize(t)) = Any +end + +""" + promote_op(f, argtypes...) + +Guess what an appropriate container eltype would be for storing results of +`f(::argtypes...)`. The guess is in part based on type inference, so can change any time. + +!!! warning + Due to its fragility, use of `promote_op` should be avoided. It is preferable to base + the container eltype on the type of the actual elements. Only in the absence of any + elements (for an empty result container), it may be unavoidable to call `promote_op`. +""" +promote_op(f, S::Type...) = _return_type(f, Tuple{S...}) + +## catch-alls to prevent infinite recursion when definitions are missing ## + +no_op_err(name, T) = error(name," not defined for ",T) +(+)(x::T, y::T) where {T<:Number} = no_op_err("+", T) +(*)(x::T, y::T) where {T<:Number} = no_op_err("*", T) +(-)(x::T, y::T) where {T<:Number} = no_op_err("-", T) +(/)(x::T, y::T) where {T<:Number} = no_op_err("/", T) +(^)(x::T, y::T) where {T<:Number} = no_op_err("^", T) + +fma(x::T, y::T, z::T) where {T<:Number} = no_op_err("fma", T) +fma(x::Integer, y::Integer, z::Integer) = x*y+z +muladd(x::T, y::T, z::T) where {T<:Number} = x*y+z + +(&)(x::T, y::T) where {T<:Integer} = no_op_err("&", T) +(|)(x::T, y::T) where {T<:Integer} = no_op_err("|", T) +xor(x::T, y::T) where {T<:Integer} = no_op_err("xor", T) + +(==)(x::T, y::T) where {T<:Number} = x === y +(< )(x::T, y::T) where {T<:Real} = no_op_err("<" , T) +(<=)(x::T, y::T) where {T<:Real} = no_op_err("<=", T) + +rem(x::T, y::T) where {T<:Real} = no_op_err("rem", T) +mod(x::T, y::T) where {T<:Real} = no_op_err("mod", T) + +min(x::Real) = x +max(x::Real) = x +minmax(x::Real) = (x, x) + +max(x::T, y::T) where {T<:Real} = ifelse(y < x, x, y) +min(x::T, y::T) where {T<:Real} = ifelse(y < x, y, x) +minmax(x::T, y::T) where {T<:Real} = y < x ? (y, x) : (x, y) + +flipsign(x::T, y::T) where {T<:Signed} = no_op_err("flipsign", T) diff --git a/base/range.jl b/base/range.jl new file mode 100644 index 0000000..1c6338e --- /dev/null +++ b/base/range.jl @@ -0,0 +1,1065 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +(:)(a::Real, b::Real) = (:)(promote(a,b)...) + +(:)(start::T, stop::T) where {T<:Real} = UnitRange{T}(start, stop) + +(:)(start::T, stop::T) where {T} = (:)(start, oftype(stop-start, 1), stop) + +# promote start and stop, leaving step alone +(:)(start::A, step, stop::C) where {A<:Real,C<:Real} = + (:)(convert(promote_type(A,C),start), step, convert(promote_type(A,C),stop)) + +# AbstractFloat specializations +(:)(a::T, b::T) where {T<:AbstractFloat} = (:)(a, T(1), b) + +(:)(a::T, b::AbstractFloat, c::T) where {T<:Real} = (:)(promote(a,b,c)...) +(:)(a::T, b::AbstractFloat, c::T) where {T<:AbstractFloat} = (:)(promote(a,b,c)...) +(:)(a::T, b::Real, c::T) where {T<:AbstractFloat} = (:)(promote(a,b,c)...) + +(:)(start::T, step::T, stop::T) where {T<:AbstractFloat} = + _colon(OrderStyle(T), ArithmeticStyle(T), start, step, stop) +(:)(start::T, step::T, stop::T) where {T<:Real} = + _colon(OrderStyle(T), ArithmeticStyle(T), start, step, stop) +_colon(::Ordered, ::Any, start::T, step, stop::T) where {T} = StepRange(start, step, stop) +# for T<:Union{Float16,Float32,Float64} see twiceprecision.jl +_colon(::Ordered, ::ArithmeticRounds, start::T, step, stop::T) where {T} = + StepRangeLen(start, step, floor(Int, (stop-start)/step)+1) +_colon(::Any, ::Any, start::T, step, stop::T) where {T} = + StepRangeLen(start, step, floor(Int, (stop-start)/step)+1) + +""" + (:)(start, [step], stop) + +Range operator. `a:b` constructs a range from `a` to `b` with a step size of 1 (a [`UnitRange`](@ref)) +, and `a:s:b` is similar but uses a step size of `s` (a [`StepRange`](@ref)). + +`:` is also used in indexing to select whole dimensions + and for [`Symbol`](@ref) literals, as in e.g. `:hello`. +""" +(:)(start::T, step, stop::T) where {T} = _colon(start, step, stop) +(:)(start::T, step, stop::T) where {T<:Real} = _colon(start, step, stop) +# without the second method above, the first method above is ambiguous with +# (:)(start::A, step, stop::C) where {A<:Real,C<:Real} +function _colon(start::T, step, stop::T) where T + T′ = typeof(start+zero(step)) + StepRange(convert(T′,start), step, convert(T′,stop)) +end + +""" + range(start[, stop]; length, stop, step=1) + +Given a starting value, construct a range either by length or from `start` to `stop`, +optionally with a given step (defaults to 1, a [`UnitRange`](@ref)). +One of `length` or `stop` is required. If `length`, `stop`, and `step` are all specified, they must agree. + +If `length` and `stop` are provided and `step` is not, the step size will be computed +automatically such that there are `length` linearly spaced elements in the range (a [`LinRange`](@ref)). + +If `step` and `stop` are provided and `length` is not, the overall range length will be computed +automatically such that the elements are `step` spaced (a [`StepRange`](@ref)). + +`stop` may be specified as either a positional or keyword argument. + +!!! compat "Julia 1.1" + `stop` as a positional argument requires at least Julia 1.1. + +# Examples +```jldoctest +julia> range(1, length=100) +1:100 + +julia> range(1, stop=100) +1:100 + +julia> range(1, step=5, length=100) +1:5:496 + +julia> range(1, step=5, stop=100) +1:5:96 + +julia> range(1, 10, length=101) +1.0:0.09:10.0 + +julia> range(1, 100, step=5) +1:5:96 +``` +""" +range(start; length::Union{Integer,Nothing}=nothing, stop=nothing, step=nothing) = + _range(start, step, stop, length) + +range(start, stop; length::Union{Integer,Nothing}=nothing, step=nothing) = + _range2(start, step, stop, length) + +_range2(start, ::Nothing, stop, ::Nothing) = + throw(ArgumentError("At least one of `length` or `step` must be specified")) + +_range2(start, step, stop, length) = _range(start, step, stop, length) + +# Range from start to stop: range(a, [step=s,] stop=b), no length +_range(start, step, stop, ::Nothing) = (:)(start, step, stop) +_range(start, ::Nothing, stop, ::Nothing) = (:)(start, stop) + +# Range of a given length: range(a, [step=s,] length=l), no stop +_range(a::Real, ::Nothing, ::Nothing, len::Integer) = UnitRange{typeof(a)}(a, oftype(a, a+len-1)) +_range(a::AbstractFloat, ::Nothing, ::Nothing, len::Integer) = _range(a, oftype(a, 1), nothing, len) +_range(a::AbstractFloat, st::AbstractFloat, ::Nothing, len::Integer) = _range(promote(a, st)..., nothing, len) +_range(a::Real, st::AbstractFloat, ::Nothing, len::Integer) = _range(float(a), st, nothing, len) +_range(a::AbstractFloat, st::Real, ::Nothing, len::Integer) = _range(a, float(st), nothing, len) +_range(a, ::Nothing, ::Nothing, len::Integer) = _range(a, oftype(a-a, 1), nothing, len) + +_range(a::T, step::T, ::Nothing, len::Integer) where {T <: AbstractFloat} = + _rangestyle(OrderStyle(T), ArithmeticStyle(T), a, step, len) +_range(a::T, step, ::Nothing, len::Integer) where {T} = + _rangestyle(OrderStyle(T), ArithmeticStyle(T), a, step, len) +_rangestyle(::Ordered, ::ArithmeticWraps, a::T, step::S, len::Integer) where {T,S} = + StepRange{T,S}(a, step, convert(T, a+step*(len-1))) +_rangestyle(::Any, ::Any, a::T, step::S, len::Integer) where {T,S} = + StepRangeLen{typeof(a+0*step),T,S}(a, step, len) + +# Malformed calls +_range(start, step, ::Nothing, ::Nothing) = # range(a, step=s) + throw(ArgumentError("At least one of `length` or `stop` must be specified")) +_range(start, ::Nothing, ::Nothing, ::Nothing) = # range(a) + throw(ArgumentError("At least one of `length` or `stop` must be specified")) +_range(::Nothing, ::Nothing, ::Nothing, ::Nothing) = # range(nothing) + throw(ArgumentError("At least one of `length` or `stop` must be specified")) +_range(start::Real, step::Real, stop::Real, length::Integer) = # range(a, step=s, stop=b, length=l) + throw(ArgumentError("Too many arguments specified; try passing only one of `stop` or `length`")) +_range(::Nothing, ::Nothing, ::Nothing, ::Integer) = # range(nothing, length=l) + throw(ArgumentError("Can't start a range at `nothing`")) + +## 1-dimensional ranges ## + +""" + AbstractRange{T} + +Supertype for ranges with elements of type `T`. +[`UnitRange`](@ref) and other types are subtypes of this. +""" +abstract type AbstractRange{T} <: AbstractArray{T,1} end + +RangeStepStyle(::Type{<:AbstractRange}) = RangeStepIrregular() +RangeStepStyle(::Type{<:AbstractRange{<:Integer}}) = RangeStepRegular() + +convert(::Type{T}, r::AbstractRange) where {T<:AbstractRange} = r isa T ? r : T(r) + +## ordinal ranges + +""" + OrdinalRange{T, S} <: AbstractRange{T} + +Supertype for ordinal ranges with elements of type `T` with +spacing(s) of type `S`. The steps should be always-exact +multiples of [`oneunit`](@ref), and `T` should be a "discrete" +type, which cannot have values smaller than `oneunit`. For example, +`Integer` or `Date` types would qualify, whereas `Float64` would not (since this +type can represent values smaller than `oneunit(Float64)`. +[`UnitRange`](@ref), [`StepRange`](@ref), and other types are subtypes of this. +""" +abstract type OrdinalRange{T,S} <: AbstractRange{T} end + +""" + AbstractUnitRange{T} <: OrdinalRange{T, T} + +Supertype for ranges with a step size of [`oneunit(T)`](@ref) with elements of type `T`. +[`UnitRange`](@ref) and other types are subtypes of this. +""" +abstract type AbstractUnitRange{T} <: OrdinalRange{T,T} end + +""" + StepRange{T, S} <: OrdinalRange{T, S} + +Ranges with elements of type `T` with spacing of type `S`. The step +between each element is constant, and the range is defined in terms +of a `start` and `stop` of type `T` and a `step` of type `S`. Neither +`T` nor `S` should be floating point types. The syntax `a:b:c` with `b > 1` +and `a`, `b`, and `c` all integers creates a `StepRange`. + +# Examples +```jldoctest +julia> collect(StepRange(1, Int8(2), 10)) +5-element Array{Int64,1}: + 1 + 3 + 5 + 7 + 9 + +julia> typeof(StepRange(1, Int8(2), 10)) +StepRange{Int64,Int8} + +julia> typeof(1:3:6) +StepRange{Int64,Int64} +``` +""" +struct StepRange{T,S} <: OrdinalRange{T,S} + start::T + step::S + stop::T + + function StepRange{T,S}(start::T, step::S, stop::T) where {T,S} + new(start, step, steprange_last(start,step,stop)) + end +end + +# to make StepRange constructor inlineable, so optimizer can see `step` value +function steprange_last(start::T, step, stop) where T + if isa(start,AbstractFloat) || isa(step,AbstractFloat) + throw(ArgumentError("StepRange should not be used with floating point")) + end + z = zero(step) + step == z && throw(ArgumentError("step cannot be zero")) + + if stop == start + last = stop + else + if (step > z) != (stop > start) + last = steprange_last_empty(start, step, stop) + else + # Compute absolute value of difference between `start` and `stop` + # (to simplify handling both signed and unsigned T and checking for signed overflow): + absdiff, absstep = stop > start ? (stop - start, step) : (start - stop, -step) + + # Compute remainder as a nonnegative number: + if T <: Signed && absdiff < zero(absdiff) + # handle signed overflow with unsigned rem + remain = convert(T, unsigned(absdiff) % absstep) + else + remain = absdiff % absstep + end + # Move `stop` closer to `start` if there is a remainder: + last = stop > start ? stop - remain : stop + remain + end + end + last +end + +function steprange_last_empty(start::Integer, step, stop) + # empty range has a special representation where stop = start-1 + # this is needed to avoid the wrap-around that can happen computing + # start - step, which leads to a range that looks very large instead + # of empty. + if step > zero(step) + last = start - oneunit(stop-start) + else + last = start + oneunit(stop-start) + end + last +end +# For types where x+oneunit(x) may not be well-defined +steprange_last_empty(start, step, stop) = start - step + +StepRange(start::T, step::S, stop::T) where {T,S} = StepRange{T,S}(start, step, stop) + +""" + UnitRange{T<:Real} + +A range parameterized by a `start` and `stop` of type `T`, filled +with elements spaced by `1` from `start` until `stop` is exceeded. +The syntax `a:b` with `a` and `b` both `Integer`s creates a `UnitRange`. + +# Examples +```jldoctest +julia> collect(UnitRange(2.3, 5.2)) +3-element Array{Float64,1}: + 2.3 + 3.3 + 4.3 + +julia> typeof(1:10) +UnitRange{Int64} +``` +""" +struct UnitRange{T<:Real} <: AbstractUnitRange{T} + start::T + stop::T + UnitRange{T}(start, stop) where {T<:Real} = new(start, unitrange_last(start,stop)) +end +UnitRange(start::T, stop::T) where {T<:Real} = UnitRange{T}(start, stop) + +unitrange_last(::Bool, stop::Bool) = stop +unitrange_last(start::T, stop::T) where {T<:Integer} = + ifelse(stop >= start, stop, convert(T,start-oneunit(stop-start))) +unitrange_last(start::T, stop::T) where {T} = + ifelse(stop >= start, convert(T,start+floor(stop-start)), + convert(T,start-oneunit(stop-start))) + +if isdefined(Main, :Base) + function getindex(t::Tuple, r::AbstractUnitRange{<:Real}) + n = length(r) + n == 0 && return () + a = Vector{eltype(t)}(undef, n) + o = first(r) - 1 + for i = 1:n + el = t[o + i] + @inbounds a[i] = el + end + (a...,) + end +end + +""" + Base.OneTo(n) + +Define an `AbstractUnitRange` that behaves like `1:n`, with the added +distinction that the lower limit is guaranteed (by the type system) to +be 1. +""" +struct OneTo{T<:Integer} <: AbstractUnitRange{T} + stop::T + OneTo{T}(stop) where {T<:Integer} = new(max(zero(T), stop)) + function OneTo{T}(r::AbstractRange) where {T<:Integer} + throwstart(r) = (@_noinline_meta; throw(ArgumentError("first element must be 1, got $(first(r))"))) + throwstep(r) = (@_noinline_meta; throw(ArgumentError("step must be 1, got $(step(r))"))) + first(r) == 1 || throwstart(r) + step(r) == 1 || throwstep(r) + return new(max(zero(T), last(r))) + end +end +OneTo(stop::T) where {T<:Integer} = OneTo{T}(stop) +OneTo(r::AbstractRange{T}) where {T<:Integer} = OneTo{T}(r) + +## Step ranges parameterized by length + +""" + StepRangeLen{T,R,S}(ref::R, step::S, len, [offset=1]) where {T,R,S} + StepRangeLen( ref::R, step::S, len, [offset=1]) where { R,S} + +A range `r` where `r[i]` produces values of type `T` (in the second +form, `T` is deduced automatically), parameterized by a `ref`erence +value, a `step`, and the `len`gth. By default `ref` is the starting +value `r[1]`, but alternatively you can supply it as the value of +`r[offset]` for some other index `1 <= offset <= len`. In conjunction +with `TwicePrecision` this can be used to implement ranges that are +free of roundoff error. +""" +struct StepRangeLen{T,R,S} <: AbstractRange{T} + ref::R # reference value (might be smallest-magnitude value in the range) + step::S # step value + len::Int # length of the range + offset::Int # the index of ref + + function StepRangeLen{T,R,S}(ref::R, step::S, len::Integer, offset::Integer = 1) where {T,R,S} + len >= 0 || throw(ArgumentError("length cannot be negative, got $len")) + 1 <= offset <= max(1,len) || throw(ArgumentError("StepRangeLen: offset must be in [1,$len], got $offset")) + new(ref, step, len, offset) + end +end + +StepRangeLen(ref::R, step::S, len::Integer, offset::Integer = 1) where {R,S} = + StepRangeLen{typeof(ref+0*step),R,S}(ref, step, len, offset) +StepRangeLen{T}(ref::R, step::S, len::Integer, offset::Integer = 1) where {T,R,S} = + StepRangeLen{T,R,S}(ref, step, len, offset) + +## range with computed step + +""" + LinRange{T} + +A range with `len` linearly spaced elements between its `start` and `stop`. +The size of the spacing is controlled by `len`, which must +be an `Int`. + +# Examples +```jldoctest +julia> LinRange(1.5, 5.5, 9) +9-element LinRange{Float64}: + 1.5,2.0,2.5,3.0,3.5,4.0,4.5,5.0,5.5 +``` +""" +struct LinRange{T} <: AbstractRange{T} + start::T + stop::T + len::Int + lendiv::Int + + function LinRange{T}(start,stop,len) where T + len >= 0 || throw(ArgumentError("range($start, stop=$stop, length=$len): negative length")) + if len == 1 + start == stop || throw(ArgumentError("range($start, stop=$stop, length=$len): endpoints differ")) + return new(start, stop, 1, 1) + end + new(start,stop,len,max(len-1,1)) + end +end + +function LinRange(start, stop, len::Integer) + T = typeof((stop-start)/len) + LinRange{T}(start, stop, len) +end + +function _range(start::T, ::Nothing, stop::S, len::Integer) where {T,S} + a, b = promote(start, stop) + _range(a, nothing, b, len) +end +_range(start::T, ::Nothing, stop::T, len::Integer) where {T<:Real} = LinRange{T}(start, stop, len) +_range(start::T, ::Nothing, stop::T, len::Integer) where {T} = LinRange{T}(start, stop, len) +_range(start::T, ::Nothing, stop::T, len::Integer) where {T<:Integer} = + _linspace(float(T), start, stop, len) +## for Float16, Float32, and Float64 we hit twiceprecision.jl to lift to higher precision StepRangeLen +# for all other types we fall back to a plain old LinRange +_linspace(::Type{T}, start::Integer, stop::Integer, len::Integer) where T = LinRange{T}(start, stop, len) + +function show(io::IO, r::LinRange) + print(io, "range(") + show(io, first(r)) + print(io, ", stop=") + show(io, last(r)) + print(io, ", length=") + show(io, length(r)) + print(io, ')') +end + +""" +`print_range(io, r)` prints out a nice looking range r in terms of its elements +as if it were `collect(r)`, dependent on the size of the +terminal, and taking into account whether compact numbers should be shown. +It figures out the width in characters of each element, and if they +end up too wide, it shows the first and last elements separated by a +horizontal ellipsis. Typical output will look like `1.0,2.0,3.0,…,4.0,5.0,6.0`. + +`print_range(io, r, pre, sep, post, hdots)` uses optional +parameters `pre` and `post` characters for each printed row, +`sep` separator string between printed elements, +`hdots` string for the horizontal ellipsis. +""" +function print_range(io::IO, r::AbstractRange, + pre::AbstractString = " ", + sep::AbstractString = ",", + post::AbstractString = "", + hdots::AbstractString = ",\u2026,") # horiz ellipsis + # This function borrows from print_matrix() in show.jl + # and should be called by show and display + limit = get(io, :limit, false) + sz = displaysize(io) + if !haskey(io, :compact) + io = IOContext(io, :compact => true) + end + screenheight, screenwidth = sz[1] - 4, sz[2] + screenwidth -= length(pre) + length(post) + postsp = "" + sepsize = length(sep) + m = 1 # treat the range as a one-row matrix + n = length(r) + # Figure out spacing alignments for r, but only need to examine the + # left and right edge columns, as many as could conceivably fit on the + # screen, with the middle columns summarized by horz, vert, or diag ellipsis + maxpossiblecols = div(screenwidth, 1+sepsize) # assume each element is at least 1 char + 1 separator + colsr = n <= maxpossiblecols ? (1:n) : [1:div(maxpossiblecols,2)+1; (n-div(maxpossiblecols,2)):n] + rowmatrix = reshape(r[colsr], 1, length(colsr)) # treat the range as a one-row matrix for print_matrix_row + A = alignment(io, rowmatrix, 1:m, 1:length(rowmatrix), screenwidth, screenwidth, sepsize) # how much space range takes + if n <= length(A) # cols fit screen, so print out all elements + print(io, pre) # put in pre chars + print_matrix_row(io,rowmatrix,A,1,1:n,sep) # the entire range + print(io, post) # add the post characters + else # cols don't fit so put horiz ellipsis in the middle + # how many chars left after dividing width of screen in half + # and accounting for the horiz ellipsis + c = div(screenwidth-length(hdots)+1,2)+1 # chars remaining for each side of rowmatrix + alignR = reverse(alignment(io, rowmatrix, 1:m, length(rowmatrix):-1:1, c, c, sepsize)) # which cols of rowmatrix to put on the right + c = screenwidth - sum(map(sum,alignR)) - (length(alignR)-1)*sepsize - length(hdots) + alignL = alignment(io, rowmatrix, 1:m, 1:length(rowmatrix), c, c, sepsize) # which cols of rowmatrix to put on the left + print(io, pre) # put in pre chars + print_matrix_row(io, rowmatrix,alignL,1,1:length(alignL),sep) # left part of range + print(io, hdots) # horizontal ellipsis + print_matrix_row(io, rowmatrix,alignR,1,length(rowmatrix)-length(alignR)+1:length(rowmatrix),sep) # right part of range + print(io, post) # post chars + end +end + +## interface implementations + +size(r::AbstractRange) = (length(r),) + +isempty(r::StepRange) = + (r.start != r.stop) & ((r.step > zero(r.step)) != (r.stop > r.start)) +isempty(r::AbstractUnitRange) = first(r) > last(r) +isempty(r::StepRangeLen) = length(r) == 0 +isempty(r::LinRange) = length(r) == 0 + +""" + step(r) + +Get the step size of an [`AbstractRange`](@ref) object. + +# Examples +```jldoctest +julia> step(1:10) +1 + +julia> step(1:2:10) +2 + +julia> step(2.5:0.3:10.9) +0.3 + +julia> step(range(2.5, stop=10.9, length=85)) +0.1 +``` +""" +step(r::StepRange) = r.step +step(r::AbstractUnitRange{T}) where{T} = oneunit(T) - zero(T) +step(r::StepRangeLen{T}) where {T} = T(r.step) +step(r::LinRange) = (last(r)-first(r))/r.lendiv + +step_hp(r::StepRangeLen) = r.step +step_hp(r::AbstractRange) = step(r) + +unsafe_length(r::AbstractRange) = length(r) # generic fallback + +function unsafe_length(r::StepRange) + n = Integer(div((r.stop - r.start) + r.step, r.step)) + isempty(r) ? zero(n) : n +end +length(r::StepRange) = unsafe_length(r) +unsafe_length(r::AbstractUnitRange) = Integer(last(r) - first(r) + step(r)) +unsafe_length(r::OneTo) = Integer(r.stop - zero(r.stop)) +length(r::AbstractUnitRange) = unsafe_length(r) +length(r::OneTo) = unsafe_length(r) +length(r::StepRangeLen) = r.len +length(r::LinRange) = r.len + +# Needed to fold the `firstindex` call in SimdLoop.simd_index +firstindex(::UnitRange) = 1 +firstindex(::StepRange) = 1 +firstindex(::LinRange) = 1 + +function length(r::StepRange{T}) where T<:Union{Int,UInt,Int64,UInt64,Int128,UInt128} + isempty(r) && return zero(T) + if r.step > 1 + return checked_add(convert(T, div(unsigned(r.stop - r.start), r.step)), one(T)) + elseif r.step < -1 + return checked_add(convert(T, div(unsigned(r.start - r.stop), -r.step)), one(T)) + elseif r.step > 0 + return checked_add(div(checked_sub(r.stop, r.start), r.step), one(T)) + else + return checked_add(div(checked_sub(r.start, r.stop), -r.step), one(T)) + end +end + +function length(r::AbstractUnitRange{T}) where T<:Union{Int,Int64,Int128} + @_inline_meta + checked_add(checked_sub(last(r), first(r)), one(T)) +end +length(r::OneTo{T}) where {T<:Union{Int,Int64}} = T(r.stop) + +length(r::AbstractUnitRange{T}) where {T<:Union{UInt,UInt64,UInt128}} = + r.stop < r.start ? zero(T) : checked_add(last(r) - first(r), one(T)) + +# some special cases to favor default Int type +let smallint = (Int === Int64 ? + Union{Int8,UInt8,Int16,UInt16,Int32,UInt32} : + Union{Int8,UInt8,Int16,UInt16}) + global length + + function length(r::StepRange{<:smallint}) + isempty(r) && return Int(0) + div(Int(r.stop)+Int(r.step) - Int(r.start), Int(r.step)) + end + + length(r::AbstractUnitRange{<:smallint}) = Int(last(r)) - Int(first(r)) + 1 + length(r::OneTo{<:smallint}) = Int(r.stop) +end + +first(r::OrdinalRange{T}) where {T} = convert(T, r.start) +first(r::OneTo{T}) where {T} = oneunit(T) +first(r::StepRangeLen) = unsafe_getindex(r, 1) +first(r::LinRange) = r.start + +last(r::OrdinalRange{T}) where {T} = convert(T, r.stop) +last(r::StepRangeLen) = unsafe_getindex(r, length(r)) +last(r::LinRange) = r.stop + +minimum(r::AbstractUnitRange) = isempty(r) ? throw(ArgumentError("range must be non-empty")) : first(r) +maximum(r::AbstractUnitRange) = isempty(r) ? throw(ArgumentError("range must be non-empty")) : last(r) +minimum(r::AbstractRange) = isempty(r) ? throw(ArgumentError("range must be non-empty")) : min(first(r), last(r)) +maximum(r::AbstractRange) = isempty(r) ? throw(ArgumentError("range must be non-empty")) : max(first(r), last(r)) + +extrema(r::AbstractRange) = (minimum(r), maximum(r)) + +# Ranges are immutable +copy(r::AbstractRange) = r + + +## iteration + +function iterate(r::Union{LinRange,StepRangeLen}, i::Int=1) + @_inline_meta + length(r) < i && return nothing + unsafe_getindex(r, i), i + 1 +end + +iterate(r::OrdinalRange) = isempty(r) ? nothing : (first(r), first(r)) + +function iterate(r::OrdinalRange{T}, i) where {T} + @_inline_meta + i == last(r) && return nothing + next = convert(T, i + step(r)) + (next, next) +end + +## indexing + +_in_unit_range(v::UnitRange, val, i::Integer) = i > 0 && val <= v.stop && val >= v.start + +function getindex(v::UnitRange{T}, i::Integer) where T + @_inline_meta + val = convert(T, v.start + (i - 1)) + @boundscheck _in_unit_range(v, val, i) || throw_boundserror(v, i) + val +end + +const OverflowSafe = Union{Bool,Int8,Int16,Int32,Int64,Int128, + UInt8,UInt16,UInt32,UInt64,UInt128} + +function getindex(v::UnitRange{T}, i::Integer) where {T<:OverflowSafe} + @_inline_meta + val = v.start + (i - 1) + @boundscheck _in_unit_range(v, val, i) || throw_boundserror(v, i) + val % T +end + +function getindex(v::OneTo{T}, i::Integer) where T + @_inline_meta + @boundscheck ((i > 0) & (i <= v.stop)) || throw_boundserror(v, i) + convert(T, i) +end + +function getindex(v::AbstractRange{T}, i::Integer) where T + @_inline_meta + ret = convert(T, first(v) + (i - 1)*step_hp(v)) + ok = ifelse(step(v) > zero(step(v)), + (ret <= last(v)) & (ret >= first(v)), + (ret <= first(v)) & (ret >= last(v))) + @boundscheck ((i > 0) & ok) || throw_boundserror(v, i) + ret +end + +function getindex(r::Union{StepRangeLen,LinRange}, i::Integer) + @_inline_meta + @boundscheck checkbounds(r, i) + unsafe_getindex(r, i) +end + +# This is separate to make it useful even when running with --check-bounds=yes +function unsafe_getindex(r::StepRangeLen{T}, i::Integer) where T + u = i - r.offset + T(r.ref + u*r.step) +end + +function _getindex_hiprec(r::StepRangeLen, i::Integer) # without rounding by T + u = i - r.offset + r.ref + u*r.step +end + +function unsafe_getindex(r::LinRange, i::Integer) + lerpi(i-1, r.lendiv, r.start, r.stop) +end + +function lerpi(j::Integer, d::Integer, a::T, b::T) where T + @_inline_meta + t = j/d + T((1-t)*a + t*b) +end + +getindex(r::AbstractRange, ::Colon) = copy(r) + +function getindex(r::AbstractUnitRange, s::AbstractUnitRange{<:Integer}) + @_inline_meta + @boundscheck checkbounds(r, s) + f = first(r) + st = oftype(f, f + first(s)-1) + range(st, length=length(s)) +end + +function getindex(r::OneTo{T}, s::OneTo) where T + @_inline_meta + @boundscheck checkbounds(r, s) + OneTo(T(s.stop)) +end + +function getindex(r::AbstractUnitRange, s::StepRange{<:Integer}) + @_inline_meta + @boundscheck checkbounds(r, s) + st = oftype(first(r), first(r) + s.start-1) + range(st, step=step(s), length=length(s)) +end + +function getindex(r::StepRange, s::AbstractRange{<:Integer}) + @_inline_meta + @boundscheck checkbounds(r, s) + st = oftype(r.start, r.start + (first(s)-1)*step(r)) + range(st, step=step(r)*step(s), length=length(s)) +end + +function getindex(r::StepRangeLen{T}, s::OrdinalRange{<:Integer}) where {T} + @_inline_meta + @boundscheck checkbounds(r, s) + # Find closest approach to offset by s + ind = LinearIndices(s) + offset = max(min(1 + round(Int, (r.offset - first(s))/step(s)), last(ind)), first(ind)) + ref = _getindex_hiprec(r, first(s) + (offset-1)*step(s)) + return StepRangeLen{T}(ref, r.step*step(s), length(s), offset) +end + +function getindex(r::LinRange, s::OrdinalRange{<:Integer}) + @_inline_meta + @boundscheck checkbounds(r, s) + vfirst = unsafe_getindex(r, first(s)) + vlast = unsafe_getindex(r, last(s)) + return LinRange(vfirst, vlast, length(s)) +end + +show(io::IO, r::AbstractRange) = print(io, repr(first(r)), ':', repr(step(r)), ':', repr(last(r))) +show(io::IO, r::UnitRange) = print(io, repr(first(r)), ':', repr(last(r))) +show(io::IO, r::OneTo) = print(io, "Base.OneTo(", r.stop, ")") + +==(r::T, s::T) where {T<:AbstractRange} = + (first(r) == first(s)) & (step(r) == step(s)) & (last(r) == last(s)) +==(r::OrdinalRange, s::OrdinalRange) = + (first(r) == first(s)) & (step(r) == step(s)) & (last(r) == last(s)) +==(r::T, s::T) where {T<:Union{StepRangeLen,LinRange}} = + (first(r) == first(s)) & (length(r) == length(s)) & (last(r) == last(s)) +==(r::Union{StepRange{T},StepRangeLen{T,T}}, s::Union{StepRange{T},StepRangeLen{T,T}}) where {T} = + (first(r) == first(s)) & (last(r) == last(s)) & (step(r) == step(s)) + +function ==(r::AbstractRange, s::AbstractRange) + lr = length(r) + if lr != length(s) + return false + end + yr, ys = iterate(r), iterate(s) + while yr !== nothing + yr[1] == ys[1] || return false + yr, ys = iterate(r, yr[2]), iterate(s, ys[2]) + end + return true +end + +intersect(r::OneTo, s::OneTo) = OneTo(min(r.stop,s.stop)) + +intersect(r::AbstractUnitRange{<:Integer}, s::AbstractUnitRange{<:Integer}) = max(first(r),first(s)):min(last(r),last(s)) + +intersect(i::Integer, r::AbstractUnitRange{<:Integer}) = + i < first(r) ? (first(r):i) : + i > last(r) ? (i:last(r)) : (i:i) + +intersect(r::AbstractUnitRange{<:Integer}, i::Integer) = intersect(i, r) + +function intersect(r::AbstractUnitRange{<:Integer}, s::StepRange{<:Integer}) + if isempty(s) + range(first(r), length=0) + elseif step(s) == 0 + intersect(first(s), r) + elseif step(s) < 0 + intersect(r, reverse(s)) + else + sta = first(s) + ste = step(s) + sto = last(s) + lo = first(r) + hi = last(r) + i0 = max(sta, lo + mod(sta - lo, ste)) + i1 = min(sto, hi - mod(hi - sta, ste)) + i0:ste:i1 + end +end + +function intersect(r::StepRange{<:Integer}, s::AbstractUnitRange{<:Integer}) + if step(r) < 0 + reverse(intersect(s, reverse(r))) + else + intersect(s, r) + end +end + +function intersect(r::StepRange, s::StepRange) + if isempty(r) || isempty(s) + return range(first(r), step=step(r), length=0) + elseif step(s) < zero(step(s)) + return intersect(r, reverse(s)) + elseif step(r) < zero(step(r)) + return reverse(intersect(reverse(r), s)) + end + + start1 = first(r) + step1 = step(r) + stop1 = last(r) + start2 = first(s) + step2 = step(s) + stop2 = last(s) + a = lcm(step1, step2) + + # if a == 0 + # # One or both ranges have step 0. + # if step1 == 0 && step2 == 0 + # return start1 == start2 ? r : AbstractRange(start1, 0, 0) + # elseif step1 == 0 + # return start2 <= start1 <= stop2 && rem(start1 - start2, step2) == 0 ? r : AbstractRange(start1, 0, 0) + # else + # return start1 <= start2 <= stop1 && rem(start2 - start1, step1) == 0 ? (start2:step1:start2) : AbstractRange(start1, step1, 0) + # end + # end + + g, x, y = gcdx(step1, step2) + + if !iszero(rem(start1 - start2, g)) + # Unaligned, no overlap possible. + return range(start1, step=a, length=0) + end + + z = div(start1 - start2, g) + b = start1 - x * z * step1 + # Possible points of the intersection of r and s are + # ..., b-2a, b-a, b, b+a, b+2a, ... + # Determine where in the sequence to start and stop. + m = max(start1 + mod(b - start1, a), start2 + mod(b - start2, a)) + n = min(stop1 - mod(stop1 - b, a), stop2 - mod(stop2 - b, a)) + m:a:n +end + +function intersect(r1::AbstractRange, r2::AbstractRange, r3::AbstractRange, r::AbstractRange...) + i = intersect(intersect(r1, r2), r3) + for t in r + i = intersect(i, t) + end + i +end + +# _findin (the index of intersection) +function _findin(r::AbstractRange{<:Integer}, span::AbstractUnitRange{<:Integer}) + local ifirst + local ilast + fspan = first(span) + lspan = last(span) + fr = first(r) + lr = last(r) + sr = step(r) + if sr > 0 + ifirst = fr >= fspan ? 1 : ceil(Integer,(fspan-fr)/sr)+1 + ilast = lr <= lspan ? length(r) : length(r) - ceil(Integer,(lr-lspan)/sr) + elseif sr < 0 + ifirst = fr <= lspan ? 1 : ceil(Integer,(lspan-fr)/sr)+1 + ilast = lr >= fspan ? length(r) : length(r) - ceil(Integer,(lr-fspan)/sr) + else + ifirst = fr >= fspan ? 1 : length(r)+1 + ilast = fr <= lspan ? length(r) : 0 + end + r isa AbstractUnitRange ? (ifirst:ilast) : (ifirst:1:ilast) +end + +issubset(r::OneTo, s::OneTo) = r.stop <= s.stop + +issubset(r::AbstractUnitRange{<:Integer}, s::AbstractUnitRange{<:Integer}) = + first(r) >= first(s) && last(r) <= last(s) + +## linear operations on ranges ## + +-(r::OrdinalRange) = range(-first(r), step=-step(r), length=length(r)) +-(r::StepRangeLen{T,R,S}) where {T,R,S} = + StepRangeLen{T,R,S}(-r.ref, -r.step, length(r), r.offset) +-(r::LinRange) = LinRange(-r.start, -r.stop, length(r)) + + +# promote eltype if at least one container wouldn't change, otherwise join container types. +el_same(::Type{T}, a::Type{<:AbstractArray{T,n}}, b::Type{<:AbstractArray{T,n}}) where {T,n} = a +el_same(::Type{T}, a::Type{<:AbstractArray{T,n}}, b::Type{<:AbstractArray{S,n}}) where {T,S,n} = a +el_same(::Type{T}, a::Type{<:AbstractArray{S,n}}, b::Type{<:AbstractArray{T,n}}) where {T,S,n} = b +el_same(::Type, a, b) = promote_typejoin(a, b) + +promote_rule(a::Type{UnitRange{T1}}, b::Type{UnitRange{T2}}) where {T1,T2} = + el_same(promote_type(T1,T2), a, b) +UnitRange{T}(r::UnitRange{T}) where {T<:Real} = r +UnitRange{T}(r::UnitRange) where {T<:Real} = UnitRange{T}(r.start, r.stop) + +promote_rule(a::Type{OneTo{T1}}, b::Type{OneTo{T2}}) where {T1,T2} = + el_same(promote_type(T1,T2), a, b) +OneTo{T}(r::OneTo{T}) where {T<:Integer} = r +OneTo{T}(r::OneTo) where {T<:Integer} = OneTo{T}(r.stop) + +promote_rule(a::Type{UnitRange{T1}}, ::Type{UR}) where {T1,UR<:AbstractUnitRange} = + promote_rule(a, UnitRange{eltype(UR)}) +UnitRange{T}(r::AbstractUnitRange) where {T<:Real} = UnitRange{T}(first(r), last(r)) +UnitRange(r::AbstractUnitRange) = UnitRange(first(r), last(r)) + +AbstractUnitRange{T}(r::AbstractUnitRange{T}) where {T} = r +AbstractUnitRange{T}(r::UnitRange) where {T} = UnitRange{T}(r) +AbstractUnitRange{T}(r::OneTo) where {T} = OneTo{T}(r) + +promote_rule(::Type{StepRange{T1a,T1b}}, ::Type{StepRange{T2a,T2b}}) where {T1a,T1b,T2a,T2b} = + el_same(promote_type(T1a,T2a), + # el_same only operates on array element type, so just promote second type parameter + StepRange{T1a, promote_type(T1b,T2b)}, + StepRange{T2a, promote_type(T1b,T2b)}) +StepRange{T1,T2}(r::StepRange{T1,T2}) where {T1,T2} = r + +promote_rule(a::Type{StepRange{T1a,T1b}}, ::Type{UR}) where {T1a,T1b,UR<:AbstractUnitRange} = + promote_rule(a, StepRange{eltype(UR), eltype(UR)}) +StepRange{T1,T2}(r::AbstractRange) where {T1,T2} = + StepRange{T1,T2}(convert(T1, first(r)), convert(T2, step(r)), convert(T1, last(r))) +StepRange(r::AbstractUnitRange{T}) where {T} = + StepRange{T,T}(first(r), step(r), last(r)) +(::Type{StepRange{T1,T2} where T1})(r::AbstractRange) where {T2} = StepRange{eltype(r),T2}(r) + +promote_rule(::Type{StepRangeLen{T1,R1,S1}},::Type{StepRangeLen{T2,R2,S2}}) where {T1,T2,R1,R2,S1,S2} = + el_same(promote_type(T1,T2), + StepRangeLen{T1,promote_type(R1,R2),promote_type(S1,S2)}, + StepRangeLen{T2,promote_type(R1,R2),promote_type(S1,S2)}) +StepRangeLen{T,R,S}(r::StepRangeLen{T,R,S}) where {T,R,S} = r +StepRangeLen{T,R,S}(r::StepRangeLen) where {T,R,S} = + StepRangeLen{T,R,S}(convert(R, r.ref), convert(S, r.step), length(r), r.offset) +StepRangeLen{T}(r::StepRangeLen) where {T} = + StepRangeLen(convert(T, r.ref), convert(T, r.step), length(r), r.offset) + +promote_rule(a::Type{StepRangeLen{T,R,S}}, ::Type{OR}) where {T,R,S,OR<:AbstractRange} = + promote_rule(a, StepRangeLen{eltype(OR), eltype(OR), eltype(OR)}) +StepRangeLen{T,R,S}(r::AbstractRange) where {T,R,S} = + StepRangeLen{T,R,S}(R(first(r)), S(step(r)), length(r)) +StepRangeLen{T}(r::AbstractRange) where {T} = + StepRangeLen(T(first(r)), T(step(r)), length(r)) +StepRangeLen(r::AbstractRange) = StepRangeLen{eltype(r)}(r) + +promote_rule(a::Type{LinRange{T1}}, b::Type{LinRange{T2}}) where {T1,T2} = + el_same(promote_type(T1,T2), a, b) +LinRange{T}(r::LinRange{T}) where {T} = r +LinRange{T}(r::AbstractRange) where {T} = LinRange{T}(first(r), last(r), length(r)) +LinRange(r::AbstractRange{T}) where {T} = LinRange{T}(r) + +promote_rule(a::Type{LinRange{T}}, ::Type{OR}) where {T,OR<:OrdinalRange} = + promote_rule(a, LinRange{eltype(OR)}) + +promote_rule(::Type{LinRange{L}}, b::Type{StepRangeLen{T,R,S}}) where {L,T,R,S} = + promote_rule(StepRangeLen{L,L,L}, b) + +## concatenation ## + +function vcat(rs::AbstractRange{T}...) where T + n::Int = 0 + for ra in rs + n += length(ra) + end + a = Vector{T}(undef, n) + i = 1 + for ra in rs, x in ra + @inbounds a[i] = x + i += 1 + end + return a +end + +Array{T,1}(r::AbstractRange{T}) where {T} = vcat(r) +collect(r::AbstractRange) = vcat(r) + +reverse(r::OrdinalRange) = (:)(last(r), -step(r), first(r)) +function reverse(r::StepRangeLen) + # If `r` is empty, `length(r) - r.offset + 1 will be nonpositive hence + # invalid. As `reverse(r)` is also empty, any offset would work so we keep + # `r.offset` + offset = isempty(r) ? r.offset : length(r)-r.offset+1 + StepRangeLen(r.ref, -r.step, length(r), offset) +end +reverse(r::LinRange) = LinRange(r.stop, r.start, length(r)) + +## sorting ## + +issorted(r::AbstractUnitRange) = true +issorted(r::AbstractRange) = length(r) <= 1 || step(r) >= zero(step(r)) + +sort(r::AbstractUnitRange) = r +sort!(r::AbstractUnitRange) = r + +sort(r::AbstractRange) = issorted(r) ? r : reverse(r) + +sortperm(r::AbstractUnitRange) = 1:length(r) +sortperm(r::AbstractRange) = issorted(r) ? (1:1:length(r)) : (length(r):-1:1) + +function sum(r::AbstractRange{<:Real}) + l = length(r) + # note that a little care is required to avoid overflow in l*(l-1)/2 + return l * first(r) + (iseven(l) ? (step(r) * (l-1)) * (l>>1) + : (step(r) * l) * ((l-1)>>1)) +end + +function _in_range(x, r::AbstractRange) + if step(r) == 0 + return !isempty(r) && first(r) == x + else + n = round(Integer, (x - first(r)) / step(r)) + 1 + return n >= 1 && n <= length(r) && r[n] == x + end +end +in(x::Real, r::AbstractRange{<:Real}) = _in_range(x, r) +# This method needs to be defined separately since -(::T, ::T) can be implemented +# even if -(::T, ::Real) is not +in(x::T, r::AbstractRange{T}) where {T} = _in_range(x, r) + +in(x::Integer, r::AbstractUnitRange{<:Integer}) = (first(r) <= x) & (x <= last(r)) + +in(x::Real, r::AbstractRange{T}) where {T<:Integer} = + isinteger(x) && !isempty(r) && x >= minimum(r) && x <= maximum(r) && + (mod(convert(T,x),step(r))-mod(first(r),step(r)) == 0) +in(x::AbstractChar, r::AbstractRange{<:AbstractChar}) = + !isempty(r) && x >= minimum(r) && x <= maximum(r) && + (mod(Int(x) - Int(first(r)), step(r)) == 0) + +# Addition/subtraction of ranges + +function _define_range_op(@nospecialize f) + @eval begin + function $f(r1::OrdinalRange, r2::OrdinalRange) + r1l = length(r1) + (r1l == length(r2) || + throw(DimensionMismatch("argument dimensions must match"))) + range($f(first(r1), first(r2)), step=$f(step(r1), step(r2)), length=r1l) + end + + function $f(r1::LinRange{T}, r2::LinRange{T}) where T + len = r1.len + (len == r2.len || + throw(DimensionMismatch("argument dimensions must match"))) + LinRange{T}(convert(T, $f(first(r1), first(r2))), + convert(T, $f(last(r1), last(r2))), len) + end + + $f(r1::Union{StepRangeLen, OrdinalRange, LinRange}, + r2::Union{StepRangeLen, OrdinalRange, LinRange}) = + $f(promote(r1, r2)...) + end +end +_define_range_op(:+) +_define_range_op(:-) + +function +(r1::StepRangeLen{T,S}, r2::StepRangeLen{T,S}) where {T,S} + len = length(r1) + (len == length(r2) || + throw(DimensionMismatch("argument dimensions must match"))) + StepRangeLen(first(r1)+first(r2), step(r1)+step(r2), len) +end + +-(r1::StepRangeLen, r2::StepRangeLen) = +(r1, -r2) + +# Modular arithmetic on ranges + +""" + mod(x::Integer, r::AbstractUnitRange) + +Find `y` in the range `r` such that ``x ≡ y (mod n)``, where `n = length(r)`, +i.e. `y = mod(x - first(r), n) + first(r)`. + +See also: [`mod1`](@ref). + +# Examples +```jldoctest +julia> mod(0, Base.OneTo(3)) +3 + +julia> mod(3, 0:2) +0 +``` + +!!! compat "Julia 1.3" + This method requires at least Julia 1.3. +""" +mod(i::Integer, r::OneTo) = mod1(i, last(r)) +mod(i::Integer, r::AbstractUnitRange{<:Integer}) = mod(i-first(r), length(r)) + first(r) diff --git a/base/rational.jl b/base/rational.jl new file mode 100644 index 0000000..95b8816 --- /dev/null +++ b/base/rational.jl @@ -0,0 +1,451 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" + Rational{T<:Integer} <: Real + +Rational number type, with numerator and denominator of type `T`. +Rationals are checked for overflow. +""" +struct Rational{T<:Integer} <: Real + num::T + den::T + + function Rational{T}(num::Integer, den::Integer) where T<:Integer + num == den == zero(T) && __throw_rational_argerror(T) + num2, den2 = signbit(den) ? divgcd(-num, -den) : divgcd(num, den) + new(num2, den2) + end +end +@noinline __throw_rational_argerror(T) = throw(ArgumentError("invalid rational: zero($T)//zero($T)")) + +Rational(n::T, d::T) where {T<:Integer} = Rational{T}(n,d) +Rational(n::Integer, d::Integer) = Rational(promote(n,d)...) +Rational(n::Integer) = Rational(n,one(n)) + +function divgcd(x::Integer,y::Integer) + g = gcd(x,y) + div(x,g), div(y,g) +end + +""" + //(num, den) + +Divide two integers or rational numbers, giving a [`Rational`](@ref) result. + +# Examples +```jldoctest +julia> 3 // 5 +3//5 + +julia> (3 // 5) // (2 // 1) +3//10 +``` +""" +//(n::Integer, d::Integer) = Rational(n,d) + +function //(x::Rational, y::Integer) + xn,yn = divgcd(x.num,y) + xn//checked_mul(x.den,yn) +end +function //(x::Integer, y::Rational) + xn,yn = divgcd(x,y.num) + checked_mul(xn,y.den)//yn +end +function //(x::Rational, y::Rational) + xn,yn = divgcd(x.num,y.num) + xd,yd = divgcd(x.den,y.den) + checked_mul(xn,yd)//checked_mul(xd,yn) +end + +//(x::Complex, y::Real) = complex(real(x)//y,imag(x)//y) +//(x::Number, y::Complex) = x*conj(y)//abs2(y) + + +//(X::AbstractArray, y::Number) = X .// y + +function show(io::IO, x::Rational) + show(io, numerator(x)) + print(io, "//") + show(io, denominator(x)) +end + +function read(s::IO, ::Type{Rational{T}}) where T<:Integer + r = read(s,T) + i = read(s,T) + r//i +end +function write(s::IO, z::Rational) + write(s,numerator(z),denominator(z)) +end + +Rational{T}(x::Rational) where {T<:Integer} = Rational{T}(convert(T,x.num), convert(T,x.den)) +Rational{T}(x::Integer) where {T<:Integer} = Rational{T}(convert(T,x), convert(T,1)) + +Rational(x::Rational) = x + +Bool(x::Rational) = x==0 ? false : x==1 ? true : + throw(InexactError(:Bool, Bool, x)) # to resolve ambiguity +(::Type{T})(x::Rational) where {T<:Integer} = (isinteger(x) ? convert(T, x.num) : + throw(InexactError(nameof(T), T, x))) + +AbstractFloat(x::Rational) = float(x.num)/float(x.den) +function (::Type{T})(x::Rational{S}) where T<:AbstractFloat where S + P = promote_type(T,S) + convert(T, convert(P,x.num)/convert(P,x.den)) +end + +function Rational{T}(x::AbstractFloat) where T<:Integer + r = rationalize(T, x, tol=0) + x == convert(typeof(x), r) || throw(InexactError(:Rational, Rational{T}, x)) + r +end +Rational(x::Float64) = Rational{Int64}(x) +Rational(x::Float32) = Rational{Int}(x) + +big(q::Rational) = big(numerator(q))//big(denominator(q)) + +big(z::Complex{<:Rational{<:Integer}}) = Complex{Rational{BigInt}}(z) + +promote_rule(::Type{Rational{T}}, ::Type{S}) where {T<:Integer,S<:Integer} = Rational{promote_type(T,S)} +promote_rule(::Type{Rational{T}}, ::Type{Rational{S}}) where {T<:Integer,S<:Integer} = Rational{promote_type(T,S)} +promote_rule(::Type{Rational{T}}, ::Type{S}) where {T<:Integer,S<:AbstractFloat} = promote_type(T,S) + +widen(::Type{Rational{T}}) where {T} = Rational{widen(T)} + +""" + rationalize([T<:Integer=Int,] x; tol::Real=eps(x)) + +Approximate floating point number `x` as a [`Rational`](@ref) number with components +of the given integer type. The result will differ from `x` by no more than `tol`. + +# Examples +```jldoctest +julia> rationalize(5.6) +28//5 + +julia> a = rationalize(BigInt, 10.3) +103//10 + +julia> typeof(numerator(a)) +BigInt +``` +""" +function rationalize(::Type{T}, x::AbstractFloat, tol::Real) where T<:Integer + if tol < 0 + throw(ArgumentError("negative tolerance $tol")) + end + isnan(x) && return T(x)//one(T) + isinf(x) && return (x < 0 ? -one(T) : one(T))//zero(T) + + p, q = (x < 0 ? -one(T) : one(T)), zero(T) + pp, qq = zero(T), one(T) + + x = abs(x) + a = trunc(x) + r = x-a + y = one(x) + + tolx = oftype(x, tol) + nt, t, tt = tolx, zero(tolx), tolx + ia = np = nq = zero(T) + + # compute the successive convergents of the continued fraction + # np // nq = (p*a + pp) // (q*a + qq) + while r > nt + try + ia = convert(T,a) + + np = checked_add(checked_mul(ia,p),pp) + nq = checked_add(checked_mul(ia,q),qq) + p, pp = np, p + q, qq = nq, q + catch e + isa(e,InexactError) || isa(e,OverflowError) || rethrow() + return p // q + end + + # naive approach of using + # x = 1/r; a = trunc(x); r = x - a + # is inexact, so we store x as x/y + x, y = y, r + a, r = divrem(x,y) + + # maintain + # x0 = (p + (-1)^i * r) / q + t, tt = nt, t + nt = a*t+tt + end + + # find optimal semiconvergent + # smallest a such that x-a*y < a*t+tt + a = cld(x-tt,y+t) + try + ia = convert(T,a) + np = checked_add(checked_mul(ia,p),pp) + nq = checked_add(checked_mul(ia,q),qq) + return np // nq + catch e + isa(e,InexactError) || isa(e,OverflowError) || rethrow() + return p // q + end +end +rationalize(::Type{T}, x::AbstractFloat; tol::Real = eps(x)) where {T<:Integer} = rationalize(T, x, tol)::Rational{T} +rationalize(x::AbstractFloat; kvs...) = rationalize(Int, x; kvs...) + +""" + numerator(x) + +Numerator of the rational representation of `x`. + +# Examples +```jldoctest +julia> numerator(2//3) +2 + +julia> numerator(4) +4 +``` +""" +numerator(x::Integer) = x +numerator(x::Rational) = x.num + +""" + denominator(x) + +Denominator of the rational representation of `x`. + +# Examples +```jldoctest +julia> denominator(2//3) +3 + +julia> denominator(4) +1 +``` +""" +denominator(x::Integer) = one(x) +denominator(x::Rational) = x.den + +sign(x::Rational) = oftype(x, sign(x.num)) +signbit(x::Rational) = signbit(x.num) +copysign(x::Rational, y::Real) = copysign(x.num,y) // x.den +copysign(x::Rational, y::Rational) = copysign(x.num,y.num) // x.den + +abs(x::Rational) = Rational(abs(x.num), x.den) + +typemin(::Type{Rational{T}}) where {T<:Integer} = -one(T)//zero(T) +typemax(::Type{Rational{T}}) where {T<:Integer} = one(T)//zero(T) + +isinteger(x::Rational) = x.den == 1 + ++(x::Rational) = (+x.num) // x.den +-(x::Rational) = (-x.num) // x.den + +function -(x::Rational{T}) where T<:BitSigned + x.num == typemin(T) && throw(OverflowError("rational numerator is typemin(T)")) + (-x.num) // x.den +end +function -(x::Rational{T}) where T<:Unsigned + x.num != zero(T) && throw(OverflowError("cannot negate unsigned number")) + x +end + +for (op,chop) in ((:+,:checked_add), (:-,:checked_sub), + (:rem,:rem), (:mod,:mod)) + @eval begin + function ($op)(x::Rational, y::Rational) + xd, yd = divgcd(x.den, y.den) + Rational(($chop)(checked_mul(x.num,yd), checked_mul(y.num,xd)), checked_mul(x.den,yd)) + end + end +end + +function *(x::Rational, y::Rational) + xn,yd = divgcd(x.num,y.den) + xd,yn = divgcd(x.den,y.num) + checked_mul(xn,yn) // checked_mul(xd,yd) +end +/(x::Rational, y::Rational) = x//y +/(x::Rational, y::Complex{<:Union{Integer,Rational}}) = x//y +inv(x::Rational) = Rational(x.den, x.num) + +fma(x::Rational, y::Rational, z::Rational) = x*y+z + +==(x::Rational, y::Rational) = (x.den == y.den) & (x.num == y.num) +<( x::Rational, y::Rational) = x.den == y.den ? x.num < y.num : + widemul(x.num,y.den) < widemul(x.den,y.num) +<=(x::Rational, y::Rational) = x.den == y.den ? x.num <= y.num : + widemul(x.num,y.den) <= widemul(x.den,y.num) + + +==(x::Rational, y::Integer ) = (x.den == 1) & (x.num == y) +==(x::Integer , y::Rational) = y == x +<( x::Rational, y::Integer ) = x.num < widemul(x.den,y) +<( x::Integer , y::Rational) = widemul(x,y.den) < y.num +<=(x::Rational, y::Integer ) = x.num <= widemul(x.den,y) +<=(x::Integer , y::Rational) = widemul(x,y.den) <= y.num + +function ==(x::AbstractFloat, q::Rational) + if isfinite(x) + (count_ones(q.den) == 1) & (x*q.den == q.num) + else + x == q.num/q.den + end +end + +==(q::Rational, x::AbstractFloat) = x == q + +for rel in (:<,:<=,:cmp) + for (Tx,Ty) in ((Rational,AbstractFloat), (AbstractFloat,Rational)) + @eval function ($rel)(x::$Tx, y::$Ty) + if isnan(x) + $(rel === :cmp ? :(return isnan(y) ? 0 : 1) : + :(return false)) + end + if isnan(y) + $(rel === :cmp ? :(return -1) : + :(return false)) + end + + xn, xp, xd = decompose(x) + yn, yp, yd = decompose(y) + + if xd < 0 + xn = -xn + xd = -xd + end + if yd < 0 + yn = -yn + yd = -yd + end + + xc, yc = widemul(xn,yd), widemul(yn,xd) + xs, ys = sign(xc), sign(yc) + + if xs != ys + return ($rel)(xs,ys) + elseif xs == 0 + # both are zero or ±Inf + return ($rel)(xn,yn) + end + + xb, yb = ndigits0z(xc,2) + xp, ndigits0z(yc,2) + yp + + if xb == yb + xc, yc = promote(xc,yc) + if xp > yp + xc = (xc<<(xp-yp)) + else + yc = (yc<<(yp-xp)) + end + return ($rel)(xc,yc) + else + return xc > 0 ? ($rel)(xb,yb) : ($rel)(yb,xb) + end + end + end +end + +# needed to avoid ambiguity between ==(x::Real, z::Complex) and ==(x::Rational, y::Number) +==(z::Complex , x::Rational) = isreal(z) & (real(z) == x) +==(x::Rational, z::Complex ) = isreal(z) & (real(z) == x) + +for op in (:div, :fld, :cld) + @eval begin + function ($op)(x::Rational, y::Integer ) + xn,yn = divgcd(x.num,y) + ($op)(xn, checked_mul(x.den,yn)) + end + function ($op)(x::Integer, y::Rational) + xn,yn = divgcd(x,y.num) + ($op)(checked_mul(xn,y.den), yn) + end + function ($op)(x::Rational, y::Rational) + xn,yn = divgcd(x.num,y.num) + xd,yd = divgcd(x.den,y.den) + ($op)(checked_mul(xn,yd), checked_mul(xd,yn)) + end + end +end + +trunc(::Type{T}, x::Rational) where {T} = convert(T,div(x.num,x.den)) +floor(::Type{T}, x::Rational) where {T} = convert(T,fld(x.num,x.den)) +ceil(::Type{T}, x::Rational) where {T} = convert(T,cld(x.num,x.den)) +round(::Type{T}, x::Rational, r::RoundingMode=RoundNearest) where {T} = _round_rational(T, x, r) +round(x::Rational, r::RoundingMode) = round(Rational, x, r) + +function _round_rational(::Type{T}, x::Rational{Tr}, ::RoundingMode{:Nearest}) where {T,Tr} + if denominator(x) == zero(Tr) && T <: Integer + throw(DivideError()) + elseif denominator(x) == zero(Tr) + return convert(T, copysign(one(Tr)//zero(Tr), numerator(x))) + end + q,r = divrem(numerator(x), denominator(x)) + s = q + if abs(r) >= abs((denominator(x)-copysign(Tr(4), numerator(x))+one(Tr)+iseven(q))>>1 + copysign(Tr(2), numerator(x))) + s += copysign(one(Tr),numerator(x)) + end + convert(T, s) +end + +function _round_rational(::Type{T}, x::Rational{Tr}, ::RoundingMode{:NearestTiesAway}) where {T,Tr} + if denominator(x) == zero(Tr) && T <: Integer + throw(DivideError()) + elseif denominator(x) == zero(Tr) + return convert(T, copysign(one(Tr)//zero(Tr), numerator(x))) + end + q,r = divrem(numerator(x), denominator(x)) + s = q + if abs(r) >= abs((denominator(x)-copysign(Tr(4), numerator(x))+one(Tr))>>1 + copysign(Tr(2), numerator(x))) + s += copysign(one(Tr),numerator(x)) + end + convert(T, s) +end + +function _round_rational(::Type{T}, x::Rational{Tr}, ::RoundingMode{:NearestTiesUp}) where {T,Tr} + if denominator(x) == zero(Tr) && T <: Integer + throw(DivideError()) + elseif denominator(x) == zero(Tr) + return convert(T, copysign(one(Tr)//zero(Tr), numerator(x))) + end + q,r = divrem(numerator(x), denominator(x)) + s = q + if abs(r) >= abs((denominator(x)-copysign(Tr(4), numerator(x))+one(Tr)+(numerator(x)<0))>>1 + copysign(Tr(2), numerator(x))) + s += copysign(one(Tr),numerator(x)) + end + convert(T, s) +end + +function round(::Type{T}, x::Rational{Bool}, ::RoundingMode=RoundNearest) where T + if denominator(x) == false && (T <: Union{Integer, Bool}) + throw(DivideError()) + end + convert(T, x) +end + +trunc(x::Rational{T}) where {T} = Rational(trunc(T,x)) +floor(x::Rational{T}) where {T} = Rational(floor(T,x)) +ceil(x::Rational{T}) where {T} = Rational(ceil(T,x)) +round(x::Rational{T}) where {T} = Rational(round(T,x)) + +function ^(x::Rational, n::Integer) + n >= 0 ? power_by_squaring(x,n) : power_by_squaring(inv(x),-n) +end + +^(x::Number, y::Rational) = x^(y.num/y.den) +^(x::T, y::Rational) where {T<:AbstractFloat} = x^convert(T,y) +^(z::Complex{T}, p::Rational) where {T<:Real} = z^convert(typeof(one(T)^p), p) + +^(z::Complex{<:Rational}, n::Bool) = n ? z : one(z) # to resolve ambiguity +function ^(z::Complex{<:Rational}, n::Integer) + n >= 0 ? power_by_squaring(z,n) : power_by_squaring(inv(z),-n) +end + +iszero(x::Rational) = iszero(numerator(x)) +isone(x::Rational) = isone(numerator(x)) & isone(denominator(x)) + +function lerpi(j::Integer, d::Integer, a::Rational, b::Rational) + ((d-j)*a)/d + (j*b)/d +end + +float(::Type{Rational{T}}) where {T<:Integer} = float(T) diff --git a/base/reduce.jl b/base/reduce.jl new file mode 100644 index 0000000..425d8d6 --- /dev/null +++ b/base/reduce.jl @@ -0,0 +1,775 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## reductions ## + +###### Generic (map)reduce functions ###### + +if Int === Int32 + const SmallSigned = Union{Int8,Int16} + const SmallUnsigned = Union{UInt8,UInt16} +else + const SmallSigned = Union{Int8,Int16,Int32} + const SmallUnsigned = Union{UInt8,UInt16,UInt32} +end + +""" + Base.add_sum(x, y) + +The reduction operator used in `sum`. The main difference from [`+`](@ref) is that small +integers are promoted to `Int`/`UInt`. +""" +add_sum(x, y) = x + y +add_sum(x::SmallSigned, y::SmallSigned) = Int(x) + Int(y) +add_sum(x::SmallUnsigned, y::SmallUnsigned) = UInt(x) + UInt(y) +add_sum(x::Real, y::Real)::Real = x + y + +""" + Base.mul_prod(x, y) + +The reduction operator used in `prod`. The main difference from [`*`](@ref) is that small +integers are promoted to `Int`/`UInt`. +""" +mul_prod(x, y) = x * y +mul_prod(x::SmallSigned, y::SmallSigned) = Int(x) * Int(y) +mul_prod(x::SmallUnsigned, y::SmallUnsigned) = UInt(x) * UInt(y) +mul_prod(x::Real, y::Real)::Real = x * y + +## foldl && mapfoldl + +function mapfoldl_impl(f, op, nt::NamedTuple{(:init,)}, itr, i...) + init = nt.init + # Unroll the while loop once; if init is known, the call to op may + # be evaluated at compile time + y = iterate(itr, i...) + y === nothing && return init + v = op(init, f(y[1])) + while true + y = iterate(itr, y[2]) + y === nothing && break + v = op(v, f(y[1])) + end + return v +end + +function mapfoldl_impl(f, op, nt::NamedTuple{()}, itr) + y = iterate(itr) + if y === nothing + return Base.mapreduce_empty_iter(f, op, itr, IteratorEltype(itr)) + end + x, i = y + init = mapreduce_first(f, op, x) + return mapfoldl_impl(f, op, (init=init,), itr, i) +end + + +""" + mapfoldl(f, op, itr; [init]) + +Like [`mapreduce`](@ref), but with guaranteed left associativity, as in [`foldl`](@ref). +If provided, the keyword argument `init` will be used exactly once. In general, it will be +necessary to provide `init` to work with empty collections. +""" +mapfoldl(f, op, itr; kw...) = mapfoldl_impl(f, op, kw.data, itr) + +""" + foldl(op, itr; [init]) + +Like [`reduce`](@ref), but with guaranteed left associativity. If provided, the keyword +argument `init` will be used exactly once. In general, it will be necessary to provide +`init` to work with empty collections. + +# Examples +```jldoctest +julia> foldl(=>, 1:4) +((1 => 2) => 3) => 4 + +julia> foldl(=>, 1:4; init=0) +(((0 => 1) => 2) => 3) => 4 +``` +""" +foldl(op, itr; kw...) = mapfoldl(identity, op, itr; kw...) + +## foldr & mapfoldr + +mapfoldr_impl(f, op, nt::NamedTuple{(:init,)}, itr) = + mapfoldl_impl(f, (x,y) -> op(y,x), nt, Iterators.reverse(itr)) + +# we can't just call mapfoldl_impl with (x,y) -> op(y,x), because +# we need to use the type of op for mapreduce_empty_iter and mapreduce_first. +function mapfoldr_impl(f, op, nt::NamedTuple{()}, itr) + ritr = Iterators.reverse(itr) + y = iterate(ritr) + if y === nothing + return Base.mapreduce_empty_iter(f, op, itr, IteratorEltype(itr)) + end + x, i = y + init = mapreduce_first(f, op, x) + return mapfoldl_impl(f, (x,y) -> op(y,x), (init=init,), ritr, i) +end + +""" + mapfoldr(f, op, itr; [init]) + +Like [`mapreduce`](@ref), but with guaranteed right associativity, as in [`foldr`](@ref). If +provided, the keyword argument `init` will be used exactly once. In general, it will be +necessary to provide `init` to work with empty collections. +""" +mapfoldr(f, op, itr; kw...) = mapfoldr_impl(f, op, kw.data, itr) + + +""" + foldr(op, itr; [init]) + +Like [`reduce`](@ref), but with guaranteed right associativity. If provided, the keyword +argument `init` will be used exactly once. In general, it will be necessary to provide +`init` to work with empty collections. + +# Examples +```jldoctest +julia> foldr(=>, 1:4) +1 => (2 => (3 => 4)) + +julia> foldr(=>, 1:4; init=0) +1 => (2 => (3 => (4 => 0))) +``` +""" +foldr(op, itr; kw...) = mapfoldr(identity, op, itr; kw...) + +## reduce & mapreduce + +# `mapreduce_impl()` is called by `mapreduce()` (via `_mapreduce()`, when `A` +# supports linear indexing) and does actual calculations (for `A[ifirst:ilast]` subset). +# For efficiency, no parameter validity checks are done, it's the caller's responsibility. +# `ifirst:ilast` range is assumed to be a valid non-empty subset of `A` indices. + +# This is a generic implementation of `mapreduce_impl()`, +# certain `op` (e.g. `min` and `max`) may have their own specialized versions. +@noinline function mapreduce_impl(f, op, A::AbstractArray, ifirst::Integer, ilast::Integer, blksize::Int) + if ifirst == ilast + @inbounds a1 = A[ifirst] + return mapreduce_first(f, op, a1) + elseif ifirst + blksize > ilast + # sequential portion + @inbounds a1 = A[ifirst] + @inbounds a2 = A[ifirst+1] + v = op(f(a1), f(a2)) + @simd for i = ifirst + 2 : ilast + @inbounds ai = A[i] + v = op(v, f(ai)) + end + return v + else + # pairwise portion + imid = (ifirst + ilast) >> 1 + v1 = mapreduce_impl(f, op, A, ifirst, imid, blksize) + v2 = mapreduce_impl(f, op, A, imid+1, ilast, blksize) + return op(v1, v2) + end +end + +mapreduce_impl(f, op, A::AbstractArray, ifirst::Integer, ilast::Integer) = + mapreduce_impl(f, op, A, ifirst, ilast, pairwise_blocksize(f, op)) + +""" + mapreduce(f, op, itrs...; [init]) + +Apply function `f` to each element(s) in `itrs`, and then reduce the result using the binary +function `op`. If provided, `init` must be a neutral element for `op` that will be returned +for empty collections. It is unspecified whether `init` is used for non-empty collections. +In general, it will be necessary to provide `init` to work with empty collections. + +[`mapreduce`](@ref) is functionally equivalent to calling +`reduce(op, map(f, itr); init=init)`, but will in general execute faster since no +intermediate collection needs to be created. See documentation for [`reduce`](@ref) and +[`map`](@ref). + +!!! compat "Julia 1.2" + `mapreduce` with multiple iterators requires Julia 1.2 or later. + +# Examples +```jldoctest +julia> mapreduce(x->x^2, +, [1:3;]) # == 1 + 4 + 9 +14 +``` + +The associativity of the reduction is implementation-dependent. Additionally, some +implementations may reuse the return value of `f` for elements that appear multiple times in +`itr`. Use [`mapfoldl`](@ref) or [`mapfoldr`](@ref) instead for +guaranteed left or right associativity and invocation of `f` for every value. +""" +mapreduce(f, op, itr; kw...) = mapfoldl(f, op, itr; kw...) +mapreduce(f, op, itrs...; kw...) = reduce(op, Generator(f, itrs...); kw...) + +# Note: sum_seq usually uses four or more accumulators after partial +# unrolling, so each accumulator gets at most 256 numbers +pairwise_blocksize(f, op) = 1024 + +# This combination appears to show a benefit from a larger block size +pairwise_blocksize(::typeof(abs2), ::typeof(+)) = 4096 + + +# handling empty arrays +_empty_reduce_error() = throw(ArgumentError("reducing over an empty collection is not allowed")) + +""" + Base.reduce_empty(op, T) + +The value to be returned when calling [`reduce`](@ref), [`foldl`](@ref) or [`foldr`](@ref) +with reduction `op` over an empty array with element type of `T`. + +If not defined, this will throw an `ArgumentError`. +""" +reduce_empty(op, T) = _empty_reduce_error() +reduce_empty(::typeof(+), T) = zero(T) +reduce_empty(::typeof(+), ::Type{Bool}) = zero(Int) +reduce_empty(::typeof(*), T) = one(T) +reduce_empty(::typeof(*), ::Type{<:AbstractChar}) = "" +reduce_empty(::typeof(&), ::Type{Bool}) = true +reduce_empty(::typeof(|), ::Type{Bool}) = false + +reduce_empty(::typeof(add_sum), T) = reduce_empty(+, T) +reduce_empty(::typeof(add_sum), ::Type{T}) where {T<:SmallSigned} = zero(Int) +reduce_empty(::typeof(add_sum), ::Type{T}) where {T<:SmallUnsigned} = zero(UInt) +reduce_empty(::typeof(mul_prod), T) = reduce_empty(*, T) +reduce_empty(::typeof(mul_prod), ::Type{T}) where {T<:SmallSigned} = one(Int) +reduce_empty(::typeof(mul_prod), ::Type{T}) where {T<:SmallUnsigned} = one(UInt) + +""" + Base.mapreduce_empty(f, op, T) + +The value to be returned when calling [`mapreduce`](@ref), [`mapfoldl`](@ref`) or +[`mapfoldr`](@ref) with map `f` and reduction `op` over an empty array with element type +of `T`. + +If not defined, this will throw an `ArgumentError`. +""" +mapreduce_empty(f, op, T) = _empty_reduce_error() +mapreduce_empty(::typeof(identity), op, T) = reduce_empty(op, T) +mapreduce_empty(::typeof(abs), op, T) = abs(reduce_empty(op, T)) +mapreduce_empty(::typeof(abs2), op, T) = abs2(reduce_empty(op, T)) + +mapreduce_empty(f::typeof(abs), ::typeof(max), T) = abs(zero(T)) +mapreduce_empty(f::typeof(abs2), ::typeof(max), T) = abs2(zero(T)) + +mapreduce_empty_iter(f, op, itr, ::HasEltype) = mapreduce_empty(f, op, eltype(itr)) +mapreduce_empty_iter(f, op::typeof(&), itr, ::EltypeUnknown) = true +mapreduce_empty_iter(f, op::typeof(|), itr, ::EltypeUnknown) = false +mapreduce_empty_iter(f, op, itr, ::EltypeUnknown) = _empty_reduce_error() + +# handling of single-element iterators +""" + Base.reduce_first(op, x) + +The value to be returned when calling [`reduce`](@ref), [`foldl`](@ref`) or +[`foldr`](@ref) with reduction `op` over an iterator which contains a single element +`x`. This value may also used to initialise the recursion, so that `reduce(op, [x, y])` +may call `op(reduce_first(op, x), y)`. + +The default is `x` for most types. The main purpose is to ensure type stability, so +additional methods should only be defined for cases where `op` gives a result with +different types than its inputs. +""" +reduce_first(op, x) = x +reduce_first(::typeof(+), x::Bool) = Int(x) +reduce_first(::typeof(*), x::AbstractChar) = string(x) + +reduce_first(::typeof(add_sum), x) = reduce_first(+, x) +reduce_first(::typeof(add_sum), x::SmallSigned) = Int(x) +reduce_first(::typeof(add_sum), x::SmallUnsigned) = UInt(x) +reduce_first(::typeof(mul_prod), x) = reduce_first(*, x) +reduce_first(::typeof(mul_prod), x::SmallSigned) = Int(x) +reduce_first(::typeof(mul_prod), x::SmallUnsigned) = UInt(x) + +""" + Base.mapreduce_first(f, op, x) + +The value to be returned when calling [`mapreduce`](@ref), [`mapfoldl`](@ref`) or +[`mapfoldr`](@ref) with map `f` and reduction `op` over an iterator which contains a +single element `x`. This value may also used to initialise the recursion, so that +`mapreduce(f, op, [x, y])` may call `op(reduce_first(op, f, x), f(y))`. + +The default is `reduce_first(op, f(x))`. +""" +mapreduce_first(f, op, x) = reduce_first(op, f(x)) + +_mapreduce(f, op, A::AbstractArray) = _mapreduce(f, op, IndexStyle(A), A) + +function _mapreduce(f, op, ::IndexLinear, A::AbstractArray{T}) where T + inds = LinearIndices(A) + n = length(inds) + if n == 0 + return mapreduce_empty(f, op, T) + elseif n == 1 + @inbounds a1 = A[first(inds)] + return mapreduce_first(f, op, a1) + elseif n < 16 # process short array here, avoid mapreduce_impl() compilation + @inbounds i = first(inds) + @inbounds a1 = A[i] + @inbounds a2 = A[i+=1] + s = op(f(a1), f(a2)) + while i < last(inds) + @inbounds Ai = A[i+=1] + s = op(s, f(Ai)) + end + return s + else + return mapreduce_impl(f, op, A, first(inds), last(inds)) + end +end + +mapreduce(f, op, a::Number) = mapreduce_first(f, op, a) + +_mapreduce(f, op, ::IndexCartesian, A::AbstractArray) = mapfoldl(f, op, A) + +""" + reduce(op, itr; [init]) + +Reduce the given collection `itr` with the given binary operator `op`. If provided, the +initial value `init` must be a neutral element for `op` that will be returned for empty +collections. It is unspecified whether `init` is used for non-empty collections. + +For empty collections, providing `init` will be necessary, except for some special cases +(e.g. when `op` is one of `+`, `*`, `max`, `min`, `&`, `|`) when Julia can determine the +neutral element of `op`. + +Reductions for certain commonly-used operators may have special implementations, and +should be used instead: `maximum(itr)`, `minimum(itr)`, `sum(itr)`, `prod(itr)`, + `any(itr)`, `all(itr)`. + +The associativity of the reduction is implementation dependent. This means that you can't +use non-associative operations like `-` because it is undefined whether `reduce(-,[1,2,3])` +should be evaluated as `(1-2)-3` or `1-(2-3)`. Use [`foldl`](@ref) or +[`foldr`](@ref) instead for guaranteed left or right associativity. + +Some operations accumulate error. Parallelism will be easier if the reduction can be +executed in groups. Future versions of Julia might change the algorithm. Note that the +elements are not reordered if you use an ordered collection. + +# Examples +```jldoctest +julia> reduce(*, [2; 3; 4]) +24 + +julia> reduce(*, [2; 3; 4]; init=-1) +-24 +``` +""" +reduce(op, itr; kw...) = mapreduce(identity, op, itr; kw...) + +reduce(op, a::Number) = a # Do we want this? + +###### Specific reduction functions ###### + +## sum + +""" + sum(f, itr) + +Sum the results of calling function `f` on each element of `itr`. + +The return type is `Int` for signed integers of less than system word size, and +`UInt` for unsigned integers of less than system word size. For all other +arguments, a common return type is found to which all arguments are promoted. + +# Examples +```jldoctest +julia> sum(abs2, [2; 3; 4]) +29 +``` + +Note the important difference between `sum(A)` and `reduce(+, A)` for arrays +with small integer eltype: + +```jldoctest +julia> sum(Int8[100, 28]) +128 + +julia> reduce(+, Int8[100, 28]) +-128 +``` + +In the former case, the integers are widened to system word size and therefore +the result is 128. In the latter case, no such widening happens and integer +overflow results in -128. +""" +sum(f, a) = mapreduce(f, add_sum, a) + +""" + sum(itr) + +Returns the sum of all elements in a collection. + +The return type is `Int` for signed integers of less than system word size, and +`UInt` for unsigned integers of less than system word size. For all other +arguments, a common return type is found to which all arguments are promoted. + +# Examples +```jldoctest +julia> sum(1:20) +210 +``` +""" +sum(a) = sum(identity, a) +sum(a::AbstractArray{Bool}) = count(a) + +## prod +""" + prod(f, itr) + +Returns the product of `f` applied to each element of `itr`. + +The return type is `Int` for signed integers of less than system word size, and +`UInt` for unsigned integers of less than system word size. For all other +arguments, a common return type is found to which all arguments are promoted. + +# Examples +```jldoctest +julia> prod(abs2, [2; 3; 4]) +576 +``` +""" +prod(f, a) = mapreduce(f, mul_prod, a) + +""" + prod(itr) + +Returns the product of all elements of a collection. + +The return type is `Int` for signed integers of less than system word size, and +`UInt` for unsigned integers of less than system word size. For all other +arguments, a common return type is found to which all arguments are promoted. + +# Examples +```jldoctest +julia> prod(1:20) +2432902008176640000 +``` +""" +prod(a) = mapreduce(identity, mul_prod, a) + +## maximum & minimum +_fast(::typeof(min),x,y) = min(x,y) +_fast(::typeof(max),x,y) = max(x,y) +function _fast(::typeof(max), x::AbstractFloat, y::AbstractFloat) + ifelse(isnan(x), + x, + ifelse(x > y, x, y)) +end + +function _fast(::typeof(min),x::AbstractFloat, y::AbstractFloat) + ifelse(isnan(x), + x, + ifelse(x < y, x, y)) +end + +isbadzero(::typeof(max), x::AbstractFloat) = (x == zero(x)) & signbit(x) +isbadzero(::typeof(min), x::AbstractFloat) = (x == zero(x)) & !signbit(x) +isbadzero(op, x) = false +isgoodzero(::typeof(max), x) = isbadzero(min, x) +isgoodzero(::typeof(min), x) = isbadzero(max, x) + +function mapreduce_impl(f, op::Union{typeof(max), typeof(min)}, + A::AbstractArray, first::Int, last::Int) + a1 = @inbounds A[first] + v1 = mapreduce_first(f, op, a1) + v2 = v3 = v4 = v1 + chunk_len = 256 + start = first + 1 + simdstop = start + chunk_len - 4 + while simdstop <= last - 3 + # short circuit in case of NaN + v1 == v1 || return v1 + v2 == v2 || return v2 + v3 == v3 || return v3 + v4 == v4 || return v4 + @inbounds for i in start:4:simdstop + v1 = _fast(op, v1, f(A[i+0])) + v2 = _fast(op, v2, f(A[i+1])) + v3 = _fast(op, v3, f(A[i+2])) + v4 = _fast(op, v4, f(A[i+3])) + end + checkbounds(A, simdstop+3) + start += chunk_len + simdstop += chunk_len + end + v = op(op(v1,v2),op(v3,v4)) + for i in start:last + @inbounds ai = A[i] + v = op(v, f(ai)) + end + + # enforce correct order of 0.0 and -0.0 + # e.g. maximum([0.0, -0.0]) === 0.0 + # should hold + if isbadzero(op, v) + for i in first:last + x = @inbounds A[i] + isgoodzero(op,x) && return x + end + end + return v +end + +""" + maximum(f, itr) + +Returns the largest result of calling function `f` on each element of `itr`. + +# Examples +```jldoctest +julia> maximum(length, ["Julion", "Julia", "Jule"]) +6 +``` +""" +maximum(f, a) = mapreduce(f, max, a) + +""" + minimum(f, itr) + +Returns the smallest result of calling function `f` on each element of `itr`. + +# Examples +```jldoctest +julia> minimum(length, ["Julion", "Julia", "Jule"]) +4 +``` +""" +minimum(f, a) = mapreduce(f, min, a) + +""" + maximum(itr) + +Returns the largest element in a collection. + +# Examples +```jldoctest +julia> maximum(-20.5:10) +9.5 + +julia> maximum([1,2,3]) +3 +``` +""" +maximum(a) = mapreduce(identity, max, a) + +""" + minimum(itr) + +Returns the smallest element in a collection. + +# Examples +```jldoctest +julia> minimum(-20.5:10) +-20.5 + +julia> minimum([1,2,3]) +1 +``` +""" +minimum(a) = mapreduce(identity, min, a) + +## all & any + +""" + any(itr) -> Bool + +Test whether any elements of a boolean collection are `true`, returning `true` as +soon as the first `true` value in `itr` is encountered (short-circuiting). + +If the input contains [`missing`](@ref) values, return `missing` if all non-missing +values are `false` (or equivalently, if the input contains no `true` value), following +[three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic). + +# Examples +```jldoctest +julia> a = [true,false,false,true] +4-element Array{Bool,1}: + 1 + 0 + 0 + 1 + +julia> any(a) +true + +julia> any((println(i); v) for (i, v) in enumerate(a)) +1 +true + +julia> any([missing, true]) +true + +julia> any([false, missing]) +missing +``` +""" +any(itr) = any(identity, itr) + +""" + all(itr) -> Bool + +Test whether all elements of a boolean collection are `true`, returning `false` as +soon as the first `false` value in `itr` is encountered (short-circuiting). + +If the input contains [`missing`](@ref) values, return `missing` if all non-missing +values are `true` (or equivalently, if the input contains no `false` value), following +[three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic). + +# Examples +```jldoctest +julia> a = [true,false,false,true] +4-element Array{Bool,1}: + 1 + 0 + 0 + 1 + +julia> all(a) +false + +julia> all((println(i); v) for (i, v) in enumerate(a)) +1 +2 +false + +julia> all([missing, false]) +false + +julia> all([true, missing]) +missing +``` +""" +all(itr) = all(identity, itr) + +""" + any(p, itr) -> Bool + +Determine whether predicate `p` returns `true` for any elements of `itr`, returning +`true` as soon as the first item in `itr` for which `p` returns `true` is encountered +(short-circuiting). + +If the input contains [`missing`](@ref) values, return `missing` if all non-missing +values are `false` (or equivalently, if the input contains no `true` value), following +[three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic). + +# Examples +```jldoctest +julia> any(i->(4<=i<=6), [3,5,7]) +true + +julia> any(i -> (println(i); i > 3), 1:10) +1 +2 +3 +4 +true + +julia> any(i -> i > 0, [1, missing]) +true + +julia> any(i -> i > 0, [-1, missing]) +missing + +julia> any(i -> i > 0, [-1, 0]) +false +``` +""" +any(f, itr) = _any(f, itr, :) + +function _any(f, itr, ::Colon) + anymissing = false + for x in itr + v = f(x) + if ismissing(v) + anymissing = true + elseif v + return true + end + end + return anymissing ? missing : false +end + +""" + all(p, itr) -> Bool + +Determine whether predicate `p` returns `true` for all elements of `itr`, returning +`false` as soon as the first item in `itr` for which `p` returns `false` is encountered +(short-circuiting). + +If the input contains [`missing`](@ref) values, return `missing` if all non-missing +values are `true` (or equivalently, if the input contains no `false` value), following +[three-valued logic](https://en.wikipedia.org/wiki/Three-valued_logic). + +# Examples +```jldoctest +julia> all(i->(4<=i<=6), [4,5,6]) +true + +julia> all(i -> (println(i); i < 3), 1:10) +1 +2 +3 +false + +julia> all(i -> i > 0, [1, missing]) +missing + +julia> all(i -> i > 0, [-1, missing]) +false + +julia> all(i -> i > 0, [1, 2]) +true +``` +""" +all(f, itr) = _all(f, itr, :) + +function _all(f, itr, ::Colon) + anymissing = false + for x in itr + v = f(x) + if ismissing(v) + anymissing = true + # this syntax allows throwing a TypeError for non-Bool, for consistency with any + elseif v + continue + else + return false + end + end + return anymissing ? missing : true +end + +## count + +""" + count(p, itr) -> Integer + count(itr) -> Integer + +Count the number of elements in `itr` for which predicate `p` returns `true`. +If `p` is omitted, counts the number of `true` elements in `itr` (which +should be a collection of boolean values). + +# Examples +```jldoctest +julia> count(i->(4<=i<=6), [2,3,4,5,6]) +3 + +julia> count([true, false, true, true]) +3 +``` +""" +function count(pred, itr) + n = 0 + for x in itr + n += pred(x)::Bool + end + return n +end +function count(pred, a::AbstractArray) + n = 0 + for i in eachindex(a) + @inbounds n += pred(a[i])::Bool + end + return n +end +count(itr) = count(identity, itr) diff --git a/base/reducedim.jl b/base/reducedim.jl new file mode 100644 index 0000000..996729b --- /dev/null +++ b/base/reducedim.jl @@ -0,0 +1,882 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## Functions to compute the reduced shape + +# for reductions that expand 0 dims to 1 +reduced_index(i::OneTo) = OneTo(1) +reduced_index(i::Union{Slice, IdentityUnitRange}) = first(i):first(i) +reduced_index(i::AbstractUnitRange) = + throw(ArgumentError( +""" +No method is implemented for reducing index range of type $typeof(i). Please implement +reduced_index for this index type or report this as an issue. +""" + )) +reduced_indices(a::AbstractArray, region) = reduced_indices(axes(a), region) + +# for reductions that keep 0 dims as 0 +reduced_indices0(a::AbstractArray, region) = reduced_indices0(axes(a), region) + +function reduced_indices(inds::Indices{N}, d::Int) where N + d < 1 && throw(ArgumentError("dimension must be ≥ 1, got $d")) + if d == 1 + return (reduced_index(inds[1]), tail(inds)...) + elseif 1 < d <= N + return tuple(inds[1:d-1]..., oftype(inds[d], reduced_index(inds[d])), inds[d+1:N]...)::typeof(inds) + else + return inds + end +end + +function reduced_indices0(inds::Indices{N}, d::Int) where N + d < 1 && throw(ArgumentError("dimension must be ≥ 1, got $d")) + if d <= N + ind = inds[d] + rd = isempty(ind) ? ind : reduced_index(inds[d]) + if d == 1 + return (rd, tail(inds)...) + else + return tuple(inds[1:d-1]..., oftype(inds[d], rd), inds[d+1:N]...)::typeof(inds) + end + else + return inds + end +end + +function reduced_indices(inds::Indices{N}, region) where N + rinds = [inds...] + for i in region + isa(i, Integer) || throw(ArgumentError("reduced dimension(s) must be integers")) + d = Int(i) + if d < 1 + throw(ArgumentError("region dimension(s) must be ≥ 1, got $d")) + elseif d <= N + rinds[d] = reduced_index(rinds[d]) + end + end + tuple(rinds...)::typeof(inds) +end + +function reduced_indices0(inds::Indices{N}, region) where N + rinds = [inds...] + for i in region + isa(i, Integer) || throw(ArgumentError("reduced dimension(s) must be integers")) + d = Int(i) + if d < 1 + throw(ArgumentError("region dimension(s) must be ≥ 1, got $d")) + elseif d <= N + rind = rinds[d] + rinds[d] = isempty(rind) ? rind : reduced_index(rind) + end + end + tuple(rinds...)::typeof(inds) +end + +###### Generic reduction functions ##### + +## initialization +# initarray! is only called by sum!, prod!, etc. +for (Op, initfun) in ((:(typeof(add_sum)), :zero), (:(typeof(mul_prod)), :one)) + @eval initarray!(a::AbstractArray{T}, ::$(Op), init::Bool, src::AbstractArray) where {T} = (init && fill!(a, $(initfun)(T)); a) +end + +for Op in (:(typeof(max)), :(typeof(min))) + @eval initarray!(a::AbstractArray{T}, ::$(Op), init::Bool, src::AbstractArray) where {T} = (init && copyfirst!(a, src); a) +end + +for (Op, initval) in ((:(typeof(&)), true), (:(typeof(|)), false)) + @eval initarray!(a::AbstractArray, ::$(Op), init::Bool, src::AbstractArray) = (init && fill!(a, $initval); a) +end + +# reducedim_initarray is called by +reducedim_initarray(A::AbstractArray, region, init, ::Type{R}) where {R} = fill!(similar(A,R,reduced_indices(A,region)), init) +reducedim_initarray(A::AbstractArray, region, init::T) where {T} = reducedim_initarray(A, region, init, T) + +# TODO: better way to handle reducedim initialization +# +# The current scheme is basically following Steven G. Johnson's original implementation +# +promote_union(T::Union) = promote_type(promote_union(T.a), promote_union(T.b)) +promote_union(T) = T + +_realtype(::Type{<:Complex}) = Real +_realtype(::Type{Complex{T}}) where T<:Real = T +_realtype(T::Type) = T +_realtype(::Union{typeof(abs),typeof(abs2)}, T) = _realtype(T) +_realtype(::Any, T) = T + +function reducedim_init(f, op::Union{typeof(+),typeof(add_sum)}, A::AbstractArray, region) + _reducedim_init(f, op, zero, sum, A, region) +end +function reducedim_init(f, op::Union{typeof(*),typeof(mul_prod)}, A::AbstractArray, region) + _reducedim_init(f, op, one, prod, A, region) +end +function _reducedim_init(f, op, fv, fop, A, region) + T = _realtype(f, promote_union(eltype(A))) + if T !== Any && applicable(zero, T) + x = f(zero(T)) + z = op(fv(x), fv(x)) + Tr = z isa T ? T : typeof(z) + else + z = fv(fop(f, A)) + Tr = typeof(z) + end + return reducedim_initarray(A, region, z, Tr) +end + +# initialization when computing minima and maxima requires a little care +for (f1, f2, initval) in ((:min, :max, :Inf), (:max, :min, :(-Inf))) + @eval function reducedim_init(f, op::typeof($f1), A::AbstractArray, region) + # First compute the reduce indices. This will throw an ArgumentError + # if any region is invalid + ri = reduced_indices(A, region) + + # Next, throw if reduction is over a region with length zero + any(i -> isempty(axes(A, i)), region) && _empty_reduce_error() + + # Make a view of the first slice of the region + A1 = view(A, ri...) + + if isempty(A1) + # If the slice is empty just return non-view version as the initial array + return copy(A1) + else + # otherwise use the min/max of the first slice as initial value + v0 = mapreduce(f, $f2, A1) + + # but NaNs need to be avoided as intial values + v0 = v0 != v0 ? typeof(v0)($initval) : v0 + + T = _realtype(f, promote_union(eltype(A))) + Tr = v0 isa T ? T : typeof(v0) + return reducedim_initarray(A, region, v0, Tr) + end + end +end +reducedim_init(f::Union{typeof(abs),typeof(abs2)}, op::typeof(max), A::AbstractArray{T}, region) where {T} = + reducedim_initarray(A, region, zero(f(zero(T))), _realtype(f, T)) + +reducedim_init(f, op::typeof(&), A::AbstractArray, region) = reducedim_initarray(A, region, true) +reducedim_init(f, op::typeof(|), A::AbstractArray, region) = reducedim_initarray(A, region, false) + +# specialize to make initialization more efficient for common cases + +let + BitIntFloat = Union{BitInteger, IEEEFloat} + T = Union{ + [AbstractArray{t} for t in uniontypes(BitIntFloat)]..., + [AbstractArray{Complex{t}} for t in uniontypes(BitIntFloat)]...} + + global function reducedim_init(f, op::Union{typeof(+),typeof(add_sum)}, A::T, region) + z = zero(f(zero(eltype(A)))) + reducedim_initarray(A, region, op(z, z)) + end + global function reducedim_init(f, op::Union{typeof(*),typeof(mul_prod)}, A::T, region) + u = one(f(one(eltype(A)))) + reducedim_initarray(A, region, op(u, u)) + end +end + +## generic (map)reduction + +has_fast_linear_indexing(a::AbstractArray) = false +has_fast_linear_indexing(a::Array) = true + +function check_reducedims(R, A) + # Check whether R has compatible dimensions w.r.t. A for reduction + # + # It returns an integer value (useful for choosing implementation) + # - If it reduces only along leading dimensions, e.g. sum(A, dims=1) or sum(A, dims=(1,2)), + # it returns the length of the leading slice. For the two examples above, + # it will be size(A, 1) or size(A, 1) * size(A, 2). + # - Otherwise, e.g. sum(A, dims=2) or sum(A, dims=(1,3)), it returns 0. + # + ndims(R) <= ndims(A) || throw(DimensionMismatch("cannot reduce $(ndims(A))-dimensional array to $(ndims(R)) dimensions")) + lsiz = 1 + had_nonreduc = false + for i = 1:ndims(A) + Ri, Ai = axes(R, i), axes(A, i) + sRi, sAi = length(Ri), length(Ai) + if sRi == 1 + if sAi > 1 + if had_nonreduc + lsiz = 0 # to reduce along i, but some previous dimensions were non-reducing + else + lsiz *= sAi # if lsiz was set to zero, it will stay to be zero + end + end + else + Ri == Ai || throw(DimensionMismatch("reduction on array with indices $(axes(A)) with output with indices $(axes(R))")) + had_nonreduc = true + end + end + return lsiz +end + +""" +Extract first entry of slices of array A into existing array R. +""" +copyfirst!(R::AbstractArray, A::AbstractArray) = mapfirst!(identity, R, A) + +function mapfirst!(f, R::AbstractArray, A::AbstractArray{<:Any,N}) where {N} + lsiz = check_reducedims(R, A) + t = _firstreducedslice(axes(R), axes(A)) + map!(f, R, view(A, t...)) +end +# We know that the axes of R and A are compatible, but R might have a different number of +# dimensions than A, which is trickier than it seems due to offset arrays and type stability +_firstreducedslice(::Tuple{}, a::Tuple{}) = () +_firstreducedslice(::Tuple, ::Tuple{}) = () +@inline _firstreducedslice(::Tuple{}, a::Tuple) = (_firstslice(a[1]), _firstreducedslice((), tail(a))...) +@inline _firstreducedslice(r::Tuple, a::Tuple) = (length(r[1])==1 ? _firstslice(a[1]) : r[1], _firstreducedslice(tail(r), tail(a))...) +_firstslice(i::OneTo) = OneTo(1) +_firstslice(i::Slice) = Slice(_firstslice(i.indices)) +_firstslice(i) = i[firstindex(i):firstindex(i)] + +function _mapreducedim!(f, op, R::AbstractArray, A::AbstractArray) + lsiz = check_reducedims(R,A) + isempty(A) && return R + + if has_fast_linear_indexing(A) && lsiz > 16 + # use mapreduce_impl, which is probably better tuned to achieve higher performance + nslices = div(length(A), lsiz) + ibase = first(LinearIndices(A))-1 + for i = 1:nslices + @inbounds R[i] = op(R[i], mapreduce_impl(f, op, A, ibase+1, ibase+lsiz)) + ibase += lsiz + end + return R + end + indsAt, indsRt = safe_tail(axes(A)), safe_tail(axes(R)) # handle d=1 manually + keep, Idefault = Broadcast.shapeindexer(indsRt) + if reducedim1(R, A) + # keep the accumulator as a local variable when reducing along the first dimension + i1 = first(axes1(R)) + @inbounds for IA in CartesianIndices(indsAt) + IR = Broadcast.newindex(IA, keep, Idefault) + r = R[i1,IR] + @simd for i in axes(A, 1) + r = op(r, f(A[i, IA])) + end + R[i1,IR] = r + end + else + @inbounds for IA in CartesianIndices(indsAt) + IR = Broadcast.newindex(IA, keep, Idefault) + @simd for i in axes(A, 1) + R[i,IR] = op(R[i,IR], f(A[i,IA])) + end + end + end + return R +end + +mapreducedim!(f, op, R::AbstractArray, A::AbstractArray) = + (_mapreducedim!(f, op, R, A); R) + +reducedim!(op, R::AbstractArray{RT}, A::AbstractArray) where {RT} = + mapreducedim!(identity, op, R, A) + +""" + mapreduce(f, op, A::AbstractArray...; dims=:, [init]) + +Evaluates to the same as `reduce(op, map(f, A); dims=dims, init=init)`, but is generally +faster because the intermediate array is avoided. + +!!! compat "Julia 1.2" + `mapreduce` with multiple iterators requires Julia 1.2 or later. + +# Examples +```jldoctest +julia> a = reshape(Vector(1:16), (4,4)) +4×4 Array{Int64,2}: + 1 5 9 13 + 2 6 10 14 + 3 7 11 15 + 4 8 12 16 + +julia> mapreduce(isodd, *, a, dims=1) +1×4 Array{Bool,2}: + 0 0 0 0 + +julia> mapreduce(isodd, |, a, dims=1) +1×4 Array{Bool,2}: + 1 1 1 1 +``` +""" +mapreduce(f, op, A::AbstractArray; dims=:, kw...) = _mapreduce_dim(f, op, kw.data, A, dims) +mapreduce(f, op, A::AbstractArray...; kw...) = reduce(op, map(f, A...); kw...) + +_mapreduce_dim(f, op, nt::NamedTuple{(:init,)}, A::AbstractArray, ::Colon) = mapfoldl(f, op, A; nt...) + +_mapreduce_dim(f, op, ::NamedTuple{()}, A::AbstractArray, ::Colon) = _mapreduce(f, op, IndexStyle(A), A) + +_mapreduce_dim(f, op, nt::NamedTuple{(:init,)}, A::AbstractArray, dims) = + mapreducedim!(f, op, reducedim_initarray(A, dims, nt.init), A) + +_mapreduce_dim(f, op, ::NamedTuple{()}, A::AbstractArray, dims) = + mapreducedim!(f, op, reducedim_init(f, op, A, dims), A) + +""" + reduce(f, A; dims=:, [init]) + +Reduce 2-argument function `f` along dimensions of `A`. `dims` is a vector specifying the +dimensions to reduce, and the keyword argument `init` is the initial value to use in the +reductions. For `+`, `*`, `max` and `min` the `init` argument is optional. + +The associativity of the reduction is implementation-dependent; if you need a particular +associativity, e.g. left-to-right, you should write your own loop or consider using +[`foldl`](@ref) or [`foldr`](@ref). See documentation for [`reduce`](@ref). + +# Examples +```jldoctest +julia> a = reshape(Vector(1:16), (4,4)) +4×4 Array{Int64,2}: + 1 5 9 13 + 2 6 10 14 + 3 7 11 15 + 4 8 12 16 + +julia> reduce(max, a, dims=2) +4×1 Array{Int64,2}: + 13 + 14 + 15 + 16 + +julia> reduce(max, a, dims=1) +1×4 Array{Int64,2}: + 4 8 12 16 +``` +""" +reduce(op, A::AbstractArray; kw...) = mapreduce(identity, op, A; kw...) + +##### Specific reduction functions ##### +""" + sum(A::AbstractArray; dims) + +Sum elements of an array over the given dimensions. + +# Examples +```jldoctest +julia> A = [1 2; 3 4] +2×2 Array{Int64,2}: + 1 2 + 3 4 + +julia> sum(A, dims=1) +1×2 Array{Int64,2}: + 4 6 + +julia> sum(A, dims=2) +2×1 Array{Int64,2}: + 3 + 7 +``` +""" +sum(A::AbstractArray; dims) + +""" + sum!(r, A) + +Sum elements of `A` over the singleton dimensions of `r`, and write results to `r`. + +# Examples +```jldoctest +julia> A = [1 2; 3 4] +2×2 Array{Int64,2}: + 1 2 + 3 4 + +julia> sum!([1; 1], A) +2-element Array{Int64,1}: + 3 + 7 + +julia> sum!([1 1], A) +1×2 Array{Int64,2}: + 4 6 +``` +""" +sum!(r, A) + +""" + prod(A::AbstractArray; dims) + +Multiply elements of an array over the given dimensions. + +# Examples +```jldoctest +julia> A = [1 2; 3 4] +2×2 Array{Int64,2}: + 1 2 + 3 4 + +julia> prod(A, dims=1) +1×2 Array{Int64,2}: + 3 8 + +julia> prod(A, dims=2) +2×1 Array{Int64,2}: + 2 + 12 +``` +""" +prod(A::AbstractArray; dims) + +""" + prod!(r, A) + +Multiply elements of `A` over the singleton dimensions of `r`, and write results to `r`. + +# Examples +```jldoctest +julia> A = [1 2; 3 4] +2×2 Array{Int64,2}: + 1 2 + 3 4 + +julia> prod!([1; 1], A) +2-element Array{Int64,1}: + 2 + 12 + +julia> prod!([1 1], A) +1×2 Array{Int64,2}: + 3 8 +``` +""" +prod!(r, A) + +""" + maximum(A::AbstractArray; dims) + +Compute the maximum value of an array over the given dimensions. See also the +[`max(a,b)`](@ref) function to take the maximum of two or more arguments, +which can be applied elementwise to arrays via `max.(a,b)`. + +# Examples +```jldoctest +julia> A = [1 2; 3 4] +2×2 Array{Int64,2}: + 1 2 + 3 4 + +julia> maximum(A, dims=1) +1×2 Array{Int64,2}: + 3 4 + +julia> maximum(A, dims=2) +2×1 Array{Int64,2}: + 2 + 4 +``` +""" +maximum(A::AbstractArray; dims) + +""" + maximum!(r, A) + +Compute the maximum value of `A` over the singleton dimensions of `r`, and write results to `r`. + +# Examples +```jldoctest +julia> A = [1 2; 3 4] +2×2 Array{Int64,2}: + 1 2 + 3 4 + +julia> maximum!([1; 1], A) +2-element Array{Int64,1}: + 2 + 4 + +julia> maximum!([1 1], A) +1×2 Array{Int64,2}: + 3 4 +``` +""" +maximum!(r, A) + +""" + minimum(A::AbstractArray; dims) + +Compute the minimum value of an array over the given dimensions. See also the +[`min(a,b)`](@ref) function to take the minimum of two or more arguments, +which can be applied elementwise to arrays via `min.(a,b)`. + +# Examples +```jldoctest +julia> A = [1 2; 3 4] +2×2 Array{Int64,2}: + 1 2 + 3 4 + +julia> minimum(A, dims=1) +1×2 Array{Int64,2}: + 1 2 + +julia> minimum(A, dims=2) +2×1 Array{Int64,2}: + 1 + 3 +``` +""" +minimum(A::AbstractArray; dims) + +""" + minimum!(r, A) + +Compute the minimum value of `A` over the singleton dimensions of `r`, and write results to `r`. + +# Examples +```jldoctest +julia> A = [1 2; 3 4] +2×2 Array{Int64,2}: + 1 2 + 3 4 + +julia> minimum!([1; 1], A) +2-element Array{Int64,1}: + 1 + 3 + +julia> minimum!([1 1], A) +1×2 Array{Int64,2}: + 1 2 +``` +""" +minimum!(r, A) + +""" + all(A; dims) + +Test whether all values along the given dimensions of an array are `true`. + +# Examples +```jldoctest +julia> A = [true false; true true] +2×2 Array{Bool,2}: + 1 0 + 1 1 + +julia> all(A, dims=1) +1×2 Array{Bool,2}: + 1 0 + +julia> all(A, dims=2) +2×1 Array{Bool,2}: + 0 + 1 +``` +""" +all(A::AbstractArray; dims) + +""" + all!(r, A) + +Test whether all values in `A` along the singleton dimensions of `r` are `true`, and write results to `r`. + +# Examples +```jldoctest +julia> A = [true false; true false] +2×2 Array{Bool,2}: + 1 0 + 1 0 + +julia> all!([1; 1], A) +2-element Array{Int64,1}: + 0 + 0 + +julia> all!([1 1], A) +1×2 Array{Int64,2}: + 1 0 +``` +""" +all!(r, A) + +""" + any(A; dims) + +Test whether any values along the given dimensions of an array are `true`. + +# Examples +```jldoctest +julia> A = [true false; true false] +2×2 Array{Bool,2}: + 1 0 + 1 0 + +julia> any(A, dims=1) +1×2 Array{Bool,2}: + 1 0 + +julia> any(A, dims=2) +2×1 Array{Bool,2}: + 1 + 1 +``` +""" +any(::AbstractArray; dims) + +""" + any!(r, A) + +Test whether any values in `A` along the singleton dimensions of `r` are `true`, and write +results to `r`. + +# Examples +```jldoctest +julia> A = [true false; true false] +2×2 Array{Bool,2}: + 1 0 + 1 0 + +julia> any!([1; 1], A) +2-element Array{Int64,1}: + 1 + 1 + +julia> any!([1 1], A) +1×2 Array{Int64,2}: + 1 0 +``` +""" +any!(r, A) + +for (fname, _fname, op) in [(:sum, :_sum, :add_sum), (:prod, :_prod, :mul_prod), + (:maximum, :_maximum, :max), (:minimum, :_minimum, :min)] + @eval begin + # User-facing methods with keyword arguments + @inline ($fname)(a::AbstractArray; dims=:) = ($_fname)(a, dims) + @inline ($fname)(f, a::AbstractArray; dims=:) = ($_fname)(f, a, dims) + + # Underlying implementations using dispatch + ($_fname)(a, ::Colon) = ($_fname)(identity, a, :) + ($_fname)(f, a, ::Colon) = mapreduce(f, $op, a) + end +end + +any(a::AbstractArray; dims=:) = _any(a, dims) +any(f::Function, a::AbstractArray; dims=:) = _any(f, a, dims) +_any(a, ::Colon) = _any(identity, a, :) +all(a::AbstractArray; dims=:) = _all(a, dims) +all(f::Function, a::AbstractArray; dims=:) = _all(f, a, dims) +_all(a, ::Colon) = _all(identity, a, :) + +for (fname, op) in [(:sum, :add_sum), (:prod, :mul_prod), + (:maximum, :max), (:minimum, :min), + (:all, :&), (:any, :|)] + fname! = Symbol(fname, '!') + _fname = Symbol('_', fname) + @eval begin + $(fname!)(f::Function, r::AbstractArray, A::AbstractArray; init::Bool=true) = + mapreducedim!(f, $(op), initarray!(r, $(op), init, A), A) + $(fname!)(r::AbstractArray, A::AbstractArray; init::Bool=true) = $(fname!)(identity, r, A; init=init) + + $(_fname)(A, dims) = $(_fname)(identity, A, dims) + $(_fname)(f, A, dims) = mapreduce(f, $(op), A, dims=dims) + end +end + +##### findmin & findmax ##### +# The initial values of Rval are not used if the corresponding indices in Rind are 0. +# +function findminmax!(f, Rval, Rind, A::AbstractArray{T,N}) where {T,N} + (isempty(Rval) || isempty(A)) && return Rval, Rind + lsiz = check_reducedims(Rval, A) + for i = 1:N + axes(Rval, i) == axes(Rind, i) || throw(DimensionMismatch("Find-reduction: outputs must have the same indices")) + end + # If we're reducing along dimension 1, for efficiency we can make use of a temporary. + # Otherwise, keep the result in Rval/Rind so that we traverse A in storage order. + indsAt, indsRt = safe_tail(axes(A)), safe_tail(axes(Rval)) + keep, Idefault = Broadcast.shapeindexer(indsRt) + ks = keys(A) + y = iterate(ks) + zi = zero(eltype(ks)) + if reducedim1(Rval, A) + i1 = first(axes1(Rval)) + @inbounds for IA in CartesianIndices(indsAt) + IR = Broadcast.newindex(IA, keep, Idefault) + tmpRv = Rval[i1,IR] + tmpRi = Rind[i1,IR] + for i in axes(A,1) + k, kss = y::Tuple + tmpAv = A[i,IA] + if tmpRi == zi || (tmpRv == tmpRv && (tmpAv != tmpAv || f(tmpAv, tmpRv))) + tmpRv = tmpAv + tmpRi = k + end + y = iterate(ks, kss) + end + Rval[i1,IR] = tmpRv + Rind[i1,IR] = tmpRi + end + else + @inbounds for IA in CartesianIndices(indsAt) + IR = Broadcast.newindex(IA, keep, Idefault) + for i in axes(A, 1) + k, kss = y::Tuple + tmpAv = A[i,IA] + tmpRv = Rval[i,IR] + tmpRi = Rind[i,IR] + if tmpRi == zi || (tmpRv == tmpRv && (tmpAv != tmpAv || f(tmpAv, tmpRv))) + Rval[i,IR] = tmpAv + Rind[i,IR] = k + end + y = iterate(ks, kss) + end + end + end + Rval, Rind +end + +""" + findmin!(rval, rind, A) -> (minval, index) + +Find the minimum of `A` and the corresponding linear index along singleton +dimensions of `rval` and `rind`, and store the results in `rval` and `rind`. +`NaN` is treated as less than all other values. +""" +function findmin!(rval::AbstractArray, rind::AbstractArray, A::AbstractArray; + init::Bool=true) + findminmax!(isless, init && !isempty(A) ? fill!(rval, first(A)) : rval, fill!(rind,zero(eltype(keys(A)))), A) +end + +""" + findmin(A; dims) -> (minval, index) + +For an array input, returns the value and index of the minimum over the given dimensions. +`NaN` is treated as less than all other values. + +# Examples +```jldoctest +julia> A = [1.0 2; 3 4] +2×2 Array{Float64,2}: + 1.0 2.0 + 3.0 4.0 + +julia> findmin(A, dims=1) +([1.0 2.0], CartesianIndex{2}[CartesianIndex(1, 1) CartesianIndex(1, 2)]) + +julia> findmin(A, dims=2) +([1.0; 3.0], CartesianIndex{2}[CartesianIndex(1, 1); CartesianIndex(2, 1)]) +``` +""" +findmin(A::AbstractArray; dims=:) = _findmin(A, dims) + +function _findmin(A, region) + ri = reduced_indices0(A, region) + if isempty(A) + if prod(map(length, reduced_indices(A, region))) != 0 + throw(ArgumentError("collection slices must be non-empty")) + end + (similar(A, ri), zeros(eltype(keys(A)), ri)) + else + findminmax!(isless, fill!(similar(A, ri), first(A)), + zeros(eltype(keys(A)), ri), A) + end +end + +isgreater(a, b) = isless(b,a) + +""" + findmax!(rval, rind, A) -> (maxval, index) + +Find the maximum of `A` and the corresponding linear index along singleton +dimensions of `rval` and `rind`, and store the results in `rval` and `rind`. +`NaN` is treated as greater than all other values. +""" +function findmax!(rval::AbstractArray, rind::AbstractArray, A::AbstractArray; + init::Bool=true) + findminmax!(isgreater, init && !isempty(A) ? fill!(rval, first(A)) : rval, fill!(rind,zero(eltype(keys(A)))), A) +end + +""" + findmax(A; dims) -> (maxval, index) + +For an array input, returns the value and index of the maximum over the given dimensions. +`NaN` is treated as greater than all other values. + +# Examples +```jldoctest +julia> A = [1.0 2; 3 4] +2×2 Array{Float64,2}: + 1.0 2.0 + 3.0 4.0 + +julia> findmax(A, dims=1) +([3.0 4.0], CartesianIndex{2}[CartesianIndex(2, 1) CartesianIndex(2, 2)]) + +julia> findmax(A, dims=2) +([2.0; 4.0], CartesianIndex{2}[CartesianIndex(1, 2); CartesianIndex(2, 2)]) +``` +""" +findmax(A::AbstractArray; dims=:) = _findmax(A, dims) + +function _findmax(A, region) + ri = reduced_indices0(A, region) + if isempty(A) + if prod(map(length, reduced_indices(A, region))) != 0 + throw(ArgumentError("collection slices must be non-empty")) + end + similar(A, ri), zeros(eltype(keys(A)), ri) + else + findminmax!(isgreater, fill!(similar(A, ri), first(A)), + zeros(eltype(keys(A)), ri), A) + end +end + +reducedim1(R, A) = length(axes1(R)) == 1 + +""" + argmin(A; dims) -> indices + +For an array input, return the indices of the minimum elements over the given dimensions. +`NaN` is treated as less than all other values. + +# Examples +```jldoctest +julia> A = [1.0 2; 3 4] +2×2 Array{Float64,2}: + 1.0 2.0 + 3.0 4.0 + +julia> argmin(A, dims=1) +1×2 Array{CartesianIndex{2},2}: + CartesianIndex(1, 1) CartesianIndex(1, 2) + +julia> argmin(A, dims=2) +2×1 Array{CartesianIndex{2},2}: + CartesianIndex(1, 1) + CartesianIndex(2, 1) +``` +""" +argmin(A::AbstractArray; dims=:) = findmin(A; dims=dims)[2] + +""" + argmax(A; dims) -> indices + +For an array input, return the indices of the maximum elements over the given dimensions. +`NaN` is treated as greater than all other values. + +# Examples +```jldoctest +julia> A = [1.0 2; 3 4] +2×2 Array{Float64,2}: + 1.0 2.0 + 3.0 4.0 + +julia> argmax(A, dims=1) +1×2 Array{CartesianIndex{2},2}: + CartesianIndex(2, 1) CartesianIndex(2, 2) + +julia> argmax(A, dims=2) +2×1 Array{CartesianIndex{2},2}: + CartesianIndex(1, 2) + CartesianIndex(2, 2) +``` +""" +argmax(A::AbstractArray; dims=:) = findmax(A; dims=dims)[2] diff --git a/base/reflection.jl b/base/reflection.jl new file mode 100644 index 0000000..0b5a755 --- /dev/null +++ b/base/reflection.jl @@ -0,0 +1,1353 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# name and module reflection + +""" + nameof(m::Module) -> Symbol + +Get the name of a `Module` as a [`Symbol`](@ref). + +# Examples +```jldoctest +julia> nameof(Base.Broadcast) +:Broadcast +``` +""" +nameof(m::Module) = ccall(:jl_module_name, Ref{Symbol}, (Any,), m) + +""" + parentmodule(m::Module) -> Module + +Get a module's enclosing `Module`. `Main` is its own parent. + +# Examples +```jldoctest +julia> parentmodule(Main) +Main + +julia> parentmodule(Base.Broadcast) +Base +``` +""" +parentmodule(m::Module) = ccall(:jl_module_parent, Ref{Module}, (Any,), m) + +""" + moduleroot(m::Module) -> Module + +Find the root module of a given module. This is the first module in the chain of +parent modules of `m` which is either a registered root module or which is its +own parent module. +""" +function moduleroot(m::Module) + while true + is_root_module(m) && return m + p = parentmodule(m) + p == m && return m + m = p + end +end + +""" + @__MODULE__ -> Module + +Get the `Module` of the toplevel eval, +which is the `Module` code is currently being read from. +""" +macro __MODULE__() + return __module__ +end + +""" + fullname(m::Module) + +Get the fully-qualified name of a module as a tuple of symbols. For example, + +# Examples +```jldoctest +julia> fullname(Base.Iterators) +(:Base, :Iterators) + +julia> fullname(Main) +(:Main,) +``` +""" +function fullname(m::Module) + mn = nameof(m) + if m === Main || m === Base || m === Core + return (mn,) + end + mp = parentmodule(m) + if mp === m + return (mn,) + end + return (fullname(mp)..., mn) +end + +""" + names(x::Module; all::Bool = false, imported::Bool = false) + +Get an array of the names exported by a `Module`, excluding deprecated names. +If `all` is true, then the list also includes non-exported names defined in the module, +deprecated names, and compiler-generated names. +If `imported` is true, then names explicitly imported from other modules +are also included. + +As a special case, all names defined in `Main` are considered \"exported\", +since it is not idiomatic to explicitly export names from `Main`. +""" +names(m::Module; all::Bool = false, imported::Bool = false) = + sort!(ccall(:jl_module_names, Array{Symbol,1}, (Any, Cint, Cint), m, all, imported)) + +isexported(m::Module, s::Symbol) = ccall(:jl_module_exports_p, Cint, (Any, Any), m, s) != 0 +isdeprecated(m::Module, s::Symbol) = ccall(:jl_is_binding_deprecated, Cint, (Any, Any), m, s) != 0 +isbindingresolved(m::Module, var::Symbol) = ccall(:jl_binding_resolved_p, Cint, (Any, Any), m, var) != 0 + +function binding_module(m::Module, s::Symbol) + p = ccall(:jl_get_module_of_binding, Ptr{Cvoid}, (Any, Any), m, s) + p == C_NULL && return m + return unsafe_pointer_to_objref(p)::Module +end + +function resolve(g::GlobalRef; force::Bool=false) + if force || isbindingresolved(g.mod, g.name) + return GlobalRef(binding_module(g.mod, g.name), g.name) + end + return g +end + +const NamedTuple_typename = NamedTuple.body.body.name + +function _fieldnames(@nospecialize t) + if t.name === NamedTuple_typename + if t.parameters[1] isa Tuple + return t.parameters[1] + else + throw(ArgumentError("type does not have definite field names")) + end + end + isdefined(t, :names) ? t.names : t.name.names +end + +""" + fieldname(x::DataType, i::Integer) + +Get the name of field `i` of a `DataType`. + +# Examples +```jldoctest +julia> fieldname(Rational, 1) +:num + +julia> fieldname(Rational, 2) +:den +``` +""" +function fieldname(t::DataType, i::Integer) + if t.abstract + throw(ArgumentError("type does not have definite field names")) + end + names = _fieldnames(t) + n_fields = length(names)::Int + field_label = n_fields == 1 ? "field" : "fields" + i > n_fields && throw(ArgumentError("Cannot access field $i since type $t only has $n_fields $field_label.")) + i < 1 && throw(ArgumentError("Field numbers must be positive integers. $i is invalid.")) + return names[i]::Symbol +end + +fieldname(t::UnionAll, i::Integer) = fieldname(unwrap_unionall(t), i) +fieldname(t::Type{<:Tuple}, i::Integer) = + i < 1 || i > fieldcount(t) ? throw(BoundsError(t, i)) : Int(i) + +""" + fieldnames(x::DataType) + +Get a tuple with the names of the fields of a `DataType`. + +# Examples +```jldoctest +julia> fieldnames(Rational) +(:num, :den) +``` +""" +fieldnames(t::DataType) = (fieldcount(t); # error check to make sure type is specific enough + (_fieldnames(t)...,)) +fieldnames(t::UnionAll) = fieldnames(unwrap_unionall(t)) +fieldnames(::Core.TypeofBottom) = + throw(ArgumentError("The empty type does not have field names since it does not have instances.")) +fieldnames(t::Type{<:Tuple}) = ntuple(identity, fieldcount(t)) + +""" + hasfield(T::Type, name::Symbol) + +Return a boolean indicating whether `T` has `name` as one of its own fields. + +!!! compat "Julia 1.2" + This function requires at least Julia 1.2. +""" +function hasfield(::Type{T}, name::Symbol) where T + @_pure_meta + return fieldindex(T, name, false) > 0 +end + +""" + nameof(t::DataType) -> Symbol + +Get the name of a (potentially `UnionAll`-wrapped) `DataType` (without its parent module) +as a symbol. + +# Examples +```jldoctest +julia> module Foo + struct S{T} + end + end +Foo + +julia> nameof(Foo.S{T} where T) +:S +``` +""" +nameof(t::DataType) = t.name.name +nameof(t::UnionAll) = nameof(unwrap_unionall(t))::Symbol + +""" + parentmodule(t::DataType) -> Module + +Determine the module containing the definition of a (potentially `UnionAll`-wrapped) `DataType`. + +# Examples +```jldoctest +julia> module Foo + struct Int end + end +Foo + +julia> parentmodule(Int) +Core + +julia> parentmodule(Foo.Int) +Foo +``` +""" +parentmodule(t::DataType) = t.name.module +parentmodule(t::UnionAll) = parentmodule(unwrap_unionall(t)) + +""" + isconst(m::Module, s::Symbol) -> Bool + +Determine whether a global is declared `const` in a given `Module`. +""" +isconst(m::Module, s::Symbol) = + ccall(:jl_is_const, Cint, (Any, Any), m, s) != 0 + +""" + @isdefined s -> Bool + +Tests whether variable `s` is defined in the current scope. + +See also [`isdefined`](@ref). + +# Examples +```jldoctest +julia> function f() + println(@isdefined x) + x = 3 + println(@isdefined x) + end +f (generic function with 1 method) + +julia> f() +false +true +``` +""" +macro isdefined(s::Symbol) + return Expr(:isdefined, esc(s)) +end + +""" + @locals() + +Construct a dictionary of the names (as symbols) and values of all local +variables defined as of the call site. + +!!! compat "Julia 1.1" + This macro requires at least Julia 1.1. + +# Examples +```jldoctest +julia> let x = 1, y = 2 + Base.@locals + end +Dict{Symbol,Any} with 2 entries: + :y => 2 + :x => 1 + +julia> function f(x) + local y + show(Base.@locals); println() + for i = 1:1 + show(Base.@locals); println() + end + y = 2 + show(Base.@locals); println() + nothing + end; + +julia> f(42) +Dict{Symbol,Any}(:x => 42) +Dict{Symbol,Any}(:i => 1,:x => 42) +Dict{Symbol,Any}(:y => 2,:x => 42) +``` +""" +macro locals() + return Expr(:locals) +end + +""" + objectid(x) + +Get a hash value for `x` based on object identity. `objectid(x)==objectid(y)` if `x === y`. +""" +objectid(@nospecialize(x)) = ccall(:jl_object_id, UInt, (Any,), x) + +# concrete datatype predicates + +datatype_fieldtypes(x::DataType) = ccall(:jl_get_fieldtypes, Any, (Any,), x) + +struct DataTypeLayout + nfields::UInt32 + alignment::UInt32 + # alignment : 28; + # haspadding : 1; + # pointerfree : 1; + # fielddesc_type : 2; +end + +""" + Base.datatype_alignment(dt::DataType) -> Int + +Memory allocation minimum alignment for instances of this type. +Can be called on any `isconcretetype`. +""" +function datatype_alignment(dt::DataType) + @_pure_meta + dt.layout == C_NULL && throw(UndefRefError()) + alignment = unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).alignment + return Int(alignment & 0x1FF) +end + +# amount of total space taken by T when stored in a container +function aligned_sizeof(T) + @_pure_meta + if isbitsunion(T) + sz = Ref{Csize_t}(0) + algn = Ref{Csize_t}(0) + ccall(:jl_islayout_inline, Cint, (Any, Ptr{Csize_t}, Ptr{Csize_t}), T, sz, algn) + al = algn[] + return (sz[] + al - 1) & -al + elseif allocatedinline(T) + al = datatype_alignment(T) + return (Core.sizeof(T) + al - 1) & -al + else + return Core.sizeof(Ptr{Cvoid}) + end +end + +gc_alignment(sz::Integer) = Int(ccall(:jl_alignment, Cint, (Csize_t,), sz)) +gc_alignment(T::Type) = gc_alignment(Core.sizeof(T)) + +""" + Base.datatype_haspadding(dt::DataType) -> Bool + +Return whether the fields of instances of this type are packed in memory, +with no intervening padding bytes. +Can be called on any `isconcretetype`. +""" +function datatype_haspadding(dt::DataType) + @_pure_meta + dt.layout == C_NULL && throw(UndefRefError()) + alignment = unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).alignment + return (alignment >> 9) & 1 == 1 +end + +""" + Base.datatype_pointerfree(dt::DataType) -> Bool + +Return whether instances of this type can contain references to gc-managed memory. +Can be called on any `isconcretetype`. +""" +function datatype_pointerfree(dt::DataType) + @_pure_meta + dt.layout == C_NULL && throw(UndefRefError()) + alignment = unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).alignment + return (alignment >> 10) & 0xFFFFF == 0 +end + +""" + Base.datatype_fielddesc_type(dt::DataType) -> Int + +Return the size in bytes of each field-description entry in the layout array, +located at `(dt.layout + sizeof(DataTypeLayout))`. +Can be called on any `isconcretetype`. + +See also [`fieldoffset`](@ref). +""" +function datatype_fielddesc_type(dt::DataType) + @_pure_meta + dt.layout == C_NULL && throw(UndefRefError()) + alignment = unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).alignment + return (alignment >> 30) & 3 +end + +""" + isimmutable(v) -> Bool + +Return `true` iff value `v` is immutable. See [Mutable Composite Types](@ref) +for a discussion of immutability. Note that this function works on values, so if you give it +a type, it will tell you that a value of `DataType` is mutable. + +# Examples +```jldoctest +julia> isimmutable(1) +true + +julia> isimmutable([1,2]) +false +``` +""" +isimmutable(@nospecialize(x)) = (@_pure_meta; !typeof(x).mutable) + +""" + isstructtype(T) -> Bool + +Determine whether type `T` was declared as a struct type +(i.e. using the `struct` or `mutable struct` keyword). +""" +function isstructtype(@nospecialize(t::Type)) + @_pure_meta + t = unwrap_unionall(t) + # TODO: what to do for `Union`? + isa(t, DataType) || return false + hasfield = !isdefined(t, :types) || !isempty(t.types) + return hasfield || (t.size == 0 && !t.abstract) +end + +""" + isprimitivetype(T) -> Bool + +Determine whether type `T` was declared as a primitive type +(i.e. using the `primitive` keyword). +""" +function isprimitivetype(@nospecialize(t::Type)) + @_pure_meta + t = unwrap_unionall(t) + # TODO: what to do for `Union`? + isa(t, DataType) || return false + hasfield = !isdefined(t, :types) || !isempty(t.types) + return !hasfield && t.size != 0 && !t.abstract +end + +""" + isbitstype(T) + +Return `true` if type `T` is a "plain data" type, +meaning it is immutable and contains no references to other values, +only `primitive` types and other `isbitstype` types. +Typical examples are numeric types such as [`UInt8`](@ref), +[`Float64`](@ref), and [`Complex{Float64}`](@ref). +This category of types is significant since they are valid as type parameters, +may not track [`isdefined`](@ref) / [`isassigned`](@ref) status, +and have a defined layout that is compatible with C. + +# Examples +```jldoctest +julia> isbitstype(Complex{Float64}) +true + +julia> isbitstype(Complex) +false +``` +""" +isbitstype(@nospecialize(t::Type)) = (@_pure_meta; isa(t, DataType) && t.isbitstype) + +""" + isbits(x) + +Return `true` if `x` is an instance of an `isbitstype` type. +""" +isbits(@nospecialize x) = (@_pure_meta; typeof(x).isbitstype) + +""" + isdispatchtuple(T) + +Determine whether type `T` is a tuple "leaf type", +meaning it could appear as a type signature in dispatch +and has no subtypes (or supertypes) which could appear in a call. +""" +isdispatchtuple(@nospecialize(t)) = (@_pure_meta; isa(t, DataType) && t.isdispatchtuple) + +iskindtype(@nospecialize t) = (t === DataType || t === UnionAll || t === Union || t === typeof(Bottom)) +isconcretedispatch(@nospecialize t) = isconcretetype(t) && !iskindtype(t) +has_free_typevars(@nospecialize(t)) = ccall(:jl_has_free_typevars, Cint, (Any,), t) != 0 + +# equivalent to isa(v, Type) && isdispatchtuple(Tuple{v}) || v === Union{} +# and is thus perhaps most similar to the old (pre-1.0) `isleaftype` query +const _TYPE_NAME = Type.body.name +function isdispatchelem(@nospecialize v) + return (v === Bottom) || (v === typeof(Bottom)) || isconcretedispatch(v) || + (isa(v, DataType) && v.name === _TYPE_NAME && !has_free_typevars(v)) # isType(v) +end + +""" + isconcretetype(T) + +Determine whether type `T` is a concrete type, meaning it could have direct instances +(values `x` such that `typeof(x) === T`). + +# Examples +```jldoctest +julia> isconcretetype(Complex) +false + +julia> isconcretetype(Complex{Float32}) +true + +julia> isconcretetype(Vector{Complex}) +true + +julia> isconcretetype(Vector{Complex{Float32}}) +true + +julia> isconcretetype(Union{}) +false + +julia> isconcretetype(Union{Int,String}) +false +``` +""" +isconcretetype(@nospecialize(t)) = (@_pure_meta; isa(t, DataType) && t.isconcretetype) + +""" + isabstracttype(T) + +Determine whether type `T` was declared as an abstract type +(i.e. using the `abstract` keyword). + +# Examples +```jldoctest +julia> isabstracttype(AbstractArray) +true + +julia> isabstracttype(Vector) +false +``` +""" +function isabstracttype(@nospecialize(t)) + @_pure_meta + t = unwrap_unionall(t) + # TODO: what to do for `Union`? + return isa(t, DataType) && t.abstract +end + +""" + Base.issingletontype(T) + +Determine whether type `T` has exactly one possible instance; for example, a +struct type with no fields. +""" +issingletontype(@nospecialize(t)) = (@_pure_meta; isa(t, DataType) && isdefined(t, :instance)) + +""" + Base.parameter_upper_bound(t::UnionAll, idx) + +Determine the upper bound of a type parameter in the underlying datatype. +This method should generally not be relied upon: +code instead should usually use static parameters in dispatch to extract these values. + +# Examples +```jldoctest +julia> struct Foo{T<:AbstractFloat, N} + x::Tuple{T, N} + end + +julia> Base.parameter_upper_bound(Foo, 1) +AbstractFloat + +julia> Base.parameter_upper_bound(Foo, 2) +Any +``` +""" +function parameter_upper_bound(t::UnionAll, idx) + @_pure_meta + return rewrap_unionall((unwrap_unionall(t)::DataType).parameters[idx], t) +end + +""" + typeintersect(T, S) + +Compute a type that contains the intersection of `T` and `S`. Usually this will be the +smallest such type or one close to it. +""" +typeintersect(@nospecialize(a),@nospecialize(b)) = (@_pure_meta; ccall(:jl_type_intersection, Any, (Any,Any), a, b)) + +""" + fieldoffset(type, i) + +The byte offset of field `i` of a type relative to the data start. For example, we could +use it in the following manner to summarize information about a struct: + +```jldoctest +julia> structinfo(T) = [(fieldoffset(T,i), fieldname(T,i), fieldtype(T,i)) for i = 1:fieldcount(T)]; + +julia> structinfo(Base.Filesystem.StatStruct) +12-element Array{Tuple{UInt64,Symbol,DataType},1}: + (0x0000000000000000, :device, UInt64) + (0x0000000000000008, :inode, UInt64) + (0x0000000000000010, :mode, UInt64) + (0x0000000000000018, :nlink, Int64) + (0x0000000000000020, :uid, UInt64) + (0x0000000000000028, :gid, UInt64) + (0x0000000000000030, :rdev, UInt64) + (0x0000000000000038, :size, Int64) + (0x0000000000000040, :blksize, Int64) + (0x0000000000000048, :blocks, Int64) + (0x0000000000000050, :mtime, Float64) + (0x0000000000000058, :ctime, Float64) +``` +""" +fieldoffset(x::DataType, idx::Integer) = (@_pure_meta; ccall(:jl_get_field_offset, Csize_t, (Any, Cint), x, idx)) + +""" + fieldtype(T, name::Symbol | index::Int) + +Determine the declared type of a field (specified by name or index) in a composite DataType `T`. + +# Examples +```jldoctest +julia> struct Foo + x::Int64 + y::String + end + +julia> fieldtype(Foo, :x) +Int64 + +julia> fieldtype(Foo, 2) +String +``` +""" +fieldtype + +""" + Base.fieldindex(T, name::Symbol, err:Bool=true) + +Get the index of a named field, throwing an error if the field does not exist (when err==true) +or returning 0 (when err==false). + +# Examples +```jldoctest +julia> struct Foo + x::Int64 + y::String + end + +julia> Base.fieldindex(Foo, :z) +ERROR: type Foo has no field z +Stacktrace: +[...] + +julia> Base.fieldindex(Foo, :z, false) +0 +``` +""" +function fieldindex(T::DataType, name::Symbol, err::Bool=true) + return Int(ccall(:jl_field_index, Cint, (Any, Any, Cint), T, name, err)+1) +end + +argument_datatype(@nospecialize t) = ccall(:jl_argument_datatype, Any, (Any,), t) + +""" + fieldcount(t::Type) + +Get the number of fields that an instance of the given type would have. +An error is thrown if the type is too abstract to determine this. +""" +function fieldcount(@nospecialize t) + if t isa UnionAll || t isa Union + t = argument_datatype(t) + if t === nothing + throw(ArgumentError("type does not have a definite number of fields")) + end + t = t::DataType + elseif t == Union{} + throw(ArgumentError("The empty type does not have a well-defined number of fields since it does not have instances.")) + end + if !(t isa DataType) + throw(TypeError(:fieldcount, DataType, t)) + end + if t.name === NamedTuple_typename + names, types = t.parameters + if names isa Tuple + return length(names) + end + if types isa DataType && types <: Tuple + return fieldcount(types) + end + abstr = true + else + abstr = t.abstract || (t.name === Tuple.name && isvatuple(t)) + end + if abstr + throw(ArgumentError("type does not have a definite number of fields")) + end + if isdefined(t, :types) + return length(t.types) + end + return length(t.name.names) +end + +""" + fieldtypes(T::Type) + +The declared types of all fields in a composite DataType `T` as a tuple. + +!!! compat "Julia 1.1" + This function requires at least Julia 1.1. + +# Examples +```jldoctest +julia> struct Foo + x::Int64 + y::String + end + +julia> fieldtypes(Foo) +(Int64, String) +``` +""" +fieldtypes(T::Type) = ntuple(i -> fieldtype(T, i), fieldcount(T)) + +# return all instances, for types that can be enumerated + +""" + instances(T::Type) + +Return a collection of all instances of the given type, if applicable. Mostly used for +enumerated types (see `@enum`). + +# Example +```jldoctest +julia> @enum Color red blue green + +julia> instances(Color) +(red, blue, green) +``` +""" +function instances end + +function to_tuple_type(@nospecialize(t)) + @_pure_meta + if isa(t,Tuple) || isa(t,AbstractArray) || isa(t,SimpleVector) + t = Tuple{t...} + end + if isa(t,Type) && t<:Tuple + for p in unwrap_unionall(t).parameters + if !(isa(p,Type) || isa(p,TypeVar)) + error("argument tuple type must contain only types") + end + end + else + error("expected tuple type") + end + t +end + +function signature_type(@nospecialize(f), @nospecialize(args)) + f_type = isa(f, Type) ? Type{f} : typeof(f) + if isa(args, Type) + u = unwrap_unionall(args) + return rewrap_unionall(Tuple{f_type, u.parameters...}, args) + else + return Tuple{f_type, args...} + end +end + +""" + code_lowered(f, types; generated=true, debuginfo=:default) + +Return an array of the lowered forms (IR) for the methods matching the given generic function +and type signature. + +If `generated` is `false`, the returned `CodeInfo` instances will correspond to fallback +implementations. An error is thrown if no fallback implementation exists. +If `generated` is `true`, these `CodeInfo` instances will correspond to the method bodies +yielded by expanding the generators. + +The keyword debuginfo controls the amount of code metadata present in the output. + +Note that an error will be thrown if `types` are not leaf types when `generated` is +`true` and any of the corresponding methods are an `@generated` method. +""" +function code_lowered(@nospecialize(f), @nospecialize(t=Tuple); generated::Bool=true, debuginfo::Symbol=:default) + if @isdefined(IRShow) + debuginfo = IRShow.debuginfo(debuginfo) + elseif debuginfo === :default + debuginfo = :source + end + if debuginfo !== :source && debuginfo !== :none + throw(ArgumentError("'debuginfo' must be either :source or :none")) + end + return map(method_instances(f, t)) do m + if generated && isgenerated(m) + if may_invoke_generator(m) + return ccall(:jl_code_for_staged, Any, (Any,), m)::CodeInfo + else + error("Could not expand generator for `@generated` method ", m, ". ", + "This can happen if the provided argument types (", t, ") are ", + "not leaf types, but the `generated` argument is `true`.") + end + end + code = uncompressed_ast(m.def::Method) + debuginfo === :none && remove_linenums!(code) + return code + end +end + +isgenerated(m::Method) = isdefined(m, :generator) +isgenerated(m::Core.MethodInstance) = isgenerated(m.def) + +# low-level method lookup functions used by the compiler + +unionlen(x::Union) = unionlen(x.a) + unionlen(x.b) +unionlen(@nospecialize(x)) = 1 + +_uniontypes(x::Union, ts) = (_uniontypes(x.a,ts); _uniontypes(x.b,ts); ts) +_uniontypes(@nospecialize(x), ts) = (push!(ts, x); ts) +uniontypes(@nospecialize(x)) = _uniontypes(x, Any[]) + +function _methods(@nospecialize(f), @nospecialize(t), lim::Int, world::UInt) + tt = signature_type(f, t) + return _methods_by_ftype(tt, lim, world) +end + +function _methods_by_ftype(@nospecialize(t), lim::Int, world::UInt) + return _methods_by_ftype(t, lim, world, UInt[typemin(UInt)], UInt[typemax(UInt)]) +end +function _methods_by_ftype(@nospecialize(t), lim::Int, world::UInt, min::Array{UInt,1}, max::Array{UInt,1}) + return ccall(:jl_matching_methods, Any, (Any, Cint, Cint, UInt, Ptr{UInt}, Ptr{UInt}), t, lim, 0, world, min, max) +end + +# high-level, more convenient method lookup functions + +# type for reflecting and pretty-printing a subset of methods +mutable struct MethodList + ms::Array{Method,1} + mt::Core.MethodTable +end + +length(m::MethodList) = length(m.ms) +isempty(m::MethodList) = isempty(m.ms) +iterate(m::MethodList, s...) = iterate(m.ms, s...) +eltype(::Type{MethodList}) = Method + +function MethodList(mt::Core.MethodTable) + ms = Method[] + visit(mt) do m + push!(ms, m) + end + return MethodList(ms, mt) +end + +""" + methods(f, [types]) + +Returns the method table for `f`. + +If `types` is specified, returns an array of methods whose types match. +""" +function methods(@nospecialize(f), @nospecialize(t)) + if isa(f, Core.Builtin) + throw(ArgumentError("argument is not a generic function")) + end + t = to_tuple_type(t) + world = typemax(UInt) + return MethodList(Method[m[3] for m in _methods(f, t, -1, world)], typeof(f).name.mt) +end + +methods(f::Core.Builtin) = MethodList(Method[], typeof(f).name.mt) + +function methods_including_ambiguous(@nospecialize(f), @nospecialize(t)) + tt = signature_type(f, t) + world = typemax(UInt) + min = UInt[typemin(UInt)] + max = UInt[typemax(UInt)] + ms = ccall(:jl_matching_methods, Any, (Any, Cint, Cint, UInt, Ptr{UInt}, Ptr{UInt}), tt, -1, 1, world, min, max)::Array{Any,1} + return MethodList(Method[m[3] for m in ms], typeof(f).name.mt) +end +function methods(@nospecialize(f)) + # return all matches + return methods(f, Tuple{Vararg{Any}}) +end + +function visit(f, mt::Core.MethodTable) + mt.defs !== nothing && visit(f, mt.defs) + nothing +end +function visit(f, mc::Core.TypeMapLevel) + if mc.targ !== nothing + e = mc.targ::Vector{Any} + for i in 1:length(e) + isassigned(e, i) && visit(f, e[i]) + end + end + if mc.arg1 !== nothing + e = mc.arg1::Vector{Any} + for i in 1:length(e) + isassigned(e, i) && visit(f, e[i]) + end + end + mc.list !== nothing && visit(f, mc.list) + mc.any !== nothing && visit(f, mc.any) + nothing +end +function visit(f, d::Core.TypeMapEntry) + while d !== nothing + f(d.func) + d = d.next + end + nothing +end + +function length(mt::Core.MethodTable) + n = 0 + visit(mt) do m + n += 1 + end + return n::Int +end +isempty(mt::Core.MethodTable) = (mt.defs === nothing) + +uncompressed_ast(m::Method) = isdefined(m, :source) ? _uncompressed_ast(m, m.source) : + isdefined(m, :generator) ? error("Method is @generated; try `code_lowered` instead.") : + error("Code for this Method is not available.") +_uncompressed_ast(m::Method, s::CodeInfo) = copy(s) +_uncompressed_ast(m::Method, s::Array{UInt8,1}) = ccall(:jl_uncompress_ast, Any, (Any, Ptr{Cvoid}, Any), m, C_NULL, s)::CodeInfo +_uncompressed_ast(ci::Core.CodeInstance, s::Array{UInt8,1}) = ccall(:jl_uncompress_ast, Any, (Any, Any, Any), ci.def.def::Method, ci, s)::CodeInfo + +function method_instances(@nospecialize(f), @nospecialize(t), world::UInt = typemax(UInt)) + tt = signature_type(f, t) + results = Core.MethodInstance[] + for method_data in _methods_by_ftype(tt, -1, world) + mtypes, msp, m = method_data + instance = ccall(:jl_specializations_get_linfo, Ref{MethodInstance}, (Any, Any, Any), m, mtypes, msp) + push!(results, instance) + end + return results +end + +default_debug_info_kind() = unsafe_load(cglobal(:jl_default_debug_info_kind, Cint)) + +# this type mirrors jl_cgparams_t (documented in julia.h) +struct CodegenParams + cached::Cint + + track_allocations::Cint + code_coverage::Cint + static_alloc::Cint + prefer_specsig::Cint + gnu_pubnames::Cint + debug_info_kind::Cint + + module_setup::Any + module_activation::Any + raise_exception::Any + emit_function::Any + emitted_function::Any + + CodegenParams(;cached::Bool=true, + track_allocations::Bool=true, code_coverage::Bool=true, + static_alloc::Bool=true, prefer_specsig::Bool=false, + gnu_pubnames=true, debug_info_kind::Cint = default_debug_info_kind(), + module_setup=nothing, module_activation=nothing, raise_exception=nothing, + emit_function=nothing, emitted_function=nothing) = + new(Cint(cached), + Cint(track_allocations), Cint(code_coverage), + Cint(static_alloc), Cint(prefer_specsig), + Cint(gnu_pubnames), debug_info_kind, + module_setup, module_activation, raise_exception, + emit_function, emitted_function) +end + +const SLOT_USED = 0x8 +ast_slotflag(@nospecialize(code), i) = ccall(:jl_ast_slotflag, UInt8, (Any, Csize_t), code, i - 1) + +""" + may_invoke_generator(method, atypes, sparams) + +Computes whether or not we may invoke the generator for the given `method` on +the given atypes and sparams. For correctness, all generated function are +required to return monotonic answers. However, since we don't expect users to +be able to successfully implement this criterion, we only call generated +functions on concrete types. The one exception to this is that we allow calling +generators with abstract types if the generator does not use said abstract type +(and thus cannot incorrectly use it to break monotonicity). This function +computes whether we are in either of these cases. + +Unlike normal functions, the compilation heuristics still can't generate good dispatch +in some cases, but this may still allow inference not to fall over in some limited cases. +""" +function may_invoke_generator(method::MethodInstance) + return may_invoke_generator(method.def::Method, method.specTypes, method.sparam_vals) +end +function may_invoke_generator(method::Method, @nospecialize(atypes), sparams::SimpleVector) + # If we have complete information, we may always call the generator + isdispatchtuple(atypes) && return true + + # We don't have complete information, but it is possible that the generator + # syntactically doesn't make use of the information we don't have. Check + # for that. + + # For now, only handle the (common, generated by the frontend case) that the + # generator only has one method + isa(method.generator, Core.GeneratedFunctionStub) || return false + gen_mthds = methods(method.generator.gen) + length(gen_mthds) == 1 || return false + + generator_method = first(gen_mthds) + nsparams = length(sparams) + isdefined(generator_method, :source) || return false + code = generator_method.source + nslots = ccall(:jl_ast_nslots, Int, (Any,), code) + at = unwrap_unionall(atypes) + (nslots >= 1 + length(sparams) + length(at.parameters)) || return false + + for i = 1:nsparams + if isa(sparams[i], TypeVar) + if (ast_slotflag(code, 1 + i) & SLOT_USED) != 0 + return false + end + end + end + for i = 1:length(at.parameters) + if !isdispatchelem(at.parameters[i]) + if (ast_slotflag(code, 1 + i + nsparams) & SLOT_USED) != 0 + return false + end + end + end + return true +end + +# give a decent error message if we try to instantiate a staged function on non-leaf types +function func_for_method_checked(m::Method, @nospecialize(types), sparams::SimpleVector) + if isdefined(m, :generator) && !may_invoke_generator(m, types, sparams) + error("cannot call @generated function `", m, "` ", + "with abstract argument types: ", types) + end + return m +end + +function func_for_method_checked(m::Method, @nospecialize(types)) + depwarn("The two argument form of `func_for_method_checked` is deprecated. Pass sparams in addition.", + :func_for_method_checked) + if isdefined(m, :generator) && !isdispatchtuple(types) + error("cannot call @generated function `", m, "` ", + "with abstract argument types: ", types) + end + return m +end + + +""" + code_typed(f, types; optimize=true, debuginfo=:default) + +Returns an array of type-inferred lowered form (IR) for the methods matching the given +generic function and type signature. The keyword argument `optimize` controls whether +additional optimizations, such as inlining, are also applied. +The keyword `debuginfo` controls the amount of code metadata present in the output, +possible options are `:source` or `:none`. +""" +function code_typed(@nospecialize(f), @nospecialize(types=Tuple); + optimize=true, + debuginfo::Symbol=:default, + world = get_world_counter(), + params = Core.Compiler.Params(world)) + ccall(:jl_is_in_pure_context, Bool, ()) && error("code reflection cannot be used from generated functions") + if isa(f, Core.Builtin) + throw(ArgumentError("argument is not a generic function")) + end + if @isdefined(IRShow) + debuginfo = IRShow.debuginfo(debuginfo) + elseif debuginfo === :default + debuginfo = :source + end + if debuginfo !== :source && debuginfo !== :none + throw(ArgumentError("'debuginfo' must be either :source or :none")) + end + types = to_tuple_type(types) + asts = [] + for x in _methods(f, types, -1, world) + meth = func_for_method_checked(x[3], types, x[2]) + (code, ty) = Core.Compiler.typeinf_code(meth, x[1], x[2], optimize, params) + code === nothing && error("inference not successful") # inference disabled? + debuginfo === :none && remove_linenums!(code) + push!(asts, code => ty) + end + return asts +end + +function return_types(@nospecialize(f), @nospecialize(types=Tuple)) + ccall(:jl_is_in_pure_context, Bool, ()) && error("code reflection cannot be used from generated functions") + if isa(f, Core.Builtin) + throw(ArgumentError("argument is not a generic function")) + end + types = to_tuple_type(types) + rt = [] + world = get_world_counter() + params = Core.Compiler.Params(world) + for x in _methods(f, types, -1, world) + meth = func_for_method_checked(x[3], types, x[2]) + ty = Core.Compiler.typeinf_type(meth, x[1], x[2], params) + ty === nothing && error("inference not successful") # inference disabled? + push!(rt, ty) + end + return rt +end + +""" + which(f, types) + +Returns the method of `f` (a `Method` object) that would be called for arguments of the given `types`. + +If `types` is an abstract type, then the method that would be called by `invoke` is returned. +""" +function which(@nospecialize(f), @nospecialize(t)) + if isa(f, Core.Builtin) + throw(ArgumentError("argument is not a generic function")) + end + t = to_tuple_type(t) + tt = signature_type(f, t) + m = ccall(:jl_gf_invoke_lookup, Any, (Any, UInt), tt, typemax(UInt)) + if m === nothing + error("no unique matching method found for the specified argument types") + end + return m.func::Method +end + +""" + which(module, symbol) + +Return the module in which the binding for the variable referenced by `symbol` in `module` was created. +""" +function which(m::Module, s::Symbol) + if !isdefined(m, s) + error("\"$s\" is not defined in module $m") + end + return binding_module(m, s) +end + +# function reflection + +""" + nameof(f::Function) -> Symbol + +Get the name of a generic `Function` as a symbol. For anonymous functions, +this is a compiler-generated name. For explicitly-declared subtypes of +`Function`, it is the name of the function's type. +""" +function nameof(f::Function) + t = typeof(f) + mt = t.name.mt::Core.MethodTable + if mt === Symbol.name.mt + # uses shared method table, so name is not unique to this function type + return nameof(t) + end + return mt.name +end + +""" + parentmodule(f::Function) -> Module + +Determine the module containing the (first) definition of a generic +function. +""" +parentmodule(f::Function) = parentmodule(typeof(f)) + +""" + parentmodule(f::Function, types) -> Module + +Determine the module containing a given definition of a generic function. +""" +function parentmodule(@nospecialize(f), @nospecialize(types)) + m = methods(f, types) + if isempty(m) + error("no matching methods") + end + return first(m).module +end + +""" + hasmethod(f, t::Type{<:Tuple}[, kwnames]; world=typemax(UInt)) -> Bool + +Determine whether the given generic function has a method matching the given +`Tuple` of argument types with the upper bound of world age given by `world`. + +If a tuple of keyword argument names `kwnames` is provided, this also checks +whether the method of `f` matching `t` has the given keyword argument names. +If the matching method accepts a variable number of keyword arguments, e.g. +with `kwargs...`, any names given in `kwnames` are considered valid. Otherwise +the provided names must be a subset of the method's keyword arguments. + +See also [`applicable`](@ref). + +!!! compat "Julia 1.2" + Providing keyword argument names requires Julia 1.2 or later. + +# Examples +```jldoctest +julia> hasmethod(length, Tuple{Array}) +true + +julia> hasmethod(sum, Tuple{Function, Array}, (:dims,)) +true + +julia> hasmethod(sum, Tuple{Function, Array}, (:apples, :bananas)) +false + +julia> g(; xs...) = 4; + +julia> hasmethod(g, Tuple{}, (:a, :b, :c, :d)) # g accepts arbitrary kwargs +true +``` +""" +function hasmethod(@nospecialize(f), @nospecialize(t); world=typemax(UInt)) + t = to_tuple_type(t) + t = signature_type(f, t) + return ccall(:jl_gf_invoke_lookup, Any, (Any, UInt), t, world) !== nothing +end + +function hasmethod(@nospecialize(f), @nospecialize(t), kwnames::Tuple{Vararg{Symbol}}; world=typemax(UInt)) + # TODO: this appears to be doing the wrong queries + hasmethod(f, t, world=world) || return false + isempty(kwnames) && return true + m = which(f, t) + kws = kwarg_decl(m, Core.kwftype(typeof(f))) + for kw in kws + endswith(String(kw), "...") && return true + end + return issubset(kwnames, kws) +end + +""" + Base.isambiguous(m1, m2; ambiguous_bottom=false) -> Bool + +Determine whether two methods `m1` and `m2` (typically of the same +function) are ambiguous. This test is performed in the context of +other methods of the same function; in isolation, `m1` and `m2` might +be ambiguous, but if a third method resolving the ambiguity has been +defined, this returns `false`. + +For parametric types, the `ambiguous_bottom` keyword argument controls whether +`Union{}` counts as an ambiguous intersection of type parameters – when `true`, +it is considered ambiguous, when `false` it is not. + +# Examples +```jldoctest +julia> foo(x::Complex{<:Integer}) = 1 +foo (generic function with 1 method) + +julia> foo(x::Complex{<:Rational}) = 2 +foo (generic function with 2 methods) + +julia> m1, m2 = collect(methods(foo)); + +julia> typeintersect(m1.sig, m2.sig) +Tuple{typeof(foo),Complex{Union{}}} + +julia> Base.isambiguous(m1, m2, ambiguous_bottom=true) +true + +julia> Base.isambiguous(m1, m2, ambiguous_bottom=false) +false +``` +""" +function isambiguous(m1::Method, m2::Method; ambiguous_bottom::Bool=false) + ti = typeintersect(m1.sig, m2.sig) + ti === Bottom && return false + if !ambiguous_bottom + has_bottom_parameter(ti) && return false + end + ml = _methods_by_ftype(ti, -1, typemax(UInt)) + isempty(ml) && return true + for m in ml + if ti <: m[3].sig + return false + end + end + return true +end + +""" + delete_method(m::Method) + +Make method `m` uncallable and force recompilation of any methods that use(d) it. +""" +function delete_method(m::Method) + ccall(:jl_method_table_disable, Cvoid, (Any, Any), get_methodtable(m), m) +end + +function get_methodtable(m::Method) + return ccall(:jl_method_table_for, Any, (Any,), m.sig)::Core.MethodTable +end + +""" + has_bottom_parameter(t) -> Bool + +Determine whether `t` is a Type for which one or more of its parameters is `Union{}`. +""" +function has_bottom_parameter(t::DataType) + for p in t.parameters + has_bottom_parameter(p) && return true + end + return false +end +has_bottom_parameter(t::typeof(Bottom)) = true +has_bottom_parameter(t::UnionAll) = has_bottom_parameter(unwrap_unionall(t)) +has_bottom_parameter(t::Union) = has_bottom_parameter(t.a) & has_bottom_parameter(t.b) +has_bottom_parameter(t::TypeVar) = has_bottom_parameter(t.ub) +has_bottom_parameter(::Any) = false + +min_world(m::Core.CodeInstance) = m.min_world +max_world(m::Core.CodeInstance) = m.max_world +min_world(m::Core.CodeInfo) = m.min_world +max_world(m::Core.CodeInfo) = m.max_world +get_world_counter() = ccall(:jl_get_world_counter, UInt, ()) + + +""" + propertynames(x, private=false) + +Get a tuple or a vector of the properties (`x.property`) of an object `x`. +This is typically the same as [`fieldnames(typeof(x))`](@ref), but types +that overload [`getproperty`](@ref) should generally overload `propertynames` +as well to get the properties of an instance of the type. + +`propertynames(x)` may return only "public" property names that are part +of the documented interface of `x`. If you want it to also return "private" +fieldnames intended for internal use, pass `true` for the optional second argument. +REPL tab completion on `x.` shows only the `private=false` properties. +""" +propertynames(x) = fieldnames(typeof(x)) +propertynames(m::Module) = names(m) +propertynames(x, private) = propertynames(x) # ignore private flag by default + +""" + hasproperty(x, s::Symbol) + +Return a boolean indicating whether the object `x` has `s` as one of its own properties. + +!!! compat "Julia 1.2" + This function requires at least Julia 1.2. +""" +hasproperty(x, s::Symbol) = s in propertynames(x) diff --git a/base/refpointer.jl b/base/refpointer.jl new file mode 100644 index 0000000..cbacfc9 --- /dev/null +++ b/base/refpointer.jl @@ -0,0 +1,134 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" + Ref{T} + +An object that safely references data of type `T`. This type is guaranteed to point to +valid, Julia-allocated memory of the correct type. The underlying data is protected from +freeing by the garbage collector as long as the `Ref` itself is referenced. + +In Julia, `Ref` objects are dereferenced (loaded or stored) with `[]`. + +Creation of a `Ref` to a value `x` of type `T` is usually written `Ref(x)`. +Additionally, for creating interior pointers to containers (such as Array or Ptr), +it can be written `Ref(a, i)` for creating a reference to the `i`-th element of `a`. + +When passed as a `ccall` argument (either as a `Ptr` or `Ref` type), a `Ref` object will be +converted to a native pointer to the data it references. + +There is no invalid (NULL) `Ref` in Julia, but a `C_NULL` instance of `Ptr` can be passed to +a `ccall` Ref argument. + +# Use in broadcasting + +Broadcasting with `Ref(x)` treats `x` as a scalar: +```jldoctest +julia> isa.(Ref([1,2,3]), [Array, Dict, Int]) +3-element BitArray{1}: + 1 + 0 + 0 +``` +""" +Ref + +# C NUL-terminated string pointers; these can be used in ccall +# instead of Ptr{Cchar} and Ptr{Cwchar_t}, respectively, to enforce +# a check for embedded NUL chars in the string (to avoid silent truncation). +if Int === Int64 + primitive type Cstring 64 end + primitive type Cwstring 64 end +else + primitive type Cstring 32 end + primitive type Cwstring 32 end +end + +### General Methods for Ref{T} type + +eltype(x::Type{<:Ref{T}}) where {T} = @isdefined(T) ? T : Any +convert(::Type{Ref{T}}, x::Ref{T}) where {T} = x +size(x::Ref) = () +axes(x::Ref) = () +length(x::Ref) = 1 +ndims(x::Ref) = 0 +ndims(::Type{<:Ref}) = 0 +iterate(r::Ref) = (r[], nothing) +iterate(r::Ref, s) = nothing +IteratorSize(::Type{<:Ref}) = HasShape{0}() + +# create Ref objects for general object conversion +unsafe_convert(::Type{Ref{T}}, x::Ref{T}) where {T} = unsafe_convert(Ptr{T}, x) +unsafe_convert(::Type{Ref{T}}, x) where {T} = unsafe_convert(Ptr{T}, x) + +convert(::Type{Ref{T}}, x) where {T} = RefValue{T}(x) + +### Methods for a Ref object that is backed by an array at index i +struct RefArray{T,A<:AbstractArray{T},R} <: Ref{T} + x::A + i::Int + roots::R # should be either ::Nothing or ::Any + RefArray{T,A,R}(x,i,roots=nothing) where {T,A<:AbstractArray{T},R} = new(x,i,roots) +end +RefArray(x::AbstractArray{T}, i::Int, roots::Any) where {T} = RefArray{T,typeof(x),Any}(x, i, roots) +RefArray(x::AbstractArray{T}, i::Int=1, roots::Nothing=nothing) where {T} = RefArray{T,typeof(x),Nothing}(x, i, nothing) +convert(::Type{Ref{T}}, x::AbstractArray{T}) where {T} = RefArray(x, 1) + +function unsafe_convert(P::Type{Ptr{T}}, b::RefArray{T}) where T + if datatype_pointerfree(RefValue{T}) + p = pointer(b.x, b.i) + elseif isconcretetype(T) && T.mutable + p = pointer_from_objref(b.x[b.i]) + else + # see comment on equivalent branch for RefValue + p = pointerref(Ptr{Ptr{Cvoid}}(pointer(b.x, b.i)), 1, Core.sizeof(Ptr{Cvoid})) + end + return convert(P, p) +end +function unsafe_convert(P::Type{Ptr{Any}}, b::RefArray{Any}) + return convert(P, pointer(b.x, b.i)) +end +unsafe_convert(::Type{Ptr{Cvoid}}, b::RefArray{T}) where {T} = convert(Ptr{Cvoid}, unsafe_convert(Ptr{T}, b)) + +### +if is_primary_base_module + Ref(x::Any) = RefValue(x) + Ref{T}() where {T} = RefValue{T}() # Ref{T}() + Ref{T}(x) where {T} = RefValue{T}(x) # Ref{T}(x) + + Ref(x::Ref, i::Integer) = (i != 1 && error("Ref only has one element"); x) + Ref(x::Ptr{T}, i::Integer) where {T} = x + (i - 1) * Core.sizeof(T) + + # convert Arrays to pointer arrays for ccall + function Ref{P}(a::Array{<:Union{Ptr,Cwstring,Cstring}}) where P<:Union{Ptr,Cwstring,Cstring} + return RefArray(a) # effectively a no-op + end + function Ref{P}(a::Array{T}) where P<:Union{Ptr,Cwstring,Cstring} where T + if (!isbitstype(T) && T <: eltype(P)) + # this Array already has the right memory layout for the requested Ref + return RefArray(a,1,false) # root something, so that this function is type-stable + else + ptrs = Vector{P}(undef, length(a)+1) + roots = Vector{Any}(undef, length(a)) + for i = 1:length(a) + root = cconvert(P, a[i]) + ptrs[i] = unsafe_convert(P, root)::P + roots[i] = root + end + ptrs[length(a)+1] = C_NULL + return RefArray(ptrs,1,roots) + end + end + Ref(x::AbstractArray, i::Integer) = RefArray(x, i) +end + +cconvert(::Type{Ptr{P}}, a::Array{<:Ptr}) where {P<:Ptr} = a +cconvert(::Type{Ref{P}}, a::Array{<:Ptr}) where {P<:Ptr} = a +cconvert(::Type{Ptr{P}}, a::Array) where {P<:Union{Ptr,Cwstring,Cstring}} = Ref{P}(a) +cconvert(::Type{Ref{P}}, a::Array) where {P<:Union{Ptr,Cwstring,Cstring}} = Ref{P}(a) + +### + +getindex(b::RefArray) = b.x[b.i] +setindex!(b::RefArray, x) = (b.x[b.i] = x; b) + +### diff --git a/base/refvalue.jl b/base/refvalue.jl new file mode 100644 index 0000000..f1f22bc --- /dev/null +++ b/base/refvalue.jl @@ -0,0 +1,33 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +### Methods for a Ref object that can store a single value of any type + +mutable struct RefValue{T} <: Ref{T} + x::T + RefValue{T}() where {T} = new() + RefValue{T}(x) where {T} = new(x) +end +RefValue(x::T) where {T} = RefValue{T}(x) +isassigned(x::RefValue) = isdefined(x, :x) + +function unsafe_convert(P::Type{Ptr{T}}, b::RefValue{T}) where T + if datatype_pointerfree(RefValue{T}) + p = pointer_from_objref(b) + elseif isconcretetype(T) && T.mutable + p = pointer_from_objref(b.x) + else + # If the slot is not leaf type, it could be either immutable or not. + # If it is actually an immutable, then we can't take it's pointer directly + # Instead, explicitly load the pointer from the `RefValue`, + # which also ensures this returns same pointer as the one rooted in the `RefValue` object. + p = pointerref(Ptr{Ptr{Cvoid}}(pointer_from_objref(b)), 1, Core.sizeof(Ptr{Cvoid})) + end + return convert(P, p) +end +function unsafe_convert(P::Type{Ptr{Any}}, b::RefValue{Any}) + return convert(P, pointer_from_objref(b)) +end +unsafe_convert(::Type{Ptr{Cvoid}}, b::RefValue{T}) where {T} = convert(Ptr{Cvoid}, unsafe_convert(Ptr{T}, b)) + +getindex(b::RefValue) = b.x +setindex!(b::RefValue, x) = (b.x = x; b) diff --git a/base/regex.jl b/base/regex.jl new file mode 100644 index 0000000..3154c56 --- /dev/null +++ b/base/regex.jl @@ -0,0 +1,712 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## object-oriented Regex interface ## + +include("pcre.jl") + +const DEFAULT_COMPILER_OPTS = PCRE.UTF | PCRE.NO_UTF_CHECK | PCRE.ALT_BSUX | PCRE.UCP +const DEFAULT_MATCH_OPTS = PCRE.NO_UTF_CHECK + +""" + Regex(pattern[, flags]) + +A type representing a regular expression. `Regex` objects can be used to match strings +with [`match`](@ref). + +`Regex` objects can be created using the [`@r_str`](@ref) string macro. The +`Regex(pattern[, flags])` constructor is usually used if the `pattern` string needs +to be interpolated. See the documentation of the string macro for details on flags. +""" +mutable struct Regex + pattern::String + compile_options::UInt32 + match_options::UInt32 + regex::Ptr{Cvoid} + + function Regex(pattern::AbstractString, compile_options::Integer, + match_options::Integer) + pattern = String(pattern) + compile_options = UInt32(compile_options) + match_options = UInt32(match_options) + if (compile_options & ~PCRE.COMPILE_MASK) != 0 + throw(ArgumentError("invalid regex compile options: $compile_options")) + end + if (match_options & ~PCRE.EXECUTE_MASK) !=0 + throw(ArgumentError("invalid regex match options: $match_options")) + end + re = compile(new(pattern, compile_options, match_options, C_NULL)) + finalizer(re) do re + re.regex == C_NULL || PCRE.free_re(re.regex) + end + re + end +end + +function Regex(pattern::AbstractString, flags::AbstractString) + options = DEFAULT_COMPILER_OPTS + for f in flags + if f == 'a' + options &= ~PCRE.UCP + else + options |= f=='i' ? PCRE.CASELESS : + f=='m' ? PCRE.MULTILINE : + f=='s' ? PCRE.DOTALL : + f=='x' ? PCRE.EXTENDED : + throw(ArgumentError("unknown regex flag: $f")) + end + end + Regex(pattern, options, DEFAULT_MATCH_OPTS) +end +Regex(pattern::AbstractString) = Regex(pattern, DEFAULT_COMPILER_OPTS, DEFAULT_MATCH_OPTS) + +function compile(regex::Regex) + if regex.regex == C_NULL + if PCRE.PCRE_COMPILE_LOCK === nothing + regex.regex = PCRE.compile(regex.pattern, regex.compile_options) + PCRE.jit_compile(regex.regex) + else + l = PCRE.PCRE_COMPILE_LOCK::Threads.SpinLock + lock(l) + try + if regex.regex == C_NULL + regex.regex = PCRE.compile(regex.pattern, regex.compile_options) + PCRE.jit_compile(regex.regex) + end + finally + unlock(l) + end + end + end + regex +end + +""" + @r_str -> Regex + +Construct a regex, such as `r"^[a-z]*\$"`, without interpolation and unescaping (except for +quotation mark `"` which still has to be escaped). The regex also accepts one or more flags, +listed after the ending quote, to change its behaviour: + +- `i` enables case-insensitive matching +- `m` treats the `^` and `\$` tokens as matching the start and end of individual lines, as + opposed to the whole string. +- `s` allows the `.` modifier to match newlines. +- `x` enables "comment mode": whitespace is enabled except when escaped with `\\`, and `#` + is treated as starting a comment. +- `a` disables `UCP` mode (enables ASCII mode). By default `\\B`, `\\b`, `\\D`, `\\d`, `\\S`, + `\\s`, `\\W`, `\\w`, etc. match based on Unicode character properties. With this option, + these sequences only match ASCII characters. + +See `Regex` if interpolation is needed. + +# Examples +```jldoctest +julia> match(r"a+.*b+.*?d\$"ism, "Goodbye,\\nOh, angry,\\nBad world\\n") +RegexMatch("angry,\\nBad world") +``` +This regex has the first three flags enabled. +""" +macro r_str(pattern, flags...) Regex(pattern, flags...) end + +function show(io::IO, re::Regex) + imsxa = PCRE.CASELESS|PCRE.MULTILINE|PCRE.DOTALL|PCRE.EXTENDED|PCRE.UCP + opts = re.compile_options + if (opts & ~imsxa) == (DEFAULT_COMPILER_OPTS & ~imsxa) + print(io, 'r') + print_quoted_literal(io, re.pattern) + if (opts & PCRE.CASELESS ) != 0; print(io, 'i'); end + if (opts & PCRE.MULTILINE) != 0; print(io, 'm'); end + if (opts & PCRE.DOTALL ) != 0; print(io, 's'); end + if (opts & PCRE.EXTENDED ) != 0; print(io, 'x'); end + if (opts & PCRE.UCP ) == 0; print(io, 'a'); end + else + print(io, "Regex(") + show(io, re.pattern) + print(io, ',') + show(io, opts) + print(io, ')') + end +end + +# TODO: map offsets into strings in other encodings back to original indices. +# or maybe it's better to just fail since that would be quite slow + +struct RegexMatch + match::SubString{String} + captures::Vector{Union{Nothing,SubString{String}}} + offset::Int + offsets::Vector{Int} + regex::Regex +end + +function show(io::IO, m::RegexMatch) + print(io, "RegexMatch(") + show(io, m.match) + idx_to_capture_name = PCRE.capture_names(m.regex.regex) + if !isempty(m.captures) + print(io, ", ") + for i = 1:length(m.captures) + # If the capture group is named, show the name. + # Otherwise show its index. + capture_name = get(idx_to_capture_name, i, i) + print(io, capture_name, "=") + show(io, m.captures[i]) + if i < length(m.captures) + print(io, ", ") + end + end + end + print(io, ")") +end + +# Capture group extraction +getindex(m::RegexMatch, idx::Integer) = m.captures[idx] +function getindex(m::RegexMatch, name::Symbol) + idx = PCRE.substring_number_from_name(m.regex.regex, name) + idx <= 0 && error("no capture group named $name found in regex") + m[idx] +end +getindex(m::RegexMatch, name::AbstractString) = m[Symbol(name)] + +function occursin(r::Regex, s::AbstractString; offset::Integer=0) + compile(r) + return PCRE.exec_r(r.regex, String(s), offset, r.match_options) +end + +function occursin(r::Regex, s::SubString; offset::Integer=0) + compile(r) + return PCRE.exec_r(r.regex, s, offset, r.match_options) +end + +""" + startswith(s::AbstractString, prefix::Regex) + +Return `true` if `s` starts with the regex pattern, `prefix`. + +!!! note + `startswith` does not compile the anchoring into the regular + expression, but instead passes the anchoring as + `match_option` to PCRE. If compile time is amortized, + `occursin(r"^...", s)` is faster than `startswith(s, r"...")`. + +See also [`occursin`](@ref) and [`endswith`](@ref). + +!!! compat "Julia 1.2" + This method requires at least Julia 1.2. + +# Examples +```jldoctest +julia> startswith("JuliaLang", r"Julia|Romeo") +true +``` +""" +function startswith(s::AbstractString, r::Regex) + compile(r) + return PCRE.exec_r(r.regex, String(s), 0, r.match_options | PCRE.ANCHORED) +end + +function startswith(s::SubString, r::Regex) + compile(r) + return PCRE.exec_r(r.regex, s, 0, r.match_options | PCRE.ANCHORED) +end + +""" + endswith(s::AbstractString, suffix::Regex) + +Return `true` if `s` ends with the regex pattern, `suffix`. + +!!! note + `endswith` does not compile the anchoring into the regular + expression, but instead passes the anchoring as + `match_option` to PCRE. If compile time is amortized, + `occursin(r"...\$", s)` is faster than `endswith(s, r"...")`. + +See also [`occursin`](@ref) and [`startswith`](@ref). + +!!! compat "Julia 1.2" + This method requires at least Julia 1.2. + +# Examples +```jldoctest +julia> endswith("JuliaLang", r"Lang|Roberts") +true +``` +""" +function endswith(s::AbstractString, r::Regex) + compile(r) + return PCRE.exec_r(r.regex, String(s), 0, r.match_options | PCRE.ENDANCHORED) +end + +function endswith(s::SubString, r::Regex) + compile(r) + return PCRE.exec_r(r.regex, s, 0, r.match_options | PCRE.ENDANCHORED) +end + +""" + match(r::Regex, s::AbstractString[, idx::Integer[, addopts]]) + +Search for the first match of the regular expression `r` in `s` and return a `RegexMatch` +object containing the match, or nothing if the match failed. The matching substring can be +retrieved by accessing `m.match` and the captured sequences can be retrieved by accessing +`m.captures` The optional `idx` argument specifies an index at which to start the search. + +# Examples +```jldoctest +julia> rx = r"a(.)a" +r"a(.)a" + +julia> m = match(rx, "cabac") +RegexMatch("aba", 1="b") + +julia> m.captures +1-element Array{Union{Nothing, SubString{String}},1}: + "b" + +julia> m.match +"aba" + +julia> match(rx, "cabac", 3) === nothing +true +``` +""" +function match end + +function match(re::Regex, str::Union{SubString{String}, String}, idx::Integer, add_opts::UInt32=UInt32(0)) + compile(re) + opts = re.match_options | add_opts + matched, data = PCRE.exec_r_data(re.regex, str, idx-1, opts) + if !matched + PCRE.free_match_data(data) + return nothing + end + n = div(PCRE.ovec_length(data), 2) - 1 + p = PCRE.ovec_ptr(data) + mat = SubString(str, unsafe_load(p, 1)+1, prevind(str, unsafe_load(p, 2)+1)) + cap = Union{Nothing,SubString{String}}[unsafe_load(p,2i+1) == PCRE.UNSET ? nothing : + SubString(str, unsafe_load(p,2i+1)+1, + prevind(str, unsafe_load(p,2i+2)+1)) for i=1:n] + off = Int[ unsafe_load(p,2i+1)+1 for i=1:n ] + result = RegexMatch(mat, cap, unsafe_load(p,1)+1, off, re) + PCRE.free_match_data(data) + return result +end + +match(r::Regex, s::AbstractString) = match(r, s, firstindex(s)) +match(r::Regex, s::AbstractString, i::Integer) = throw(ArgumentError( + "regex matching is only available for the String type; use String(s) to convert" +)) + +findnext(re::Regex, str::Union{String,SubString}, idx::Integer) = _findnext_re(re, str, idx, C_NULL) + +# TODO: return only start index and update deprecation +function _findnext_re(re::Regex, str::Union{String,SubString}, idx::Integer, match_data::Ptr{Cvoid}) + if idx > nextind(str,lastindex(str)) + throw(BoundsError()) + end + opts = re.match_options + compile(re) + alloc = match_data == C_NULL + if alloc + matched, data = PCRE.exec_r_data(re.regex, str, idx-1, opts) + else + matched = PCRE.exec(re.regex, str, idx-1, opts, match_data) + data = match_data + end + if matched + p = PCRE.ovec_ptr(data) + ans = (Int(unsafe_load(p,1))+1):prevind(str,Int(unsafe_load(p,2))+1) + else + ans = nothing + end + alloc && PCRE.free_match_data(data) + return ans +end +findnext(r::Regex, s::AbstractString, idx::Integer) = throw(ArgumentError( + "regex search is only available for the String type; use String(s) to convert" +)) +findfirst(r::Regex, s::AbstractString) = findnext(r,s,firstindex(s)) + + +""" + findall( + pattern::Union{AbstractString,Regex}, + string::AbstractString; + overlap::Bool = false, + ) + +Return a `Vector{UnitRange{Int}}` of all the matches for `pattern` in `string`. +Each element of the returned vector is a range of indices where the +matching sequence is found, like the return value of [`findnext`](@ref). + +If `overlap=true`, the matching sequences are allowed to overlap indices in the +original string, otherwise they must be from disjoint character ranges. +""" +function findall(t::Union{AbstractString,Regex}, s::AbstractString; overlap::Bool=false) + found = UnitRange{Int}[] + i, e = firstindex(s), lastindex(s) + while true + r = findnext(t, s, i) + isnothing(r) && break + push!(found, r) + j = overlap || isempty(r) ? first(r) : last(r) + j > e && break + @inbounds i = nextind(s, j) + end + return found +end + +""" + count( + pattern::Union{AbstractString,Regex}, + string::AbstractString; + overlap::Bool = false, + ) + +Return the number of matches for `pattern` in `string`. This is equivalent to +calling `length(findall(pattern, string))` but more efficient. + +If `overlap=true`, the matching sequences are allowed to overlap indices in the +original string, otherwise they must be from disjoint character ranges. +""" +function count(t::Union{AbstractString,Regex}, s::AbstractString; overlap::Bool=false) + n = 0 + i, e = firstindex(s), lastindex(s) + while true + r = findnext(t, s, i) + isnothing(r) && break + n += 1 + j = overlap || isempty(r) ? first(r) : last(r) + j > e && break + @inbounds i = nextind(s, j) + end + return n +end + +""" + SubstitutionString(substr) + +Stores the given string `substr` as a `SubstitutionString`, for use in regular expression +substitutions. Most commonly constructed using the [`@s_str`](@ref) macro. + +```jldoctest +julia> SubstitutionString("Hello \\\\g, it's \\\\1") +s"Hello \\\\g, it's \\\\1" + +julia> subst = s"Hello \\g, it's \\1" +s"Hello \\\\g, it's \\\\1" + +julia> typeof(subst) +SubstitutionString{String} + +``` + +""" +struct SubstitutionString{T<:AbstractString} <: AbstractString + string::T +end + +ncodeunits(s::SubstitutionString) = ncodeunits(s.string) +codeunit(s::SubstitutionString) = codeunit(s.string) +codeunit(s::SubstitutionString, i::Integer) = codeunit(s.string, i) +isvalid(s::SubstitutionString, i::Integer) = isvalid(s.string, i) +iterate(s::SubstitutionString, i::Integer...) = iterate(s.string, i...) + +function show(io::IO, s::SubstitutionString) + print(io, "s") + show(io, s.string) +end + +""" + @s_str -> SubstitutionString + +Construct a substitution string, used for regular expression substitutions. Within the +string, sequences of the form `\\N` refer to the Nth capture group in the regex, and +`\\g` refers to a named capture group with name `groupname`. + +```jldoctest +julia> msg = "#Hello# from Julia"; + +julia> replace(msg, r"#(.+)# from (?\\w+)" => s"FROM: \\g; MESSAGE: \\1") +"FROM: Julia; MESSAGE: Hello" +``` +""" +macro s_str(string) SubstitutionString(string) end + +# replacement + +struct RegexAndMatchData + re::Regex + match_data::Ptr{Cvoid} + RegexAndMatchData(re::Regex) = (compile(re); new(re, PCRE.create_match_data(re.regex))) +end + +findnext(pat::RegexAndMatchData, str, i) = _findnext_re(pat.re, str, i, pat.match_data) + +_pat_replacer(r::Regex) = RegexAndMatchData(r) + +_free_pat_replacer(r::RegexAndMatchData) = PCRE.free_match_data(r.match_data) + +replace_err(repl) = error("Bad replacement string: $repl") + +function _write_capture(io, re::RegexAndMatchData, group) + len = PCRE.substring_length_bynumber(re.match_data, group) + ensureroom(io, len+1) + PCRE.substring_copy_bynumber(re.match_data, group, + pointer(io.data, io.ptr), len+1) + io.ptr += len + io.size = max(io.size, io.ptr - 1) +end + + +const SUB_CHAR = '\\' +const GROUP_CHAR = 'g' +const KEEP_ESC = [SUB_CHAR, GROUP_CHAR, '0':'9'...] + +function _replace(io, repl_s::SubstitutionString, str, r, re::RegexAndMatchData) + LBRACKET = '<' + RBRACKET = '>' + repl = unescape_string(repl_s.string, KEEP_ESC) + i = firstindex(repl) + e = lastindex(repl) + while i <= e + if repl[i] == SUB_CHAR + next_i = nextind(repl, i) + next_i > e && replace_err(repl) + if repl[next_i] == SUB_CHAR + write(io, SUB_CHAR) + i = nextind(repl, next_i) + elseif isdigit(repl[next_i]) + group = parse(Int, repl[next_i]) + i = nextind(repl, next_i) + while i <= e + if isdigit(repl[i]) + group = 10group + parse(Int, repl[i]) + i = nextind(repl, i) + else + break + end + end + _write_capture(io, re, group) + elseif repl[next_i] == GROUP_CHAR + i = nextind(repl, next_i) + if i > e || repl[i] != LBRACKET + replace_err(repl) + end + i = nextind(repl, i) + i > e && replace_err(repl) + groupstart = i + while repl[i] != RBRACKET + i = nextind(repl, i) + i > e && replace_err(repl) + end + # TODO: avoid this allocation + groupname = SubString(repl, groupstart, prevind(repl, i)) + if all(isdigit, groupname) + _write_capture(io, re, parse(Int, groupname)) + else + group = PCRE.substring_number_from_name(re.re.regex, groupname) + group < 0 && replace_err("Group $groupname not found in regex $(re.re)") + _write_capture(io, re, group) + end + i = nextind(repl, i) + else + replace_err(repl) + end + else + write(io, repl[i]) + i = nextind(repl, i) + end + end +end + +struct RegexMatchIterator + regex::Regex + string::String + overlap::Bool + + function RegexMatchIterator(regex::Regex, string::AbstractString, ovr::Bool=false) + new(regex, string, ovr) + end +end +compile(itr::RegexMatchIterator) = (compile(itr.regex); itr) +eltype(::Type{RegexMatchIterator}) = RegexMatch +IteratorSize(::Type{RegexMatchIterator}) = SizeUnknown() + +function iterate(itr::RegexMatchIterator, (offset,prevempty)=(1,false)) + opts_nonempty = UInt32(PCRE.ANCHORED | PCRE.NOTEMPTY_ATSTART) + while true + mat = match(itr.regex, itr.string, offset, + prevempty ? opts_nonempty : UInt32(0)) + + if mat === nothing + if prevempty && offset <= sizeof(itr.string) + offset = nextind(itr.string, offset) + prevempty = false + continue + else + break + end + else + if itr.overlap + if !isempty(mat.match) + offset = nextind(itr.string, mat.offset) + else + offset = mat.offset + end + else + offset = mat.offset + ncodeunits(mat.match) + end + return (mat, (offset, isempty(mat.match))) + end + end + nothing +end + +""" + eachmatch(r::Regex, s::AbstractString; overlap::Bool=false) + +Search for all matches of a the regular expression `r` in `s` and return a iterator over the +matches. If overlap is `true`, the matching sequences are allowed to overlap indices in the +original string, otherwise they must be from distinct character ranges. + +# Examples +```jldoctest +julia> rx = r"a.a" +r"a.a" + +julia> m = eachmatch(rx, "a1a2a3a") +Base.RegexMatchIterator(r"a.a", "a1a2a3a", false) + +julia> collect(m) +2-element Array{RegexMatch,1}: + RegexMatch("a1a") + RegexMatch("a3a") + +julia> collect(eachmatch(rx, "a1a2a3a", overlap = true)) +3-element Array{RegexMatch,1}: + RegexMatch("a1a") + RegexMatch("a2a") + RegexMatch("a3a") +``` +""" +eachmatch(re::Regex, str::AbstractString; overlap = false) = + RegexMatchIterator(re, str, overlap) + +## comparison ## + +function ==(a::Regex, b::Regex) + a.pattern == b.pattern && a.compile_options == b.compile_options && a.match_options == b.match_options +end + +## hash ## +const hashre_seed = UInt === UInt64 ? 0x67e195eb8555e72d : 0xe32373e4 +function hash(r::Regex, h::UInt) + h += hashre_seed + h = hash(r.pattern, h) + h = hash(r.compile_options, h) + h = hash(r.match_options, h) +end + +## String operations ## + +""" + *(s::Regex, t::Union{Regex,AbstractString,AbstractChar}) -> Regex + *(s::Union{Regex,AbstractString,AbstractChar}, t::Regex) -> Regex + +Concatenate regexes, strings and/or characters, producing a [`Regex`](@ref). +String and character arguments must be matched exactly in the resulting regex, +meaning that the contained characters are devoid of any special meaning +(they are quoted with "\\Q" and "\\E"). + +!!! compat "Julia 1.3" + This method requires at least Julia 1.3. + +# Examples +```jldoctest +julia> match(r"Hello|Good bye" * ' ' * "world", "Hello world") +RegexMatch("Hello world") + +julia> r = r"a|b" * "c|d" +r"(?:a|b)\\Qc|d\\E" + +julia> match(r, "ac") == nothing +true + +julia> match(r, "ac|d") +RegexMatch("ac|d") +``` +""" +function *(r1::Union{Regex,AbstractString,AbstractChar}, rs::Union{Regex,AbstractString,AbstractChar}...) + mask = PCRE.CASELESS | PCRE.MULTILINE | PCRE.DOTALL | PCRE.EXTENDED # imsx + match_opts = nothing # all args must agree on this + compile_opts = nothing # all args must agree on this + shared = mask + for r in (r1, rs...) + r isa Regex || continue + if match_opts === nothing + match_opts = r.match_options + compile_opts = r.compile_options & ~mask + else + r.match_options == match_opts && + r.compile_options & ~mask == compile_opts || + throw(ArgumentError("cannot multiply regexes: incompatible options")) + end + shared &= r.compile_options + end + unshared = mask & ~shared + Regex(string(wrap_string(r1, unshared), wrap_string.(rs, Ref(unshared))...), compile_opts | shared, match_opts) +end + +*(r::Regex) = r # avoids wrapping r in a useless subpattern + +wrap_string(r::Regex, unshared::UInt32) = string("(?", regex_opts_str(r.compile_options & unshared), ':', r.pattern, ')') +# if s contains raw"\E", split '\' and 'E' within two distinct \Q...\E groups: +wrap_string(s::AbstractString, ::UInt32) = string("\\Q", replace(s, raw"\E" => raw"\\E\QE"), "\\E") +wrap_string(s::AbstractChar, ::UInt32) = string("\\Q", s, "\\E") + +regex_opts_str(opts) = (isassigned(_regex_opts_str) ? _regex_opts_str[] : init_regex())[opts] + +# UInt32 to String mapping for some compile options +const _regex_opts_str = Ref{ImmutableDict{UInt32,String}}() + +@noinline init_regex() = _regex_opts_str[] = foldl(0:15, init=ImmutableDict{UInt32,String}()) do d, o + opt = UInt32(0) + str = "" + if o & 1 != 0 + opt |= PCRE.CASELESS + str *= 'i' + end + if o & 2 != 0 + opt |= PCRE.MULTILINE + str *= 'm' + end + if o & 4 != 0 + opt |= PCRE.DOTALL + str *= 's' + end + if o & 8 != 0 + opt |= PCRE.EXTENDED + str *= 'x' + end + ImmutableDict(d, opt => str) +end + + +""" + ^(s::Regex, n::Integer) + +Repeat a regex `n` times. + +!!! compat "Julia 1.3" + This method requires at least Julia 1.3. + +# Examples +```jldoctest +julia> r"Test "^2 +r"(?:Test ){2}" + +julia> match(r"Test "^2, "Test Test ") +RegexMatch("Test Test ") +``` +""" +^(r::Regex, i::Integer) = Regex(string("(?:", r.pattern, "){$i}"), r.compile_options, r.match_options) diff --git a/base/reinterpretarray.jl b/base/reinterpretarray.jl new file mode 100644 index 0000000..fa2af7a --- /dev/null +++ b/base/reinterpretarray.jl @@ -0,0 +1,326 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" +Gives a reinterpreted view (of element type T) of the underlying array (of element type S). +If the size of `T` differs from the size of `S`, the array will be compressed/expanded in +the first dimension. +""" +struct ReinterpretArray{T,N,S,A<:AbstractArray{S, N}} <: AbstractArray{T, N} + parent::A + readable::Bool + writable::Bool + global reinterpret + function reinterpret(::Type{T}, a::A) where {T,N,S,A<:AbstractArray{S, N}} + function throwbits(::Type{S}, ::Type{T}, ::Type{U}) where {S,T,U} + @_noinline_meta + throw(ArgumentError("cannot reinterpret `$(S)` `$(T)`, type `$(U)` is not a bits type")) + end + function throwsize0(::Type{S}, ::Type{T}) + @_noinline_meta + throw(ArgumentError("cannot reinterpret a zero-dimensional `$(S)` array to `$(T)` which is of a different size")) + end + function thrownonint(::Type{S}, ::Type{T}, dim) + @_noinline_meta + throw(ArgumentError(""" + cannot reinterpret an `$(S)` array to `$(T)` whose first dimension has size `$(dim)`. + The resulting array would have non-integral first dimension. + """)) + end + function throwaxes1(::Type{S}, ::Type{T}, ax1) + @_noinline_meta + throw(ArgumentError("cannot reinterpret a `$(S)` array to `$(T)` when the first axis is $ax1. Try reshaping first.")) + end + isbitstype(T) || throwbits(S, T, T) + isbitstype(S) || throwbits(S, T, S) + (N != 0 || sizeof(T) == sizeof(S)) || throwsize0(S, T) + if N != 0 && sizeof(S) != sizeof(T) + ax1 = axes(a)[1] + dim = length(ax1) + rem(dim*sizeof(S),sizeof(T)) == 0 || thrownonint(S, T, dim) + first(ax1) == 1 || throwaxes1(S, T, ax1) + end + readable = array_subpadding(T, S) + writable = array_subpadding(S, T) + new{T, N, S, A}(a, readable, writable) + end +end + +# Definition of StridedArray +StridedFastContiguousSubArray{T,N,A<:DenseArray} = FastContiguousSubArray{T,N,A} +StridedReinterpretArray{T,N,A<:Union{DenseArray,StridedFastContiguousSubArray}} = ReinterpretArray{T,N,S,A} where S +StridedReshapedArray{T,N,A<:Union{DenseArray,StridedFastContiguousSubArray,StridedReinterpretArray}} = ReshapedArray{T,N,A} +StridedSubArray{T,N,A<:Union{DenseArray,StridedReshapedArray,StridedReinterpretArray}, + I<:Tuple{Vararg{Union{RangeIndex, AbstractCartesianIndex}}}} = SubArray{T,N,A,I} +StridedArray{T,N} = Union{DenseArray{T,N}, StridedSubArray{T,N}, StridedReshapedArray{T,N}, StridedReinterpretArray{T,N}} +StridedVector{T} = Union{DenseArray{T,1}, StridedSubArray{T,1}, StridedReshapedArray{T,1}, StridedReinterpretArray{T,1}} +StridedMatrix{T} = Union{DenseArray{T,2}, StridedSubArray{T,2}, StridedReshapedArray{T,2}, StridedReinterpretArray{T,2}} +StridedVecOrMat{T} = Union{StridedVector{T}, StridedMatrix{T}} + +# the definition of strides for Array{T,N} is tuple() if N = 0, otherwise it is +# a tuple containing 1 and a cumulative product of the first N-1 sizes +# this definition is also used for StridedReshapedArray and StridedReinterpretedArray +# which have the same memory storage as Array +function stride(a::Union{DenseArray,StridedReshapedArray,StridedReinterpretArray}, i::Int) + if i > ndims(a) + return length(a) + end + s = 1 + for n = 1:(i-1) + s *= size(a, n) + end + return s +end + +strides(a::Union{DenseArray,StridedReshapedArray,StridedReinterpretArray}) = size_to_strides(1, size(a)...) + +function check_readable(a::ReinterpretArray{T, N, S} where N) where {T,S} + # See comment in check_writable + if !a.readable && !array_subpadding(T, S) + throw(PaddingError(T, S)) + end +end + +function check_writable(a::ReinterpretArray{T, N, S} where N) where {T,S} + # `array_subpadding` is relatively expensive (compared to a simple arrayref), + # so it is cached in the array. However, it is computable at compile time if, + # inference has the types available. By using this form of the check, we can + # get the best of both worlds for the success case. If the types were not + # available to inference, we simply need to check the field (relatively cheap) + # and if they were we should be able to fold this check away entirely. + if !a.writable && !array_subpadding(S, T) + throw(PaddingError(T, S)) + end +end + +IndexStyle(a::ReinterpretArray) = IndexStyle(a.parent) + +parent(a::ReinterpretArray) = a.parent +dataids(a::ReinterpretArray) = dataids(a.parent) +unaliascopy(a::ReinterpretArray{T}) where {T} = reinterpret(T, unaliascopy(a.parent)) + +function size(a::ReinterpretArray{T,N,S} where {N}) where {T,S} + psize = size(a.parent) + size1 = div(psize[1]*sizeof(S), sizeof(T)) + tuple(size1, tail(psize)...) +end +size(a::ReinterpretArray{T,0}) where {T} = () + +function axes(a::ReinterpretArray{T,N,S} where {N}) where {T,S} + paxs = axes(a.parent) + f, l = first(paxs[1]), length(paxs[1]) + size1 = div(l*sizeof(S), sizeof(T)) + tuple(oftype(paxs[1], f:f+size1-1), tail(paxs)...) +end +axes(a::ReinterpretArray{T,0}) where {T} = () + +elsize(::Type{<:ReinterpretArray{T}}) where {T} = sizeof(T) +unsafe_convert(::Type{Ptr{T}}, a::ReinterpretArray{T,N,S} where N) where {T,S} = Ptr{T}(unsafe_convert(Ptr{S},a.parent)) + +@inline @propagate_inbounds getindex(a::ReinterpretArray{T,0}) where {T} = reinterpret(T, a.parent[]) +@inline @propagate_inbounds getindex(a::ReinterpretArray) = a[1] + +@inline @propagate_inbounds function getindex(a::ReinterpretArray{T,N,S}, inds::Vararg{Int, N}) where {T,N,S} + check_readable(a) + _getindex_ra(a, inds[1], tail(inds)) +end + +@inline @propagate_inbounds function getindex(a::ReinterpretArray{T,N,S}, i::Int) where {T,N,S} + check_readable(a) + if isa(IndexStyle(a), IndexLinear) + return _getindex_ra(a, i, ()) + end + # Convert to full indices here, to avoid needing multiple conversions in + # the loop in _getindex_ra + inds = _to_subscript_indices(a, i) + _getindex_ra(a, inds[1], tail(inds)) +end + +@inline _memcpy!(dst, src, n) = ccall(:memcpy, Cvoid, (Ptr{UInt8}, Ptr{UInt8}, Csize_t), dst, src, n) + +@inline @propagate_inbounds function _getindex_ra(a::ReinterpretArray{T,N,S}, i1::Int, tailinds::TT) where {T,N,S,TT} + # Make sure to match the scalar reinterpret if that is applicable + if sizeof(T) == sizeof(S) && (fieldcount(T) + fieldcount(S)) == 0 + return reinterpret(T, a.parent[i1, tailinds...]) + else + @boundscheck checkbounds(a, i1, tailinds...) + ind_start, sidx = divrem((i1-1)*sizeof(T), sizeof(S)) + t = Ref{T}() + s = Ref{S}() + GC.@preserve t s begin + tptr = Ptr{UInt8}(unsafe_convert(Ref{T}, t)) + sptr = Ptr{UInt8}(unsafe_convert(Ref{S}, s)) + i = 1 + nbytes_copied = 0 + # This is a bit complicated to deal with partial elements + # at both the start and the end. LLVM will fold as appropriate, + # once it knows the data layout + while nbytes_copied < sizeof(T) + s[] = a.parent[ind_start + i, tailinds...] + nb = min(sizeof(S) - sidx, sizeof(T)-nbytes_copied) + _memcpy!(tptr + nbytes_copied, sptr + sidx, nb) + nbytes_copied += nb + sidx = 0 + i += 1 + end + end + return t[] + end +end + + +@inline @propagate_inbounds setindex!(a::ReinterpretArray{T,0,S} where T, v) where {S} = (a.parent[] = reinterpret(S, v)) +@inline @propagate_inbounds setindex!(a::ReinterpretArray, v) = (a[1] = v) + +@inline @propagate_inbounds function setindex!(a::ReinterpretArray{T,N,S}, v, inds::Vararg{Int, N}) where {T,N,S} + check_writable(a) + _setindex_ra!(a, v, inds[1], tail(inds)) +end + +@inline @propagate_inbounds function setindex!(a::ReinterpretArray{T,N,S}, v, i::Int) where {T,N,S} + check_writable(a) + if isa(IndexStyle(a), IndexLinear) + return _setindex_ra!(a, v, i, ()) + end + inds = _to_subscript_indices(a, i) + _setindex_ra!(a, v, inds[1], tail(inds)) +end + +@inline @propagate_inbounds function _setindex_ra!(a::ReinterpretArray{T,N,S}, v, i1::Int, tailinds::TT) where {T,N,S,TT} + v = convert(T, v)::T + # Make sure to match the scalar reinterpret if that is applicable + if sizeof(T) == sizeof(S) && (fieldcount(T) + fieldcount(S)) == 0 + return setindex!(a.parent, reinterpret(S, v), i1, tailinds...) + else + @boundscheck checkbounds(a, i1, tailinds...) + ind_start, sidx = divrem((i1-1)*sizeof(T), sizeof(S)) + t = Ref{T}(v) + s = Ref{S}() + GC.@preserve t s begin + tptr = Ptr{UInt8}(unsafe_convert(Ref{T}, t)) + sptr = Ptr{UInt8}(unsafe_convert(Ref{S}, s)) + nbytes_copied = 0 + i = 1 + # Deal with any partial elements at the start. We'll have to copy in the + # element from the original array and overwrite the relevant parts + if sidx != 0 + s[] = a.parent[ind_start + i, tailinds...] + nb = min(sizeof(S) - sidx, sizeof(T)) + _memcpy!(sptr + sidx, tptr, nb) + nbytes_copied += nb + a.parent[ind_start + i, tailinds...] = s[] + i += 1 + sidx = 0 + end + # Deal with the main body of elements + while nbytes_copied < sizeof(T) && (sizeof(T) - nbytes_copied) > sizeof(S) + nb = min(sizeof(S), sizeof(T) - nbytes_copied) + _memcpy!(sptr, tptr + nbytes_copied, nb) + nbytes_copied += nb + a.parent[ind_start + i, tailinds...] = s[] + i += 1 + end + # Deal with trailing partial elements + if nbytes_copied < sizeof(T) + s[] = a.parent[ind_start + i, tailinds...] + nb = min(sizeof(S), sizeof(T) - nbytes_copied) + _memcpy!(sptr, tptr + nbytes_copied, nb) + a.parent[ind_start + i, tailinds...] = s[] + end + end + end + return a +end + +# Padding +struct Padding + offset::Int + size::Int +end +function intersect(p1::Padding, p2::Padding) + start = max(p1.offset, p2.offset) + stop = min(p1.offset + p1.size, p2.offset + p2.size) + Padding(start, max(0, stop-start)) +end + +struct PaddingError + S::Type + T::Type +end + +function showerror(io::IO, p::PaddingError) + print(io, "Padding of type $(p.S) is not compatible with type $(p.T).") +end + +""" + CyclePadding(padding, total_size) + +Cylces an iterator of `Padding` structs, restarting the padding at `total_size`. +E.g. if `padding` is all the padding in a struct and `total_size` is the total +aligned size of that array, `CyclePadding` will correspond to the padding in an +infinite vector of such structs. +""" +struct CyclePadding{P} + padding::P + total_size::Int +end +eltype(::Type{<:CyclePadding}) = Padding +IteratorSize(::Type{<:CyclePadding}) = IsInfinite() +isempty(cp::CyclePadding) = isempty(cp.padding) +function iterate(cp::CyclePadding) + y = iterate(cp.padding) + y === nothing && return nothing + y[1], (0, y[2]) +end +function iterate(cp::CyclePadding, state::Tuple) + y = iterate(cp.padding, tail(state)...) + y === nothing && return iterate(cp, (state[1]+cp.total_size,)) + Padding(y[1].offset+state[1], y[1].size), (state[1], tail(y)...) +end + +""" + Compute the location of padding in a type. +""" +function padding(T) + padding = Padding[] + last_end::Int = 0 + for i = 1:fieldcount(T) + offset = fieldoffset(T, i) + fT = fieldtype(T, i) + if offset != last_end + push!(padding, Padding(offset, offset-last_end)) + end + last_end = offset + sizeof(fT) + end + padding +end + +function CyclePadding(T::DataType) + a, s = datatype_alignment(T), sizeof(T) + as = s + (a - (s % a)) % a + pad = padding(T) + s != as && push!(pad, Padding(s, as - s)) + CyclePadding(pad, as) +end + +using .Iterators: Stateful +@pure function array_subpadding(S, T) + checked_size = 0 + lcm_size = lcm(sizeof(S), sizeof(T)) + s, t = Stateful{<:Any, Any}(CyclePadding(S)), + Stateful{<:Any, Any}(CyclePadding(T)) + isempty(t) && return true + isempty(s) && return false + while checked_size < lcm_size + # Take padding in T + pad = popfirst!(t) + # See if there's corresponding padding in S + while true + ps = peek(s) + ps.offset > pad.offset && return false + intersect(ps, pad) == pad && break + popfirst!(s) + end + checked_size = pad.offset + pad.size + end + return true +end diff --git a/base/reshapedarray.jl b/base/reshapedarray.jl new file mode 100644 index 0000000..d8b154b --- /dev/null +++ b/base/reshapedarray.jl @@ -0,0 +1,281 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +using Base.MultiplicativeInverses: SignedMultiplicativeInverse + +struct ReshapedArray{T,N,P<:AbstractArray,MI<:Tuple{Vararg{SignedMultiplicativeInverse{Int}}}} <: AbstractArray{T,N} + parent::P + dims::NTuple{N,Int} + mi::MI +end +ReshapedArray(parent::AbstractArray{T}, dims::NTuple{N,Int}, mi) where {T,N} = ReshapedArray{T,N,typeof(parent),typeof(mi)}(parent, dims, mi) + +# IndexLinear ReshapedArray +const ReshapedArrayLF{T,N,P<:AbstractArray} = ReshapedArray{T,N,P,Tuple{}} + +# Fast iteration on ReshapedArrays: use the parent iterator +struct ReshapedArrayIterator{I,M} + iter::I + mi::NTuple{M,SignedMultiplicativeInverse{Int}} +end +ReshapedArrayIterator(A::ReshapedArray) = _rs_iterator(parent(A), A.mi) +function _rs_iterator(P, mi::NTuple{M}) where M + iter = eachindex(P) + ReshapedArrayIterator{typeof(iter),M}(iter, mi) +end + +struct ReshapedIndex{T} + parentindex::T +end + +# eachindex(A::ReshapedArray) = ReshapedArrayIterator(A) # TODO: uncomment this line +@inline function iterate(R::ReshapedArrayIterator, i...) + item, inext = iterate(R.iter, i...) + ReshapedIndex(item), inext +end +length(R::ReshapedArrayIterator) = length(R.iter) +eltype(::Type{<:ReshapedArrayIterator{I}}) where {I} = @isdefined(I) ? ReshapedIndex{eltype(I)} : Any + +## reshape(::Array, ::Dims) returns an Array, except for isbitsunion eltypes (issue #28611) +# reshaping to same # of dimensions +function reshape(a::Array{T,M}, dims::NTuple{N,Int}) where {T,N,M} + throw_dmrsa(dims, len) = + throw(DimensionMismatch("new dimensions $(dims) must be consistent with array size $len")) + + if prod(dims) != length(a) + throw_dmrsa(dims, length(a)) + end + isbitsunion(T) && return ReshapedArray(a, dims, ()) + if N == M && dims == size(a) + return a + end + ccall(:jl_reshape_array, Array{T,N}, (Any, Any, Any), Array{T,N}, a, dims) +end + +""" + reshape(A, dims...) -> AbstractArray + reshape(A, dims) -> AbstractArray + +Return an array with the same data as `A`, but with different +dimension sizes or number of dimensions. The two arrays share the same +underlying data, so that the result is mutable if and only if `A` is +mutable, and setting elements of one alters the values of the other. + +The new dimensions may be specified either as a list of arguments or +as a shape tuple. At most one dimension may be specified with a `:`, +in which case its length is computed such that its product with all +the specified dimensions is equal to the length of the original array +`A`. The total number of elements must not change. + +# Examples +```jldoctest +julia> A = Vector(1:16) +16-element Array{Int64,1}: + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + +julia> reshape(A, (4, 4)) +4×4 Array{Int64,2}: + 1 5 9 13 + 2 6 10 14 + 3 7 11 15 + 4 8 12 16 + +julia> reshape(A, 2, :) +2×8 Array{Int64,2}: + 1 3 5 7 9 11 13 15 + 2 4 6 8 10 12 14 16 + +julia> reshape(1:6, 2, 3) +2×3 reshape(::UnitRange{Int64}, 2, 3) with eltype Int64: + 1 3 5 + 2 4 6 +``` +""" +reshape + +reshape(parent::AbstractArray, dims::IntOrInd...) = reshape(parent, dims) +reshape(parent::AbstractArray, shp::Tuple{Union{Integer,OneTo}, Vararg{Union{Integer,OneTo}}}) = reshape(parent, to_shape(shp)) +reshape(parent::AbstractArray, dims::Dims) = _reshape(parent, dims) + +# Allow missing dimensions with Colon(): +reshape(parent::AbstractVector, ::Colon) = parent +reshape(parent::AbstractArray, dims::Int...) = reshape(parent, dims) +reshape(parent::AbstractArray, dims::Union{Int,Colon}...) = reshape(parent, dims) +reshape(parent::AbstractArray, dims::Tuple{Vararg{Union{Int,Colon}}}) = _reshape(parent, _reshape_uncolon(parent, dims)) +@inline function _reshape_uncolon(A, dims) + @noinline throw1(dims) = throw(DimensionMismatch(string("new dimensions $(dims) ", + "may have at most one omitted dimension specified by `Colon()`"))) + @noinline throw2(A, dims) = throw(DimensionMismatch(string("array size $(length(A)) ", + "must be divisible by the product of the new dimensions $dims"))) + pre = _before_colon(dims...) + post = _after_colon(dims...) + _any_colon(post...) && throw1(dims) + sz, remainder = divrem(length(A), prod(pre)*prod(post)) + remainder == 0 || throw2(A, dims) + (pre..., Int(sz), post...) +end +@inline _any_colon() = false +@inline _any_colon(dim::Colon, tail...) = true +@inline _any_colon(dim::Any, tail...) = _any_colon(tail...) +@inline _before_colon(dim::Any, tail...) = (dim, _before_colon(tail...)...) +@inline _before_colon(dim::Colon, tail...) = () +@inline _after_colon(dim::Any, tail...) = _after_colon(tail...) +@inline _after_colon(dim::Colon, tail...) = tail + +reshape(parent::AbstractArray{T,N}, ndims::Val{N}) where {T,N} = parent +function reshape(parent::AbstractArray, ndims::Val{N}) where N + reshape(parent, rdims(Val(N), axes(parent))) +end + +# Move elements from inds to out until out reaches the desired +# dimensionality N, either filling with OneTo(1) or collapsing the +# product of trailing dims into the last element +rdims_trailing(l, inds...) = length(l) * rdims_trailing(inds...) +rdims_trailing(l) = length(l) +rdims(out::Val{N}, inds::Tuple) where {N} = rdims(ntuple(i -> OneTo(1), Val(N)), inds) +rdims(out::Tuple{}, inds::Tuple{}) = () # N == 0, M == 0 +rdims(out::Tuple{}, inds::Tuple{Any}) = () +rdims(out::Tuple{}, inds::NTuple{M,Any}) where {M} = () +rdims(out::Tuple{Any}, inds::Tuple{}) = out # N == 1, M == 0 +rdims(out::NTuple{N,Any}, inds::Tuple{}) where {N} = out # N > 1, M == 0 +rdims(out::Tuple{Any}, inds::Tuple{Any}) = inds # N == 1, M == 1 +rdims(out::Tuple{Any}, inds::NTuple{M,Any}) where {M} = (OneTo(rdims_trailing(inds...)),) # N == 1, M > 1 +rdims(out::NTuple{N,Any}, inds::NTuple{N,Any}) where {N} = inds # N > 1, M == N +rdims(out::NTuple{N,Any}, inds::NTuple{M,Any}) where {N,M} = (first(inds), rdims(tail(out), tail(inds))...) # N > 1, M > 1, M != N + + +# _reshape on Array returns an Array +_reshape(parent::Vector, dims::Dims{1}) = parent +_reshape(parent::Array, dims::Dims{1}) = reshape(parent, dims) +_reshape(parent::Array, dims::Dims) = reshape(parent, dims) + +# When reshaping Vector->Vector, don't wrap with a ReshapedArray +function _reshape(v::AbstractVector, dims::Dims{1}) + require_one_based_indexing(v) + len = dims[1] + len == length(v) || _throw_dmrs(length(v), "length", len) + v +end +# General reshape +function _reshape(parent::AbstractArray, dims::Dims) + n = length(parent) + prod(dims) == n || _throw_dmrs(n, "size", dims) + __reshape((parent, IndexStyle(parent)), dims) +end + +@noinline function _throw_dmrs(n, str, dims) + throw(DimensionMismatch("parent has $n elements, which is incompatible with $str $dims")) +end + +# Reshaping a ReshapedArray +_reshape(v::ReshapedArray{<:Any,1}, dims::Dims{1}) = _reshape(v.parent, dims) +_reshape(R::ReshapedArray, dims::Dims) = _reshape(R.parent, dims) + +function __reshape(p::Tuple{AbstractArray,IndexCartesian}, dims::Dims) + parent = p[1] + strds = front(size_to_strides(map(length, axes(parent))..., 1)) + strds1 = map(s->max(1,Int(s)), strds) # for resizing empty arrays + mi = map(SignedMultiplicativeInverse, strds1) + ReshapedArray(parent, dims, reverse(mi)) +end + +function __reshape(p::Tuple{AbstractArray{<:Any,0},IndexCartesian}, dims::Dims) + parent = p[1] + ReshapedArray(parent, dims, ()) +end + +function __reshape(p::Tuple{AbstractArray,IndexLinear}, dims::Dims) + parent = p[1] + ReshapedArray(parent, dims, ()) +end + +size(A::ReshapedArray) = A.dims +similar(A::ReshapedArray, eltype::Type, dims::Dims) = similar(parent(A), eltype, dims) +IndexStyle(::Type{<:ReshapedArrayLF}) = IndexLinear() +parent(A::ReshapedArray) = A.parent +parentindices(A::ReshapedArray) = map(OneTo, size(parent(A))) +reinterpret(::Type{T}, A::ReshapedArray, dims::Dims) where {T} = reinterpret(T, parent(A), dims) +elsize(::Type{<:ReshapedArray{<:Any,<:Any,P}}) where {P} = elsize(P) + +unaliascopy(A::ReshapedArray) = typeof(A)(unaliascopy(A.parent), A.dims, A.mi) +dataids(A::ReshapedArray) = dataids(A.parent) + +@inline ind2sub_rs(ax, ::Tuple{}, i::Int) = (i,) +@inline ind2sub_rs(ax, strds, i) = _ind2sub_rs(ax, strds, i - 1) +@inline _ind2sub_rs(ax, ::Tuple{}, ind) = (ind + first(ax[end]),) +@inline function _ind2sub_rs(ax, strds, ind) + d, r = divrem(ind, strds[1]) + (_ind2sub_rs(front(ax), tail(strds), r)..., d + first(ax[end])) +end +offset_if_vec(i::Integer, axs::Tuple{<:AbstractUnitRange}) = i + first(axs[1]) - 1 +offset_if_vec(i::Integer, axs::Tuple) = i + +@inline function getindex(A::ReshapedArrayLF, index::Int) + @boundscheck checkbounds(A, index) + @inbounds ret = parent(A)[index] + ret +end +@inline function getindex(A::ReshapedArray{T,N}, indices::Vararg{Int,N}) where {T,N} + @boundscheck checkbounds(A, indices...) + _unsafe_getindex(A, indices...) +end +@inline function getindex(A::ReshapedArray, index::ReshapedIndex) + @boundscheck checkbounds(parent(A), index.parentindex) + @inbounds ret = parent(A)[index.parentindex] + ret +end + +@inline function _unsafe_getindex(A::ReshapedArray{T,N}, indices::Vararg{Int,N}) where {T,N} + axp = axes(A.parent) + i = offset_if_vec(Base._sub2ind(size(A), indices...), axp) + I = ind2sub_rs(axp, A.mi, i) + _unsafe_getindex_rs(parent(A), I) +end +@inline _unsafe_getindex_rs(A, i::Integer) = (@inbounds ret = A[i]; ret) +@inline _unsafe_getindex_rs(A, I) = (@inbounds ret = A[I...]; ret) + +@inline function setindex!(A::ReshapedArrayLF, val, index::Int) + @boundscheck checkbounds(A, index) + @inbounds parent(A)[index] = val + val +end +@inline function setindex!(A::ReshapedArray{T,N}, val, indices::Vararg{Int,N}) where {T,N} + @boundscheck checkbounds(A, indices...) + _unsafe_setindex!(A, val, indices...) +end +@inline function setindex!(A::ReshapedArray, val, index::ReshapedIndex) + @boundscheck checkbounds(parent(A), index.parentindex) + @inbounds parent(A)[index.parentindex] = val + val +end + +@inline function _unsafe_setindex!(A::ReshapedArray{T,N}, val, indices::Vararg{Int,N}) where {T,N} + axp = axes(A.parent) + i = offset_if_vec(Base._sub2ind(size(A), indices...), axp) + @inbounds parent(A)[ind2sub_rs(axes(A.parent), A.mi, i)...] = val + val +end + +# helpful error message for a common failure case +const ReshapedRange{T,N,A<:AbstractRange} = ReshapedArray{T,N,A,Tuple{}} +setindex!(A::ReshapedRange, val, index::Int) = _rs_setindex!_err() +setindex!(A::ReshapedRange{T,N}, val, indices::Vararg{Int,N}) where {T,N} = _rs_setindex!_err() +setindex!(A::ReshapedRange, val, index::ReshapedIndex) = _rs_setindex!_err() + +@noinline _rs_setindex!_err() = error("indexed assignment fails for a reshaped range; consider calling collect") + +unsafe_convert(::Type{Ptr{T}}, a::ReshapedArray{T}) where {T} = unsafe_convert(Ptr{T}, parent(a)) diff --git a/base/rounding.jl b/base/rounding.jl new file mode 100644 index 0000000..03f2062 --- /dev/null +++ b/base/rounding.jl @@ -0,0 +1,249 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +module Rounding + +let fenv_consts = Vector{Cint}(undef, 9) + ccall(:jl_get_fenv_consts, Cvoid, (Ptr{Cint},), fenv_consts) + global const JL_FE_INEXACT = fenv_consts[1] + global const JL_FE_UNDERFLOW = fenv_consts[2] + global const JL_FE_OVERFLOW = fenv_consts[3] + global const JL_FE_DIVBYZERO = fenv_consts[4] + global const JL_FE_INVALID = fenv_consts[5] + + global const JL_FE_TONEAREST = fenv_consts[6] + global const JL_FE_UPWARD = fenv_consts[7] + global const JL_FE_DOWNWARD = fenv_consts[8] + global const JL_FE_TOWARDZERO = fenv_consts[9] +end + +export + RoundingMode, RoundNearest, RoundToZero, RoundUp, RoundDown, RoundFromZero, + RoundNearestTiesAway, RoundNearestTiesUp, + rounding, setrounding, + get_zero_subnormals, set_zero_subnormals + +## rounding modes ## +""" + RoundingMode + +A type used for controlling the rounding mode of floating point operations (via +[`rounding`](@ref)/[`setrounding`](@ref) functions), or as +optional arguments for rounding to the nearest integer (via the [`round`](@ref) +function). + +Currently supported rounding modes are: + +- [`RoundNearest`](@ref) (default) +- [`RoundNearestTiesAway`](@ref) +- [`RoundNearestTiesUp`](@ref) +- [`RoundToZero`](@ref) +- [`RoundFromZero`](@ref) ([`BigFloat`](@ref) only) +- [`RoundUp`](@ref) +- [`RoundDown`](@ref) +""" +struct RoundingMode{T} end + +""" + RoundNearest + +The default rounding mode. Rounds to the nearest integer, with ties (fractional values of +0.5) being rounded to the nearest even integer. +""" +const RoundNearest = RoundingMode{:Nearest}() + +""" + RoundToZero + +[`round`](@ref) using this rounding mode is an alias for [`trunc`](@ref). +""" +const RoundToZero = RoundingMode{:ToZero}() + +""" + RoundUp + +[`round`](@ref) using this rounding mode is an alias for [`ceil`](@ref). +""" +const RoundUp = RoundingMode{:Up}() + +""" + RoundDown + +[`round`](@ref) using this rounding mode is an alias for [`floor`](@ref). +""" +const RoundDown = RoundingMode{:Down}() + +""" + RoundFromZero + +Rounds away from zero. +This rounding mode may only be used with `T == BigFloat` inputs to [`round`](@ref). + +# Examples +```jldoctest +julia> BigFloat("1.0000000000000001", 5, RoundFromZero) +1.06 +``` +""" +const RoundFromZero = RoundingMode{:FromZero}() # mpfr only + +""" + RoundNearestTiesAway + +Rounds to nearest integer, with ties rounded away from zero (C/C++ +[`round`](@ref) behaviour). +""" +const RoundNearestTiesAway = RoundingMode{:NearestTiesAway}() + +""" + RoundNearestTiesUp + +Rounds to nearest integer, with ties rounded toward positive infinity (Java/JavaScript +[`round`](@ref) behaviour). +""" +const RoundNearestTiesUp = RoundingMode{:NearestTiesUp}() + +to_fenv(::RoundingMode{:Nearest}) = JL_FE_TONEAREST +to_fenv(::RoundingMode{:ToZero}) = JL_FE_TOWARDZERO +to_fenv(::RoundingMode{:Up}) = JL_FE_UPWARD +to_fenv(::RoundingMode{:Down}) = JL_FE_DOWNWARD + +function from_fenv(r::Integer) + if r == JL_FE_TONEAREST + return RoundNearest + elseif r == JL_FE_DOWNWARD + return RoundDown + elseif r == JL_FE_UPWARD + return RoundUp + elseif r == JL_FE_TOWARDZERO + return RoundToZero + else + throw(ArgumentError("invalid rounding mode code: $r")) + end +end + +""" + setrounding(T, mode) + +Set the rounding mode of floating point type `T`, controlling the rounding of basic +arithmetic functions ([`+`](@ref), [`-`](@ref), [`*`](@ref), +[`/`](@ref) and [`sqrt`](@ref)) and type conversion. Other numerical +functions may give incorrect or invalid values when using rounding modes other than the +default [`RoundNearest`](@ref). + +Note that this is currently only supported for `T == BigFloat`. + +!!! warning + + This function is not thread-safe. It will affect code running on all threads, but + its behavior is undefined if called concurrently with computations that use the + setting. +""" +setrounding(T::Type, mode) + +""" + rounding(T) + +Get the current floating point rounding mode for type `T`, controlling the rounding of basic +arithmetic functions ([`+`](@ref), [`-`](@ref), [`*`](@ref), [`/`](@ref) +and [`sqrt`](@ref)) and type conversion. + +See [`RoundingMode`](@ref) for available modes. +""" +:rounding + +setrounding_raw(::Type{<:Union{Float32,Float64}}, i::Integer) = ccall(:fesetround, Int32, (Int32,), i) +rounding_raw(::Type{<:Union{Float32,Float64}}) = ccall(:fegetround, Int32, ()) + +rounding(::Type{T}) where {T<:Union{Float32,Float64}} = from_fenv(rounding_raw(T)) + +""" + setrounding(f::Function, T, mode) + +Change the rounding mode of floating point type `T` for the duration of `f`. It is logically +equivalent to: + + old = rounding(T) + setrounding(T, mode) + f() + setrounding(T, old) + +See [`RoundingMode`](@ref) for available rounding modes. +""" +function setrounding(f::Function, ::Type{T}, rounding::RoundingMode) where T + old_rounding_raw = rounding_raw(T) + setrounding(T,rounding) + try + return f() + finally + setrounding_raw(T,old_rounding_raw) + end +end +function setrounding_raw(f::Function, ::Type{T}, rounding) where T + old_rounding_raw = rounding_raw(T) + setrounding_raw(T,rounding) + try + return f() + finally + setrounding_raw(T,old_rounding_raw) + end +end + + +# Should be equivalent to: +# setrounding(Float64,r) do +# convert(T,x) +# end +# but explicit checks are currently quicker (~20x). +# Assumes conversion is performed by rounding to nearest value. + +# To avoid ambiguous dispatch with methods in mpfr.jl: +(::Type{T})(x::Real, r::RoundingMode) where {T<:AbstractFloat} = _convert_rounding(T,x,r) + +_convert_rounding(::Type{T}, x::Real, r::RoundingMode{:Nearest}) where {T<:AbstractFloat} = convert(T,x) +function _convert_rounding(::Type{T}, x::Real, r::RoundingMode{:Down}) where T<:AbstractFloat + y = convert(T,x) + y > x ? prevfloat(y) : y +end +function _convert_rounding(::Type{T}, x::Real, r::RoundingMode{:Up}) where T<:AbstractFloat + y = convert(T,x) + y < x ? nextfloat(y) : y +end +function _convert_rounding(::Type{T}, x::Real, r::RoundingMode{:ToZero}) where T<:AbstractFloat + y = convert(T,x) + if x > 0.0 + y > x ? prevfloat(y) : y + else + y < x ? nextfloat(y) : y + end +end + +""" + set_zero_subnormals(yes::Bool) -> Bool + +If `yes` is `false`, subsequent floating-point operations follow rules for IEEE arithmetic +on subnormal values ("denormals"). Otherwise, floating-point operations are permitted (but +not required) to convert subnormal inputs or outputs to zero. Returns `true` unless +`yes==true` but the hardware does not support zeroing of subnormal numbers. + +`set_zero_subnormals(true)` can speed up some computations on some hardware. However, it can +break identities such as `(x-y==0) == (x==y)`. + +!!! warning + + This function only affects the current thread. +""" +set_zero_subnormals(yes::Bool) = ccall(:jl_set_zero_subnormals,Int32,(Int8,),yes)==0 + +""" + get_zero_subnormals() -> Bool + +Return `false` if operations on subnormal floating-point values ("denormals") obey rules +for IEEE arithmetic, and `true` if they might be converted to zeros. + +!!! warning + + This function only affects the current thread. +""" +get_zero_subnormals() = ccall(:jl_get_zero_subnormals,Int32,())!=0 + +end #module diff --git a/base/secretbuffer.jl b/base/secretbuffer.jl new file mode 100644 index 0000000..a3abfe2 --- /dev/null +++ b/base/secretbuffer.jl @@ -0,0 +1,188 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" + Base.SecretBuffer() + +An [`IOBuffer`](@ref)-like object where the contents will be securely wiped when garbage collected. + +It is considered best practice to wipe the buffer using `Base.shred!(::SecretBuffer)` as +soon as the secure data are no longer required. When initializing with existing data, the +`SecretBuffer!` method is highly recommended to securely zero the passed argument. Avoid +initializing with and converting to `String`s as they are unable to be securely zeroed. + +# Examples +```jldoctest +julia> s = Base.SecretBuffer() +SecretBuffer("*******") + +julia> write(s, 's', 'e', 'c', 'r', 'e', 't') +6 + +julia> seek(s, 0); Char(read(s, UInt8)) +'s': ASCII/Unicode U+0073 (category Ll: Letter, lowercase) + +julia> Base.shred!(s) +SecretBuffer("*******") + +julia> eof(s) +true +``` +""" +mutable struct SecretBuffer <: IO + data::Vector{UInt8} + size::Int + ptr::Int + + function SecretBuffer(; sizehint=128) + s = new(Vector{UInt8}(undef, sizehint), 0, 1) + finalizer(final_shred!, s) + return s + end +end + +""" + SecretBuffer(str::AbstractString) + +A convenience constructor to initialize a `SecretBuffer` from a non-secret string. + +Strings are bad at keeping secrets because they are unable to be securely +zeroed or destroyed. Therefore, avoid using this constructor with secret data. +Instead of starting with a string, either construct the `SecretBuffer` +incrementally with `SecretBuffer()` and [`write`](@ref), or use a `Vector{UInt8}` with +the `Base.SecretBuffer!(::Vector{UInt8})` constructor. +""" +SecretBuffer(str::AbstractString) = SecretBuffer(String(str)) +function SecretBuffer(str::String) + buf = codeunits(str) + s = SecretBuffer(sizehint=length(buf)) + for c in buf + write(s, c) + end + seek(s, 0) + s +end +convert(::Type{SecretBuffer}, s::AbstractString) = SecretBuffer(String(s)) + +""" + SecretBuffer!(data::Vector{UInt8}) + +Initialize a new `SecretBuffer` from `data`, securely zeroing `data` afterwards. +""" +function SecretBuffer!(d::Vector{UInt8}) + len = length(d) + s = SecretBuffer(sizehint=len) + for i in 1:len + write(s, d[i]) + end + seek(s, 0) + securezero!(d) + s +end + +unsafe_SecretBuffer!(s::Cstring) = unsafe_SecretBuffer!(convert(Ptr{UInt8}, s), ccall(:strlen, Cint, (Cstring,), s)) +function unsafe_SecretBuffer!(p::Ptr{UInt8}, len=1) + s = SecretBuffer(sizehint=len) + for i in 1:len + write(s, unsafe_load(p, i)) + end + seek(s, 0) + unsafe_securezero!(p, len) + s +end + + +show(io::IO, s::SecretBuffer) = print(io, "SecretBuffer(\"*******\")") + +# Unlike other IO objects, equality is computed by value for convenience +==(s1::SecretBuffer, s2::SecretBuffer) = (s1.ptr == s2.ptr) && (s1.size == s2.size) && (UInt8(0) == _bufcmp(s1.data, s2.data, min(s1.size, s2.size))) +# Also attempt a constant time buffer comparison algorithm — the length of the secret might be +# inferred by a timing attack, but not its values. +@noinline function _bufcmp(data1::Vector{UInt8}, data2::Vector{UInt8}, sz::Int) + res = UInt8(0) + for i = 1:sz + res |= xor(data1[i], data2[i]) + end + return res +end +# All SecretBuffers hash the same to avoid leaking information or breaking consistency with == +const _sb_hash = UInt === UInt32 ? 0x111c0925 : 0xb06061e370557428 +hash(s::SecretBuffer, h::UInt) = hash(_sb_hash, h) + + +function write(io::SecretBuffer, b::UInt8) + if io.ptr > length(io.data) + # We need to resize! the array: do this manually to ensure no copies are left behind + newdata = Vector{UInt8}(undef, (io.size+16)*2) + copyto!(newdata, io.data) + securezero!(io.data) + io.data = newdata + end + io.size == io.ptr-1 && (io.size += 1) + io.data[io.ptr] = b + io.ptr += 1 + return 1 +end + +function write(io::IO, s::SecretBuffer) + nb = 0 + for i in 1:s.size + nb += write(io, s.data[i]) + end + return nb +end + +cconvert(::Type{Cstring}, s::SecretBuffer) = unsafe_convert(Cstring, s) +function unsafe_convert(::Type{Cstring}, s::SecretBuffer) + # Ensure that no nuls appear in the valid region + if any(==(0x00), s.data[i] for i in 1:s.size) + throw(ArgumentError("`SecretBuffers` containing nul bytes cannot be converted to C strings")) + end + # Add a hidden nul byte just past the end of the valid region + p = s.ptr + s.ptr = s.size + 1 + write(s, '\0') + s.ptr = p + s.size -= 1 + return Cstring(unsafe_convert(Ptr{Cchar}, s.data)) +end + +seek(io::SecretBuffer, n::Integer) = (io.ptr = max(min(n+1, io.size+1), 1); io) +seekend(io::SecretBuffer) = seek(io, io.size+1) +skip(io::SecretBuffer, n::Integer) = seek(io, position(io) + n) + +bytesavailable(io::SecretBuffer) = io.size - io.ptr + 1 +position(io::SecretBuffer) = io.ptr-1 +eof(io::SecretBuffer) = io.ptr > io.size +isempty(io::SecretBuffer) = io.size == 0 +function peek(io::SecretBuffer) + eof(io) && throw(EOFError()) + return io.data[io.ptr] +end +function read(io::SecretBuffer, ::Type{UInt8}) + eof(io) && throw(EOFError()) + byte = io.data[io.ptr] + io.ptr += 1 + return byte +end + +function final_shred!(s::SecretBuffer) + !isshredded(s) && @warn("a SecretBuffer was `shred!`ed by the GC; use `shred!` manually after use to minimize exposure.") + shred!(s) +end + +function shred!(s::SecretBuffer) + securezero!(s.data) + s.ptr = 1 + s.size = 0 + return s +end + +isshredded(s::SecretBuffer) = all(iszero, s.data) + +function shred!(f::Function, x) + try + f(x) + finally + shred!(x) + end +end diff --git a/base/set.jl b/base/set.jl new file mode 100644 index 0000000..d4f91ee --- /dev/null +++ b/base/set.jl @@ -0,0 +1,641 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +struct Set{T} <: AbstractSet{T} + dict::Dict{T,Nothing} + + Set{T}() where {T} = new(Dict{T,Nothing}()) + Set{T}(s::Set{T}) where {T} = new(Dict{T,Nothing}(s.dict)) +end + +Set{T}(itr) where {T} = union!(Set{T}(), itr) +Set() = Set{Any}() + + +""" + Set([itr]) + +Construct a [`Set`](@ref) of the values generated by the given iterable object, or an +empty set. Should be used instead of [`BitSet`](@ref) for sparse integer sets, or +for sets of arbitrary objects. +""" +Set(itr) = _Set(itr, IteratorEltype(itr)) + +_Set(itr, ::HasEltype) = Set{eltype(itr)}(itr) + +function _Set(itr, ::EltypeUnknown) + T = @default_eltype(itr) + (isconcretetype(T) || T === Union{}) || return grow_to!(Set{T}(), itr) + return Set{T}(itr) +end + +empty(s::AbstractSet{T}, ::Type{U}=T) where {T,U} = Set{U}() + +# return an empty set with eltype T, which is mutable (can be grown) +# by default, a Set is returned +emptymutable(s::AbstractSet{T}, ::Type{U}=T) where {T,U} = Set{U}() + +_similar_for(c::AbstractSet, ::Type{T}, itr, isz) where {T} = empty(c, T) + +function show(io::IO, s::Set) + print(io, "Set(") + show_vector(io, s) + print(io, ')') +end + +isempty(s::Set) = isempty(s.dict) +length(s::Set) = length(s.dict) +in(x, s::Set) = haskey(s.dict, x) +push!(s::Set, x) = (s.dict[x] = nothing; s) +pop!(s::Set, x) = (pop!(s.dict, x); x) +pop!(s::Set, x, default) = (x in s ? pop!(s, x) : default) + +function pop!(s::Set) + isempty(s) && throw(ArgumentError("set must be non-empty")) + return pop!(s.dict)[1] +end + +delete!(s::Set, x) = (delete!(s.dict, x); s) + +copy(s::Set) = copymutable(s) + +# Set is the default mutable fall-back +copymutable(s::AbstractSet{T}) where {T} = Set{T}(s) + +sizehint!(s::Set, newsz) = (sizehint!(s.dict, newsz); s) +empty!(s::Set) = (empty!(s.dict); s) +rehash!(s::Set) = (rehash!(s.dict); s) + +iterate(s::Set, i...) = iterate(KeySet(s.dict), i...) + +# In case the size(s) is smaller than size(t) its more efficient to iterate through +# elements of s instead and only delete the ones also contained in t. +# The threshold for this decision boils down to a tradeoff between +# size(s) * cost(in() + delete!()) ≶ size(t) * cost(delete!()) +# Empirical observations on Ints point towards a threshold of 0.8. +# To be on the safe side (e.g. cost(in) >>> cost(delete!) ) a +# conservative threshold of 0.5 was chosen. +function setdiff!(s::Set, t::Set) + if 2 * length(s) < length(t) + for x in s + x in t && delete!(s, x) + end + else + for x in t + delete!(s, x) + end + end + return s +end + +""" + unique(itr) + +Return an array containing only the unique elements of collection `itr`, +as determined by [`isequal`](@ref), in the order that the first of each +set of equivalent elements originally appears. The element type of the +input is preserved. + +# Examples +```jldoctest +julia> unique([1, 2, 6, 2]) +3-element Array{Int64,1}: + 1 + 2 + 6 + +julia> unique(Real[1, 1.0, 2]) +2-element Array{Real,1}: + 1 + 2 +``` +""" +function unique(itr) + T = @default_eltype(itr) + out = Vector{T}() + seen = Set{T}() + y = iterate(itr) + y === nothing && return out + x, i = y + if !isconcretetype(T) && IteratorEltype(itr) == EltypeUnknown() + S = typeof(x) + return _unique_from(itr, S[x], Set{S}((x,)), i) + end + push!(seen, x) + push!(out, x) + return unique_from(itr, out, seen, i) +end + +_unique_from(itr, out, seen, i) = unique_from(itr, out, seen, i) +@inline function unique_from(itr, out::Vector{T}, seen, i) where T + while true + y = iterate(itr, i) + y === nothing && break + x, i = y + S = typeof(x) + if !(S === T || S <: T) + R = promote_typejoin(S, T) + seenR = convert(Set{R}, seen) + outR = convert(Vector{R}, out) + if !in(x, seenR) + push!(seenR, x) + push!(outR, x) + end + return _unique_from(itr, outR, seenR, i) + end + if !in(x, seen) + push!(seen, x) + push!(out, x) + end + end + return out +end + +""" + unique(f, itr) + +Returns an array containing one value from `itr` for each unique value produced by `f` +applied to elements of `itr`. + +# Examples +```jldoctest +julia> unique(x -> x^2, [1, -1, 3, -3, 4]) +3-element Array{Int64,1}: + 1 + 3 + 4 +``` +""" +function unique(f, C) + out = Vector{eltype(C)}() + + s = iterate(C) + if s === nothing + return out + end + (x, i) = s + y = f(x) + seen = Set{typeof(y)}() + push!(seen, y) + push!(out, x) + + return _unique!(f, out, C, seen, i) +end + +function _unique!(f, out::AbstractVector, C, seen::Set, i) + s = iterate(C, i) + while s !== nothing + (x, i) = s + y = f(x) + if y ∉ seen + push!(out, x) + if y isa eltype(seen) + push!(seen, y) + else + seen2 = convert(Set{promote_typejoin(eltype(seen), typeof(y))}, seen) + push!(seen2, y) + return _unique!(f, out, C, seen2, i) + end + end + s = iterate(C, i) + end + + return out +end + +""" + unique!(f, A::AbstractVector) + +Selects one value from `A` for each unique value produced by `f` applied to +elements of `A` , then return the modified A. + +!!! compat "Julia 1.1" + This method is available as of Julia 1.1. + +# Examples +```jldoctest +julia> unique!(x -> x^2, [1, -1, 3, -3, 4]) +3-element Array{Int64,1}: + 1 + 3 + 4 + +julia> unique!(n -> n%3, [5, 1, 8, 9, 3, 4, 10, 7, 2, 6]) +3-element Array{Int64,1}: + 5 + 1 + 9 + +julia> unique!(iseven, [2, 3, 5, 7, 9]) +2-element Array{Int64,1}: + 2 + 3 +``` +""" +function unique!(f, A::AbstractVector) + if length(A) <= 1 + return A + end + + i = firstindex(A) + x = @inbounds A[i] + y = f(x) + seen = Set{typeof(y)}() + push!(seen, y) + return _unique!(f, A, seen, i, i+1) +end + +function _unique!(f, A::AbstractVector, seen::Set, current::Integer, i::Integer) + while i <= lastindex(A) + x = @inbounds A[i] + y = f(x) + if y ∉ seen + current += 1 + @inbounds A[current] = x + if y isa eltype(seen) + push!(seen, y) + else + seen2 = convert(Set{promote_typejoin(eltype(seen), typeof(y))}, seen) + push!(seen2, y) + return _unique!(f, A, seen2, current, i+1) + end + end + i += 1 + end + return resize!(A, current - firstindex(A) + 1) +end + + +# If A is not grouped, then we will need to keep track of all of the elements that we have +# seen so far. +_unique!(A::AbstractVector) = unique!(identity, A::AbstractVector) + +# If A is grouped, so that each unique element is in a contiguous group, then we only +# need to keep track of one element at a time. We replace the elements of A with the +# unique elements that we see in the order that we see them. Once we have iterated +# through A, we resize A based on the number of unique elements that we see. +function _groupedunique!(A::AbstractVector) + isempty(A) && return A + idxs = eachindex(A) + y = first(A) + # We always keep the first element + it = iterate(idxs, iterate(idxs)[2]) + count = 1 + for x in Iterators.drop(A, 1) + if !isequal(x, y) + y = A[it[1]] = x + count += 1 + it = iterate(idxs, it[2]) + end + end + resize!(A, count) +end + +""" + unique!(A::AbstractVector) + +Remove duplicate items as determined by [`isequal`](@ref), then return the modified `A`. +`unique!` will return the elements of `A` in the order that they occur. If you do not care +about the order of the returned data, then calling `(sort!(A); unique!(A))` will be much +more efficient as long as the elements of `A` can be sorted. + +# Examples +```jldoctest +julia> unique!([1, 1, 1]) +1-element Array{Int64,1}: + 1 + +julia> A = [7, 3, 2, 3, 7, 5]; + +julia> unique!(A) +4-element Array{Int64,1}: + 7 + 3 + 2 + 5 + +julia> B = [7, 6, 42, 6, 7, 42]; + +julia> sort!(B); # unique! is able to process sorted data much more efficiently. + +julia> unique!(B) +3-element Array{Int64,1}: + 6 + 7 + 42 +``` +""" +function unique!(A::Union{AbstractVector{<:Real}, AbstractVector{<:AbstractString}, + AbstractVector{<:Symbol}}) + if isempty(A) + return A + elseif issorted(A) || issorted(A, rev=true) + return _groupedunique!(A) + else + return _unique!(A) + end +end +# issorted fails for some element types, so the method above has to be restricted to +# elements with isless/< defined. +function unique!(A) + if isempty(A) + return A + else + return _unique!(A) + end +end + +""" + allunique(itr) -> Bool + +Return `true` if all values from `itr` are distinct when compared with [`isequal`](@ref). + +# Examples +```jldoctest +julia> a = [1; 2; 3] +3-element Array{Int64,1}: + 1 + 2 + 3 + +julia> allunique([a, a]) +false +``` +""" +function allunique(C) + seen = Set{eltype(C)}() + for x in C + if in(x, seen) + return false + else + push!(seen, x) + end + end + true +end + +allunique(::Set) = true + +allunique(r::AbstractRange{T}) where {T} = (step(r) != zero(T)) || (length(r) <= 1) +allunique(r::StepRange{T,S}) where {T,S} = (step(r) != zero(S)) || (length(r) <= 1) + +filter!(f, s::Set) = unsafe_filter!(f, s) + +const hashs_seed = UInt === UInt64 ? 0x852ada37cfe8e0ce : 0xcfe8e0ce +function hash(s::AbstractSet, h::UInt) + hv = hashs_seed + for x in s + hv ⊻= hash(x) + end + hash(hv, h) +end + +convert(::Type{T}, s::T) where {T<:AbstractSet} = s +convert(::Type{T}, s::AbstractSet) where {T<:AbstractSet} = T(s) + + +## replace/replace! ## + +function check_count(count::Integer) + count < 0 && throw(DomainError(count, "`count` must not be negative (got $count)")) + return min(count, typemax(Int)) % Int +end + +# TODO: use copy!, which is currently unavailable from here since it is defined in Future +_copy_oftype(x, ::Type{T}) where {T} = copyto!(similar(x, T), x) +# TODO: use similar() once deprecation is removed and it preserves keys +_copy_oftype(x::AbstractDict, ::Type{T}) where {T} = merge!(empty(x, T), x) +_copy_oftype(x::AbstractSet, ::Type{T}) where {T} = union!(empty(x, T), x) + +_copy_oftype(x::AbstractArray{T}, ::Type{T}) where {T} = copy(x) +_copy_oftype(x::AbstractDict{K,V}, ::Type{Pair{K,V}}) where {K,V} = copy(x) +_copy_oftype(x::AbstractSet{T}, ::Type{T}) where {T} = copy(x) + +_similar_or_copy(x::Any) = similar(x) +_similar_or_copy(x::Any, ::Type{T}) where {T} = similar(x, T) +# Make a copy on construction since it is faster than inserting elements separately +_similar_or_copy(x::Union{AbstractDict,AbstractSet}) = copy(x) +_similar_or_copy(x::Union{AbstractDict,AbstractSet}, ::Type{T}) where {T} = _copy_oftype(x, T) + +# to make replace/replace! work for a new container type Cont, only +# _replace!(new::Callable, res::Cont, A::Cont, count::Int) +# has to be implemented + +""" + replace!(A, old_new::Pair...; [count::Integer]) + +For each pair `old=>new` in `old_new`, replace all occurrences +of `old` in collection `A` by `new`. +Equality is determined using [`isequal`](@ref). +If `count` is specified, then replace at most `count` occurrences in total. +See also [`replace`](@ref replace(A, old_new::Pair...)). + +# Examples +```jldoctest +julia> replace!([1, 2, 1, 3], 1=>0, 2=>4, count=2) +4-element Array{Int64,1}: + 0 + 4 + 1 + 3 + +julia> replace!(Set([1, 2, 3]), 1=>0) +Set([0, 2, 3]) +``` +""" +replace!(A, old_new::Pair...; count::Integer=typemax(Int)) = + replace_pairs!(A, A, check_count(count), old_new) + +function replace_pairs!(res, A, count::Int, old_new::Tuple{Vararg{Pair}}) + @inline function new(x) + for o_n in old_new + isequal(first(o_n), x) && return last(o_n) + end + return x # no replace + end + _replace!(new, res, A, count) +end + +""" + replace!(new::Function, A; [count::Integer]) + +Replace each element `x` in collection `A` by `new(x)`. +If `count` is specified, then replace at most `count` values in total +(replacements being defined as `new(x) !== x`). + +# Examples +```jldoctest +julia> replace!(x -> isodd(x) ? 2x : x, [1, 2, 3, 4]) +4-element Array{Int64,1}: + 2 + 2 + 6 + 4 + +julia> replace!(Dict(1=>2, 3=>4)) do kv + first(kv) < 3 ? first(kv)=>3 : kv + end +Dict{Int64,Int64} with 2 entries: + 3 => 4 + 1 => 3 + +julia> replace!(x->2x, Set([3, 6])) +Set([6, 12]) +``` +""" +replace!(new::Callable, A; count::Integer=typemax(Int)) = + _replace!(new, A, A, check_count(count)) + +""" + replace(A, old_new::Pair...; [count::Integer]) + +Return a copy of collection `A` where, for each pair `old=>new` in `old_new`, +all occurrences of `old` are replaced by `new`. +Equality is determined using [`isequal`](@ref). +If `count` is specified, then replace at most `count` occurrences in total. + +The element type of the result is chosen using promotion (see [`promote_type`](@ref)) +based on the element type of `A` and on the types of the `new` values in pairs. +If `count` is omitted and the element type of `A` is a `Union`, the element type +of the result will not include singleton types which are replaced with values of +a different type: for example, `Union{T,Missing}` will become `T` if `missing` is +replaced. + +See also [`replace!`](@ref). + +# Examples +```jldoctest +julia> replace([1, 2, 1, 3], 1=>0, 2=>4, count=2) +4-element Array{Int64,1}: + 0 + 4 + 1 + 3 + +julia> replace([1, missing], missing=>0) +2-element Array{Int64,1}: + 1 + 0 +``` +""" +function replace(A, old_new::Pair...; count::Union{Integer,Nothing}=nothing) + V = promote_valuetype(old_new...) + if count isa Nothing + T = promote_type(subtract_singletontype(eltype(A), old_new...), V) + replace_pairs!(_similar_or_copy(A, T), A, typemax(Int), old_new) + else + U = promote_type(eltype(A), V) + replace_pairs!(_similar_or_copy(A, U), A, check_count(count), old_new) + end +end + +promote_valuetype(x::Pair{K, V}) where {K, V} = V +promote_valuetype(x::Pair{K, V}, y::Pair...) where {K, V} = + promote_type(V, promote_valuetype(y...)) + +# Subtract singleton types which are going to be replaced +function subtract_singletontype(::Type{T}, x::Pair{K}) where {T, K} + if issingletontype(K) + Core.Compiler.typesubtract(T, K) + else + T + end +end +subtract_singletontype(::Type{T}, x::Pair{K}, y::Pair...) where {T, K} = + subtract_singletontype(subtract_singletontype(T, y...), x) + +""" + replace(new::Function, A; [count::Integer]) + +Return a copy of `A` where each value `x` in `A` is replaced by `new(x)` +If `count` is specified, then replace at most `count` values in total +(replacements being defined as `new(x) !== x`). + +# Examples +```jldoctest +julia> replace(x -> isodd(x) ? 2x : x, [1, 2, 3, 4]) +4-element Array{Int64,1}: + 2 + 2 + 6 + 4 + +julia> replace(Dict(1=>2, 3=>4)) do kv + first(kv) < 3 ? first(kv)=>3 : kv + end +Dict{Int64,Int64} with 2 entries: + 3 => 4 + 1 => 3 +``` +""" +replace(new::Callable, A; count::Integer=typemax(Int)) = + _replace!(new, _similar_or_copy(A), A, check_count(count)) + +# Handle ambiguities +replace!(a::Callable, b::Pair; count::Integer=-1) = throw(MethodError(replace!, (a, b))) +replace!(a::Callable, b::Pair, c::Pair; count::Integer=-1) = throw(MethodError(replace!, (a, b, c))) +replace(a::Callable, b::Pair; count::Integer=-1) = throw(MethodError(replace, (a, b))) +replace(a::Callable, b::Pair, c::Pair; count::Integer=-1) = throw(MethodError(replace, (a, b, c))) +replace(a::AbstractString, b::Pair, c::Pair) = throw(MethodError(replace, (a, b, c))) + +### replace! for AbstractDict/AbstractSet + +askey(k, ::AbstractDict) = k.first +askey(k, ::AbstractSet) = k + +function _replace!(new::Callable, res::T, A::T, + count::Int) where T<:Union{AbstractDict,AbstractSet} + c = 0 + if res === A # cannot replace elements while iterating over A + repl = Pair{eltype(A),eltype(A)}[] + for x in A + y = new(x) + if x !== y + push!(repl, x => y) + c += 1 + end + c == count && break + end + for oldnew in repl + pop!(res, askey(first(oldnew), res)) + end + for oldnew in repl + push!(res, last(oldnew)) + end + else + for x in A + y = new(x) + if x !== y + pop!(res, askey(x, res)) + push!(res, y) + c += 1 + end + c == count && break + end + end + res +end + +### replace! for AbstractArray + +function _replace!(new::Callable, res::AbstractArray, A::AbstractArray, count::Int) + c = 0 + if count >= length(A) # simpler loop allows for SIMD + for i in eachindex(A) + @inbounds Ai = A[i] + y = new(Ai) + @inbounds res[i] = y + end + else + for i in eachindex(A) + @inbounds Ai = A[i] + if c < count + y = new(Ai) + @inbounds res[i] = y + c += (Ai !== y) + else + @inbounds res[i] = Ai + end + end + end + res +end diff --git a/base/shell.jl b/base/shell.jl new file mode 100644 index 0000000..099aefb --- /dev/null +++ b/base/shell.jl @@ -0,0 +1,255 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## shell-like command parsing ## + +const shell_special = "#{}()[]<>|&*?~;" + +# strips the end but respects the space when the string ends with "\\ " +function rstrip_shell(s::AbstractString) + c_old = nothing + for (i, c) in Iterators.reverse(pairs(s)) + ((c == '\\') && c_old == ' ') && return SubString(s, 1, i+1) + isspace(c) || return SubString(s, 1, i) + c_old = c + end + SubString(s, 1, 0) +end + + +# needs to be factored out so depwarn only warns once +# when removed, also need to update shell_escape for a Cmd to pass shell_special +# and may want to use it in the test for #10120 (currently the implementation is essentially copied there) +@noinline warn_shell_special(str,special) = + depwarn("Parsing command \"$str\". Special characters \"$special\" should now be quoted in commands", :warn_shell_special) + +function shell_parse(str::AbstractString, interpolate::Bool=true; + special::AbstractString="") + s::SubString = SubString(str, firstindex(str)) + s = rstrip_shell(lstrip(s)) + + # N.B.: This is used by REPLCompletions + last_parse = 0:-1 + isempty(s) && return interpolate ? (Expr(:tuple,:()),last_parse) : ([],last_parse) + + in_single_quotes = false + in_double_quotes = false + + args::Vector{Any} = [] + arg::Vector{Any} = [] + i = firstindex(s) + st = Iterators.Stateful(pairs(s)) + + function update_arg(x) + if !isa(x,AbstractString) || !isempty(x) + push!(arg, x) + end + end + function consume_upto(j) + update_arg(s[i:prevind(s, j)]) + i = something(peek(st), (lastindex(s)+1,'\0'))[1] + end + function append_arg() + if isempty(arg); arg = Any["",]; end + push!(args, arg) + arg = [] + end + + for (j, c) in st + if !in_single_quotes && !in_double_quotes && isspace(c) + consume_upto(j) + append_arg() + while !isempty(st) + # We've made sure above that we don't end in whitespace, + # so updating `i` here is ok + (i, c) = peek(st) + isspace(c) || break + popfirst!(st) + end + elseif interpolate && !in_single_quotes && c == '$' + consume_upto(j) + isempty(st) && error("\$ right before end of command") + stpos, c = popfirst!(st) + isspace(c) && error("space not allowed right after \$") + if startswith(SubString(s, stpos), "var\"") + # Disallow var"#" syntax in cmd interpolations. + # TODO: Allow only identifiers after the $ for consistency with + # string interpolation syntax (see #3150) + ex, j = :var, stpos+3 + else + ex, j = Meta.parse(s,stpos,greedy=false) + end + last_parse = (stpos:prevind(s, j)) .+ s.offset + update_arg(ex); + s = SubString(s, j) + Iterators.reset!(st, pairs(s)) + i = firstindex(s) + else + if !in_double_quotes && c == '\'' + in_single_quotes = !in_single_quotes + consume_upto(j) + elseif !in_single_quotes && c == '"' + in_double_quotes = !in_double_quotes + consume_upto(j) + elseif c == '\\' + if in_double_quotes + isempty(st) && error("unterminated double quote") + k, c′ = peek(st) + if c′ == '"' || c′ == '$' || c′ == '\\' + consume_upto(j) + _ = popfirst!(st) + end + elseif !in_single_quotes + isempty(st) && error("dangling backslash") + consume_upto(j) + _ = popfirst!(st) + end + elseif !in_single_quotes && !in_double_quotes && c in special + warn_shell_special(str,special) # noinline depwarn + end + end + end + + if in_single_quotes; error("unterminated single quote"); end + if in_double_quotes; error("unterminated double quote"); end + + update_arg(s[i:end]) + append_arg() + + interpolate || return args, last_parse + + # construct an expression + ex = Expr(:tuple) + for arg in args + push!(ex.args, Expr(:tuple, arg...)) + end + return ex, last_parse +end + +function shell_split(s::AbstractString) + parsed = shell_parse(s, false)[1] + args = String[] + for arg in parsed + push!(args, string(arg...)) + end + args +end + +function print_shell_word(io::IO, word::AbstractString, special::AbstractString = "") + has_single = false + has_special = false + for c in word + if isspace(c) || c=='\\' || c=='\'' || c=='"' || c=='$' || c in special + has_special = true + if c == '\'' + has_single = true + end + end + end + if isempty(word) + print(io, "''") + elseif !has_special + print(io, word) + elseif !has_single + print(io, '\'', word, '\'') + else + print(io, '"') + for c in word + if c == '"' || c == '$' + print(io, '\\') + end + print(io, c) + end + print(io, '"') + end + nothing +end + +function print_shell_escaped(io::IO, cmd::AbstractString, args::AbstractString...; + special::AbstractString="") + print_shell_word(io, cmd, special) + for arg in args + print(io, ' ') + print_shell_word(io, arg, special) + end +end +print_shell_escaped(io::IO; special::String="") = nothing + +""" + shell_escape(args::Union{Cmd,AbstractString...}; special::AbstractString="") + +The unexported `shell_escape` function is the inverse of the unexported `shell_split` function: +it takes a string or command object and escapes any special characters in such a way that calling +`shell_split` on it would give back the array of words in the original command. The `special` +keyword argument controls what characters in addition to whitespace, backslashes, quotes and +dollar signs are considered to be special (default: none). + +# Examples +```jldoctest +julia> Base.shell_escape("cat", "/foo/bar baz", "&&", "echo", "done") +"cat '/foo/bar baz' && echo done" + +julia> Base.shell_escape("echo", "this", "&&", "that") +"echo this && that" +``` +""" +shell_escape(args::AbstractString...; special::AbstractString="") = + sprint((io, args...) -> print_shell_escaped(io, args..., special=special), args...) + + +function print_shell_escaped_posixly(io::IO, args::AbstractString...) + first = true + for arg in args + first || print(io, ' ') + # avoid printing quotes around simple enough strings + # that any (reasonable) shell will definitely never consider them to be special + have_single = false + have_double = false + function isword(c::AbstractChar) + if '0' <= c <= '9' || 'a' <= c <= 'z' || 'A' <= c <= 'Z' + # word characters + elseif c == '_' || c == '/' || c == '+' || c == '-' + # other common characters + elseif c == '\'' + have_single = true + elseif c == '"' + have_double && return false # switch to single quoting + have_double = true + elseif !first && c == '=' + # equals is special if it is first (e.g. `env=val ./cmd`) + else + # anything else + return false + end + return true + end + if isempty(arg) + print(io, "''") + elseif all(isword, arg) + have_single && (arg = replace(arg, '\'' => "\\'")) + have_double && (arg = replace(arg, '"' => "\\\"")) + print(io, arg) + else + print(io, '\'', replace(arg, '\'' => "'\\''"), '\'') + end + first = false + end +end + +""" + shell_escape_posixly(args::Union{Cmd,AbstractString...}) + +The unexported `shell_escape_posixly` function +takes a string or command object and escapes any special characters in such a way that +it is safe to pass it as an argument to a posix shell. + +# Examples +```jldoctest +julia> Base.shell_escape_posixly("cat", "/foo/bar baz", "&&", "echo", "done") +"cat '/foo/bar baz' '&&' echo done" + +julia> Base.shell_escape_posixly("echo", "this", "&&", "that") +"echo this '&&' that" +``` +""" +shell_escape_posixly(args::AbstractString...) = + sprint(print_shell_escaped_posixly, args...) diff --git a/base/show.jl b/base/show.jl new file mode 100644 index 0000000..6dc55f0 --- /dev/null +++ b/base/show.jl @@ -0,0 +1,2012 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +show(io::IO, ::UndefInitializer) = print(io, "array initializer with undefined values") + +# first a few multiline show functions for types defined before the MIME type: + +show(io::IO, ::MIME"text/plain", r::AbstractRange) = show(io, r) # always use the compact form for printing ranges + +function show(io::IO, ::MIME"text/plain", r::LinRange) + # show for LinRange, e.g. + # range(1, stop=3, length=7) + # 7-element LinRange{Float64}: + # 1.0,1.33333,1.66667,2.0,2.33333,2.66667,3.0 + summary(io, r) + if !isempty(r) + println(io, ":") + print_range(io, r) + end +end + +function show(io::IO, ::MIME"text/plain", f::Function) + ft = typeof(f) + mt = ft.name.mt + if isa(f, Core.IntrinsicFunction) + show(io, f) + id = Core.Intrinsics.bitcast(Int32, f) + print(io, " (intrinsic function #$id)") + elseif isa(f, Core.Builtin) + print(io, mt.name, " (built-in function)") + else + name = mt.name + isself = isdefined(ft.name.module, name) && + ft == typeof(getfield(ft.name.module, name)) + n = length(methods(f)) + m = n==1 ? "method" : "methods" + sname = string(name) + ns = (isself || '#' in sname) ? sname : string("(::", ft, ")") + what = startswith(ns, '@') ? "macro" : "generic function" + print(io, ns, " (", what, " with $n $m)") + end +end + +function show(io::IO, ::MIME"text/plain", iter::Union{KeySet,ValueIterator}) + summary(io, iter) + isempty(iter) && return + print(io, ". ", isa(iter,KeySet) ? "Keys" : "Values", ":") + limit::Bool = get(io, :limit, false) + if limit + sz = displaysize(io) + rows, cols = sz[1] - 3, sz[2] + rows < 2 && (print(io, " …"); return) + cols < 4 && (cols = 4) + cols -= 2 # For prefix " " + rows -= 1 # For summary + else + rows = cols = typemax(Int) + end + + for (i, v) in enumerate(iter) + print(io, "\n ") + i == rows < length(iter) && (print(io, "⋮"); break) + + if limit + str = sprint(show, v, context=io, sizehint=0) + str = _truncate_at_width_or_chars(str, cols, "\r\n") + print(io, str) + else + show(io, v) + end + end +end + +function show(io::IO, ::MIME"text/plain", t::AbstractDict{K,V}) where {K,V} + # show more descriptively, with one line per key/value pair + recur_io = IOContext(io, :SHOWN_SET => t) + limit::Bool = get(io, :limit, false) + if !haskey(io, :compact) + recur_io = IOContext(recur_io, :compact => true) + end + + summary(io, t) + isempty(t) && return + print(io, ":") + show_circular(io, t) && return + if limit + sz = displaysize(io) + rows, cols = sz[1] - 3, sz[2] + rows < 2 && (print(io, " …"); return) + cols < 12 && (cols = 12) # Minimum widths of 2 for key, 4 for value + cols -= 6 # Subtract the widths of prefix " " separator " => " + rows -= 1 # Subtract the summary + + # determine max key width to align the output, caching the strings + ks = Vector{String}(undef, min(rows, length(t))) + vs = Vector{String}(undef, min(rows, length(t))) + keylen = 0 + vallen = 0 + for (i, (k, v)) in enumerate(t) + i > rows && break + ks[i] = sprint(show, k, context=recur_io, sizehint=0) + vs[i] = sprint(show, v, context=recur_io, sizehint=0) + keylen = clamp(length(ks[i]), keylen, cols) + vallen = clamp(length(vs[i]), vallen, cols) + end + if keylen > max(div(cols, 2), cols - vallen) + keylen = max(cld(cols, 3), cols - vallen) + end + else + rows = cols = typemax(Int) + end + + for (i, (k, v)) in enumerate(t) + print(io, "\n ") + i == rows < length(t) && (print(io, rpad("⋮", keylen), " => ⋮"); break) + + if limit + key = rpad(_truncate_at_width_or_chars(ks[i], keylen, "\r\n"), keylen) + else + key = sprint(show, k, context=recur_io, sizehint=0) + end + print(recur_io, key) + print(io, " => ") + + if limit + val = _truncate_at_width_or_chars(vs[i], cols - keylen, "\r\n") + print(io, val) + else + show(recur_io, v) + end + end +end + +function show(io::IO, ::MIME"text/plain", opt::JLOptions) + println(io, "JLOptions(") + fields = fieldnames(JLOptions) + nfields = length(fields) + for (i, f) in enumerate(fields) + v = getfield(opt, i) + if isa(v, Ptr{UInt8}) + v = (v != C_NULL) ? unsafe_string(v) : "" + elseif isa(v, Ptr{Ptr{UInt8}}) + v = unsafe_load_commands(v) + end + println(io, " ", f, " = ", repr(v), i < nfields ? "," : "") + end + print(io, ")") +end + +function show(io::IO, ::MIME"text/plain", t::Task) + show(io, t) + if t.state === :failed + println(io) + showerror(io, CapturedException(t.result, t.backtrace)) + end +end + + +print(io::IO, s::Symbol) = (write(io,s); nothing) + +""" + IOContext + +`IOContext` provides a mechanism for passing output configuration settings among [`show`](@ref) methods. + +In short, it is an immutable dictionary that is a subclass of `IO`. It supports standard +dictionary operations such as [`getindex`](@ref), and can also be used as an I/O stream. +""" +struct IOContext{IO_t <: IO} <: AbstractPipe + io::IO_t + dict::ImmutableDict{Symbol, Any} + + function IOContext{IO_t}(io::IO_t, dict::ImmutableDict{Symbol, Any}) where IO_t<:IO + @assert !(IO_t <: IOContext) "Cannot create `IOContext` from another `IOContext`." + return new(io, dict) + end +end + +# (Note that TTY and TTYTerminal io types have a :color property.) +unwrapcontext(io::IO) = io, get(io,:color,false) ? ImmutableDict{Symbol,Any}(:color, true) : ImmutableDict{Symbol,Any}() +unwrapcontext(io::IOContext) = io.io, io.dict + +function IOContext(io::IO, dict::ImmutableDict) + io0 = unwrapcontext(io)[1] + IOContext{typeof(io0)}(io0, dict) +end + +convert(::Type{IOContext}, io::IO) = IOContext(unwrapcontext(io)...) + +IOContext(io::IO) = convert(IOContext, io) + +function IOContext(io::IO, KV::Pair) + io0, d = unwrapcontext(io) + IOContext(io0, ImmutableDict{Symbol,Any}(d, KV[1], KV[2])) +end + +""" + IOContext(io::IO, context::IOContext) + +Create an `IOContext` that wraps an alternate `IO` but inherits the properties of `context`. +""" +IOContext(io::IO, context::IO) = IOContext(unwrapcontext(io)[1], unwrapcontext(context)[2]) + +""" + IOContext(io::IO, KV::Pair...) + +Create an `IOContext` that wraps a given stream, adding the specified `key=>value` pairs to +the properties of that stream (note that `io` can itself be an `IOContext`). + + - use `(key => value) in io` to see if this particular combination is in the properties set + - use `get(io, key, default)` to retrieve the most recent value for a particular key + +The following properties are in common use: + + - `:compact`: Boolean specifying that small values should be printed more compactly, e.g. + that numbers should be printed with fewer digits. This is set when printing array + elements. + - `:limit`: Boolean specifying that containers should be truncated, e.g. showing `…` in + place of most elements. + - `:displaysize`: A `Tuple{Int,Int}` giving the size in rows and columns to use for text + output. This can be used to override the display size for called functions, but to + get the size of the screen use the `displaysize` function. + - `:typeinfo`: a `Type` characterizing the information already printed + concerning the type of the object about to be displayed. This is mainly useful when + displaying a collection of objects of the same type, so that redundant type information + can be avoided (e.g. `[Float16(0)]` can be shown as "Float16[0.0]" instead + of "Float16[Float16(0.0)]" : while displaying the elements of the array, the `:typeinfo` + property will be set to `Float16`). + - `:color`: Boolean specifying whether ANSI color/escape codes are supported/expected. + By default, this is determined by whether `io` is a compatible terminal and by any + `--color` command-line flag when `julia` was launched. + +# Examples + +```jldoctest +julia> io = IOBuffer(); + +julia> printstyled(IOContext(io, :color => true), "string", color=:red) + +julia> String(take!(io)) +"\\e[31mstring\\e[39m" + +julia> printstyled(io, "string", color=:red) + +julia> String(take!(io)) +"string" +``` + +```jldoctest +julia> print(IOContext(stdout, :compact => false), 1.12341234) +1.12341234 +julia> print(IOContext(stdout, :compact => true), 1.12341234) +1.12341 +``` + +```jldoctest +julia> function f(io::IO) + if get(io, :short, false) + print(io, "short") + else + print(io, "loooooong") + end + end +f (generic function with 1 method) + +julia> f(stdout) +loooooong +julia> f(IOContext(stdout, :short => true)) +short +``` +""" +IOContext(io::IO, KV::Pair, KVs::Pair...) = IOContext(IOContext(io, KV), KVs...) + +show(io::IO, ctx::IOContext) = (print(io, "IOContext("); show(io, ctx.io); print(io, ")")) + +pipe_reader(io::IOContext) = io.io +pipe_writer(io::IOContext) = io.io +lock(io::IOContext) = lock(io.io) +unlock(io::IOContext) = unlock(io.io) + +in(key_value::Pair, io::IOContext) = in(key_value, io.dict, ===) +in(key_value::Pair, io::IO) = false +haskey(io::IOContext, key) = haskey(io.dict, key) +haskey(io::IO, key) = false +getindex(io::IOContext, key) = getindex(io.dict, key) +getindex(io::IO, key) = throw(KeyError(key)) +get(io::IOContext, key, default) = get(io.dict, key, default) +get(io::IO, key, default) = default + +displaysize(io::IOContext) = haskey(io, :displaysize) ? io[:displaysize] : displaysize(io.io) + +show_circular(io::IO, @nospecialize(x)) = false +function show_circular(io::IOContext, @nospecialize(x)) + d = 1 + for (k, v) in io.dict + if k === :SHOWN_SET + if v === x + print(io, "#= circular reference @-$d =#") + return true + end + d += 1 + end + end + return false +end + +""" + show(x) + +Write an informative text representation of a value to the current output stream. New types +should overload `show(io::IO, x)` where the first argument is a stream. The representation used +by `show` generally includes Julia-specific formatting and type information. + +[`repr`](@ref) returns the output of `show` as a string. + +See also [`print`](@ref), which writes un-decorated representations. + +# Examples +```jldoctest +julia> show("Hello World!") +"Hello World!" +julia> print("Hello World!") +Hello World! +``` +""" +show(x) = show(stdout::IO, x) + +show(io::IO, @nospecialize(x)) = show_default(io, x) + +# avoid inferring show_default on the type of `x` +show_default(io::IO, @nospecialize(x)) = _show_default(io, inferencebarrier(x)) + +function _show_default(io::IO, @nospecialize(x)) + t = typeof(x) + show(io, inferencebarrier(t)) + print(io, '(') + nf = nfields(x) + nb = sizeof(x) + if nf != 0 || nb == 0 + if !show_circular(io, x) + recur_io = IOContext(io, Pair{Symbol,Any}(:SHOWN_SET, x), + Pair{Symbol,Any}(:typeinfo, Any)) + for i in 1:nf + f = fieldname(t, i) + if !isdefined(x, f) + print(io, undef_ref_str) + else + show(recur_io, getfield(x, i)) + end + if i < nf + print(io, ", ") + end + end + end + else + print(io, "0x") + r = Ref(x) + GC.@preserve r begin + p = unsafe_convert(Ptr{Cvoid}, r) + for i in (nb - 1):-1:0 + print(io, string(unsafe_load(convert(Ptr{UInt8}, p + i)), base = 16, pad = 2)) + end + end + end + print(io,')') +end + +# Check if a particular symbol is exported from a standard library module +function is_exported_from_stdlib(name::Symbol, mod::Module) + !isdefined(mod, name) && return false + orig = getfield(mod, name) + while !(mod === Base || mod === Core) + parent = parentmodule(mod) + if mod === Main || mod === parent || parent === Main + return false + end + mod = parent + end + return isexported(mod, name) && isdefined(mod, name) && !isdeprecated(mod, name) && getfield(mod, name) === orig +end + +function show_function(io::IO, f::Function, compact::Bool) + ft = typeof(f) + mt = ft.name.mt + if mt === Symbol.name.mt + # uses shared method table + show_default(io, f) + elseif compact + print(io, mt.name) + elseif isdefined(mt, :module) && isdefined(mt.module, mt.name) && + getfield(mt.module, mt.name) === f + if is_exported_from_stdlib(mt.name, mt.module) || mt.module === Main + print(io, mt.name) + else + print(io, mt.module, ".", mt.name) + end + else + show_default(io, f) + end +end + +show(io::IO, f::Function) = show_function(io, f, get(io, :compact, false)) +print(io::IO, f::Function) = show_function(io, f, true) + +function show(io::IO, x::Core.IntrinsicFunction) + name = ccall(:jl_intrinsic_name, Cstring, (Core.IntrinsicFunction,), x) + print(io, unsafe_string(name)) +end + +show(io::IO, ::Core.TypeofBottom) = print(io, "Union{}") +show(io::IO, ::MIME"text/plain", ::Core.TypeofBottom) = print(io, "Union{}") + +function print_without_params(@nospecialize(x)) + if isa(x,UnionAll) + b = unwrap_unionall(x) + return isa(b,DataType) && b.name.wrapper === x + end + return false +end + +has_typevar(@nospecialize(t), v::TypeVar) = ccall(:jl_has_typevar, Cint, (Any, Any), t, v)!=0 + +function io_has_tvar_name(io::IOContext, name::Symbol, @nospecialize(x)) + for (key, val) in io.dict + if key === :unionall_env && val isa TypeVar && val.name === name && has_typevar(x, val) + return true + end + end + return false +end +io_has_tvar_name(io::IO, name::Symbol, @nospecialize(x)) = false + +function show(io::IO, @nospecialize(x::Type)) + if x isa DataType + show_datatype(io, x) + return + elseif x isa Union + print(io, "Union") + show_delim_array(io, uniontypes(x), '{', ',', '}', false) + return + end + x::UnionAll + + if print_without_params(x) + return show(io, unwrap_unionall(x).name) + end + + if x.var.name === :_ || io_has_tvar_name(io, x.var.name, x) + counter = 1 + while true + newname = Symbol(x.var.name, counter) + if !io_has_tvar_name(io, newname, x) + newtv = TypeVar(newname, x.var.lb, x.var.ub) + x = UnionAll(newtv, x{newtv}) + break + end + counter += 1 + end + end + + show(IOContext(io, :unionall_env => x.var), x.body) + print(io, " where ") + show(io, x.var) +end + +# Check whether 'sym' (defined in module 'parent') is visible from module 'from' +# If an object with this name exists in 'from', we need to check that it's the same binding +# and that it's not deprecated. +function isvisible(sym::Symbol, parent::Module, from::Module) + owner = ccall(:jl_binding_owner, Any, (Any, Any), parent, sym) + from_owner = ccall(:jl_binding_owner, Any, (Any, Any), from, sym) + return owner !== nothing && from_owner === owner && + !isdeprecated(parent, sym) && + isdefined(from, sym) # if we're going to return true, force binding resolution +end + +function show_type_name(io::IO, tn::Core.TypeName) + if tn === UnionAll.name + # by coincidence, `typeof(Type)` is a valid representation of the UnionAll type. + # intercept this case and print `UnionAll` instead. + return print(io, "UnionAll") + end + globname = isdefined(tn, :mt) ? tn.mt.name : nothing + globfunc = false + if globname !== nothing + globname_str = string(globname::Symbol) + if ('#' ∉ globname_str && '@' ∉ globname_str && isdefined(tn, :module) && + isbindingresolved(tn.module, globname) && isdefined(tn.module, globname) && + isconcretetype(tn.wrapper) && isa(getfield(tn.module, globname), tn.wrapper)) + globfunc = true + end + end + sym = (globfunc ? globname : tn.name)::Symbol + globfunc && print(io, "typeof(") + quo = false + if !get(io, :compact, false) + # Print module prefix unless type is visible from module passed to + # IOContext If :module is not set, default to Main. nothing can be used + # to force printing prefix + from = get(io, :module, Main) + if isdefined(tn, :module) && (from === nothing || !isvisible(sym, tn.module, from)) + show(io, tn.module) + print(io, ".") + if globfunc && !is_id_start_char(first(string(sym))) + print(io, ':') + if sym in quoted_syms + print(io, '(') + quo = true + end + end + end + end + show_sym(io, sym) + quo && print(io, ")") + globfunc && print(io, ")") +end + +function show_datatype(io::IO, x::DataType) + istuple = x.name === Tuple.name + if (!isempty(x.parameters) || istuple) && x !== Tuple + n = length(x.parameters)::Int + + # Print homogeneous tuples with more than 3 elements compactly as NTuple{N, T} + if istuple && n > 3 && all(i -> (x.parameters[1] === i), x.parameters) + print(io, "NTuple{", n, ',', x.parameters[1], "}") + else + show_type_name(io, x.name) + # Do not print the type parameters for the primary type if we are + # printing a method signature or type parameter. + # Always print the type parameter if we are printing the type directly + # since this information is still useful. + print(io, '{') + for (i, p) in enumerate(x.parameters) + show(io, p) + i < n && print(io, ',') + end + print(io, '}') + end + else + show_type_name(io, x.name) + end +end + +function show_supertypes(io::IO, typ::DataType) + print(io, typ) + while typ != Any + typ = supertype(typ) + print(io, " <: ", typ) + end +end + +show_supertypes(typ::DataType) = show_supertypes(stdout, typ) + +""" + @show + +Show an expression and result, returning the result. See also [`show`](@ref). +""" +macro show(exs...) + blk = Expr(:block) + for ex in exs + push!(blk.args, :(println($(sprint(show_unquoted,ex)*" = "), + repr(begin value=$(esc(ex)) end)))) + end + isempty(exs) || push!(blk.args, :value) + return blk +end + +function show(io::IO, tn::Core.TypeName) + show_type_name(io, tn) +end + +show(io::IO, ::Nothing) = print(io, "nothing") +show(io::IO, b::Bool) = print(io, get(io, :typeinfo, Any) === Bool ? (b ? "1" : "0") : (b ? "true" : "false")) +show(io::IO, n::Signed) = (write(io, string(n)); nothing) +show(io::IO, n::Unsigned) = print(io, "0x", string(n, pad = sizeof(n)<<1, base = 16)) +print(io::IO, n::Unsigned) = print(io, string(n)) + +show(io::IO, p::Ptr) = print(io, typeof(p), " @0x$(string(UInt(p), base = 16, pad = Sys.WORD_SIZE>>2))") + +has_tight_type(p::Pair) = + typeof(p.first) == typeof(p).parameters[1] && + typeof(p.second) == typeof(p).parameters[2] + +isdelimited(io::IO, x) = true +isdelimited(io::IO, x::Function) = !isoperator(Symbol(x)) + +# !isdelimited means that the Pair is printed with "=>" (like in "1 => 2"), +# without its explicit type (like in "Pair{Integer,Integer}(1, 2)") +isdelimited(io::IO, p::Pair) = !(has_tight_type(p) || get(io, :typeinfo, Any) == typeof(p)) + +function gettypeinfos(io::IO, p::Pair) + typeinfo = get(io, :typeinfo, Any) + p isa typeinfo <: Pair ? + fieldtype(typeinfo, 1) => fieldtype(typeinfo, 2) : + Any => Any +end + +function show(io::IO, p::Pair) + isdelimited(io, p) && return show_default(io, p) + typeinfos = gettypeinfos(io, p) + for i = (1, 2) + io_i = IOContext(io, :typeinfo => typeinfos[i]) + isdelimited(io_i, p[i]) || print(io, "(") + show(io_i, p[i]) + isdelimited(io_i, p[i]) || print(io, ")") + i == 1 && print(io, get(io, :compact, false) ? "=>" : " => ") + end +end + +function show(io::IO, m::Module) + if is_root_module(m) + print(io, nameof(m)) + else + print(io, join(fullname(m),".")) + end +end + +function sourceinfo_slotnames(src::CodeInfo) + slotnames = src.slotnames + names = Dict{String,Int}() + printnames = Vector{String}(undef, length(slotnames)) + for i in eachindex(slotnames) + name = string(slotnames[i]) + idx = get!(names, name, i) + if idx != i || isempty(name) + printname = "$name@_$i" + idx > 0 && (printnames[idx] = "$name@_$idx") + names[name] = 0 + else + printname = name + end + printnames[i] = printname + end + return printnames +end + +function show(io::IO, l::Core.MethodInstance) + def = l.def + if isa(def, Method) + if isdefined(def, :generator) && l === def.generator + print(io, "MethodInstance generator for ") + show(io, def) + else + print(io, "MethodInstance for ") + show_tuple_as_call(io, def.name, l.specTypes) + end + else + print(io, "Toplevel MethodInstance thunk") + end +end + +function show_delim_array(io::IO, itr::Union{AbstractArray,SimpleVector}, op, delim, cl, + delim_one, i1=first(LinearIndices(itr)), l=last(LinearIndices(itr))) + print(io, op) + if !show_circular(io, itr) + recur_io = IOContext(io, :SHOWN_SET => itr) + first = true + i = i1 + if l >= i1 + while true + if !isassigned(itr, i) + print(io, undef_ref_str) + else + x = itr[i] + show(recur_io, x) + end + i += 1 + if i > l + delim_one && first && print(io, delim) + break + end + first = false + print(io, delim) + print(io, ' ') + end + end + end + print(io, cl) +end + +function show_delim_array(io::IO, itr, op, delim, cl, delim_one, i1=1, n=typemax(Int)) + print(io, op) + if !show_circular(io, itr) + recur_io = IOContext(io, :SHOWN_SET => itr) + y = iterate(itr) + first = true + i0 = i1-1 + while i1 > 1 && y !== nothing + y = iterate(itr, y[2]) + i1 -= 1 + end + if y !== nothing + typeinfo = get(io, :typeinfo, Any) + while true + x = y[1] + y = iterate(itr, y[2]) + show(IOContext(recur_io, :typeinfo => itr isa typeinfo <: Tuple ? + fieldtype(typeinfo, i1+i0) : + typeinfo), + x) + i1 += 1 + if y === nothing || i1 > n + delim_one && first && print(io, delim) + break + end + first = false + print(io, delim) + print(io, ' ') + end + end + end + print(io, cl) +end + +show(io::IO, t::Tuple) = show_delim_array(io, t, '(', ',', ')', true) +show(io::IO, v::SimpleVector) = show_delim_array(io, v, "svec(", ',', ')', false) + +show(io::IO, s::Symbol) = show_unquoted_quote_expr(io, s, 0, 0) + +## Abstract Syntax Tree (AST) printing ## + +# Summary: +# print(io, ex) defers to show_unquoted(io, ex) +# show(io, ex) defers to show_unquoted(io, QuoteNode(ex)) +# show_unquoted(io, ex) does the heavy lifting +# +# AST printing should follow two rules: +# 1. Meta.parse(string(ex)) == ex +# 2. eval(Meta.parse(repr(ex))) == ex +# +# Rule 1 means that printing an expression should generate Julia code which +# could be reparsed to obtain the original expression. This code should be +# unambiguous and as readable as possible. +# +# Rule 2 means that showing an expression should generate a quoted version of +# print’s output. Parsing and then evaling this output should return the +# original expression. +# +# This is consistent with many other show methods, i.e.: +# show(Set([1,2,3])) # ==> "Set{Int64}([2,3,1])" +# eval(Meta.parse("Set{Int64}([2,3,1])”) # ==> An actual set +# While this isn’t true of ALL show methods, it is of all ASTs. + +const ExprNode = Union{Expr, QuoteNode, Slot, LineNumberNode, SSAValue, + GotoNode, GlobalRef, PhiNode, PhiCNode, UpsilonNode, + Core.Compiler.GotoIfNot, Core.Compiler.ReturnNode} +# Operators have precedence levels from 1-N, and show_unquoted defaults to a +# precedence level of 0 (the fourth argument). The top-level print and show +# methods use a precedence of -1 to specially allow space-separated macro syntax +print( io::IO, ex::ExprNode) = (show_unquoted(io, ex, 0, -1); nothing) +show( io::IO, ex::ExprNode) = show_unquoted_quote_expr(io, ex, 0, -1) +show_unquoted(io::IO, ex) = show_unquoted(io, ex, 0, 0) +show_unquoted(io::IO, ex, indent::Int) = show_unquoted(io, ex, indent, 0) +show_unquoted(io::IO, ex, ::Int,::Int) = show(io, ex) + +## AST printing constants ## + +const indent_width = 4 +const quoted_syms = Set{Symbol}([:(:),:(::),:(:=),:(=),:(==),:(===),:(=>)]) +const uni_syms = Set{Symbol}([:(::), :(<:), :(>:)]) +const uni_ops = Set{Symbol}([:(+), :(-), :(!), :(¬), :(~), :(<:), :(>:), :(√), :(∛), :(∜)]) +const expr_infix_wide = Set{Symbol}([ + :(=), :(+=), :(-=), :(*=), :(/=), :(\=), :(^=), :(&=), :(|=), :(÷=), :(%=), :(>>>=), :(>>=), :(<<=), + :(.=), :(.+=), :(.-=), :(.*=), :(./=), :(.\=), :(.^=), :(.&=), :(.|=), :(.÷=), :(.%=), :(.>>>=), :(.>>=), :(.<<=), + :(&&), :(||), :(<:), :($=), :(⊻=)]) +const expr_infix = Set{Symbol}([:(:), :(->), Symbol("::")]) +const expr_infix_any = union(expr_infix, expr_infix_wide) +const expr_calls = Dict(:call => ('(',')'), :calldecl => ('(',')'), + :ref => ('[',']'), :curly => ('{','}'), :(.) => ('(',')')) +const expr_parens = Dict(:tuple=>('(',')'), :vcat=>('[',']'), + :hcat =>('[',']'), :row =>('[',']'), :vect=>('[',']'), + :braces=>('{','}'), :bracescat=>('{','}')) + +## AST decoding helpers ## + +is_id_start_char(c::AbstractChar) = ccall(:jl_id_start_char, Cint, (UInt32,), c) != 0 +is_id_char(c::AbstractChar) = ccall(:jl_id_char, Cint, (UInt32,), c) != 0 +function isidentifier(s::AbstractString) + isempty(s) && return false + c, rest = Iterators.peel(s) + is_id_start_char(c) || return false + return all(is_id_char, rest) +end +isidentifier(s::Symbol) = isidentifier(string(s)) + +""" + isoperator(s::Symbol) + +Return `true` if the symbol can be used as an operator, `false` otherwise. + +# Examples +```jldoctest +julia> Base.isoperator(:+), Base.isoperator(:f) +(true, false) +``` +""" +isoperator(s::Union{Symbol,AbstractString}) = ccall(:jl_is_operator, Cint, (Cstring,), s) != 0 + +""" + isunaryoperator(s::Symbol) + +Return `true` if the symbol can be used as a unary (prefix) operator, `false` otherwise. + +# Examples +```jldoctest +julia> Base.isunaryoperator(:-), Base.isunaryoperator(:√), Base.isunaryoperator(:f) +(true, true, false) +``` +""" +isunaryoperator(s::Symbol) = ccall(:jl_is_unary_operator, Cint, (Cstring,), s) != 0 +is_unary_and_binary_operator(s::Symbol) = ccall(:jl_is_unary_and_binary_operator, Cint, (Cstring,), s) != 0 + +""" + isbinaryoperator(s::Symbol) + +Return `true` if the symbol can be used as a binary (infix) operator, `false` otherwise. + +# Examples +```jldoctest +julia> Base.isbinaryoperator(:-), Base.isbinaryoperator(:√), Base.isbinaryoperator(:f) +(true, false, false) +``` +""" +isbinaryoperator(s::Symbol) = isoperator(s) && (!isunaryoperator(s) || is_unary_and_binary_operator(s)) + +""" + operator_precedence(s::Symbol) + +Return an integer representing the precedence of operator `s`, relative to +other operators. Higher-numbered operators take precedence over lower-numbered +operators. Return `0` if `s` is not a valid operator. + +# Examples +```jldoctest +julia> Base.operator_precedence(:+), Base.operator_precedence(:*), Base.operator_precedence(:.) +(11, 13, 17) + +julia> Base.operator_precedence(:sin), Base.operator_precedence(:+=), Base.operator_precedence(:(=)) # (Note the necessary parens on `:(=)`) +(0, 1, 1) +``` +""" +operator_precedence(s::Symbol) = Int(ccall(:jl_operator_precedence, Cint, (Cstring,), s)) +operator_precedence(x::Any) = 0 # fallback for generic expression nodes +const prec_assignment = operator_precedence(:(=)) +const prec_pair = operator_precedence(:(=>)) +const prec_control_flow = operator_precedence(:(&&)) +const prec_arrow = operator_precedence(:(-->)) +const prec_comparison = operator_precedence(:(>)) +const prec_power = operator_precedence(:(^)) +const prec_decl = operator_precedence(:(::)) + +""" + operator_associativity(s::Symbol) + +Return a symbol representing the associativity of operator `s`. Left- and right-associative +operators return `:left` and `:right`, respectively. Return `:none` if `s` is non-associative +or an invalid operator. + +# Examples +```jldoctest +julia> Base.operator_associativity(:-), Base.operator_associativity(:+), Base.operator_associativity(:^) +(:left, :none, :right) + +julia> Base.operator_associativity(:⊗), Base.operator_associativity(:sin), Base.operator_associativity(:→) +(:left, :none, :right) +``` +""" +function operator_associativity(s::Symbol) + if operator_precedence(s) in (prec_arrow, prec_assignment, prec_control_flow, prec_pair, prec_power) || + (isunaryoperator(s) && !is_unary_and_binary_operator(s)) || s === :<| + return :right + elseif operator_precedence(s) in (0, prec_comparison) || s in (:+, :++, :*) + return :none + end + return :left +end + +is_expr(ex, head::Symbol) = (isa(ex, Expr) && (ex.head == head)) +is_expr(ex, head::Symbol, n::Int) = is_expr(ex, head) && length(ex.args) == n + +is_quoted(ex) = false +is_quoted(ex::QuoteNode) = true +is_quoted(ex::Expr) = is_expr(ex, :quote, 1) || is_expr(ex, :inert, 1) + +unquoted(ex::QuoteNode) = ex.value +unquoted(ex::Expr) = ex.args[1] + +## AST printing helpers ## + +function printstyled end +function with_output_color end + +const indent_width = 4 + +is_expected_union(u::Union) = u.a == Nothing || u.b == Nothing || u.a == Missing || u.b == Missing + +emphasize(io, str::AbstractString, col = Base.error_color()) = get(io, :color, false) ? + printstyled(io, str; color=col, bold=true) : + print(io, uppercase(str)) + +show_linenumber(io::IO, line) = print(io, "#= line ", line, " =#") +show_linenumber(io::IO, line, file) = print(io, "#= ", file, ":", line, " =#") +show_linenumber(io::IO, line, file::Nothing) = show_linenumber(io, line) + +# show a block, e g if/for/etc +function show_block(io::IO, head, args::Vector, body, indent::Int) + print(io, head) + if !isempty(args) + print(io, ' ') + if head === :elseif + show_list(io, args, " ", indent) + else + show_list(io, args, ", ", indent) + end + end + + ind = head === :module || head === :baremodule ? indent : indent + indent_width + exs = is_expr(body, :block) ? body.args : Any[body] + for ex in exs + print(io, '\n', " "^ind) + show_unquoted(io, ex, ind, -1) + end + print(io, '\n', " "^indent) +end +show_block(io::IO,head, block,i::Int) = show_block(io,head, [], block,i) +function show_block(io::IO, head, arg, block, i::Int) + if is_expr(arg, :block) + show_block(io, head, arg.args, block, i) + else + show_block(io, head, Any[arg], block, i) + end +end + +# show an indented list +function show_list(io::IO, items, sep, indent::Int, prec::Int=0, enclose_operators::Bool=false) + n = length(items) + n == 0 && return + indent += indent_width + first = true + for item in items + !first && print(io, sep) + parens = !is_quoted(item) && + (first && prec >= prec_power && + ((item isa Expr && item.head === :call && item.args[1] in uni_ops) || + (item isa Real && item < 0))) || + (enclose_operators && item isa Symbol && isoperator(item)) + parens && print(io, '(') + show_unquoted(io, item, indent, parens ? 0 : prec) + parens && print(io, ')') + first = false + end +end +# show an indented list inside the parens (op, cl) +function show_enclosed_list(io::IO, op, items, sep, cl, indent, prec=0, encl_ops=false) + print(io, op) + show_list(io, items, sep, indent, prec, encl_ops) + print(io, cl) +end + +# show a normal (non-operator) function call, e.g. f(x, y) or A[z] +function show_call(io::IO, head, func, func_args, indent) + op, cl = expr_calls[head] + if (isa(func, Symbol) && func !== :(:) && !(head === :. && isoperator(func))) || + (isa(func, Expr) && (func.head === :. || func.head === :curly || func.head === :macroname)) || + isa(func, GlobalRef) + show_unquoted(io, func, indent) + else + print(io, '(') + show_unquoted(io, func, indent) + print(io, ')') + end + if head === :(.) + print(io, '.') + end + if !isempty(func_args) && isa(func_args[1], Expr) && func_args[1].head === :parameters + print(io, op) + show_list(io, func_args[2:end], ", ", indent) + print(io, "; ") + show_list(io, func_args[1].args, ", ", indent) + print(io, cl) + else + show_enclosed_list(io, op, func_args, ", ", cl, indent) + end +end + +# Print `sym` as it would appear as an identifier name in code +# * Print valid identifiers & operators literally; also macros names if allow_macroname=true +# * Escape invalid identifiers with var"" syntax +function show_sym(io::IO, sym; allow_macroname=false) + if isidentifier(sym) || isoperator(sym) + print(io, sym) + elseif allow_macroname && (sym_str = string(sym); startswith(sym_str, '@')) + print(io, '@') + show_sym(io, sym_str[2:end]) + else + print(io, "var", repr(string(sym))) + end +end + +## AST printing ## + +show_unquoted(io::IO, val::SSAValue, ::Int, ::Int) = print(io, "%", val.id) +show_unquoted(io::IO, sym::Symbol, ::Int, ::Int) = show_sym(io, sym) +show_unquoted(io::IO, ex::LineNumberNode, ::Int, ::Int) = show_linenumber(io, ex.line, ex.file) +show_unquoted(io::IO, ex::GotoNode, ::Int, ::Int) = print(io, "goto %", ex.label) +function show_unquoted(io::IO, ex::GlobalRef, ::Int, ::Int) + print(io, ex.mod) + print(io, '.') + quoted = !isidentifier(ex.name) && !startswith(string(ex.name), "@") + parens = quoted && (!isoperator(ex.name) || (ex.name in quoted_syms)) + quoted && print(io, ':') + parens && print(io, '(') + show_sym(io, ex.name, allow_macroname=true) + parens && print(io, ')') + nothing +end + +function show_unquoted(io::IO, ex::Slot, ::Int, ::Int) + typ = isa(ex, TypedSlot) ? ex.typ : Any + slotid = ex.id + slotnames = get(io, :SOURCE_SLOTNAMES, false) + if (isa(slotnames, Vector{String}) && + slotid <= length(slotnames::Vector{String})) + print(io, (slotnames::Vector{String})[slotid]) + else + print(io, "_", slotid) + end + if typ !== Any && isa(ex, TypedSlot) + print(io, "::", typ) + end +end + +function show_unquoted(io::IO, ex::QuoteNode, indent::Int, prec::Int) + if isa(ex.value, Symbol) + show_unquoted_quote_expr(io, ex.value, indent, prec) + else + print(io, "\$(QuoteNode(") + show(io, ex.value) + print(io, "))") + end +end + +function show_unquoted_quote_expr(io::IO, @nospecialize(value), indent::Int, prec::Int) + if isa(value, Symbol) && !(value in quoted_syms) + s = string(value) + if isidentifier(s) || isoperator(value) + print(io, ":") + print(io, value) + else + print(io, "Symbol(", repr(s), ")") + end + else + if isa(value,Expr) && value.head === :block + show_block(io, "quote", value, indent) + print(io, "end") + else + print(io, ":(") + show_unquoted(io, value, indent+2, -1) # +2 for `:(` + print(io, ")") + end + end +end + +function show_generator(io, ex, indent) + if ex.head === :flatten + fg = ex + ranges = Any[] + while isa(fg, Expr) && fg.head === :flatten + push!(ranges, fg.args[1].args[2:end]) + fg = fg.args[1].args[1] + end + push!(ranges, fg.args[2:end]) + show_unquoted(io, fg.args[1], indent) + for r in ranges + print(io, " for ") + show_list(io, r, ", ", indent) + end + else + show_unquoted(io, ex.args[1], indent) + print(io, " for ") + show_list(io, ex.args[2:end], ", ", indent) + end +end + +function valid_import_path(@nospecialize ex) + return Meta.isexpr(ex, :(.)) && length((ex::Expr).args) > 0 && all(a->isa(a,Symbol), (ex::Expr).args) +end + +function show_import_path(io::IO, ex) + if !isa(ex, Expr) + show_unquoted(io, ex) + elseif ex.head === :(:) + show_import_path(io, ex.args[1]) + print(io, ": ") + for i = 2:length(ex.args) + if i > 2 + print(io, ", ") + end + show_import_path(io, ex.args[i]) + end + elseif ex.head === :(.) + for i = 1:length(ex.args) + if i > 1 && ex.args[i-1] !== :(.) + print(io, '.') + end + show_sym(io, ex.args[i], allow_macroname=(i==length(ex.args))) + end + else + show_unquoted(io, ex) + end +end + +# Wrap symbols for macro names to allow them to be printed literally +allow_macroname(ex) = ex isa Symbol && first(string(ex)) == '@' ? + Expr(:macroname, ex) : ex + +# TODO: implement interpolated strings +function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) + head, args, nargs = ex.head, ex.args, length(ex.args) + unhandled = false + # dot (i.e. "x.y"), but not compact broadcast exps + if head === :(.) && (nargs != 2 || !is_expr(args[2], :tuple)) + if nargs == 2 && is_quoted(args[2]) + item = args[1] + # field + field = unquoted(args[2]) + parens = !is_quoted(item) && !(item isa Symbol && isidentifier(item)) && !Meta.isexpr(item, :(.)) + parens && print(io, '(') + show_unquoted(io, item, indent) + parens && print(io, ')') + # . + print(io, '.') + # item + parens = !(field isa Symbol) || (field in quoted_syms) + quoted = parens || isoperator(field) + quoted && print(io, ':') + parens && print(io, '(') + show_unquoted(io, field, indent) + parens && print(io, ')') + else + unhandled = true + end + + # infix (i.e. "x <: y" or "x = y") + elseif (head in expr_infix_any && nargs==2) + func_prec = operator_precedence(head) + head_ = head in expr_infix_wide ? " $head " : head + if func_prec <= prec + show_enclosed_list(io, '(', args, head_, ')', indent, func_prec, true) + else + show_list(io, args, head_, indent, func_prec, true) + end + + # list (i.e. "(1, 2, 3)" or "[1, 2, 3]") + elseif haskey(expr_parens, head) # :tuple/:vcat + op, cl = expr_parens[head] + if head === :vcat || head === :bracescat + sep = "; " + elseif head === :hcat || head === :row + sep = " " + else + sep = ", " + end + head !== :row && print(io, op) + show_list(io, args, sep, indent) + if nargs == 1 + if head === :tuple + print(io, ',') + elseif head === :vcat + print(io, ';') + end + end + head !== :row && print(io, cl) + + # function call + elseif head === :call && nargs >= 1 + func = args[1] + fname = isa(func, GlobalRef) ? func.name : func + func_prec = operator_precedence(fname) + if func_prec > 0 || fname in uni_ops + func = fname + end + func_args = args[2:end] + + # scalar multiplication (i.e. "100x") + if (func === :* && + length(func_args)==2 && isa(func_args[1], Real) && isa(func_args[2], Symbol)) + if func_prec <= prec + show_enclosed_list(io, '(', func_args, "", ')', indent, func_prec) + else + show_list(io, func_args, "", indent, func_prec) + end + + # unary operator (i.e. "!z") + elseif isa(func,Symbol) && func in uni_ops && length(func_args) == 1 + show_unquoted(io, func, indent) + arg1 = func_args[1] + if isa(arg1, Expr) || (isa(arg1, Symbol) && isoperator(arg1)) + show_enclosed_list(io, '(', func_args, ", ", ')', indent, func_prec) + else + show_unquoted(io, arg1, indent, func_prec) + end + + # binary operator (i.e. "x + y") + elseif func_prec > 0 # is a binary operator + na = length(func_args) + if (na == 2 || (na > 2 && func in (:+, :++, :*)) || (na == 3 && func === :(:))) && + all(!isa(a, Expr) || a.head !== :... for a in func_args) + sep = func === :(:) ? "$func" : " $func " + + if func_prec <= prec + show_enclosed_list(io, '(', func_args, sep, ')', indent, func_prec, true) + else + show_list(io, func_args, sep, indent, func_prec, true) + end + elseif na == 1 + # 1-argument call to normally-binary operator + op, cl = expr_calls[head] + print(io, "(") + show_unquoted(io, func, indent) + print(io, ")") + show_enclosed_list(io, op, func_args, ", ", cl, indent) + else + show_call(io, head, func, func_args, indent) + end + + # normal function (i.e. "f(x,y)") + else + show_call(io, head, func, func_args, indent) + end + + # new expr + elseif head === :new || head === :splatnew + show_enclosed_list(io, "%$head(", args, ", ", ")", indent) + + # other call-like expressions ("A[1,2]", "T{X,Y}", "f.(X,Y)") + elseif haskey(expr_calls, head) && nargs >= 1 # :ref/:curly/:calldecl/:(.) + funcargslike = head === :(.) ? args[2].args : args[2:end] + show_call(io, head, args[1], funcargslike, indent) + + # comprehensions + elseif head === :typed_comprehension && nargs == 2 + show_unquoted(io, args[1], indent) + print(io, '[') + show_generator(io, args[2], indent) + print(io, ']') + + elseif head === :comprehension && nargs == 1 + print(io, '[') + show_generator(io, args[1], indent) + print(io, ']') + + elseif (head === :generator && nargs >= 2) || (head === :flatten && nargs == 1) + print(io, '(') + show_generator(io, ex, indent) + print(io, ')') + + elseif head === :filter && nargs == 2 + show_unquoted(io, args[2], indent) + print(io, " if ") + show_unquoted(io, args[1], indent) + + # comparison (i.e. "x < y < z") + elseif head === :comparison && nargs >= 3 && (nargs&1==1) + comp_prec = minimum(operator_precedence, args[2:2:end]) + if comp_prec <= prec + show_enclosed_list(io, '(', args, " ", ')', indent, comp_prec) + else + show_list(io, args, " ", indent, comp_prec) + end + + # function calls need to transform the function from :call to :calldecl + # so that operators are printed correctly + elseif head === :function && nargs==2 && is_expr(args[1], :call) + show_block(io, head, Expr(:calldecl, args[1].args...), args[2], indent) + print(io, "end") + + elseif (head === :function || head === :macro) && nargs == 1 + print(io, head, ' ') + show_unquoted(io, args[1]) + print(io, " end") + + elseif head === :do && nargs == 2 + show_unquoted(io, args[1], indent, -1) + print(io, " do ") + show_list(io, args[2].args[1].args, ", ", 0) + for stmt in args[2].args[2].args + print(io, '\n', " "^(indent + indent_width)) + show_unquoted(io, stmt, indent + indent_width, -1) + end + print(io, '\n', " "^indent) + print(io, "end") + + # block with argument + elseif head in (:for,:while,:function,:macro,:if,:elseif,:let) && nargs==2 + show_block(io, head, args[1], args[2], indent) + print(io, "end") + + elseif (head === :if || head === :elseif) && nargs == 3 + show_block(io, head, args[1], args[2], indent) + if isa(args[3],Expr) && args[3].head === :elseif + show_unquoted(io, args[3], indent, prec) + else + show_block(io, "else", args[3], indent) + print(io, "end") + end + + elseif head === :module && nargs==3 && isa(args[1],Bool) + show_block(io, args[1] ? :module : :baremodule, args[2], args[3], indent) + print(io, "end") + + # type declaration + elseif head === :struct && nargs==3 + show_block(io, args[1] ? Symbol("mutable struct") : Symbol("struct"), args[2], args[3], indent) + print(io, "end") + + elseif head === :primitive && nargs == 2 + print(io, "primitive type ") + show_list(io, args, ' ', indent) + print(io, " end") + + elseif head === :abstract && nargs == 1 + print(io, "abstract type ") + show_list(io, args, ' ', indent) + print(io, " end") + + # empty return (i.e. "function f() return end") + elseif head === :return && nargs == 1 && args[1] === nothing + print(io, head) + + # type annotation (i.e. "::Int") + elseif head in uni_syms && nargs == 1 + print(io, head) + show_unquoted(io, args[1], indent) + + # var-arg declaration or expansion + # (i.e. "function f(L...) end" or "f(B...)") + elseif head === :(...) && nargs == 1 + show_unquoted(io, args[1], indent) + print(io, "...") + + elseif (nargs == 0 && head in (:break, :continue)) + print(io, head) + + elseif (nargs == 1 && head in (:return, :const)) || + head in (:local, :global) + print(io, head, ' ') + show_list(io, args, ", ", indent) + + elseif head === :export + print(io, head, ' ') + show_list(io, allow_macroname.(args), ", ", indent) + + elseif head === :macrocall && nargs >= 2 + # first show the line number argument as a comment + if isa(args[2], LineNumberNode) || is_expr(args[2], :line) + print(io, args[2], ' ') + end + # Use the functional syntax unless specifically designated with prec=-1 + # and hide the line number argument from the argument list + mname = allow_macroname(args[1]) + if prec >= 0 + show_call(io, :call, mname, args[3:end], indent) + else + show_args = Vector{Any}(undef, nargs - 1) + show_args[1] = mname + show_args[2:end] = args[3:end] + show_list(io, show_args, ' ', indent) + end + + elseif head === :macroname && nargs == 1 + arg1 = args[1] + if arg1 isa Symbol + show_sym(io, arg1, allow_macroname=true) + else + show_unquoted(io, arg1) + end + + elseif head === :line && 1 <= nargs <= 2 + show_linenumber(io, args...) + + elseif head === :try && 3 <= nargs <= 4 + show_block(io, "try", args[1], indent) + if is_expr(args[3], :block) + show_block(io, "catch", args[2] === false ? Any[] : args[2], args[3], indent) + end + if nargs >= 4 && is_expr(args[4], :block) + show_block(io, "finally", Any[], args[4], indent) + end + print(io, "end") + + elseif head === :block + show_block(io, "begin", ex, indent) + print(io, "end") + + elseif head === :quote && nargs == 1 && isa(args[1], Symbol) + show_unquoted_quote_expr(io, args[1]::Symbol, indent, 0) + + elseif head === :gotoifnot && nargs == 2 && isa(args[2], Int) + print(io, "unless ") + show_unquoted(io, args[1], indent, 0) + print(io, " goto %") + print(io, args[2]::Int) + + elseif head === :string && nargs == 1 && isa(args[1], AbstractString) + show(io, args[1]) + + elseif head === :null + print(io, "nothing") + + elseif head === :kw && nargs == 2 + show_unquoted(io, args[1], indent+indent_width) + print(io, '=') + show_unquoted(io, args[2], indent+indent_width) + + elseif head === :string + print(io, '"') + for x in args + if !isa(x,AbstractString) + print(io, "\$(") + show_unquoted(io, x) + print(io, ")") + else + escape_string(io, x, "\"\$") + end + end + print(io, '"') + + elseif (head === :&#= || head === :$=#) && nargs == 1 + print(io, head) + a1 = args[1] + parens = (isa(a1,Expr) && a1.head !== :tuple) || (isa(a1,Symbol) && isoperator(a1)) + parens && print(io, "(") + show_unquoted(io, a1) + parens && print(io, ")") + + # transpose + elseif head === Symbol('\'') && nargs == 1 + if isa(args[1], Symbol) + show_unquoted(io, args[1]) + else + print(io, "(") + show_unquoted(io, args[1]) + print(io, ")") + end + print(io, head) + + # `where` syntax + elseif head === :where && nargs > 1 + parens = 1 <= prec + parens && print(io, "(") + show_unquoted(io, args[1], indent, operator_precedence(:(::))) + print(io, " where ") + if nargs == 2 + show_unquoted(io, args[2], indent, 1) + else + print(io, "{") + show_list(io, args[2:end], ", ", indent) + print(io, "}") + end + parens && print(io, ")") + + elseif (head === :import || head === :using) && nargs == 1 && + (valid_import_path(args[1]) || + (Meta.isexpr(args[1], :(:)) && length((args[1]::Expr).args) > 1 && all(valid_import_path, (args[1]::Expr).args))) + print(io, head) + print(io, ' ') + first = true + for a in args + if !first + print(io, ", ") + end + first = false + show_import_path(io, a) + end + elseif head === :meta && nargs >= 2 && args[1] === :push_loc + print(io, "# meta: location ", join(args[2:end], " ")) + elseif head === :meta && nargs == 1 && args[1] === :pop_loc + print(io, "# meta: pop location") + elseif head === :meta && nargs == 2 && args[1] === :pop_loc + print(io, "# meta: pop locations ($(args[2]))") + # print anything else as "Expr(head, args...)" + else + unhandled = true + end + if unhandled + print(io, "\$(Expr(") + show(io, ex.head) + for arg in args + print(io, ", ") + show(io, arg) + end + print(io, "))") + end + nothing +end + +function show_tuple_as_call(io::IO, name::Symbol, sig::Type) + # print a method signature tuple for a lambda definition + color = get(io, :color, false) && get(io, :backtrace, false) ? stackframe_function_color() : :nothing + if sig === Tuple + printstyled(io, name, "(...)", color=color) + return + end + sig = unwrap_unionall(sig).parameters + with_output_color(color, io) do io + ft = sig[1] + uw = unwrap_unionall(ft) + if ft <: Function && isa(uw,DataType) && isempty(uw.parameters) && + isdefined(uw.name.module, uw.name.mt.name) && + ft == typeof(getfield(uw.name.module, uw.name.mt.name)) + print(io, uw.name.mt.name) + elseif isa(ft, DataType) && ft.name === Type.body.name && !Core.Compiler.has_free_typevars(ft) + f = ft.parameters[1] + print(io, f) + else + print(io, "(::", ft, ")") + end + end + first = true + print_style = get(io, :color, false) && get(io, :backtrace, false) ? :bold : :nothing + printstyled(io, "(", color=print_style) + for i = 2:length(sig) # fixme (iter): `eachindex` with offset? + first || print(io, ", ") + first = false + print(io, "::", sig[i]) + end + printstyled(io, ")", color=print_style) + nothing +end + +resolvebinding(@nospecialize(ex)) = ex +resolvebinding(ex::QuoteNode) = ex.value +resolvebinding(ex::Symbol) = resolvebinding(GlobalRef(Main, ex)) +function resolvebinding(ex::Expr) + if ex.head === :. && isa(ex.args[2], Symbol) + parent = resolvebinding(ex.args[1]) + if isa(parent, Module) + return resolvebinding(GlobalRef(parent, ex.args[2])) + end + end + return nothing +end +function resolvebinding(ex::GlobalRef) + isdefined(ex.mod, ex.name) || return nothing + isconst(ex.mod, ex.name) || return nothing + m = getfield(ex.mod, ex.name) + isa(m, Module) || return nothing + return m +end + +function ismodulecall(ex::Expr) + return ex.head === :call && (ex.args[1] === GlobalRef(Base,:getfield) || + ex.args[1] === GlobalRef(Core,:getfield)) && + isa(resolvebinding(ex.args[2]), Module) +end + +function show(io::IO, tv::TypeVar) + # If we are in the `unionall_env`, the type-variable is bound + # and the type constraints are already printed. + # We don't need to print it again. + # Otherwise, the lower bound should be printed if it is not `Bottom` + # and the upper bound should be printed if it is not `Any`. + in_env = (:unionall_env => tv) in io + function show_bound(io::IO, @nospecialize(b)) + parens = isa(b,UnionAll) && !print_without_params(b) + parens && print(io, "(") + show(io, b) + parens && print(io, ")") + end + lb, ub = tv.lb, tv.ub + if !in_env && lb !== Bottom + if ub === Any + write(io, tv.name) + print(io, ">:") + show_bound(io, lb) + else + show_bound(io, lb) + print(io, "<:") + write(io, tv.name) + end + else + write(io, tv.name) + end + if !in_env && ub !== Any + print(io, "<:") + show_bound(io, ub) + end + nothing +end + +module IRShow + const Compiler = Core.Compiler + using Core.IR + import ..Base + import .Compiler: IRCode, ReturnNode, GotoIfNot, CFG, scan_ssa_use!, Argument, isexpr, compute_basic_blocks, block_for_inst + Base.getindex(r::Compiler.StmtRange, ind::Integer) = Compiler.getindex(r, ind) + Base.size(r::Compiler.StmtRange) = Compiler.size(r) + Base.first(r::Compiler.StmtRange) = Compiler.first(r) + Base.last(r::Compiler.StmtRange) = Compiler.last(r) + include("compiler/ssair/show.jl") + + const __debuginfo = Dict{Symbol, Any}( + # :full => src -> Base.IRShow.DILineInfoPrinter(src.linetable), # and add variable slot information + :source => src -> Base.IRShow.DILineInfoPrinter(src.linetable), + # :oneliner => src -> Base.IRShow.PartialLineInfoPrinter(src.linetable), + :none => src -> Base.IRShow.lineinfo_disabled, + ) + const default_debuginfo = Ref{Symbol}(:none) + debuginfo(sym) = sym === :default ? default_debuginfo[] : sym +end + +function show(io::IO, src::CodeInfo; debuginfo::Symbol=:source) + # Fix slot names and types in function body + print(io, "CodeInfo(") + lambda_io::IOContext = io + if src.slotnames !== nothing + lambda_io = IOContext(lambda_io, :SOURCE_SLOTNAMES => sourceinfo_slotnames(src)) + end + @assert src.codelocs !== nothing + if isempty(src.linetable) || src.linetable[1] isa LineInfoNode + println(io) + # TODO: static parameter values? + # only accepts :source or :none, we can't have a fallback for default since + # that would break code_typed(, debuginfo=:source) iff IRShow.default_debuginfo[] = :none + IRShow.show_ir(lambda_io, src, IRShow.__debuginfo[debuginfo](src)) + else + # this is a CodeInfo that has not been used as a method yet, so its locations are still LineNumberNodes + body = Expr(:block) + body.args = src.code + show(lambda_io, body) + end + print(io, ")") +end + + +function dump(io::IOContext, x::SimpleVector, n::Int, indent) + if isempty(x) + print(io, "empty SimpleVector") + return + end + print(io, "SimpleVector") + if n > 0 + for i = 1:length(x) + println(io) + print(io, indent, " ", i, ": ") + if isassigned(x,i) + dump(io, x[i], n - 1, string(indent, " ")) + else + print(io, undef_ref_str) + end + end + end + nothing +end + +function dump(io::IOContext, @nospecialize(x), n::Int, indent) + T = typeof(x) + if isa(x, Function) + print(io, x, " (function of type ", T, ")") + else + print(io, T) + end + nf = nfields(x) + if nf > 0 + if n > 0 && !show_circular(io, x) + recur_io = IOContext(io, Pair{Symbol,Any}(:SHOWN_SET, x)) + for field in 1:nf + println(io) + fname = string(fieldname(T, field)) + print(io, indent, " ", fname, ": ") + if isdefined(x,field) + dump(recur_io, getfield(x, field), n - 1, string(indent, " ")) + else + print(io, undef_ref_str) + end + end + end + elseif !isa(x, Function) + print(io, " ") + show(io, x) + end + nothing +end + +dump(io::IOContext, x::Module, n::Int, indent) = print(io, "Module ", x) +dump(io::IOContext, x::String, n::Int, indent) = (print(io, "String "); show(io, x)) +dump(io::IOContext, x::Symbol, n::Int, indent) = print(io, typeof(x), " ", x) +dump(io::IOContext, x::Union, n::Int, indent) = print(io, x) +dump(io::IOContext, x::Ptr, n::Int, indent) = print(io, x) + +function dump_elts(io::IOContext, x::Array, n::Int, indent, i0, i1) + for i in i0:i1 + print(io, indent, " ", i, ": ") + if !isassigned(x,i) + print(io, undef_ref_str) + else + dump(io, x[i], n - 1, string(indent, " ")) + end + i < i1 && println(io) + end +end + +function dump(io::IOContext, x::Array, n::Int, indent) + print(io, "Array{", eltype(x), "}(", size(x), ")") + if eltype(x) <: Number + print(io, " ") + show(io, x) + else + if n > 0 && !isempty(x) && !show_circular(io, x) + println(io) + recur_io = IOContext(io, :SHOWN_SET => x) + lx = length(x) + if get(io, :limit, false) + dump_elts(recur_io, x, n, indent, 1, (lx <= 10 ? lx : 5)) + if lx > 10 + println(io) + println(io, indent, " ...") + dump_elts(recur_io, x, n, indent, lx - 4, lx) + end + else + dump_elts(recur_io, x, n, indent, 1, lx) + end + end + end + nothing +end + +# Types +function dump(io::IOContext, x::DataType, n::Int, indent) + print(io, x) + if x !== Any + print(io, " <: ", supertype(x)) + end + if n > 0 && !(x <: Tuple) && !x.abstract + tvar_io::IOContext = io + for tparam in x.parameters + # approximately recapture the list of tvar parameterization + # that may be used by the internal fields + if isa(tparam, TypeVar) + tvar_io = IOContext(tvar_io, :unionall_env => tparam) + end + end + fields = fieldnames(x) + fieldtypes = datatype_fieldtypes(x) + for idx in 1:length(fields) + println(io) + print(io, indent, " ", fields[idx], "::") + print(tvar_io, fieldtypes[idx]) + end + end + nothing +end + +const DUMP_DEFAULT_MAXDEPTH = 8 + +function dump(io::IO, @nospecialize(x); maxdepth=DUMP_DEFAULT_MAXDEPTH) + dump(IOContext(io), x, maxdepth, "") + println(io) +end + +""" + dump(x; maxdepth=$DUMP_DEFAULT_MAXDEPTH) + +Show every part of the representation of a value. +The depth of the output is truncated at `maxdepth`. + +# Examples +```jldoctest +julia> struct MyStruct + x + y + end + +julia> x = MyStruct(1, (2,3)); + +julia> dump(x) +MyStruct + x: Int64 1 + y: Tuple{Int64,Int64} + 1: Int64 2 + 2: Int64 3 + +julia> dump(x; maxdepth = 1) +MyStruct + x: Int64 1 + y: Tuple{Int64,Int64} +``` +""" +function dump(arg; maxdepth=DUMP_DEFAULT_MAXDEPTH) + # this is typically used interactively, so default to being in Main + mod = get(stdout, :module, Main) + dump(IOContext(stdout::IO, :limit => true, :module => mod), arg; maxdepth=maxdepth) +end + + +""" +`alignment(X)` returns a tuple (left,right) showing how many characters are +needed on either side of an alignment feature such as a decimal point. +""" +alignment(io::IO, x::Any) = (0, length(sprint(show, x, context=io, sizehint=0))) +alignment(io::IO, x::Number) = (length(sprint(show, x, context=io, sizehint=0)), 0) +"`alignment(42)` yields (2,0)" +alignment(io::IO, x::Integer) = (length(sprint(show, x, context=io, sizehint=0)), 0) +"`alignment(4.23)` yields (1,3) for `4` and `.23`" +function alignment(io::IO, x::Real) + m = match(r"^(.*?)((?:[\.eE].*)?)$", sprint(show, x, context=io, sizehint=0)) + m === nothing ? (length(sprint(show, x, context=io, sizehint=0)), 0) : + (length(m.captures[1]), length(m.captures[2])) +end +"`alignment(1 + 10im)` yields (3,5) for `1 +` and `_10im` (plus sign on left, space on right)" +function alignment(io::IO, x::Complex) + m = match(r"^(.*[^e][\+\-])(.*)$", sprint(show, x, context=io, sizehint=0)) + m === nothing ? (length(sprint(show, x, context=io, sizehint=0)), 0) : + (length(m.captures[1]), length(m.captures[2])) +end +function alignment(io::IO, x::Rational) + m = match(r"^(.*?/)(/.*)$", sprint(show, x, context=io, sizehint=0)) + m === nothing ? (length(sprint(show, x, context=io, sizehint=0)), 0) : + (length(m.captures[1]), length(m.captures[2])) +end + +function alignment(io::IO, x::Pair) + s = sprint(show, x, context=io, sizehint=0) + if !isdelimited(io, x) # i.e. use "=>" for display + ctx = IOContext(io, :typeinfo => gettypeinfos(io, x)[1]) + left = length(sprint(show, x.first, context=ctx, sizehint=0)) + left += 2 * !isdelimited(ctx, x.first) # for parens around p.first + left += !get(io, :compact, false) # spaces are added around "=>" + (left+1, length(s)-left-1) # +1 for the "=" part of "=>" + else + (0, length(s)) # as for x::Any + end +end + +const undef_ref_str = "#undef" + + +""" + summary(io::IO, x) + str = summary(x) + +Print to a stream `io`, or return a string `str`, giving a brief description of +a value. By default returns `string(typeof(x))`, e.g. [`Int64`](@ref). + +For arrays, returns a string of size and type info, +e.g. `10-element Array{Int64,1}`. + +# Examples +```jldoctest +julia> summary(1) +"Int64" + +julia> summary(zeros(2)) +"2-element Array{Float64,1}" +``` +""" +summary(io::IO, x) = print(io, typeof(x)) +function summary(x) + io = IOBuffer() + summary(io, x) + String(take!(io)) +end +summary(io::IO, t::Tuple) = print(io, t) + +## `summary` for AbstractArrays +# sizes such as 0-dimensional, 4-dimensional, 2x3 +dims2string(d) = isempty(d) ? "0-dimensional" : + length(d) == 1 ? "$(d[1])-element" : + join(map(string,d), '×') + +inds2string(inds) = join(map(_indsstring,inds), '×') +_indsstring(i) = string(i) +_indsstring(i::Union{IdentityUnitRange, Slice}) = string(i.indices) + +# anything array-like gets summarized e.g. 10-element Array{Int64,1} +summary(io::IO, a::AbstractArray) = array_summary(io, a, axes(a)) +function array_summary(io::IO, a, inds::Tuple{Vararg{OneTo}}) + print(io, dims2string(length.(inds)), " ") + showarg(io, a, true) +end +function array_summary(io::IO, a, inds) + print(io, dims2string(length.(inds)), " ") + showarg(io, a, true) + print(io, " with indices ", inds2string(inds)) +end + +""" + showarg(io::IO, x, toplevel) + +Show `x` as if it were an argument to a function. This function is +used by [`summary`](@ref) to display type information in terms of sequences of +function calls on objects. `toplevel` is `true` if this is +the direct call from `summary` and `false` for nested (recursive) calls. + +The fallback definition is to print `x` as "::\\\$(typeof(x))", +representing argument `x` in terms of its type. (The double-colon is +omitted if `toplevel=true`.) However, you can +specialize this function for specific types to customize printing. + +# Example + +A SubArray created as `view(a, :, 3, 2:5)`, where `a` is a +3-dimensional Float64 array, has type + + SubArray{Float64,2,Array{Float64,3},Tuple{Colon,Int64,UnitRange{Int64}},false} + +The default `show` printing would display this full type. +However, the summary for SubArrays actually prints as + + 2×4 view(::Array{Float64,3}, :, 3, 2:5) with eltype Float64 + +because of a definition similar to + + function Base.showarg(io::IO, v::SubArray, toplevel) + print(io, "view(") + showarg(io, parent(v), false) + print(io, ", ", join(v.indices, ", ")) + print(io, ')') + toplevel && print(io, " with eltype ", eltype(v)) + end + +Note that we're calling `showarg` recursively for the parent array +type, indicating that any recursed calls are not at the top level. +Printing the parent as `::Array{Float64,3}` is the fallback (non-toplevel) +behavior, because no specialized method for `Array` has been defined. +""" +function showarg(io::IO, ::Type{T}, toplevel) where {T} + toplevel || print(io, "::") + print(io, "Type{", T, "}") +end +function showarg(io::IO, x, toplevel) + toplevel || print(io, "::") + print(io, typeof(x)) +end +# This method resolves an ambiguity for packages that specialize on eltype +function showarg(io::IO, a::Array{Union{}}, toplevel) + toplevel || print(io, "::") + print(io, typeof(a)) +end + +# Container specializations +function showarg(io::IO, v::SubArray, toplevel) + print(io, "view(") + showarg(io, parent(v), false) + showindices(io, v.indices...) + print(io, ')') + toplevel && print(io, " with eltype ", eltype(v)) +end +showindices(io, ::Union{Slice,IdentityUnitRange}, inds...) = + (print(io, ", :"); showindices(io, inds...)) +showindices(io, ind1, inds...) = + (print(io, ", ", ind1); showindices(io, inds...)) +showindices(io) = nothing + +function showarg(io::IO, r::ReshapedArray, toplevel) + print(io, "reshape(") + showarg(io, parent(r), false) + print(io, ", ", join(r.dims, ", ")) + print(io, ')') + toplevel && print(io, " with eltype ", eltype(r)) +end + +function showarg(io::IO, r::ReinterpretArray{T}, toplevel) where {T} + print(io, "reinterpret(", T, ", ") + showarg(io, parent(r), false) + print(io, ')') +end + +# pretty printing for Iterators.Pairs +function Base.showarg(io::IO, r::Iterators.Pairs{<:Integer, <:Any, <:Any, T}, toplevel) where T<:AbstractArray + print(io, "pairs(IndexLinear(), ::", T, ")") +end + +function Base.showarg(io::IO, r::Iterators.Pairs{Symbol, <:Any, <:Any, T}, toplevel) where {T <: NamedTuple} + print(io, "pairs(::NamedTuple)") +end + +function Base.showarg(io::IO, r::Iterators.Pairs{<:Any, <:Any, I, D}, toplevel) where {D, I} + print(io, "Iterators.Pairs(::", D, ", ::", I, ")") +end + +# printing BitArrays + +# (following functions not exported - mainly intended for debug) + +function print_bit_chunk(io::IO, c::UInt64, l::Integer = 64) + for s = 0:l-1 + d = (c >>> s) & 1 + print(io, "01"[d + 1]) + if (s + 1) & 7 == 0 + print(io, " ") + end + end +end + +print_bit_chunk(c::UInt64, l::Integer) = print_bit_chunk(stdout, c, l) +print_bit_chunk(c::UInt64) = print_bit_chunk(stdout, c) + +function bitshow(io::IO, B::BitArray) + isempty(B) && return + Bc = B.chunks + for i = 1:length(Bc)-1 + print_bit_chunk(io, Bc[i]) + print(io, ": ") + end + l = _mod64(length(B)-1) + 1 + print_bit_chunk(io, Bc[end], l) +end +bitshow(B::BitArray) = bitshow(stdout, B) + +bitstring(B::BitArray) = sprint(bitshow, B) diff --git a/base/simdloop.jl b/base/simdloop.jl new file mode 100644 index 0000000..933a309 --- /dev/null +++ b/base/simdloop.jl @@ -0,0 +1,139 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Support for @simd for + +module SimdLoop + +export @simd, simd_outer_range, simd_inner_length, simd_index + +# Error thrown from ill-formed uses of @simd +struct SimdError <: Exception + msg::AbstractString +end + +# Parse iteration space expression +# symbol '=' range +# symbol 'in' range +function parse_iteration_space(x) + (isa(x, Expr) && (x.head === :(=) || x.head === :in)) || throw(SimdError("= or in expected")) + length(x.args) == 2 || throw(SimdError("simd range syntax is wrong")) + isa(x.args[1], Symbol) || throw(SimdError("simd loop index must be a symbol")) + x.args # symbol, range +end + +# reject invalid control flow statements in @simd loop body +function check_body!(x::Expr) + if x.head === :break || x.head === :continue + throw(SimdError("$(x.head) is not allowed inside a @simd loop body")) + elseif x.head === :macrocall && x.args[1] === Symbol("@goto") + throw(SimdError("$(x.args[1]) is not allowed inside a @simd loop body")) + end + for arg in x.args + check_body!(arg) + end + return true +end +check_body!(x::QuoteNode) = check_body!(x.value) +check_body!(x) = true + +# @simd splits a for loop into two loops: an outer scalar loop and +# an inner loop marked with :loopinfo. The simd_... functions define +# the splitting. +# Custom iterators that do not support random access cannot support +# vectorization. In order to be compatible with `@simd` annotated loops, +#they should override `simd_inner_length(v::MyIter, j) = 1`, +#`simd_outer_range(v::MyIter) = v`, and `simd_index(v::MyIter, j, i) = j`. + +# Get range for outer loop. +simd_outer_range(r) = 0:0 + +# Get trip count for inner loop. +@inline simd_inner_length(r, j) = Base.length(r) + +# Construct user-level element from original range, outer loop index j, and inner loop index i. +@inline simd_index(r, j, i) = (@inbounds ret = r[i+firstindex(r)]; ret) + +# Compile Expr x in context of @simd. +function compile(x, ivdep) + (isa(x, Expr) && x.head === :for) || throw(SimdError("for loop expected")) + length(x.args) == 2 || throw(SimdError("1D for loop expected")) + check_body!(x) + + var,range = parse_iteration_space(x.args[1]) + r = gensym("r") # Range value + j = gensym("i") # Iteration variable for outer loop + n = gensym("n") # Trip count for inner loop + i = gensym("i") # Trip index for inner loop + quote + # Evaluate range value once, to enhance type and data flow analysis by optimizers. + let $r = $range + for $j in Base.simd_outer_range($r) + let $n = Base.simd_inner_length($r,$j) + if zero($n) < $n + # Lower loop in way that seems to work best for LLVM 3.3 vectorizer. + let $i = zero($n) + while $i < $n + local $var = Base.simd_index($r,$j,$i) + $(x.args[2]) # Body of loop + $i += 1 + $(Expr(:loopinfo, Symbol("julia.simdloop"), ivdep)) # Mark loop as SIMD loop + end + end + end + end + end + end + nothing + end +end + +""" + @simd + +Annotate a `for` loop to allow the compiler to take extra liberties to allow loop re-ordering + +!!! warning + This feature is experimental and could change or disappear in future versions of Julia. + Incorrect use of the `@simd` macro may cause unexpected results. + +The object iterated over in a `@simd for` loop should be a one-dimensional range. +By using `@simd`, you are asserting several properties of the loop: + +* It is safe to execute iterations in arbitrary or overlapping order, with special consideration for reduction variables. +* Floating-point operations on reduction variables can be reordered, possibly causing different results than without `@simd`. + +In many cases, Julia is able to automatically vectorize inner for loops without the use of `@simd`. +Using `@simd` gives the compiler a little extra leeway to make it possible in more situations. In +either case, your inner loop should have the following properties to allow vectorization: + +* The loop must be an innermost loop +* The loop body must be straight-line code. Therefore, [`@inbounds`](@ref) is + currently needed for all array accesses. The compiler can sometimes turn + short `&&`, `||`, and `?:` expressions into straight-line code if it is safe + to evaluate all operands unconditionally. Consider using the [`ifelse`](@ref) + function instead of `?:` in the loop if it is safe to do so. +* Accesses must have a stride pattern and cannot be "gathers" (random-index + reads) or "scatters" (random-index writes). +* The stride should be unit stride. + +!!! note + The `@simd` does not assert by default that the loop is completely free of loop-carried + memory dependencies, which is an assumption that can easily be violated in generic code. + If you are writing non-generic code, you can use `@simd ivdep for ... end` to also assert that: + +* There exists no loop-carried memory dependencies +* No iteration ever waits on a previous iteration to make forward progress. +""" +macro simd(forloop) + esc(compile(forloop, nothing)) +end + +macro simd(ivdep, forloop) + if ivdep === :ivdep + esc(compile(forloop, Symbol("julia.ivdep"))) + else + throw(SimdError("Only ivdep is valid as the first argument to @simd")) + end +end + +end # module SimdLoop diff --git a/base/some.jl b/base/some.jl new file mode 100644 index 0000000..c79460b --- /dev/null +++ b/base/some.jl @@ -0,0 +1,95 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" + Some{T} + +A wrapper type used in `Union{Some{T}, Nothing}` to distinguish between the absence +of a value ([`nothing`](@ref)) and the presence of a `nothing` value (i.e. `Some(nothing)`). + +Use [`something`](@ref) to access the value wrapped by a `Some` object. +""" +struct Some{T} + value::T +end + +promote_rule(::Type{Some{T}}, ::Type{Some{S}}) where {T, S<:T} = Some{T} + +nonnothingtype(::Type{T}) where {T} = Core.Compiler.typesubtract(T, Nothing) +promote_rule(T::Type{Nothing}, S::Type) = Union{S, Nothing} +function promote_rule(T::Type{>:Nothing}, S::Type) + R = nonnothingtype(T) + R >: T && return Any + T = R + R = promote_type(T, S) + return Union{R, Nothing} +end + +function nonnothingtype_checked(T::Type) + R = nonnothingtype(T) + R >: T && error("could not compute non-nothing type") + return R +end + +convert(::Type{T}, x::T) where {T>:Nothing} = x +convert(::Type{T}, x) where {T>:Nothing} = convert(nonnothingtype_checked(T), x) +convert(::Type{Some{T}}, x::Some{T}) where {T} = x +convert(::Type{Some{T}}, x::Some) where {T} = Some{T}(convert(T, x.value)) + +function show(io::IO, x::Some) + if get(io, :typeinfo, Any) == typeof(x) + show(io, x.value) + else + print(io, "Some(") + show(io, x.value) + print(io, ')') + end +end + +""" + notnothing(x) + +Throw an error if `x === nothing`, and return `x` if not. +""" +notnothing(x::Any) = x +notnothing(::Nothing) = throw(ArgumentError("nothing passed to notnothing")) + +""" + isnothing(x) + +Return `true` if `x === nothing`, and return `false` if not. + +!!! compat "Julia 1.1" + This function requires at least Julia 1.1. +""" +isnothing(::Any) = false +isnothing(::Nothing) = true + + +""" + something(x, y...) + +Return the first value in the arguments which is not equal to [`nothing`](@ref), +if any. Otherwise throw an error. +Arguments of type [`Some`](@ref) are unwrapped. + +# Examples +```jldoctest +julia> something(nothing, 1) +1 + +julia> something(Some(1), nothing) +1 + +julia> something(missing, nothing) +missing + +julia> something(nothing, nothing) +ERROR: ArgumentError: No value arguments present +``` +""" +function something end + +something() = throw(ArgumentError("No value arguments present")) +something(x::Nothing, y...) = something(y...) +something(x::Some, y...) = x.value +something(x::Any, y...) = x diff --git a/base/sort.jl b/base/sort.jl new file mode 100644 index 0000000..cb48279 --- /dev/null +++ b/base/sort.jl @@ -0,0 +1,1137 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +module Sort + +import ..@__MODULE__, ..parentmodule +const Base = parentmodule(@__MODULE__) +using .Base.Order +using .Base: copymutable, LinearIndices, length, (:), + eachindex, axes, first, last, similar, zip, OrdinalRange, + AbstractVector, @inbounds, AbstractRange, @eval, @inline, Vector, @noinline, + AbstractMatrix, AbstractUnitRange, isless, identity, eltype, >, <, <=, >=, |, +, -, *, !, + extrema, sub_with_overflow, add_with_overflow, oneunit, div, getindex, setindex!, + length, resize!, fill, Missing, require_one_based_indexing + +using .Base: >>>, !== + +import .Base: + sort, + sort!, + issorted, + sortperm, + to_indices + +export # also exported by Base + # order-only: + issorted, + searchsorted, + searchsortedfirst, + searchsortedlast, + # order & algorithm: + sort, + sort!, + sortperm, + sortperm!, + partialsort, + partialsort!, + partialsortperm, + partialsortperm!, + # algorithms: + InsertionSort, + QuickSort, + MergeSort, + PartialQuickSort + +export # not exported by Base + Algorithm, + DEFAULT_UNSTABLE, + DEFAULT_STABLE, + SMALL_ALGORITHM, + SMALL_THRESHOLD + + +## functions requiring only ordering ## + +function issorted(itr, order::Ordering) + y = iterate(itr) + y === nothing && return true + prev, state = y + y = iterate(itr, state) + while y !== nothing + this, state = y + lt(order, this, prev) && return false + prev = this + y = iterate(itr, state) + end + return true +end + +""" + issorted(v, lt=isless, by=identity, rev:Bool=false, order::Ordering=Forward) + +Test whether a vector is in sorted order. The `lt`, `by` and `rev` keywords modify what +order is considered to be sorted just as they do for [`sort`](@ref). + +# Examples +```jldoctest +julia> issorted([1, 2, 3]) +true + +julia> issorted([(1, "b"), (2, "a")], by = x -> x[1]) +true + +julia> issorted([(1, "b"), (2, "a")], by = x -> x[2]) +false + +julia> issorted([(1, "b"), (2, "a")], by = x -> x[2], rev=true) +true +``` +""" +issorted(itr; + lt=isless, by=identity, rev::Union{Bool,Nothing}=nothing, order::Ordering=Forward) = + issorted(itr, ord(lt,by,rev,order)) + +function partialsort!(v::AbstractVector, k::Union{Int,OrdinalRange}, o::Ordering) + inds = axes(v, 1) + sort!(v, first(inds), last(inds), PartialQuickSort(k), o) + maybeview(v, k) +end + +maybeview(v, k) = view(v, k) +maybeview(v, k::Integer) = v[k] + +""" + partialsort!(v, k; by=, lt=, rev=false) + +Partially sort the vector `v` in place, according to the order specified by `by`, `lt` and +`rev` so that the value at index `k` (or range of adjacent values if `k` is a range) occurs +at the position where it would appear if the array were fully sorted via a non-stable +algorithm. If `k` is a single index, that value is returned; if `k` is a range, an array of +values at those indices is returned. Note that `partialsort!` does not fully sort the input +array. + +# Examples +```jldoctest +julia> a = [1, 2, 4, 3, 4] +5-element Array{Int64,1}: + 1 + 2 + 4 + 3 + 4 + +julia> partialsort!(a, 4) +4 + +julia> a +5-element Array{Int64,1}: + 1 + 2 + 3 + 4 + 4 + +julia> a = [1, 2, 4, 3, 4] +5-element Array{Int64,1}: + 1 + 2 + 4 + 3 + 4 + +julia> partialsort!(a, 4, rev=true) +2 + +julia> a +5-element Array{Int64,1}: + 4 + 4 + 3 + 2 + 1 +``` +""" +partialsort!(v::AbstractVector, k::Union{Int,OrdinalRange}; + lt=isless, by=identity, rev::Union{Bool,Nothing}=nothing, order::Ordering=Forward) = + partialsort!(v, k, ord(lt,by,rev,order)) + +""" + partialsort(v, k, by=, lt=, rev=false) + +Variant of [`partialsort!`](@ref) which copies `v` before partially sorting it, thereby returning the +same thing as `partialsort!` but leaving `v` unmodified. +""" +partialsort(v::AbstractVector, k::Union{Int,OrdinalRange}; kws...) = + partialsort!(copymutable(v), k; kws...) + + +# reference on sorted binary search: +# http://www.tbray.org/ongoing/When/200x/2003/03/22/Binary + +# index of the first value of vector a that is greater than or equal to x; +# returns length(v)+1 if x is greater than all values in v. +function searchsortedfirst(v::AbstractVector, x, lo::T, hi::T, o::Ordering) where T<:Integer + u = T(1) + lo = lo - u + hi = hi + u + @inbounds while lo < hi - u + m = (lo + hi) >>> 1 + if lt(o, v[m], x) + lo = m + else + hi = m + end + end + return hi +end + +# index of the last value of vector a that is less than or equal to x; +# returns 0 if x is less than all values of v. +function searchsortedlast(v::AbstractVector, x, lo::T, hi::T, o::Ordering) where T<:Integer + u = T(1) + lo = lo - u + hi = hi + u + @inbounds while lo < hi - u + m = (lo + hi) >>> 1 + if lt(o, x, v[m]) + hi = m + else + lo = m + end + end + return lo +end + +# returns the range of indices of v equal to x +# if v does not contain x, returns a 0-length range +# indicating the insertion point of x +function searchsorted(v::AbstractVector, x, ilo::T, ihi::T, o::Ordering) where T<:Integer + u = T(1) + lo = ilo - u + hi = ihi + u + @inbounds while lo < hi - u + m = (lo + hi) >>> 1 + if lt(o, v[m], x) + lo = m + elseif lt(o, x, v[m]) + hi = m + else + a = searchsortedfirst(v, x, max(lo,ilo), m, o) + b = searchsortedlast(v, x, m, min(hi,ihi), o) + return a : b + end + end + return (lo + 1) : (hi - 1) +end + +function searchsortedlast(a::AbstractRange{<:Real}, x::Real, o::DirectOrdering) + require_one_based_indexing(a) + if step(a) == 0 + lt(o, x, first(a)) ? 0 : length(a) + else + n = round(Integer, clamp((x - first(a)) / step(a) + 1, 1, length(a))) + lt(o, x, a[n]) ? n - 1 : n + end +end + +function searchsortedfirst(a::AbstractRange{<:Real}, x::Real, o::DirectOrdering) + require_one_based_indexing(a) + if step(a) == 0 + lt(o, first(a), x) ? length(a) + 1 : 1 + else + n = round(Integer, clamp((x - first(a)) / step(a) + 1, 1, length(a))) + lt(o, a[n] ,x) ? n + 1 : n + end +end + +function searchsortedlast(a::AbstractRange{<:Integer}, x::Real, o::DirectOrdering) + require_one_based_indexing(a) + if step(a) == 0 + lt(o, x, first(a)) ? 0 : length(a) + else + clamp( fld(floor(Integer, x) - first(a), step(a)) + 1, 0, length(a)) + end +end + +function searchsortedfirst(a::AbstractRange{<:Integer}, x::Real, o::DirectOrdering) + require_one_based_indexing(a) + if step(a) == 0 + lt(o, first(a), x) ? length(a)+1 : 1 + else + clamp(-fld(floor(Integer, -x) + first(a), step(a)) + 1, 1, length(a) + 1) + end +end + +function searchsortedfirst(a::AbstractRange{<:Integer}, x::Unsigned, o::DirectOrdering) + require_one_based_indexing(a) + if lt(o, first(a), x) + if step(a) == 0 + length(a) + 1 + else + min(cld(x - first(a), step(a)), length(a)) + 1 + end + else + 1 + end +end + +function searchsortedlast(a::AbstractRange{<:Integer}, x::Unsigned, o::DirectOrdering) + require_one_based_indexing(a) + if lt(o, x, first(a)) + 0 + elseif step(a) == 0 + length(a) + else + min(fld(x - first(a), step(a)) + 1, length(a)) + end +end + +searchsorted(a::AbstractRange{<:Real}, x::Real, o::DirectOrdering) = + searchsortedfirst(a, x, o) : searchsortedlast(a, x, o) + +for s in [:searchsortedfirst, :searchsortedlast, :searchsorted] + @eval begin + $s(v::AbstractVector, x, o::Ordering) = (inds = axes(v, 1); $s(v,x,first(inds),last(inds),o)) + $s(v::AbstractVector, x; + lt=isless, by=identity, rev::Union{Bool,Nothing}=nothing, order::Ordering=Forward) = + $s(v,x,ord(lt,by,rev,order)) + end +end + +""" + searchsorted(a, x; by=, lt=, rev=false) + +Return the range of indices of `a` which compare as equal to `x` (using binary search) +according to the order specified by the `by`, `lt` and `rev` keywords, assuming that `a` +is already sorted in that order. Return an empty range located at the insertion point +if `a` does not contain values equal to `x`. + +# Examples +```jldoctest +julia> searchsorted([1, 2, 4, 5, 5, 7], 4) # single match +3:3 + +julia> searchsorted([1, 2, 4, 5, 5, 7], 5) # multiple matches +4:5 + +julia> searchsorted([1, 2, 4, 5, 5, 7], 3) # no match, insert in the middle +3:2 + +julia> searchsorted([1, 2, 4, 5, 5, 7], 9) # no match, insert at end +7:6 + +julia> searchsorted([1, 2, 4, 5, 5, 7], 0) # no match, insert at start +1:0 +``` +""" searchsorted + +""" + searchsortedfirst(a, x; by=, lt=, rev=false) + +Return the index of the first value in `a` greater than or equal to `x`, according to the +specified order. Return `length(a) + 1` if `x` is greater than all values in `a`. +`a` is assumed to be sorted. + +# Examples +```jldoctest +julia> searchsortedfirst([1, 2, 4, 5, 5, 7], 4) # single match +3 + +julia> searchsortedfirst([1, 2, 4, 5, 5, 7], 5) # multiple matches +4 + +julia> searchsortedfirst([1, 2, 4, 5, 5, 7], 3) # no match, insert in the middle +3 + +julia> searchsortedfirst([1, 2, 4, 5, 5, 7], 9) # no match, insert at end +7 + +julia> searchsortedfirst([1, 2, 4, 5, 5, 7], 0) # no match, insert at start +1 +``` +""" searchsortedfirst + +""" + searchsortedlast(a, x; by=, lt=, rev=false) + +Return the index of the last value in `a` less than or equal to `x`, according to the +specified order. Return `0` if `x` is less than all values in `a`. `a` is assumed to +be sorted. + +# Examples +```jldoctest +julia> searchsortedlast([1, 2, 4, 5, 5, 7], 4) # single match +3 + +julia> searchsortedlast([1, 2, 4, 5, 5, 7], 5) # multiple matches +5 + +julia> searchsortedlast([1, 2, 4, 5, 5, 7], 3) # no match, insert in the middle +2 + +julia> searchsortedlast([1, 2, 4, 5, 5, 7], 9) # no match, insert at end +6 + +julia> searchsortedlast([1, 2, 4, 5, 5, 7], 0) # no match, insert at start +0 +``` +""" searchsortedlast + + +## sorting algorithms ## + +abstract type Algorithm end + +struct InsertionSortAlg <: Algorithm end +struct QuickSortAlg <: Algorithm end +struct MergeSortAlg <: Algorithm end + +""" + PartialQuickSort{T <: Union{Int,OrdinalRange}} + +Indicate that a sorting function should use the partial quick sort +algorithm. Partial quick sort returns the smallest `k` elements sorted from smallest +to largest, finding them and sorting them using [`QuickSort`](@ref). + +Characteristics: + * *not stable*: does not preserve the ordering of elements which + compare equal (e.g. "a" and "A" in a sort of letters which + ignores case). + * *in-place* in memory. + * *divide-and-conquer*: sort strategy similar to [`MergeSort`](@ref). +""" +struct PartialQuickSort{T <: Union{Int,OrdinalRange}} <: Algorithm + k::T +end + + +""" + InsertionSort + +Indicate that a sorting function should use the insertion sort +algorithm. Insertion sort traverses the collection one element +at a time, inserting each element into its correct, sorted position in +the output list. + +Characteristics: + * *stable*: preserves the ordering of elements which + compare equal (e.g. "a" and "A" in a sort of letters + which ignores case). + * *in-place* in memory. + * *quadratic performance* in the number of elements to be sorted: + it is well-suited to small collections but should not be used for large ones. +""" +const InsertionSort = InsertionSortAlg() +""" + QuickSort + +Indicate that a sorting function should use the quick sort +algorithm, which is *not* stable. + +Characteristics: + * *not stable*: does not preserve the ordering of elements which + compare equal (e.g. "a" and "A" in a sort of letters which + ignores case). + * *in-place* in memory. + * *divide-and-conquer*: sort strategy similar to [`MergeSort`](@ref). + * *good performance* for large collections. +""" +const QuickSort = QuickSortAlg() +""" + MergeSort + +Indicate that a sorting function should use the merge sort +algorithm. Merge sort divides the collection into +subcollections and repeatedly merges them, sorting each +subcollection at each step, until the entire +collection has been recombined in sorted form. + +Characteristics: + * *stable*: preserves the ordering of elements which compare + equal (e.g. "a" and "A" in a sort of letters which ignores + case). + * *not in-place* in memory. + * *divide-and-conquer* sort strategy. +""" +const MergeSort = MergeSortAlg() + +const DEFAULT_UNSTABLE = QuickSort +const DEFAULT_STABLE = MergeSort +const SMALL_ALGORITHM = InsertionSort +const SMALL_THRESHOLD = 20 + +function sort!(v::AbstractVector, lo::Int, hi::Int, ::InsertionSortAlg, o::Ordering) + @inbounds for i = lo+1:hi + j = i + x = v[i] + while j > lo + if lt(o, x, v[j-1]) + v[j] = v[j-1] + j -= 1 + continue + end + break + end + v[j] = x + end + return v +end + +# selectpivot! +# +# Given 3 locations in an array (lo, mi, and hi), sort v[lo], v[mi], v[hi]) and +# choose the middle value as a pivot +# +# Upon return, the pivot is in v[lo], and v[hi] is guaranteed to be +# greater than the pivot + +@inline function selectpivot!(v::AbstractVector, lo::Int, hi::Int, o::Ordering) + @inbounds begin + mi = (lo+hi)>>>1 + + # sort v[mi] <= v[lo] <= v[hi] such that the pivot is immediately in place + if lt(o, v[lo], v[mi]) + v[mi], v[lo] = v[lo], v[mi] + end + + if lt(o, v[hi], v[lo]) + if lt(o, v[hi], v[mi]) + v[hi], v[lo], v[mi] = v[lo], v[mi], v[hi] + else + v[hi], v[lo] = v[lo], v[hi] + end + end + + # return the pivot + return v[lo] + end +end + +# partition! +# +# select a pivot, and partition v according to the pivot + +function partition!(v::AbstractVector, lo::Int, hi::Int, o::Ordering) + pivot = selectpivot!(v, lo, hi, o) + # pivot == v[lo], v[hi] > pivot + i, j = lo, hi + @inbounds while true + i += 1; j -= 1 + while lt(o, v[i], pivot); i += 1; end; + while lt(o, pivot, v[j]); j -= 1; end; + i >= j && break + v[i], v[j] = v[j], v[i] + end + v[j], v[lo] = pivot, v[j] + + # v[j] == pivot + # v[k] >= pivot for k > j + # v[i] <= pivot for i < j + return j +end + +function sort!(v::AbstractVector, lo::Int, hi::Int, a::QuickSortAlg, o::Ordering) + @inbounds while lo < hi + hi-lo <= SMALL_THRESHOLD && return sort!(v, lo, hi, SMALL_ALGORITHM, o) + j = partition!(v, lo, hi, o) + if j-lo < hi-j + # recurse on the smaller chunk + # this is necessary to preserve O(log(n)) + # stack space in the worst case (rather than O(n)) + lo < (j-1) && sort!(v, lo, j-1, a, o) + lo = j+1 + else + j+1 < hi && sort!(v, j+1, hi, a, o) + hi = j-1 + end + end + return v +end + +function sort!(v::AbstractVector, lo::Int, hi::Int, a::MergeSortAlg, o::Ordering, t=similar(v,0)) + @inbounds if lo < hi + hi-lo <= SMALL_THRESHOLD && return sort!(v, lo, hi, SMALL_ALGORITHM, o) + + m = (lo+hi)>>>1 + (length(t) < m-lo+1) && resize!(t, m-lo+1) + + sort!(v, lo, m, a, o, t) + sort!(v, m+1, hi, a, o, t) + + i, j = 1, lo + while j <= m + t[i] = v[j] + i += 1 + j += 1 + end + + i, k = 1, lo + while k < j <= hi + if lt(o, v[j], t[i]) + v[k] = v[j] + j += 1 + else + v[k] = t[i] + i += 1 + end + k += 1 + end + while k < j + v[k] = t[i] + k += 1 + i += 1 + end + end + + return v +end + +function sort!(v::AbstractVector, lo::Int, hi::Int, a::PartialQuickSort{Int}, + o::Ordering) + @inbounds while lo < hi + hi-lo <= SMALL_THRESHOLD && return sort!(v, lo, hi, SMALL_ALGORITHM, o) + j = partition!(v, lo, hi, o) + if j >= a.k + # we don't need to sort anything bigger than j + hi = j-1 + elseif j-lo < hi-j + # recurse on the smaller chunk + # this is necessary to preserve O(log(n)) + # stack space in the worst case (rather than O(n)) + lo < (j-1) && sort!(v, lo, j-1, a, o) + lo = j+1 + else + (j+1) < hi && sort!(v, j+1, hi, a, o) + hi = j-1 + end + end + return v +end + + +function sort!(v::AbstractVector, lo::Int, hi::Int, a::PartialQuickSort{T}, + o::Ordering) where T<:OrdinalRange + @inbounds while lo < hi + hi-lo <= SMALL_THRESHOLD && return sort!(v, lo, hi, SMALL_ALGORITHM, o) + j = partition!(v, lo, hi, o) + + if j <= first(a.k) + lo = j+1 + elseif j >= last(a.k) + hi = j-1 + else + if j-lo < hi-j + lo < (j-1) && sort!(v, lo, j-1, a, o) + lo = j+1 + else + hi > (j+1) && sort!(v, j+1, hi, a, o) + hi = j-1 + end + end + end + return v +end + + +## generic sorting methods ## + +defalg(v::AbstractArray) = DEFAULT_STABLE +defalg(v::AbstractArray{<:Union{Number, Missing}}) = DEFAULT_UNSTABLE + +function sort!(v::AbstractVector, alg::Algorithm, order::Ordering) + inds = axes(v,1) + sort!(v,first(inds),last(inds),alg,order) +end + +""" + sort!(v; alg::Algorithm=defalg(v), lt=isless, by=identity, rev::Bool=false, order::Ordering=Forward) + +Sort the vector `v` in place. `QuickSort` is used by default for numeric arrays while +`MergeSort` is used for other arrays. You can specify an algorithm to use via the `alg` +keyword (see Sorting Algorithms for available algorithms). The `by` keyword lets you provide +a function that will be applied to each element before comparison; the `lt` keyword allows +providing a custom "less than" function; use `rev=true` to reverse the sorting order. These +options are independent and can be used together in all possible combinations: if both `by` +and `lt` are specified, the `lt` function is applied to the result of the `by` function; +`rev=true` reverses whatever ordering specified via the `by` and `lt` keywords. + +# Examples +```jldoctest +julia> v = [3, 1, 2]; sort!(v); v +3-element Array{Int64,1}: + 1 + 2 + 3 + +julia> v = [3, 1, 2]; sort!(v, rev = true); v +3-element Array{Int64,1}: + 3 + 2 + 1 + +julia> v = [(1, "c"), (3, "a"), (2, "b")]; sort!(v, by = x -> x[1]); v +3-element Array{Tuple{Int64,String},1}: + (1, "c") + (2, "b") + (3, "a") + +julia> v = [(1, "c"), (3, "a"), (2, "b")]; sort!(v, by = x -> x[2]); v +3-element Array{Tuple{Int64,String},1}: + (3, "a") + (2, "b") + (1, "c") +``` +""" +function sort!(v::AbstractVector; + alg::Algorithm=defalg(v), + lt=isless, + by=identity, + rev::Union{Bool,Nothing}=nothing, + order::Ordering=Forward) + ordr = ord(lt,by,rev,order) + if ordr === Forward && isa(v,Vector) && eltype(v)<:Integer + n = length(v) + if n > 1 + min, max = extrema(v) + (diff, o1) = sub_with_overflow(max, min) + (rangelen, o2) = add_with_overflow(diff, oneunit(diff)) + if !o1 && !o2 && rangelen < div(n,2) + return sort_int_range!(v, rangelen, min) + end + end + end + sort!(v, alg, ordr) +end + +# sort! for vectors of few unique integers +function sort_int_range!(x::Vector{<:Integer}, rangelen, minval) + offs = 1 - minval + n = length(x) + + where = fill(0, rangelen) + @inbounds for i = 1:n + where[x[i] + offs] += 1 + end + + idx = 1 + @inbounds for i = 1:rangelen + lastidx = idx + where[i] - 1 + val = i-offs + for j = idx:lastidx + x[j] = val + end + idx = lastidx + 1 + end + + return x +end + +""" + sort(v; alg::Algorithm=defalg(v), lt=isless, by=identity, rev::Bool=false, order::Ordering=Forward) + +Variant of [`sort!`](@ref) that returns a sorted copy of `v` leaving `v` itself unmodified. + +# Examples +```jldoctest +julia> v = [3, 1, 2]; + +julia> sort(v) +3-element Array{Int64,1}: + 1 + 2 + 3 + +julia> v +3-element Array{Int64,1}: + 3 + 1 + 2 +``` +""" +sort(v::AbstractVector; kws...) = sort!(copymutable(v); kws...) + +## partialsortperm: the permutation to sort the first k elements of an array ## + +""" + partialsortperm(v, k; by=, lt=, rev=false) + +Return a partial permutation `I` of the vector `v`, so that `v[I]` returns values of a fully +sorted version of `v` at index `k`. If `k` is a range, a vector of indices is returned; if +`k` is an integer, a single index is returned. The order is specified using the same +keywords as `sort!`. The permutation is stable, meaning that indices of equal elements +appear in ascending order. + +Note that this function is equivalent to, but more efficient than, calling `sortperm(...)[k]`. + +# Examples +```jldoctest +julia> v = [3, 1, 2, 1]; + +julia> v[partialsortperm(v, 1)] +1 + +julia> p = partialsortperm(v, 1:3) +3-element view(::Array{Int64,1}, 1:3) with eltype Int64: + 2 + 4 + 3 + +julia> v[p] +3-element Array{Int64,1}: + 1 + 1 + 2 +``` +""" +partialsortperm(v::AbstractVector, k::Union{Integer,OrdinalRange}; kwargs...) = + partialsortperm!(similar(Vector{eltype(k)}, axes(v,1)), v, k; kwargs..., initialized=false) + +""" + partialsortperm!(ix, v, k; by=, lt=, rev=false, initialized=false) + +Like [`partialsortperm`](@ref), but accepts a preallocated index vector `ix`. If `initialized` is `false` +(the default), `ix` is initialized to contain the values `1:length(ix)`. +""" +function partialsortperm!(ix::AbstractVector{<:Integer}, v::AbstractVector, + k::Union{Int, OrdinalRange}; + lt::Function=isless, + by::Function=identity, + rev::Union{Bool,Nothing}=nothing, + order::Ordering=Forward, + initialized::Bool=false) + if !initialized + @inbounds for i = axes(ix,1) + ix[i] = i + end + end + + # do partial quicksort + sort!(ix, PartialQuickSort(k), Perm(ord(lt, by, rev, order), v)) + + maybeview(ix, k) +end + +## sortperm: the permutation to sort an array ## + +""" + sortperm(v; alg::Algorithm=DEFAULT_UNSTABLE, lt=isless, by=identity, rev::Bool=false, order::Ordering=Forward) + +Return a permutation vector `I` that puts `v[I]` in sorted order. The order is specified +using the same keywords as [`sort!`](@ref). The permutation is guaranteed to be stable even +if the sorting algorithm is unstable, meaning that indices of equal elements appear in +ascending order. + +See also [`sortperm!`](@ref). + +# Examples +```jldoctest +julia> v = [3, 1, 2]; + +julia> p = sortperm(v) +3-element Array{Int64,1}: + 2 + 3 + 1 + +julia> v[p] +3-element Array{Int64,1}: + 1 + 2 + 3 +``` +""" +function sortperm(v::AbstractVector; + alg::Algorithm=DEFAULT_UNSTABLE, + lt=isless, + by=identity, + rev::Union{Bool,Nothing}=nothing, + order::Ordering=Forward) + ordr = ord(lt,by,rev,order) + if ordr === Forward && isa(v,Vector) && eltype(v)<:Integer + n = length(v) + if n > 1 + min, max = extrema(v) + (diff, o1) = sub_with_overflow(max, min) + (rangelen, o2) = add_with_overflow(diff, oneunit(diff)) + if !o1 && !o2 && rangelen < div(n,2) + return sortperm_int_range(v, rangelen, min) + end + end + end + p = similar(Vector{Int}, axes(v, 1)) + for (i,ind) in zip(eachindex(p), axes(v, 1)) + p[i] = ind + end + sort!(p, alg, Perm(ordr,v)) +end + + +""" + sortperm!(ix, v; alg::Algorithm=DEFAULT_UNSTABLE, lt=isless, by=identity, rev::Bool=false, order::Ordering=Forward, initialized::Bool=false) + +Like [`sortperm`](@ref), but accepts a preallocated index vector `ix`. If `initialized` is `false` +(the default), `ix` is initialized to contain the values `1:length(v)`. + +# Examples +```jldoctest +julia> v = [3, 1, 2]; p = zeros(Int, 3); + +julia> sortperm!(p, v); p +3-element Array{Int64,1}: + 2 + 3 + 1 + +julia> v[p] +3-element Array{Int64,1}: + 1 + 2 + 3 +``` +""" +function sortperm!(x::AbstractVector{<:Integer}, v::AbstractVector; + alg::Algorithm=DEFAULT_UNSTABLE, + lt=isless, + by=identity, + rev::Union{Bool,Nothing}=nothing, + order::Ordering=Forward, + initialized::Bool=false) + if axes(x,1) != axes(v,1) + throw(ArgumentError("index vector must have the same indices as the source vector, $(axes(x,1)) != $(axes(v,1))")) + end + if !initialized + @inbounds for i = axes(v,1) + x[i] = i + end + end + sort!(x, alg, Perm(ord(lt,by,rev,order),v)) +end + +# sortperm for vectors of few unique integers +function sortperm_int_range(x::Vector{<:Integer}, rangelen, minval) + offs = 1 - minval + n = length(x) + + where = fill(0, rangelen+1) + where[1] = 1 + @inbounds for i = 1:n + where[x[i] + offs + 1] += 1 + end + + #cumsum!(where, where) + @inbounds for i = 2:length(where) + where[i] += where[i-1] + end + + P = Vector{Int}(undef, n) + @inbounds for i = 1:n + label = x[i] + offs + P[where[label]] = i + where[label] += 1 + end + + return P +end + +## sorting multi-dimensional arrays ## + +""" + sort(A; dims::Integer, alg::Algorithm=DEFAULT_UNSTABLE, lt=isless, by=identity, rev::Bool=false, order::Ordering=Forward) + +Sort a multidimensional array `A` along the given dimension. +See [`sort!`](@ref) for a description of possible +keyword arguments. + +To sort slices of an array, refer to [`sortslices`](@ref). + +# Examples +```jldoctest +julia> A = [4 3; 1 2] +2×2 Array{Int64,2}: + 4 3 + 1 2 + +julia> sort(A, dims = 1) +2×2 Array{Int64,2}: + 1 2 + 4 3 + +julia> sort(A, dims = 2) +2×2 Array{Int64,2}: + 3 4 + 1 2 +``` +""" +function sort(A::AbstractArray; + dims::Integer, + alg::Algorithm=DEFAULT_UNSTABLE, + lt=isless, + by=identity, + rev::Union{Bool,Nothing}=nothing, + order::Ordering=Forward) + dim = dims + order = ord(lt,by,rev,order) + n = length(axes(A, dim)) + if dim != 1 + pdims = (dim, setdiff(1:ndims(A), dim)...) # put the selected dimension first + Ap = permutedims(A, pdims) + Av = vec(Ap) + sort_chunks!(Av, n, alg, order) + permutedims(Ap, invperm(pdims)) + else + Av = A[:] + sort_chunks!(Av, n, alg, order) + reshape(Av, axes(A)) + end +end + +@noinline function sort_chunks!(Av, n, alg, order) + inds = LinearIndices(Av) + for s = first(inds):n:last(inds) + sort!(Av, s, s+n-1, alg, order) + end + Av +end + +""" + sort!(A; dims::Integer, alg::Algorithm=defalg(A), lt=isless, by=identity, rev::Bool=false, order::Ordering=Forward) + +Sort the multidimensional array `A` along dimension `dims`. +See [`sort!`](@ref) for a description of possible keyword arguments. + +To sort slices of an array, refer to [`sortslices`](@ref). + +!!! compat "Julia 1.1" + This function requires at least Julia 1.1. + +# Examples +```jldoctest +julia> A = [4 3; 1 2] +2×2 Array{Int64,2}: + 4 3 + 1 2 + +julia> sort!(A, dims = 1); A +2×2 Array{Int64,2}: + 1 2 + 4 3 + +julia> sort!(A, dims = 2); A +2×2 Array{Int64,2}: + 1 2 + 3 4 +``` +""" +function sort!(A::AbstractArray; + dims::Integer, + alg::Algorithm=defalg(A), + lt=isless, + by=identity, + rev::Union{Bool,Nothing}=nothing, + order::Ordering=Forward) + ordr = ord(lt, by, rev, order) + nd = ndims(A) + k = dims + + 1 <= k <= nd || throw(ArgumentError("dimension out of range")) + + remdims = ntuple(i -> i == k ? 1 : size(A, i), nd) + for idx in CartesianIndices(remdims) + Av = view(A, ntuple(i -> i == k ? Colon() : idx[i], nd)...) + sort!(Av, alg, ordr) + end + A +end + +## fast clever sorting for floats ## + +module Float +using ..Sort +using ...Order +using ..Base: @inbounds, AbstractVector, Vector, last, axes + +import Core.Intrinsics: slt_int +import ..Sort: sort! +import ...Order: lt, DirectOrdering + +const Floats = Union{Float32,Float64} + +struct Left <: Ordering end +struct Right <: Ordering end + +left(::DirectOrdering) = Left() +right(::DirectOrdering) = Right() + +left(o::Perm) = Perm(left(o.order), o.data) +right(o::Perm) = Perm(right(o.order), o.data) + +lt(::Left, x::T, y::T) where {T<:Floats} = slt_int(y, x) +lt(::Right, x::T, y::T) where {T<:Floats} = slt_int(x, y) + +isnan(o::DirectOrdering, x::Floats) = (x!=x) +isnan(o::Perm, i::Int) = isnan(o.order,o.data[i]) + +function nans2left!(v::AbstractVector, o::Ordering, lo::Int=first(axes(v,1)), hi::Int=last(axes(v,1))) + i = lo + @inbounds while i <= hi && isnan(o,v[i]) + i += 1 + end + j = i + 1 + @inbounds while j <= hi + if isnan(o,v[j]) + v[i], v[j] = v[j], v[i] + i += 1 + end + j += 1 + end + return i, hi +end +function nans2right!(v::AbstractVector, o::Ordering, lo::Int=first(axes(v,1)), hi::Int=last(axes(v,1))) + i = hi + @inbounds while lo <= i && isnan(o,v[i]) + i -= 1 + end + j = i - 1 + @inbounds while lo <= j + if isnan(o,v[j]) + v[i], v[j] = v[j], v[i] + i -= 1 + end + j -= 1 + end + return lo, i +end + +nans2end!(v::AbstractVector, o::ForwardOrdering) = nans2right!(v,o) +nans2end!(v::AbstractVector, o::ReverseOrdering) = nans2left!(v,o) +nans2end!(v::AbstractVector{Int}, o::Perm{<:ForwardOrdering}) = nans2right!(v,o) +nans2end!(v::AbstractVector{Int}, o::Perm{<:ReverseOrdering}) = nans2left!(v,o) + +issignleft(o::ForwardOrdering, x::Floats) = lt(o, x, zero(x)) +issignleft(o::ReverseOrdering, x::Floats) = lt(o, x, -zero(x)) +issignleft(o::Perm, i::Int) = issignleft(o.order, o.data[i]) + +function fpsort!(v::AbstractVector, a::Algorithm, o::Ordering) + i, j = lo, hi = nans2end!(v,o) + @inbounds while true + while i <= j && issignleft(o,v[i]); i += 1; end + while i <= j && !issignleft(o,v[j]); j -= 1; end + i <= j || break + v[i], v[j] = v[j], v[i] + i += 1; j -= 1 + end + sort!(v, lo, j, a, left(o)) + sort!(v, i, hi, a, right(o)) + return v +end + + +fpsort!(v::AbstractVector, a::Sort.PartialQuickSort, o::Ordering) = + sort!(v, first(axes(v,1)), last(axes(v,1)), a, o) + +sort!(v::AbstractVector{<:Floats}, a::Algorithm, o::DirectOrdering) = fpsort!(v,a,o) +sort!(v::Vector{Int}, a::Algorithm, o::Perm{<:DirectOrdering,<:Vector{<:Floats}}) = fpsort!(v,a,o) + +end # module Sort.Float + +end # module Sort diff --git a/base/special/cbrt.jl b/base/special/cbrt.jl new file mode 100644 index 0000000..e902cf0 --- /dev/null +++ b/base/special/cbrt.jl @@ -0,0 +1,149 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Float32/Float64 based on C implementations from FDLIBM (http://www.netlib.org/fdlibm/) +# and FreeBSD: +# +## ==================================================== +## Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +## +## Developed at SunPro, a Sun Microsystems, Inc. business. +## Permission to use, copy, modify, and distribute this +## software is freely granted, provided that this notice +## is preserved. +## ==================================================== +## Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. +## Debugged and optimized by Bruce D. Evans. + +""" + cbrt(x::Real) + +Return the cube root of `x`, i.e. ``x^{1/3}``. Negative values are accepted +(returning the negative real root when ``x < 0``). + +The prefix operator `∛` is equivalent to `cbrt`. + +# Examples +```jldoctest +julia> cbrt(big(27)) +3.0 + +julia> cbrt(big(-27)) +-3.0 +``` +""" +cbrt(x::Real) = cbrt(float(x)) +cbrt(x::AbstractFloat) = x < 0 ? -(-x)^(1//3) : x^(1//3) + +""" + _approx_cbrt(x) + +Approximate `cbrt` to 5 bits precision + + cbrt(2^e * (1+m)) ≈ 2^(e÷3) * (1 + (e%3+m)÷3) + +where: + - `e` is integral and >= 0 + - `m` is real and in [0, 1), + - `÷` is integer division + - `%` is integer remainder + +The RHS is always >= the LHS and has a maximum relative error of about 1 in 16. +Adding a bias of -0.03306235651 to the `(e%3+m)÷3` term reduces the error to about 1 in +32. + +With the IEEE floating point representation, for finite positive normal values, ordinary +integer divison of the value in bits magically gives almost exactly the RHS of the above +provided we first subtract the exponent bias and later add it back. We do the +subtraction virtually to keep e >= 0 so that ordinary integer division rounds towards +minus infinity; this is also efficient. All operations can be done in 32-bit. + +These implementations assume that NaNs, infinities and zeros have already been filtered. +""" +@inline function _approx_cbrt(x::T) where {T<:Union{Float32,Float64}} + # floor(UInt32, adj * exp2(k)) should be evaluated to 2 constants. + adj = exponent_bias(T)*2/3 - 0.03306235651 + k = significand_bits(T) - (8*sizeof(T) - 32) + + u = highword(x) & 0x7fff_ffff + if u >= Base.Math.highword(floatmin(T)) + v = div(u, UInt32(3)) + floor(UInt32, adj * exp2(k)) + else + # subnormal + x *= maxintfloat(T) + adj -= exponent(maxintfloat(T))/3 + u = highword(x) & 0x7fff_ffff + v = div(u, UInt32(3)) + floor(UInt32, adj * exp2(k)) + end + return copysign(fromhighword(T, v), x) +end + +@inline function _improve_cbrt(x::Float32, t::Float32) + # Newton iterations solving + # t^2 - x/t == 0 + # with update + # t <- t*(t^3 + 2*x)/(2*t^3 + x) + + # Use double precision so that its terms can be arranged for efficiency + # without causing overflow or underflow. + xx = Float64(x) + tt = Float64(t) + + # 1st step: 16 bits accuracy + tt3 = tt^3 + tt *= (2*xx + tt3)/(x + 2*tt3) + + # 2nd step: 47 bits accuracy + tt3 = tt^3 + tt *= (2*xx + tt3)/(x + 2*tt3) + + return Float32(tt) +end + +@inline function _improve_cbrt(x::Float64, t::Float64) + # cbrt to 23 bits: + # + # cbrt(x) = t * cbrt(x / t^3) ~= t * P(t^3 / x) + # + # where P(r) is a polynomial of degree 4 that approximates 1/cbrt(r) + # to within 2^-23.5 when |r - 1| < 1/10. The rough approximation + # has produced t such than |t/cbrt(x) - 1| ~< 1/32, and cubing this + # gives us bounds for r = t^3/x. + + r = (t*t)*(t/x) + t *= (@horner(r, 1.87595182427177009643, -1.88497979543377169875, 1.621429720105354466140) + + r^3 * @horner(r, -0.758397934778766047437, 0.145996192886612446982)) + + # Round t away from zero to 23 bits (sloppily except for ensuring that + # the result is larger in magnitude than cbrt(x) but not much more than + # 2 23-bit ulps larger). With rounding towards zero, the error bound + # would be ~5/6 instead of ~4/6. With a maximum error of 2 23-bit ulps + # in the rounded t, the infinite-precision error in the Newton + # approximation barely affects third digit in the final error + # 0.667; the error in the rounded t can be up to about 3 23-bit ulps + # before the final error is larger than 0.667 ulps. + + u = reinterpret(UInt64, t) + u = (u + 0x8000_0000) & UInt64(0xffff_ffff_c000_0000) + t = reinterpret(Float64, u) + + # one step Newton iteration solving + # t^3 - x == 0 + # with update + # t <- t + t * (x/t^2 - t) / (3*t) + + # to 53 bits with error < 0.667 ulps + s = t*t # t*t is exact + r = x/s # error <= 0.5 ulps; |r| < |t| + w = t+t # t+t is exact + r = (r - t)/(w + r) # r-t is exact; w+r ~= 3*t + t = muladd(t, r, t) # error <= 0.5 + 0.5/3 + epsilon + return t +end + +function cbrt(x::Union{Float32,Float64}) + if !isfinite(x) || iszero(x) + return x + end + t = _approx_cbrt(x) + return _improve_cbrt(x, t) +end diff --git a/base/special/exp.jl b/base/special/exp.jl new file mode 100644 index 0000000..493e516 --- /dev/null +++ b/base/special/exp.jl @@ -0,0 +1,138 @@ +# Based on FDLIBM http://www.netlib.org/fdlibm/e_exp.c +# which is made available under the following licence + +## Copyright (C) 2004 by Sun Microsystems, Inc. All rights reserved. Permission +## to use, copy, modify, and distribute this software is freely granted, +## provided that this notice is preserved. + +# Method +# 1. Argument reduction: Reduce x to an r so that |r| <= 0.5*ln(2). Given x, +# find r and integer k such that +# x = k*ln(2) + r, |r| <= 0.5*ln(2). +# Here r is represented as r = hi - lo for better accuracy. +# +# 2. Approximate exp(r) by a special rational function on [0, 0.5*ln(2)]: +# R(r^2) = r*(exp(r)+1)/(exp(r)-1) = 2 + r*r/6 - r^4/360 + ... +# +# A special Remez algorithm on [0, 0.5*ln(2)] is used to generate a +# polynomial to approximate R. +# +# The computation of exp(r) thus becomes +# 2*r +# exp(r) = 1 + ---------- +# R(r) - r +# r*c(r) +# = 1 + r + ----------- (for better accuracy) +# 2 - c(r) +# where +# c(r) = r - (P1*r^2 + P2*r^4 + ... + P5*r^10 + ...). +# +# 3. Scale back: exp(x) = 2^k * exp(r) + +# log(2) +const LN2 = 6.931471805599453094172321214581765680755001343602552541206800094933936219696955e-01 +# log2(e) +const LOG2_E = 1.442695040888963407359924681001892137426646 + +# log(2) into upper and lower bits +LN2U(::Type{Float64}) = 6.93147180369123816490e-1 +LN2U(::Type{Float32}) = 6.9313812256f-1 + +LN2L(::Type{Float64}) = 1.90821492927058770002e-10 +LN2L(::Type{Float32}) = 9.0580006145f-6 + +# max and min arguments +MAX_EXP(::Type{Float64}) = 7.09782712893383996732e2 # log 2^1023*(2-2^-52) +MAX_EXP(::Type{Float32}) = 88.72283905206835f0 # log 2^127 *(2-2^-23) + +# one less than the min exponent since we can sqeeze a bit more from the exp function +MIN_EXP(::Type{Float64}) = -7.451332191019412076235e2 # log 2^-1075 +MIN_EXP(::Type{Float32}) = -103.97207708f0 # log 2^-150 + +@inline exp_kernel(x::Float64) = @horner(x, 1.66666666666666019037e-1, + -2.77777777770155933842e-3, 6.61375632143793436117e-5, + -1.65339022054652515390e-6, 4.13813679705723846039e-8) + +@inline exp_kernel(x::Float32) = @horner(x, 1.6666625440f-1, -2.7667332906f-3) + +# for values smaller than this threshold just use a Taylor expansion +@eval exp_small_thres(::Type{Float64}) = $(2.0^-28) +@eval exp_small_thres(::Type{Float32}) = $(2.0f0^-13) + +""" + exp(x) + +Compute the natural base exponential of `x`, in other words ``e^x``. + +# Examples +```jldoctest +julia> exp(1.0) +2.718281828459045 +``` +""" +exp(x::Real) = exp(float(x)) +function exp(x::T) where T<:Union{Float32,Float64} + xa = reinterpret(Unsigned, x) & ~sign_mask(T) + xsb = signbit(x) + + # filter out non-finite arguments + if xa > reinterpret(Unsigned, MAX_EXP(T)) + if xa >= exponent_mask(T) + xa & significand_mask(T) != 0 && return T(NaN) + return xsb ? T(0.0) : T(Inf) # exp(+-Inf) + end + x > MAX_EXP(T) && return T(Inf) + x < MIN_EXP(T) && return T(0.0) + end + # This implementation gives 2.7182818284590455 for exp(1.0) when T == + # Float64, which is well within the allowable error; however, + # 2.718281828459045 is closer to the true value so we prefer that answer, + # given that 1.0 is such an important argument value. + if x == T(1.0) && T == Float64 + return 2.718281828459045235360 + end + # compute approximation + if xa > reinterpret(Unsigned, T(0.5)*T(LN2)) # |x| > 0.5 log(2) + # argument reduction + if xa < reinterpret(Unsigned, T(1.5)*T(LN2)) # |x| < 1.5 log(2) + if xsb + k = -1 + hi = x + LN2U(T) + lo = -LN2L(T) + else + k = 1 + hi = x - LN2U(T) + lo = LN2L(T) + end + else + n = round(T(LOG2_E)*x) + k = unsafe_trunc(Int,n) + hi = muladd(n, -LN2U(T), x) + lo = n*LN2L(T) + end + # compute approximation on reduced argument + r = hi - lo + z = r*r + p = r - z*exp_kernel(z) + y = T(1.0) - ((lo - (r*p)/(T(2.0) - p)) - hi) + # scale back + if k > -significand_bits(T) + # multiply by 2.0 first to prevent overflow, which helps extends the range + k == exponent_max(T) && return y * T(2.0) * T(2.0)^(exponent_max(T) - 1) + twopk = reinterpret(T, rem(exponent_bias(T) + k, uinttype(T)) << significand_bits(T)) + return y*twopk + else + # add significand_bits(T) + 1 to lift the range outside the subnormals + twopk = reinterpret(T, rem(exponent_bias(T) + significand_bits(T) + 1 + k, uinttype(T)) << significand_bits(T)) + return y * twopk * T(2.0)^(-significand_bits(T) - 1) + end + elseif xa < reinterpret(Unsigned, exp_small_thres(T)) # |x| < exp_small_thres + # Taylor approximation for small values: exp(x) ≈ 1.0 + x + return T(1.0) + x + else + # primary range with k = 0, so compute approximation directly + z = x*x + p = x - z*exp_kernel(z) + return T(1.0) - ((x*p)/(p - T(2.0)) - x) + end +end diff --git a/base/special/exp10.jl b/base/special/exp10.jl new file mode 100644 index 0000000..c32d0a9 --- /dev/null +++ b/base/special/exp10.jl @@ -0,0 +1,139 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Method +# 1. Argument reduction: Reduce x to an r so that |r| <= 0.5*log10(2). Given x, +# find r and integer k such that +# +# x = k*log10(2) + r, |r| <= 0.5*log10(2). +# +# 2. Approximate exp10(r) by a polynomial on the interval [-0.5*log10(2), 0.5*log10(2)]: +# +# exp10(x) = 1.0 + polynomial(x), +# +# sup norm relative error within the interval of the polynomial approximations: +# Float64 : [2.7245504724394698952e-18; 2.7245529895753476720e-18] +# Float32 : [9.6026471477842205871e-10; 9.6026560194009888672e-10] +# +# 3. Scale back: exp10(x) = 2^k * exp10(r) + +# log2(10) +const LOG2_10 = 3.321928094887362347870319429489390175864831393024580612054756395815934776608624 +# log10(2) +const LOG10_2 = 3.010299956639811952137388947244930267681898814621085413104274611271081892744238e-01 +# log(10) +const LN10 = 2.302585092994045684017991454684364207601101488628772976033327900967572609677367 + +# log10(2) into upper and lower bits +LOG10_2U(::Type{Float64}) = 3.01025390625000000000e-1 +LOG10_2U(::Type{Float32}) = 3.00781250000000000000f-1 + +LOG10_2L(::Type{Float64}) = 4.60503898119521373889e-6 +LOG10_2L(::Type{Float32}) = 2.48745663981195213739f-4 + +# max and min arguments +MAX_EXP10(::Type{Float64}) = 3.08254715559916743851e2 # log 2^1023*(2-2^-52) +MAX_EXP10(::Type{Float32}) = 38.531839419103626f0 # log 2^127 *(2-2^-23) + +# one less than the min exponent since we can sqeeze a bit more from the exp10 function +MIN_EXP10(::Type{Float64}) = -3.23607245338779784854769e2 # log10 2^-1075 +MIN_EXP10(::Type{Float32}) = -45.15449934959718f0 # log10 2^-150 + +@inline exp10_kernel(x::Float64) = + @horner(x, 1.0, + 2.30258509299404590109361379290930926799774169921875, + 2.6509490552391992146397114993305876851081848144531, + 2.03467859229323178027470930828712880611419677734375, + 1.17125514891212478829629617393948137760162353515625, + 0.53938292928868392106522833273629657924175262451172, + 0.20699584873167015119932443667494226247072219848633, + 6.8089348259156870502017966373387025669217109680176e-2, + 1.9597690535095281527677713029333972372114658355713e-2, + 5.015553121397981796436571499953060992993414402008e-3, + 1.15474960721768829356725927226534622604958713054657e-3, + 1.55440426715227567738830671828509366605430841445923e-4, + 3.8731032432074128681303432086835414338565897196531e-5, + 2.3804466459036747669197886523306806338950991630554e-3, + 9.3881392238209649520573607528461934634833596646786e-5, + -2.64330486232183387018679354696359951049089431762695e-2) + +@inline exp10_kernel(x::Float32) = + @horner(x, 1.0f0, + 2.302585124969482421875f0, + 2.650949001312255859375f0, + 2.0346698760986328125f0, + 1.17125606536865234375f0, + 0.5400512218475341796875f0, + 0.20749187469482421875f0, + 5.2789829671382904052734375f-2) + +@eval exp10_small_thres(::Type{Float64}) = $(2.0^-29) +@eval exp10_small_thres(::Type{Float32}) = $(2.0f0^-14) + +""" + exp10(x) + +Compute ``10^x``. + +# Examples +```jldoctest +julia> exp10(2) +100.0 + +julia> exp10(0.2) +1.5848931924611136 +``` +""" +exp10(x::Real) = exp10(float(x)) +function exp10(x::T) where T<:Union{Float32,Float64} + xa = reinterpret(Unsigned, x) & ~sign_mask(T) + xsb = signbit(x) + + # filter out non-finite arguments + if xa > reinterpret(Unsigned, MAX_EXP10(T)) + if xa >= exponent_mask(T) + xa & significand_mask(T) != 0 && return T(NaN) + return xsb ? T(0.0) : T(Inf) # exp10(+-Inf) + end + x > MAX_EXP10(T) && return T(Inf) + x < MIN_EXP10(T) && return T(0.0) + end + # compute approximation + if xa > reinterpret(Unsigned, T(0.5)*T(LOG10_2)) # |x| > 0.5 log10(2). + # argument reduction + if xa < reinterpret(Unsigned, T(1.5)*T(LOG10_2)) # |x| <= 1.5 log10(2) + if xsb + k = -1 + r = LOG10_2U(T) + x + r = LOG10_2L(T) + r + else + k = 1 + r = x - LOG10_2U(T) + r = r - LOG10_2L(T) + end + else + n = round(T(LOG2_10)*x) + k = unsafe_trunc(Int,n) + r = muladd(n, -LOG10_2U(T), x) + r = muladd(n, -LOG10_2L(T), r) + end + # compute approximation on reduced argument + y = exp10_kernel(r) + # scale back + if k > -significand_bits(T) + # multiply by 2.0 first to prevent overflow, extending the range + k == exponent_max(T) && return y * T(2.0) * T(2.0)^(exponent_max(T) - 1) + twopk = reinterpret(T, rem(exponent_bias(T) + k, uinttype(T)) << significand_bits(T)) + return y*twopk + else + # add significand_bits(T) + 1 to lift the range outside the subnormals + twopk = reinterpret(T, rem(exponent_bias(T) + significand_bits(T) + 1 + k, uinttype(T)) << significand_bits(T)) + return y * twopk * T(2.0)^(-significand_bits(T) - 1) + end + elseif xa < reinterpret(Unsigned, exp10_small_thres(T)) # |x| < exp10_small_thres + # Taylor approximation for small values: exp10(x) ≈ 1.0 + log(10)*x + return muladd(x, T(LN10), T(1.0)) + else + # primary range with k = 0, so compute approximation directly + return exp10_kernel(x) + end +end diff --git a/base/special/hyperbolic.jl b/base/special/hyperbolic.jl new file mode 100644 index 0000000..4b0e994 --- /dev/null +++ b/base/special/hyperbolic.jl @@ -0,0 +1,300 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# sinh, cosh, tanh, asinh, acosh, and atanh are heavily based on FDLIBM code: +# e_sinh.c, e_sinhf, e_cosh.c, e_coshf, s_tanh.c, s_tanhf.c, s_asinh.c, +# s_asinhf.c, e_acosh.c, e_coshf.c, e_atanh.c, and e_atanhf.c +# that are made available under the following licence: + +# ==================================================== +# Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +# +# Developed at SunSoft, a Sun Microsystems, Inc. business. +# Permission to use, copy, modify, and distribute this +# software is freely granted, provided that this notice +# is preserved. +# ==================================================== + +# Hyperbolic functions +# sinh methods +H_SMALL_X(::Type{Float64}) = 2.0^-28 +H_MEDIUM_X(::Type{Float64}) = 22.0 + +H_SMALL_X(::Type{Float32}) = 2f-12 +H_MEDIUM_X(::Type{Float32}) = 9f0 + +H_LARGE_X(::Type{Float64}) = 709.7822265633563 # nextfloat(709.7822265633562) +H_OVERFLOW_X(::Type{Float64}) = 710.475860073944 # nextfloat(710.4758600739439) + +H_LARGE_X(::Type{Float32}) = 88.72283f0 +H_OVERFLOW_X(::Type{Float32}) = 89.415985f0 +function sinh(x::T) where T <: Union{Float32, Float64} + # Method + # mathematically sinh(x) is defined to be (exp(x)-exp(-x))/2 + # 1. Replace x by |x| (sinh(-x) = -sinh(x)). + # 2. Find the branch and the expression to calculate and return it + # a) 0 <= x < H_SMALL_X + # return x + # b) H_SMALL_X <= x < H_MEDIUM_X + # return sinh(x) = (E + E/(E+1))/2, where E=expm1(x) + # c) H_MEDIUM_X <= x < H_LARGE_X + # return sinh(x) = exp(x)/2 + # d) H_LARGE_X <= x < H_OVERFLOW_X + # return sinh(x) = exp(x/2)/2 * exp(x/2) + # e) H_OVERFLOW_X <= x + # return sinh(x) = T(Inf) + # + # Notes: + # only sinh(0) = 0 is exact for finite x. + + isnan(x) && return x + + absx = abs(x) + + h = T(0.5) + if x < 0 + h = -h + end + # in a) or b) + if absx < H_MEDIUM_X(T) + # in a) + if absx < H_SMALL_X(T) + return x + end + t = expm1(absx) + if absx < T(1) + return h*(T(2)*t - t*t/(t + T(1))) + end + return h*(t + t/(t + T(1))) + end + # in c) + if absx < H_LARGE_X(T) + return h*exp(absx) + end + # in d) + if absx < H_OVERFLOW_X(T) + return h*T(2)*_ldexp_exp(absx, Int32(-1)) + end + # in e) + return copysign(T(Inf), x) +end +sinh(x::Real) = sinh(float(x)) + +# cosh methods +COSH_SMALL_X(::Type{Float32}) = 0.00024414062f0 +COSH_SMALL_X(::Type{Float64}) = 2.7755602085408512e-17 +function cosh(x::T) where T <: Union{Float32, Float64} + # Method + # mathematically cosh(x) is defined to be (exp(x)+exp(-x))/2 + # 1. Replace x by |x| (cosh(x) = cosh(-x)). + # 2. Find the branch and the expression to calculate and return it + # a) x <= COSH_SMALL_X + # return T(1) + # b) COSH_SMALL_X <= x <= ln2/2 + # return 1+expm1(|x|)^2/(2*exp(|x|)) + # c) ln2/2 <= x <= H_MEDIUM_X + # return (exp(|x|)+1/exp(|x|)/2 + # d) H_MEDIUM_X <= x < H_LARGE_X + # return cosh(x) = exp(x)/2 + # e) H_LARGE_X <= x < H_OVERFLOW_X + # return cosh(x) = exp(x/2)/2 * exp(x/2) + # f) H_OVERFLOW_X <= x + # return cosh(x) = T(Inf) + + isnan(x) && return x + + absx = abs(x) + h = T(0.5) + # in a) or b) + if absx < log(T(2))/2 + # in a) + if absx < COSH_SMALL_X(T) + return T(1) + end + t = expm1(absx) + w = T(1) + t + return T(1) + (t*t)/(w + w) + end + # in c) + if absx < H_MEDIUM_X(T) + t = exp(absx) + return h*t + h/t + end + # in d) + if absx < H_LARGE_X(T) + return h*exp(absx) + end + # in e) + if absx < H_OVERFLOW_X(T) + return _ldexp_exp(absx, Int32(-1)) + end + # in f) + return T(Inf) +end +cosh(x::Real) = cosh(float(x)) + +# tanh methods +TANH_LARGE_X(::Type{Float64}) = 22.0 +TANH_LARGE_X(::Type{Float32}) = 9.0f0 +function tanh(x::T) where T<:Union{Float32, Float64} + # Method + # mathematically tanh(x) is defined to be (exp(x)-exp(-x))/(exp(x)+exp(-x)) + # 1. reduce x to non-negative by tanh(-x) = -tanh(x). + # 2. Find the branch and the expression to calculate and return it + # a) 0 <= x < H_SMALL_X + # return x + # b) H_SMALL_X <= x < 1 + # -expm1(-2x)/(expm1(-2x) + 2) + # c) 1 <= x < TANH_LARGE_X + # 1 - 2/(expm1(2x) + 2) + # d) TANH_LARGE_X <= x + # return 1 + if isnan(x) + return x + elseif isinf(x) + return copysign(T(1), x) + end + + absx = abs(x) + if absx < TANH_LARGE_X(T) + # in a) + if absx < H_SMALL_X(T) + return x + end + if absx >= T(1) + # in c) + t = expm1(T(2)*absx) + z = T(1) - T(2)/(t + T(2)) + else + # in b) + t = expm1(-T(2)*absx) + z = -t/(t + T(2)) + end + else + # in d) + z = T(1) + end + return copysign(z, x) +end +tanh(x::Real) = tanh(float(x)) + +# Inverse hyperbolic functions +AH_LN2(::Type{Float64}) = 6.93147180559945286227e-01 +AH_LN2(::Type{Float32}) = 6.9314718246f-01 +# asinh methods +function asinh(x::T) where T <: Union{Float32, Float64} + # Method + # mathematically asinh(x) = sign(x)*log(|x| + sqrt(x*x + 1)) + # is the principle value of the inverse hyperbolic sine + # 1. Find the branch and the expression to calculate and return it + # a) |x| < 2^-28 + # return x + # b) |x| < 2 + # return sign(x)*log1p(|x| + x^2/(1 + sqrt(1+x^2))) + # c) 2 <= |x| < 2^28 + # return sign(x)*log(2|x|+1/(|x|+sqrt(x*x+1))) + # d) |x| >= 2^28 + # return sign(x)*(log(x)+ln2)) + if isnan(x) || isinf(x) + return x + end + absx = abs(x) + if absx < T(2) + # in a) + if absx < T(2)^-28 + return x + end + # in b) + t = x*x + w = log1p(absx + t/(T(1) + sqrt(T(1) + t))) + elseif absx < T(2)^28 + # in c) + t = absx + w = log(T(2)*t + T(1)/(sqrt(x*x + T(1)) + t)) + else + # in d) + w = log(absx) + AH_LN2(T) + end + return copysign(w, x) +end +asinh(x::Real) = asinh(float(x)) + +# acosh methods +@noinline acosh_domain_error(x) = throw(DomainError(x, "acosh(x) is only defined for x ≥ 1.")) +function acosh(x::T) where T <: Union{Float32, Float64} + # Method + # mathematically acosh(x) if defined to be log(x + sqrt(x*x-1)) + # 1. Find the branch and the expression to calculate and return it + # a) x = 1 + # return log1p(t+sqrt(2.0*t+t*t)) where t=x-1. + # b) 1 < x < 2 + # return log1p(t+sqrt(2.0*t+t*t)) where t=x-1. + # c) 2 <= x < + # return log(2x-1/(sqrt(x*x-1)+x)) + # d) x >= 2^28 + # return log(x)+ln2 + # Special cases: + # if x < 1 throw DomainError + + isnan(x) && return x + + if x < T(1) + return acosh_domain_error(x) + elseif x == T(1) + # in a) + return T(0) + elseif x < T(2) + # in b) + t = x - T(1) + return log1p(t + sqrt(T(2)*t + t*t)) + elseif x < T(2)^28 + # in c) + t = x*x + return log(T(2)*x - T(1)/(x+sqrt(t - T(1)))) + else + # in d) + return log(x) + AH_LN2(T) + end +end +acosh(x::Real) = acosh(float(x)) + +# atanh methods +@noinline atanh_domain_error(x) = throw(DomainError(x, "atanh(x) is only defined for |x| ≤ 1.")) +function atanh(x::T) where T <: Union{Float32, Float64} + # Method + # 1.Reduced x to positive by atanh(-x) = -atanh(x) + # 2. Find the branch and the expression to calculate and return it + # a) 0 <= x < 2^-28 + # return x + # b) 2^-28 <= x < 0.5 + # return 0.5*log1p(2x+2x*x/(1-x)) + # c) 0.5 <= x < 1 + # return 0.5*log1p(2x/1-x) + # d) x = 1 + # return Inf + # Special cases: + # if |x| > 1 throw DomainError + isnan(x) && return x + + absx = abs(x) + + if absx > 1 + atanh_domain_error(x) + end + if absx < T(2)^-28 + # in a) + return x + end + if absx < T(0.5) + # in b) + t = absx+absx + t = T(0.5)*log1p(t+t*absx/(T(1)-absx)) + elseif absx < T(1) + # in c) + t = T(0.5)*log1p((absx + absx)/(T(1)-absx)) + elseif absx == T(1) + # in d) + return copysign(T(Inf), x) + end + return copysign(t, x) +end +atanh(x::Real) = atanh(float(x)) diff --git a/base/special/ldexp_exp.jl b/base/special/ldexp_exp.jl new file mode 100644 index 0000000..3ea0f39 --- /dev/null +++ b/base/special/ldexp_exp.jl @@ -0,0 +1,105 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# This code is a Julia translation of the C code from Openlibm (http://www.openlibm.org/) +# with the following license: + +# Copyright (c) 2011 David Schultz +# All rights reserved. +# +# 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. + +modify_highword(x::Float32, hw) = reinterpret(Float32, hw) +modify_highword(x::Float64, hw) = reinterpret(Float64, (UInt64(hw)<<32)|(reinterpret(UInt64, x)<<32)>>32) + +exponent_rshift(T::Type{Float32}, hw) = hw >> 23 # this comes from 32 (bits in UInt32) minus 9 bits for the sign and exponent +exponent_rshift(T::Type{Float64}, hw) = hw >> 20 # this comes from 32 (bits in UInt32) minus 12 bits for the sign and exponent +exponent_lshift(T::Type{Float32}, hw) = hw << 23 # this comes from 32 (bits in UInt32) minus 9 bits for the sign and exponent +exponent_lshift(T::Type{Float64}, hw) = hw << 20 # this comes from 32 (bits in UInt32) minus 12 bits for the sign and exponent + +function modify_exponent(x::T, expnt_x) where T <: Union{Float32, Float64} + # mask away exponent; "100...0111..111" with 9 or 12 leading 0's + high_mask = T == Float32 ? 0x807fffff : 0x800fffff # don't mask away the sign + # use mask to replace with first 9 or 12 bits with expnt_x << appropriately + modify_highword(x, (highword(x) & high_mask) | exponent_lshift(T, expnt_x)) +end + +""" + _ldexp_exp(x, l2) +Returns exp(x) * 2^l2. The function is intended for large arguments, x, where +x >= ln(prevfloat(typemax(x)) and care is needed to avoid overflow. + +The present implementation is narrowly tailored for our hyperbolic and +exponential functions. We assume l2 is small (0 or -1), and the caller +has filtered out very large x, for which overflow would be inevitable. +""" +function _ldexp_exp(x::T, l2) where T <: Union{Float32, Float64} + # This function is intended for use in our hyperbolic and exponential functions. + + # Calculate exp(x) = (exp(x-kr*log(2))*2^ks*)2^k2 = exp_x*2^k2 + exp_x, k2 = _frexp_exp(x) + + # Add the two exponents together to form (2^l2)*(2^k2) = 2^(l2+k2) = 2^L + l2 += k2 + L_as_hw = exponent_lshift(T, UInt32(exponent_bias(T) + l2)) + # Form 2^L + scale = fromhighword(T, L_as_hw) + # Return exp(x)*2^l2 + return exp_x * scale +end + +""" + exp_x, k2 = _frexp_exp(x) + +Calculate exp(x) as exp_x*2^k2 and return exp_x = exp(x-kr*log(w))*2^ks where kr +is a type dependant range reduction constant, ks scales exp_x towards the largest +finite number, and k2 is used to absorb the remaning scale to allow for exp(x) +to be outside the normal floating point range. + +This function is intended for use in our hyperbolic and exponential functions. +""" +function _frexp_exp(x::T) where T<:Union{Float32, Float64} + # and should only be used for values in the range (let T = typeof(x)): + # + # log(prevfloat(typemax(x))) <= x < log(2 * prevfloat(typemax(x) / nextfloat(T(0))) + # + # where the upper bound is around 192.7f0 and ~= 1454.91. The function outputs + # exp_x in the ranges + # [2f0^127, 2f0^128) and + # [2.0^1023, 2.0^1024) + # respectively. + + # We use exp(x) = exp(x - kln2) * 2**k, carefully chosen to + # minimize |exp(kln2) - 2**k|. + kr = T == Float32 ? UInt32(235) : UInt32(1799) + + # We also scale the exponent of exp_x to exponent_bias + the largest finite + # exponent (exponent of T(Inf)-1, so that the result can be multiplied by + # a tiny number without losing accuracy due to denormalization. + exp_x = exp(x - kr*log(T(2))) # exp_x*2^k = exp(x) + + # Calculate the ks in exp_x*2^ks + ks = exponent_rshift(T, highword(exp_x)) - (exponent_bias(T) + (exponent_max(T) - 1)) + kr + + # Rescale exp_x to have exponent k2 = exponent_max(T) - 1 + exp_x = modify_exponent(exp_x, UInt32(exponent_bias(T) + (exponent_max(T) - 1))) + return exp_x, ks +end diff --git a/base/special/log.jl b/base/special/log.jl new file mode 100644 index 0000000..ef578ed --- /dev/null +++ b/base/special/log.jl @@ -0,0 +1,397 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Implementation of +# "Table-driven Implementation of the Logarithm Function in IEEE Floating-point Arithmetic" +# Tang, Ping-Tak Peter +# ACM Trans. Math. Softw. (1990), 16(4):378--400 +# https://doi.org/10.1145/98267.98294 + +# Does not currently handle floating point flags (inexact, div-by-zero, etc). + +import .Base.unsafe_trunc +import .Base.Math.@horner + +# Float64 lookup table. +# to generate values: + # N=39 # (can be up to N=42). + # sN = 2.0^N + # isN = 1.0/sN + # s7 = 2.0^7 + # is7 = 1.0/s7 + # for j=0:128 + # l_big = Base.log(big(1.0+j*is7)) + # l_hi = isN*Float64(round(sN*l_big)) + # l_lo = Float64(l_big-l_hi) + # j % 2 == 0 && print("\n ") + # print("(",l_hi,",",l_lo,"),") + # end + +const t_log_Float64 = ((0.0,0.0),(0.007782140442941454,-8.865052917267247e-13), + (0.015504186536418274,-4.530198941364935e-13),(0.0231670592820592,-5.248209479295644e-13), + (0.03077165866670839,4.529814257790929e-14),(0.0383188643027097,-5.730994833076631e-13), + (0.04580953603181115,-5.16945692881222e-13),(0.053244514518155484,6.567993368985218e-13), + (0.06062462181580486,6.299848199383311e-13),(0.06795066190898069,-4.729424109166329e-13), + (0.07522342123775161,-1.6408301585598662e-13),(0.08244366921098845,8.614512936087814e-14), + (0.08961215869021544,-5.283050530808144e-13),(0.09672962645890948,-3.5836667430094137e-13), + (0.10379679368088546,7.581073923016376e-13),(0.11081436634049169,-2.0157368416016215e-13), + (0.11778303565552051,8.629474042969438e-13),(0.1247034785010328,-7.556920687451337e-14), + (0.1315763577895268,-8.075373495358435e-13),(0.13840232285838283,7.363043577087051e-13), + (0.14518200984457508,-7.718001336828099e-14),(0.15191604202664166,-7.996871607743758e-13), + (0.15860503017574956,8.890223439724663e-13),(0.16524957289584563,-5.384682618788232e-13), + (0.17185025692742784,-7.686134224018169e-13),(0.17840765747314435,-3.2605717931058157e-13), + (0.18492233849428885,-2.7685884310448306e-13),(0.1913948530000198,-3.903387893794952e-13), + (0.1978257433293038,6.160755775588723e-13),(0.20421554142922105,-5.30156516006026e-13), + (0.21056476910780475,-4.55112422774782e-13),(0.21687393830143264,-8.182853292737783e-13), + (0.22314355131493357,-7.238189921749681e-13),(0.22937410106533207,-4.86240001538379e-13), + (0.23556607131286,-9.30945949519689e-14),(0.24171993688651128,6.338907368997553e-13), + (0.24783616390413954,4.4171755371315547e-13),(0.25391520998164196,-6.785208495970588e-13), + (0.25995752443668607,2.3999540484211735e-13),(0.2659635484978935,-7.555569400283742e-13), + (0.27193371548310097,5.407904186145515e-13),(0.2778684510030871,3.692037508208009e-13), + (0.28376817313073843,-9.3834172236637e-14),(0.28963329258294834,9.43339818951269e-14), + (0.29546421289342106,4.148131870425857e-13),(0.3012613305781997,-3.7923164802093147e-14), + (0.3070250352957373,-8.25463138725004e-13),(0.31275571000333,5.668653582900739e-13), + (0.318453731119007,-4.723727821986367e-13),(0.32411946865431673,-1.0475750058776541e-13), + (0.32975328637257917,-1.1118671389559323e-13),(0.33535554192167183,-5.339989292003297e-13), + (0.3409265869704541,1.3912841212197566e-13),(0.3464667673470103,-8.017372713972018e-13), + (0.35197642315688427,2.9391859187648e-13),(0.3574558889213222,4.815896111723205e-13), + (0.3629054936900502,-6.817539406325327e-13),(0.36832556115950865,-8.009990055432491e-13), + (0.3737164097929053,6.787566823158706e-13),(0.37907835293481185,1.5761203773969435e-13), + (0.3844116989112081,-8.760375990774874e-13),(0.38971675114044046,-4.152515806343612e-13), + (0.3949938082405424,3.2655698896907146e-13),(0.40024316412745975,-4.4704265010452445e-13), + (0.4054651081078191,3.452764795203977e-13),(0.4106599249844294,8.390050778518307e-13), + (0.4158278951435932,1.1776978751369214e-13),(0.4209692946442374,-1.0774341461609579e-13), + (0.42608439531068143,2.186334329321591e-13),(0.43117346481813,2.413263949133313e-13), + (0.4362367667745275,3.90574622098307e-13),(0.44127456080423144,6.437879097373207e-13), + (0.44628710262804816,3.713514191959202e-13),(0.45127464413963025,-1.7166921336082432e-13), + (0.4562374334818742,-2.8658285157914353e-13),(0.4611757151214988,6.713692791384601e-13), + (0.46608972992544295,-8.437281040871276e-13),(0.4709797152190731,-2.821014384618127e-13), + (0.4758459048698569,1.0701931762114255e-13),(0.4806885293455707,1.8119346366441111e-13), + (0.4855078157816024,9.840465278232627e-14),(0.49030398804461583,5.780031989454028e-13), + (0.49507726679803454,-1.8302857356041668e-13),(0.4998278695566114,-1.620740015674495e-13), + (0.5045560107519123,4.83033149495532e-13),(0.5092619017905236,-7.156055317238212e-13), + (0.5139457511013461,8.882123951857185e-13),(0.5186077642083546,-3.0900580513238243e-13), + (0.5232481437651586,-6.10765519728515e-13),(0.5278670896204858,3.565996966334783e-13), + (0.532464798869114,3.5782396591276384e-13),(0.5370414658973459,-4.622608700154458e-13), + (0.5415972824321216,6.227976291722515e-13),(0.5461324375974073,7.283894727206574e-13), + (0.5506471179523942,2.680964661521167e-13),(0.5551415075406112,-1.0960825046059278e-13), + (0.5596157879353996,2.3119493838005378e-14),(0.5640701382853877,-5.846905800529924e-13), + (0.5685047353526897,-2.1037482511444942e-14),(0.5729197535620187,-2.332318294558741e-13), + (0.5773153650352469,-4.2333694288141915e-13),(0.5816917396350618,-4.3933937969737843e-13), + (0.5860490450031648,4.1341647073835564e-13),(0.590387446602108,6.841763641591467e-14), + (0.5947071077462169,4.758553400443064e-13),(0.5990081896452466,8.367967867475769e-13), + (0.6032908514389419,-8.576373464665864e-13),(0.6075552502243227,2.1913281229340092e-13), + (0.6118015411066153,-6.224284253643115e-13),(0.6160298772156239,-1.098359432543843e-13), + (0.6202404097512044,6.531043137763365e-13),(0.6244332880123693,-4.758019902171077e-13), + (0.6286086594227527,-3.785425126545704e-13),(0.6327666695706284,4.0939233218678666e-13), + (0.636907462236195,8.742438391485829e-13),(0.6410311794206791,2.521818845684288e-13), + (0.6451379613736208,-3.6081313604225574e-14),(0.649227946625615,-5.05185559242809e-13), + (0.6533012720119586,7.869940332335532e-13),(0.6573580727090302,-6.702087696194906e-13), + (0.6613984822452039,1.6108575753932459e-13),(0.6654226325445052,5.852718843625151e-13), + (0.6694306539429817,-3.5246757297904794e-13),(0.6734226752123504,-1.8372084495629058e-13), + (0.6773988235909201,8.860668981349492e-13),(0.6813592248072382,6.64862680714687e-13), + (0.6853040030982811,6.383161517064652e-13),(0.6892332812385575,2.5144230728376075e-13), + (0.6931471805601177,-1.7239444525614835e-13)) + + +# Float32 lookup table +# to generate values: + # N=16 + # sN = 2f0^N + # isN = 1f0/sN + # s7 = 2.0^7 + # is7 = 1.0/s7 + # for j=0:128 + # j % 4 == 0 && print("\n ") + # print(float64(Base.log(big(1.0+j*is7))),",") + # end + +const t_log_Float32 = (0.0,0.007782140442054949,0.015504186535965254,0.02316705928153438, + 0.030771658666753687,0.0383188643021366,0.0458095360312942,0.053244514518812285, + 0.06062462181643484,0.06795066190850775,0.07522342123758753,0.08244366921107459, + 0.08961215868968714,0.09672962645855111,0.10379679368164356,0.11081436634029011, + 0.11778303565638346,0.12470347850095724,0.13157635778871926,0.13840232285911913, + 0.1451820098444979,0.15191604202584197,0.15860503017663857,0.16524957289530717, + 0.17185025692665923,0.1784076574728183,0.184922338494012,0.19139485299962947, + 0.19782574332991987,0.2042155414286909,0.21056476910734964,0.21687393830061436, + 0.22314355131420976,0.22937410106484582,0.2355660713127669,0.24171993688714516, + 0.24783616390458127,0.25391520998096345,0.25995752443692605,0.26596354849713794, + 0.27193371548364176,0.2778684510034563,0.2837681731306446,0.28963329258304266, + 0.2954642128938359,0.3012613305781618,0.3070250352949119,0.3127557100038969, + 0.3184537311185346,0.324119468654212,0.329753286372468,0.3353555419211378, + 0.3409265869705932,0.34646676734620857,0.3519764231571782,0.3574558889218038, + 0.3629054936893685,0.3683255611587076,0.37371640979358406,0.37907835293496944, + 0.38441169891033206,0.3897167511400252,0.394993808240869,0.4002431641270127, + 0.4054651081081644,0.4106599249852684,0.415827895143711,0.42096929464412963, + 0.4260843953109001,0.4311734648183713,0.43623676677491807,0.4412745608048752, + 0.44628710262841953,0.45127464413945856,0.4562374334815876,0.46117571512217015, + 0.46608972992459924,0.470979715218791,0.4758459048699639,0.4806885293457519, + 0.4855078157817008,0.4903039880451938,0.4950772667978515,0.4998278695564493, + 0.5045560107523953,0.5092619017898079,0.5139457511022343,0.5186077642080457, + 0.5232481437645479,0.5278670896208424,0.5324647988694718,0.5370414658968836, + 0.5415972824327444,0.5461324375981357,0.5506471179526623,0.5551415075405016, + 0.5596157879354227,0.564070138284803,0.5685047353526688,0.5729197535617855, + 0.5773153650348236,0.5816917396346225,0.5860490450035782,0.5903874466021763, + 0.5947071077466928,0.5990081896460834,0.6032908514380843,0.6075552502245418, + 0.6118015411059929,0.616029877215514,0.6202404097518576,0.6244332880118935, + 0.6286086594223741,0.6327666695710378,0.6369074622370692,0.6410311794209312, + 0.6451379613735847,0.6492279466251099,0.6533012720127457,0.65735807270836, + 0.661398482245365,0.6654226325450905,0.6694306539426292,0.6734226752121667, + 0.6773988235918061,0.6813592248079031,0.6853040030989194,0.689233281238809, + 0.6931471805599453) + +# determine if hardware FMA is available +# should probably check with LLVM, see #9855. +const FMA_NATIVE = muladd(nextfloat(1.0),nextfloat(1.0),-nextfloat(1.0,2)) != 0 + +# truncate lower order bits (up to 26) +# ideally, this should be able to use ANDPD instructions, see #9868. +@inline function truncbits(x::Float64) + reinterpret(Float64, reinterpret(UInt64,x) & 0xffff_ffff_f800_0000) +end + + +# Procedure 1 +@inline function log_proc1(y::Float64,mf::Float64,F::Float64,f::Float64,jp::Int) + ## Steps 1 and 2 + @inbounds hi,lo = t_log_Float64[jp] + l_hi = mf* 0.6931471805601177 + hi + l_lo = mf*-1.7239444525614835e-13 + lo + + ## Step 3 + # @inbounds u = f*c_invF[jp] + # u = f/F + # q = u*u*@horner(u, + # -0x1.0_0000_0000_0001p-1, + # +0x1.5_5555_5550_9ba5p-2, + # -0x1.f_ffff_ffeb_6526p-3, + # +0x1.9_99b4_dfed_6fe4p-3, + # -0x1.5_5576_6647_2e04p-3) + + ## Step 3' (alternative) + u = (2.0f)/(y+F) + v = u*u + q = u*v*@horner(v, + 0.08333333333303913, + 0.012500053168098584) + + ## Step 4 + l_hi + (u + (q + l_lo)) +end + +# Procedure 2 +@inline function log_proc2(f::Float64) + ## Step 1 + g = 1.0/(2.0+f) + u = 2.0*f*g + v = u*u + + ## Step 2 + q = u*v*@horner(v, + 0.08333333333333179, + 0.012500000003771751, + 0.0022321399879194482, + 0.0004348877777076146) + + ## Step 3 + # based on: + # 2(f-u) = 2(f(2+f)-2f)/(2+f) = 2f^2/(2+f) = fu + # 2(f-u1-u2) - f*(u1+u2) = 0 + # 2(f-u1) - f*u1 = (2+f)u2 + # u2 = (2(f-u1) - f*u1)/(2+f) + if FMA_NATIVE + return u + fma(fma(-u,f,2(f-u)), g, q) + else + u1 = truncbits(u) # round to 24 bits + f1 = truncbits(f) + f2 = f-f1 + u2 = ((2.0*(f-u1)-u1*f1)-u1*f2)*g + ## Step 4 + return u1 + (u2 + q) + end +end + + +@inline function log_proc1(y::Float32,mf::Float32,F::Float32,f::Float32,jp::Int) + ## Steps 1 and 2 + @inbounds hi = t_log_Float32[jp] + l = mf*0.6931471805599453 + hi + + ## Step 3 + # @inbounds u = f*c_invF[jp] + # q = u*u*@horner(u, + # Float32(-0x1.00006p-1), + # Float32(0x1.55546cp-2)) + + ## Step 3' (alternative) + u = (2f0f)/(y+F) + v = u*u + q = u*v*0.08333351f0 + + ## Step 4 + Float32(l + (u + q)) +end + +@inline function log_proc2(f::Float32) + ## Step 1 + # compute in higher precision + u64 = Float64(2f0*f)/(2.0+f) + u = Float32(u64) + v = u*u + + ## Step 2 + q = u*v*@horner(v, + 0.08333332f0, + 0.012512346f0) + + ## Step 3: not required + + ## Step 4 + Float32(u64 + q) +end + + +function log(x::Float64) + if x > 0.0 + x == Inf && return x + + # Step 2 + if 0.9394130628134757 < x < 1.0644944589178595 + f = x-1.0 + return log_proc2(f) + end + + # Step 3 + xu = reinterpret(UInt64,x) + m = Int(xu >> 52) & 0x07ff + if m == 0 # x is subnormal + x *= 1.8014398509481984e16 # 0x1p54, normalise significand + xu = reinterpret(UInt64,x) + m = Int(xu >> 52) & 0x07ff - 54 + end + m -= 1023 + y = reinterpret(Float64,(xu & 0x000f_ffff_ffff_ffff) | 0x3ff0_0000_0000_0000) + + mf = Float64(m) + F = (y + 3.5184372088832e13) - 3.5184372088832e13 # 0x1p-7*round(0x1p7*y) + f = y-F + jp = unsafe_trunc(Int,128.0*F)-127 + + return log_proc1(y,mf,F,f,jp) + elseif x == 0.0 + -Inf + elseif isnan(x) + NaN + else + throw_complex_domainerror(:log, x) + end +end + +function log(x::Float32) + if x > 0f0 + x == Inf32 && return x + + # Step 2 + if 0.939413f0 < x < 1.0644945f0 + f = x-1f0 + return log_proc2(f) + end + + # Step 3 + xu = reinterpret(UInt32,x) + m = Int(xu >> 23) & 0x00ff + if m == 0 # x is subnormal + x *= 3.3554432f7 # 0x1p25, normalise significand + xu = reinterpret(UInt32,x) + m = Int(xu >> 23) & 0x00ff - 25 + end + m -= 127 + y = reinterpret(Float32,(xu & 0x007f_ffff) | 0x3f80_0000) + + mf = Float32(m) + F = (y + 65536.0f0) - 65536.0f0 # 0x1p-7*round(0x1p7*y) + f = y-F + jp = unsafe_trunc(Int,128.0f0*F)-127 + + log_proc1(y,mf,F,f,jp) + elseif x == 0f0 + -Inf32 + elseif isnan(x) + NaN32 + else + throw_complex_domainerror(:log, x) + end +end + + +function log1p(x::Float64) + if x > -1.0 + x == Inf && return x + if -1.1102230246251565e-16 < x < 1.1102230246251565e-16 + return x # Inexact + + # Step 2 + elseif -0.06058693718652422 < x < 0.06449445891785943 + return log_proc2(x) + end + + # Step 3 + z = 1.0 + x + zu = reinterpret(UInt64,z) + s = reinterpret(Float64,0x7fe0_0000_0000_0000 - (zu & 0xfff0_0000_0000_0000)) # 2^-m + m = Int(zu >> 52) & 0x07ff - 1023 # z cannot be subnormal + c = m > 0 ? 1.0-(z-x) : x-(z-1.0) # 1+x = z+c exactly + y = reinterpret(Float64,(zu & 0x000f_ffff_ffff_ffff) | 0x3ff0_0000_0000_0000) + + mf = Float64(m) + F = (y + 3.5184372088832e13) - 3.5184372088832e13 # 0x1p-7*round(0x1p7*y) + f = (y - F) + c*s #2^m(F+f) = 1+x = z+c + jp = unsafe_trunc(Int,128.0*F)-127 + + log_proc1(y,mf,F,f,jp) + elseif x == -1.0 + -Inf + elseif isnan(x) + NaN + else + throw_complex_domainerror(:log1p, x) + end +end + +function log1p(x::Float32) + if x > -1f0 + x == Inf32 && return x + if -5.9604645f-8 < x < 5.9604645f-8 + return x # Inexact + # Step 2 + elseif -0.06058694f0 < x < 0.06449446f0 + return log_proc2(x) + end + + # Step 3 + z = 1f0 + x + zu = reinterpret(UInt32,z) + s = reinterpret(Float32,0x7f000000 - (zu & 0xff80_0000)) # 2^-m + m = Int(zu >> 23) & 0x00ff - 127 # z cannot be subnormal + c = m > 0 ? 1f0-(z-x) : x-(z-1f0) # 1+x = z+c + y = reinterpret(Float32,(zu & 0x007f_ffff) | 0x3f80_0000) + + mf = Float32(m) + F = (y + 65536.0f0) - 65536.0f0 # 0x1p-7*round(0x1p7*y) + f = (y - F) + s*c #2^m(F+f) = 1+x = z+c + jp = unsafe_trunc(Int,128.0*F)-127 + + log_proc1(y,mf,F,f,jp) + elseif x == -1f0 + -Inf32 + elseif isnan(x) + NaN32 + else + throw_complex_domainerror(:log1p, x) + end +end + +for f in (:log,:log1p) + @eval begin + ($f)(x::Real) = ($f)(float(x)) + end +end diff --git a/base/special/rem_pio2.jl b/base/special/rem_pio2.jl new file mode 100644 index 0000000..c8d1018 --- /dev/null +++ b/base/special/rem_pio2.jl @@ -0,0 +1,326 @@ +# This file is a part of Julia. Except for the rem_pio2_kernel, and +# cody_waite_* methods (see below) license is MIT: https://julialang.org/license + +# rem_pio2_kernel and cody_waite_* methods are heavily based on FDLIBM code: +# __ieee754_rem_pio2, that is made available under the following licence: + +## Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +## +## Developed at SunPro, a Sun Microsystems, Inc. business. +## Permission to use, copy, modify, and distribute this +## software is freely granted, provided that this notice +## is preserved. + +# Bits of 1/2π +# 1/2π == sum(x / 0x1p64^i for i,x = enumerate(INV_2PI)) +# Can be obtained by: +# +# setprecision(BigFloat, 4096) +# I = 0.5/big(pi) +# for i = 1:19 +# I *= 0x1p64 +# k = trunc(UInt64, I) +# @printf "0x%016x,\n" k +# I -= k +# end +const INV_2PI = UInt64[ + 0x28be_60db_9391_054a, + 0x7f09_d5f4_7d4d_3770, + 0x36d8_a566_4f10_e410, + 0x7f94_58ea_f7ae_f158, + 0x6dc9_1b8e_9093_74b8, + 0x0192_4bba_8274_6487, + 0x3f87_7ac7_2c4a_69cf, + 0xba20_8d7d_4bae_d121, + 0x3a67_1c09_ad17_df90, + 0x4e64_758e_60d4_ce7d, + 0x2721_17e2_ef7e_4a0e, + 0xc7fe_25ff_f781_6603, + 0xfbcb_c462_d682_9b47, + 0xdb4d_9fb3_c9f2_c26d, + 0xd3d1_8fd9_a797_fa8b, + 0x5d49_eeb1_faf9_7c5e, + 0xcf41_ce7d_e294_a4ba, + 0x9afe_d7ec_47e3_5742, + 0x1580_cc11_bf1e_daea] + +@inline function cody_waite_2c_pio2(x::Float64, fn, n) + pio2_1 = 1.57079632673412561417e+00 + pio2_1t = 6.07710050650619224932e-11 + + z = muladd(-fn, pio2_1, x) # x - fn*pio2_1 + y1 = muladd(-fn, pio2_1t, z) # z - fn*pio2_1t + y2 = muladd(-fn, pio2_1t, (z - y1)) # (z - y1) - fn*pio2_1t + n, DoubleFloat64(y1, y2) +end + +@inline function cody_waite_ext_pio2(x::Float64, xhp) + pio2_1 = 1.57079632673412561417e+00 + pio2_1t = 6.07710050650619224932e-11 + pio2_2 = 6.07710050630396597660e-11 + pio2_2t = 2.02226624879595063154e-21 + pio2_3 = 2.02226624871116645580e-21 + pio2_3t = 8.47842766036889956997e-32 + + fn = round(x*(2/pi)) # round to integer + # on older systems, the above could be faster with + # rf = 1.5/eps(Float64) + # fn = (x*(2/pi)+rf)-rf + + r = muladd(-fn, pio2_1, x) # x - fn*pio2_1 + w = fn*pio2_1t # 1st round good to 85 bit + j = xhp>>20 + y1 = r-w + high = highword(y1) + i = j-((high>>20)&0x7ff) + if i>16 # 2nd iteration needed, good to 118 + t = r + w = fn*pio2_2 + r = t-w + w = muladd(fn, pio2_2t,-((t-r)-w)) + y1 = r-w + high = highword(y1) + i = j-((high>>20)&0x7ff) + if i>49 # 3rd iteration need, 151 bits acc + t = r # will cover all possible cases + w = fn*pio2_3 + r = t-w + w = muladd(fn, pio2_3t, -((t-r)-w)) + y1 = r-w + end + end + y2 = (r-y1)-w + return unsafe_trunc(Int, fn), DoubleFloat64(y1, y2) +end + +""" + fromfraction(f::Int128) + +Compute a tuple of values `(z1,z2)` such that + ``z1 + z2 == f / 2^128`` +and the significand of `z1` has 27 trailing zeros. +""" +function fromfraction(f::Int128) + if f == 0 + return (0.0,0.0) + end + + # 1. get leading term truncated to 26 bits + s = ((f < 0) % UInt64) << 63 # sign bit + x = abs(f) % UInt128 # magnitude + n1 = 128-leading_zeros(x) # ndigits0z(x,2) + m1 = ((x >> (n1-26)) % UInt64) << 27 + d1 = ((n1-128+1021) % UInt64) << 52 + z1 = reinterpret(Float64, s | (d1 + m1)) + + # 2. compute remaining term + x2 = (x - (UInt128(m1) << (n1-53))) + if x2 == 0 + return (z1, 0.0) + end + n2 = 128-leading_zeros(x2) + m2 = (x2 >> (n2-53)) % UInt64 + d2 = ((n2-128+1021) % UInt64) << 52 + z2 = reinterpret(Float64, s | (d2 + m2)) + return (z1,z2) +end + +function paynehanek(x::Float64) + # 1. Convert to form + # + # x = X * 2^k, + # + # where 2^(n-1) <= X < 2^n is an n-bit integer (n = 53, k = exponent(x)-52 ) + + # Computations are integer based, so reinterpret x as UInt64 + u = reinterpret(UInt64, x) + # Strip x of exponent bits and replace with ^1 + X = (u & significand_mask(Float64)) | (one(UInt64) << significand_bits(Float64)) + # Get k from formula above + # k = exponent(x)-52 + k = Int((u & exponent_mask(Float64)) >> significand_bits(Float64)) - exponent_bias(Float64) - significand_bits(Float64) + + # 2. Let α = 1/2π, then: + # + # α*x mod 1 ≡ [(α*2^k mod 1)*X] mod 1 + # + # so we can ignore the first k bits of α. Extract the next 3 64-bit parts of α. + # + # i.e. equivalent to + # setprecision(BigFloat,4096) + # α = 1/(2*big(pi)) + # A = mod(ldexp(α,k), 1) + # z1 = ldexp(A,64) + # a1 = trunc(UInt64, z1) + # z2 = ldexp(z1-a1, 64) + # a2 = trunc(UInt64, z2) + # z3 = ldexp(z2-a2, 64) + # a3 = trunc(UInt64, z3) + + # This is equivalent to + # idx, shift = divrem(k, 64) + # but divrem is slower. + idx = k >> 6 + + shift = k - (idx << 6) + if shift == 0 + @inbounds a1 = INV_2PI[idx+1] + @inbounds a2 = INV_2PI[idx+2] + @inbounds a3 = INV_2PI[idx+3] + else + # use shifts to extract the relevant 64 bit window + @inbounds a1 = (idx < 0 ? zero(UInt64) : INV_2PI[idx+1] << shift) | (INV_2PI[idx+2] >> (64 - shift)) + @inbounds a2 = (INV_2PI[idx+2] << shift) | (INV_2PI[idx+3] >> (64 - shift)) + @inbounds a3 = (INV_2PI[idx+3] << shift) | (INV_2PI[idx+4] >> (64 - shift)) + end + + # 3. Perform the multiplication: + # + # X. 0 0 0 + # × 0. a1 a2 a3 + # ============== + # _. w w _ + # + # (i.e. ignoring integer and lowest bit parts of result) + + w1 = UInt128(X*a1) << 64 # overflow becomes integer + w2 = widemul(X,a2) + w3 = widemul(X,a3) >> 64 + w = w1 + w2 + w3 # quotient fraction after division by 2π + + # adjust for sign of x + w = flipsign(w,x) + + # 4. convert to quadrant, quotient fraction after division by π/2: + q = (((w>>125)%Int +1)>>1) # nearest quadrant + f = (w<<2) % Int128 # fraction part of quotient after division by π/2, taking values on [-0.5,0.5) + + # 5. convert quotient fraction to split precision Float64 + z_hi,z_lo = fromfraction(f) + + # 6. multiply by π/2 + pio2 = 1.5707963267948966 + pio2_hi = 1.5707963407039642 + pio2_lo = -1.3909067614167116e-8 + y_hi = (z_hi+z_lo)*pio2 + y_lo = (((z_hi*pio2_hi - y_hi) + z_hi*pio2_lo) + z_lo*pio2_hi) + z_lo*pio2_lo + return q, DoubleFloat64(y_hi, y_lo) +end + +""" + rem_pio2_kernel(x) + +Return the remainder of `x` modulo π/2 as a double-double pair, along with a `k` +such that ``k \\mod 3 == K \\mod 3`` where ``K*π/2 = x - rem``. Note, that it is +only meant for use when ``|x|>=π/4``, and that ``π/2`` is always subtracted or +added for ``π/4<|x|<=π/2`` instead of simply returning `x`. +""" +@inline function rem_pio2_kernel(x::Float64) + xhp = poshighword(x) + # xhp <= highword(5pi/4) implies |x| ~<= 5pi/4, + if xhp <= 0x400f6a7a + # last five bits of xhp == last five bits of highword(pi/2) or + # highword(2pi/2) implies |x| ~= pi/2 or 2pi/2, + if (xhp & 0xfffff) == 0x921fb # use precise Cody Waite scheme + return cody_waite_ext_pio2(x, xhp) + end + # use Cody Waite with two constants + # xhp <= highword(3pi/4) implies |x| ~<= 3pi/4 + if xhp <= 0x4002d97c + if x > 0.0 + return cody_waite_2c_pio2(x, 1.0, 1) + else + return cody_waite_2c_pio2(x, -1.0, -1) + end + # 3pi/4 < |x| <= 5pi/4 + else + if x > 0.0 + return cody_waite_2c_pio2(x, 2.0, 2) + else + return cody_waite_2c_pio2(x, -2.0, -2) + end + end + end + # xhp <= highword(9pi/4) implies |x| ~<= 9pi/4 + if xhp <= 0x401c463b + # xhp <= highword(7pi/4) implies |x| ~<= 7pi/4 + if xhp <= 0x4015fdbc + # xhp == highword(3pi/2) implies |x| ~= 3pi/2 + if xhp == 0x4012d97c # use precise Cody Waite scheme + return cody_waite_ext_pio2(x, xhp) + end + # use Cody Waite with two constants + if x > 0.0 + return cody_waite_2c_pio2(x, 3.0, 3) + else + return cody_waite_2c_pio2(x, -3.0, -3) + end + # 7pi/4 < |x| =< 9pi/4 + else + # xhp == highword(4pi/2) implies |x| ~= 4pi/2 + if xhp == 0x401921fb # use precise Cody Waite scheme + return cody_waite_ext_pio2(x, xhp) + end + # use Cody Waite with two constants + if x > 0.0 + return cody_waite_2c_pio2(x, 4.0, 4) + else + return cody_waite_2c_pio2(x, -4.0, -4) + end + end + end + # xhp < highword(2.0^20*pi/2) implies |x| ~< 2^20*pi/2 + if xhp < 0x413921fb # use precise Cody Waite scheme + return cody_waite_ext_pio2(x, xhp) + end + # if |x| >= 2^20*pi/2 switch to Payne Hanek + return paynehanek(x) +end + +## Float32 +@inline function rem_pio2_kernel(x::Float32) + pio2_1 = 1.57079631090164184570e+00 + pio2_1t = 1.58932547735281966916e-08 + inv_pio2 = 6.36619772367581382433e-01 + xd = convert(Float64, x) + absxd = abs(xd) + # it is assumed that NaN and Infs have been checked + if absxd <= pi*5/4 + if absxd <= pi*3/4 + if x > 0 + return 1, DoubleFloat32(xd - pi/2) + else + return -1, DoubleFloat32(xd + pi/2) + end + end + if x > 0 + return 2, DoubleFloat32(xd - pi) + else + return -2, DoubleFloat32(xd + pi) + end + elseif absxd <= pi*9/4 + if absxd <= pi*7/4 + if x > 0 + return 3, DoubleFloat32(xd - pi*3/2) + else + return -3, DoubleFloat32(xd + pi*3/2) + end + end + if x > 0 + return 4, DoubleFloat32(xd - pi*4/2) + else + return -4, DoubleFloat32(xd + pi*4/2) + end + end + #/* 33+53 bit pi is good enough for medium size */ + if absxd < Float32(pi)/2*2.0f0^28 # medium size */ + # use Cody Waite reduction with two coefficients + fn = round(xd*inv_pio2) + r = xd-fn*pio2_1 + w = fn*pio2_1t + y = r-w; + return unsafe_trunc(Int, fn), DoubleFloat32(y) + end + n, y = rem_pio2_kernel(xd) + return n, DoubleFloat32(y.hi) +end diff --git a/base/special/trig.jl b/base/special/trig.jl new file mode 100644 index 0000000..c848ebf --- /dev/null +++ b/base/special/trig.jl @@ -0,0 +1,1125 @@ +# This file is a part of Julia. Except for the *_kernel functions (see below), +# license is MIT: https://julialang.org/license + +struct DoubleFloat64 + hi::Float64 + lo::Float64 +end +struct DoubleFloat32 + hi::Float64 +end + +# sin_kernel and cos_kernel functions are only valid for |x| < pi/4 = 0.7854 +# translated from openlibm code: k_sin.c, k_cos.c, k_sinf.c, k_cosf.c. +# atan functions are based on openlibm code: s_atan.c, s_atanf.c. +# acos functions are based on openlibm code: e_acos.c, e_acosf.c. +# asin functions are based on openlibm code: e_asin.c, e_asinf.c. The above +# functions are made available under the following licence: + +## Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. +## +## Developed at SunPro, a Sun Microsystems, Inc. business. +## Permission to use, copy, modify, and distribute this +## software is freely granted, provided that this notice +## is preserved. + +# Trigonometric functions +# sin methods +@noinline sin_domain_error(x) = throw(DomainError(x, "sin(x) is only defined for finite x.")) +function sin(x::T) where T<:Union{Float32, Float64} + absx = abs(x) + if absx < T(pi)/4 #|x| ~<= pi/4, no need for reduction + if absx < sqrt(eps(T)) + return x + end + return sin_kernel(x) + elseif isnan(x) + return T(NaN) + elseif isinf(x) + sin_domain_error(x) + end + n, y = rem_pio2_kernel(x) + n = n&3 + if n == 0 + return sin_kernel(y) + elseif n == 1 + return cos_kernel(y) + elseif n == 2 + return -sin_kernel(y) + else + return -cos_kernel(y) + end +end +sin(x::Real) = sin(float(x)) + +# Coefficients in 13th order polynomial approximation on [0; π/4] +# sin(x) ≈ x + S1*x³ + S2*x⁵ + S3*x⁷ + S4*x⁹ + S5*x¹¹ + S6*x¹³ +# D for double, S for sin, number is the order of x-1 +const DS1 = -1.66666666666666324348e-01 +const DS2 = 8.33333333332248946124e-03 +const DS3 = -1.98412698298579493134e-04 +const DS4 = 2.75573137070700676789e-06 +const DS5 = -2.50507602534068634195e-08 +const DS6 = 1.58969099521155010221e-10 + +""" + sin_kernel(yhi, ylo) + +Computes the sine on the interval [-π/4; π/4]. +""" +@inline function sin_kernel(y::DoubleFloat64) + y² = y.hi*y.hi + y⁴ = y²*y² + r = @horner(y², DS2, DS3, DS4) + y²*y⁴*@horner(y², DS5, DS6) + y³ = y²*y.hi + y.hi-((y²*(0.5*y.lo-y³*r)-y.lo)-y³*DS1) +end +@inline function sin_kernel(y::Float64) + y² = y*y + y⁴ = y²*y² + r = @horner(y², DS2, DS3, DS4) + y²*y⁴*@horner(y², DS5, DS6) + y³ = y²*y + y+y³*(DS1+y²*r) +end + +# sin_kernels accepting values from rem_pio2 in the Float32 case +@inline sin_kernel(x::Float32) = sin_kernel(DoubleFloat32(x)) +@inline function sin_kernel(y::DoubleFloat32) + S1 = -0.16666666641626524 + S2 = 0.008333329385889463 + z = y.hi*y.hi + w = z*z + r = @horner(z, -0.00019839334836096632, 2.718311493989822e-6) + s = z*y.hi + Float32((y.hi + s*@horner(z, S1, S2)) + s*w*r) +end + +# cos methods +@noinline cos_domain_error(x) = throw(DomainError(x, "cos(x) is only defined for finite x.")) +function cos(x::T) where T<:Union{Float32, Float64} + absx = abs(x) + if absx < T(pi)/4 + if absx < sqrt(eps(T)/T(2.0)) + return T(1.0) + end + return cos_kernel(x) + elseif isnan(x) + return T(NaN) + elseif isinf(x) + cos_domain_error(x) + else + n, y = rem_pio2_kernel(x) + n = n&3 + if n == 0 + return cos_kernel(y) + elseif n == 1 + return -sin_kernel(y) + elseif n == 2 + return -cos_kernel(y) + else + return sin_kernel(y) + end + end +end +cos(x::Real) = cos(float(x)) + +const DC1 = 4.16666666666666019037e-02 +const DC2 = -1.38888888888741095749e-03 +const DC3 = 2.48015872894767294178e-05 +const DC4 = -2.75573143513906633035e-07 +const DC5 = 2.08757232129817482790e-09 +const DC6 = -1.13596475577881948265e-11 +""" + cos_kernel(y) + +Compute the cosine on the interval y∈[-π/4; π/4]. +""" +@inline function cos_kernel(y::DoubleFloat64) + y² = y.hi*y.hi + y⁴ = y²*y² + r = y²*@horner(y², DC1, DC2, DC3) + y⁴*y⁴*@horner(y², DC4, DC5, DC6) + half_y² = 0.5*y² + w = 1.0-half_y² + w + (((1.0-w)-half_y²) + (y²*r-y.hi*y.lo)) +end +@inline function cos_kernel(y::Float64) + y² = y*y + y⁴ = y²*y² + r = y²*@horner(y², DC1, DC2, DC3) + y⁴*y⁴*@horner(y², DC4, DC5, DC6) + half_y² = 0.5*y² + w = 1.0-half_y² + w + (((1.0-w)-half_y²) + (y²*r)) +end + +# cos_kernels accepting values from rem_pio2 in the Float32 case +cos_kernel(x::Float32) = cos_kernel(DoubleFloat32(x)) +@inline function cos_kernel(y::DoubleFloat32) + C0 = -0.499999997251031 + C1 = 0.04166662332373906 + y² = y.hi*y.hi + y⁴ = y²*y² + r = @horner(y², -0.001388676377460993, 2.439044879627741e-5) + Float32(((1.0+y²*C0) + y⁴*C1) + (y⁴*y²)*r) +end + +### sincos methods +@noinline sincos_domain_error(x) = throw(DomainError(x, "sincos(x) is only defined for finite x.")) + +""" + sincos(x) + +Simultaneously compute the sine and cosine of `x`, where the `x` is in radians. +""" +function sincos(x::T) where T<:Union{Float32, Float64} + if abs(x) < T(pi)/4 + if x == zero(T) + return x, one(T) + end + return sincos_kernel(x) + elseif isnan(x) + return T(NaN), T(NaN) + elseif isinf(x) + sincos_domain_error(x) + end + n, y = rem_pio2_kernel(x) + n = n&3 + # calculate both kernels at the reduced y... + si, co = sincos_kernel(y) + # ... and use the same selection scheme as above: (sin, cos, -sin, -cos) for + # for sin and (cos, -sin, -cos, sin) for cos + if n == 0 + return si, co + elseif n == 1 + return co, -si + elseif n == 2 + return -si, -co + else + return -co, si + end +end + +_sincos(x::AbstractFloat) = sincos(x) +_sincos(x) = (sin(x), cos(x)) + +sincos(x) = _sincos(float(x)) + + + +# There's no need to write specialized kernels, as inlining takes care of remo- +# ving superfluous calculations. +@inline sincos_kernel(y::Union{Float32, Float64, DoubleFloat32, DoubleFloat64}) = (sin_kernel(y), cos_kernel(y)) + +# tangent methods +@noinline tan_domain_error(x) = throw(DomainError(x, "tan(x) is only defined for finite x.")) +function tan(x::T) where T<:Union{Float32, Float64} + absx = abs(x) + if absx < T(pi)/4 + if absx < sqrt(eps(T))/2 # first order dominates, but also allows tan(-0)=-0 + return x + end + return tan_kernel(x) + elseif isnan(x) + return T(NaN) + elseif isinf(x) + tan_domain_error(x) + end + n, y = rem_pio2_kernel(x) + if iseven(n) + return tan_kernel(y,1) + else + return tan_kernel(y,-1) + end +end +tan(x::Real) = tan(float(x)) + +@inline tan_kernel(y::Float64) = tan_kernel(DoubleFloat64(y, 0.0), 1) +@inline function tan_kernel(y::DoubleFloat64, k) + # kernel tan function on ~[-pi/4, pi/4] (except on -0) + # Input y is assumed to be bounded by ~pi/4 in magnitude. + # Input k indicates whether tan (if k = 1) or -1/tan (if k = -1) is returned. + + # Algorithm + # 1. Since tan(-y) = -tan(y), we need only to consider positive y. + # 2. Callers must return tan(-0) = -0 without calling here since our + # odd polynomial is not evaluated in a way that preserves -0. + # Callers may do the optimization tan(y) ~ y for tiny y. + # 3. tan(y) is approximated by a odd polynomial of degree 27 on + # [0,0.67434] + # 3 27 + # tan(y) ~ y + T1*y + ... + T13*y ≡ P(y) + # where + # + # |tan(y) 2 4 26 | -59.2 + # (tan(y)-P(y))/y = |----- - (1+T1*y +T2*y +.... +T13*y )| <= 2 + # | y | + # + # Note: tan(y+z) = tan(y) + tan'(y)*z + # ~ tan(y) + (1+y*y)*z + # Therefore, for better accuracz in computing tan(y+z), let + # 3 2 2 2 2 + # r = y *(T2+y *(T3+y *(...+y *(T12+y *T13)))) + # then + # 3 2 + # tan(y+z) = y + (T1*y + (y *(r+z)+z)) + # + # 4. For y in [0.67434,pi/4], let z = pi/4 - y, then + # tan(y) = tan(pi/4-z) = (1-tan(z))/(1+tan(z)) + # = 1 - 2*(tan(z) - (tan(z)^2)/(1+tan(z))) + + yhi = y.hi + ylo = y.lo + + if abs(yhi) >= 0.6744 + if yhi < 0.0 + yhi = -yhi + ylo = -ylo + end + # Then, accurately reduce y as "pio4hi"-yhi+"pio4lo"-ylo + yhi = (pi/4 - yhi) + (3.06161699786838301793e-17 - ylo) + # yhi is guaranteed to be exact, so ylo is identically zero + ylo = 0.0 + end + y² = yhi * yhi + y⁴ = y² * y² + + # Break P(y)-T1*y³ = y^5*(T[2]+y^2*T[3]+...) into y⁵*r + y⁵*v where + # r = T[2]+y^4*T[4]+...+y^20*T[12]) + # v = (y^2*(T[3]+y^4*T[5]+...+y^22*[T13])) + r = @horner(y⁴, + 1.33333333333201242699e-01, # T2 + 2.18694882948595424599e-02, # T4 + 3.59207910759131235356e-03, # T6 + 5.88041240820264096874e-04, # T8 + 7.81794442939557092300e-05, # T10 + -1.85586374855275456654e-05) # T12 + v = y² * @horner(y⁴, + 5.39682539762260521377e-02, # T3 + 8.86323982359930005737e-03, # T5 + 1.45620945432529025516e-03, # T7 + 2.46463134818469906812e-04, # T9 + 7.14072491382608190305e-05, # T11 + 2.59073051863633712884e-05) # T13 + # Precompute y³ + y³ = y² * yhi + # Calculate P(y)-y-T1*y³ = y⁵*r + y⁵*v = y²(y³*(r+v)) + r = ylo + y² * (y³ * (r + v) + ylo) + # Calculate P(y)-y = r+T1*y³ + r += 3.33333333333334091986e-01*y³ + # Calculate w = r+y = P(y) + Px = yhi + r + if abs(y.hi) >= 0.6744 + # If the original y was above the threshold, then we calculate + # tan(y) = 1 - 2*(tan(y) - (tan(y)^2)/(1+tan(y))) + # ≈ 1 - 2*(P(z) - (P(z)^2)/(1+P(z))) + # where z = y-π/4. + return (signbit(y.hi) ? -1.0 : 1.0)*(k - 2*(yhi-(Px^2/(k+Px)-r))) + end + if k == 1 + # Else, we simply return w = P(y) if k == 1 (integer multiple from argument + # reduction was even)... + return Px + else + # ...or tan(y) ≈ -1.0/(y+r) if !(k == 1) (integer multiple from argument + # reduction was odd). If 2ulp error is allowed, simply return the frac- + # tion directly. Instead, we calculate it accurately. + + # Px0 is w with zeroed out low word + Px0 = reinterpret(Float64, (reinterpret(UInt64, Px) >> 32) << 32) + v = r - (Px0 - yhi) # Px0+v = r+y + t = a = -1.0 / Px + # zero out low word of t + t = reinterpret(Float64, (reinterpret(UInt64, t) >> 32) << 32) + s = 1.0 + t * Px0 + return t + a * (s + t * v) + end +end + +@inline tan_kernel(y::Float32) = tan_kernel(DoubleFloat32(y), 1) +@inline function tan_kernel(y::DoubleFloat32, k) + # |tan(y)/y - t(y)| < 2**-25.5 (~[-2e-08, 2e-08]). */ + y² = y.hi*y.hi + r = @horner(y², 0.00297435743359967304927, 0.00946564784943673166728) + t = @horner(y², 0.0533812378445670393523, 0.0245283181166547278873) + y⁴ = y²*y² + y³ = y²*y.hi + u = @horner(y², 0.333331395030791399758, 0.133392002712976742718) + Py = (y.hi+y³*u)+(y³*y⁴)*(t+y⁴*r) + if k == 1 + return Float32(Py) + end + + return Float32(-1.0/Py) +end + +# fallback methods +sin_kernel(x::Real) = sin(x) +cos_kernel(x::Real) = cos(x) +tan_kernel(x::Real) = tan(x) +sincos_kernel(x::Real) = sincos(x) + +# Inverse trigonometric functions +# asin methods +ASIN_X_MIN_THRESHOLD(::Type{Float32}) = 2.0f0^-12 +ASIN_X_MIN_THRESHOLD(::Type{Float64}) = sqrt(eps(Float64)) + +arc_p(t::Float64) = + t*@horner(t, + 1.66666666666666657415e-01, + -3.25565818622400915405e-01, + 2.01212532134862925881e-01, + -4.00555345006794114027e-02, + 7.91534994289814532176e-04, + 3.47933107596021167570e-05) + +arc_q(z::Float64) = + @horner(z, + 1.0, + -2.40339491173441421878e+00, + 2.02094576023350569471e+00, + -6.88283971605453293030e-01, + 7.70381505559019352791e-02) + +arc_p(t::Float32) = + t*@horner(t, + 1.6666586697f-01, + -4.2743422091f-02, + -8.6563630030f-03) + +arc_q(t::Float32) = @horner(t, 1.0f0, -7.0662963390f-01) + +@inline arc_tRt(t) = arc_p(t)/arc_q(t) + + +@inline function asin_kernel(t::Float64, x::Float64) + # we use that for 1/2 <= x < 1 we have + # asin(x) = pi/2-2*asin(sqrt((1-x)/2)) + # Let y = (1-x), z = y/2, s := sqrt(z), and pio2_hi+pio2_lo=pi/2; + # then for x>0.98 + # asin(x) = pi/2 - 2*(s+s*z*R(z)) + # = pio2_hi - (2*(s+s*z*R(z)) - pio2_lo) + # For x<=0.98, let pio4_hi = pio2_hi/2, then + # f = hi part of s; + # c = sqrt(z) - f = (z-f*f)/(s+f) ...f+c=sqrt(z) + # and + # asin(x) = pi/2 - 2*(s+s*z*R(z)) + # = pio4_hi+(pio4-2s)-(2s*z*R(z)-pio2_lo) + # = pio4_hi+(pio4-2f)-(2s*z*R(z)-(pio2_lo+2c)) + pio2_lo = 6.12323399573676603587e-17 + s = sqrt_llvm(t) + tRt = arc_tRt(t) + if abs(x) >= 0.975 # |x| > 0.975 + return flipsign(pi/2 - (2.0*(s + s*tRt) - pio2_lo), x) + else + s0 = reinterpret(Float64, (reinterpret(UInt64, s) >> 32) << 32) + c = (t - s0*s0)/(s + s0) + p = 2.0*s*tRt - (pio2_lo - 2.0*c) + q = pi/4 - 2.0*s0 + return flipsign(pi/4 - (p-q), x) + end +end +@inline function asin_kernel(t::Float32, x::Float32) + s = sqrt_llvm(Float64(t)) + tRt = arc_tRt(t) # rational approximation + flipsign(Float32(pi/2 - 2*(s + s*tRt)), x) +end + +@noinline asin_domain_error(x) = throw(DomainError(x, "asin(x) is not defined for |x|>1.")) +function asin(x::T) where T<:Union{Float32, Float64} + # Since asin(x) = x + x^3/6 + x^5*3/40 + x^7*15/336 + ... + # we approximate asin(x) on [0,0.5] by + # asin(x) = x + x*x^2*R(x^2) + # where + # R(x^2) is a rational approximation of (asin(x)-x)/x^3 + # and its remez error is bounded by + # |(asin(x)-x)/x^3 - R(x^2)| < 2^(-58.75) + absx = abs(x) + if absx >= T(1.0) # |x|>= 1 + if absx == T(1.0) + return flipsign(T(pi)/2, x) + end + asin_domain_error(x) + elseif absx < T(1.0)/2 + # if |x| sufficiently small, |x| is a good approximation + if absx < ASIN_X_MIN_THRESHOLD(T) + return x + end + return muladd(x, arc_tRt(x*x), x) + end + # else 1/2 <= |x| < 1 + t = (T(1.0) - absx)/2 + return asin_kernel(t, x) +end +asin(x::Real) = asin(float(x)) + +# atan methods +ATAN_1_O_2_HI(::Type{Float64}) = 4.63647609000806093515e-01 # atan(0.5).hi +ATAN_2_O_2_HI(::Type{Float64}) = 7.85398163397448278999e-01 # atan(1.0).hi +ATAN_3_O_2_HI(::Type{Float64}) = 9.82793723247329054082e-01 # atan(1.5).hi +ATAN_INF_HI(::Type{Float64}) = 1.57079632679489655800e+00 # atan(Inf).hi + +ATAN_1_O_2_HI(::Type{Float32}) = 4.6364760399f-01 # atan(0.5).hi +ATAN_2_O_2_HI(::Type{Float32}) = 7.8539812565f-01 # atan(1.0).hi +ATAN_3_O_2_HI(::Type{Float32}) = 9.8279368877f-01 # atan(1.5).hi +ATAN_INF_HI(::Type{Float32}) = 1.5707962513f+00 # atan(Inf).hi + +ATAN_1_O_2_LO(::Type{Float64}) = 2.26987774529616870924e-17 # atan(0.5).lo +ATAN_2_O_2_LO(::Type{Float64}) = 3.06161699786838301793e-17 # atan(1.0).lo +ATAN_3_O_2_LO(::Type{Float64}) = 1.39033110312309984516e-17 # atan(1.5).lo +ATAN_INF_LO(::Type{Float64}) = 6.12323399573676603587e-17 # atan(Inf).lo + +ATAN_1_O_2_LO(::Type{Float32}) = 5.0121582440f-09 # atan(0.5).lo +ATAN_2_O_2_LO(::Type{Float32}) = 3.7748947079f-08 # atan(1.0).lo +ATAN_3_O_2_LO(::Type{Float32}) = 3.4473217170f-08 # atan(1.5).lo +ATAN_INF_LO(::Type{Float32}) = 7.5497894159f-08 # atan(Inf).lo + +ATAN_LARGE_X(::Type{Float64}) = 2.0^66 # seems too large? 2.0^60 gives the same +ATAN_SMALL_X(::Type{Float64}) = 2.0^-27 +ATAN_LARGE_X(::Type{Float32}) = 2.0f0^26 +ATAN_SMALL_X(::Type{Float32}) = 2.0f0^-12 + +atan_p(z::Float64, w::Float64) = z*@horner(w, + 3.33333333333329318027e-01, + 1.42857142725034663711e-01, + 9.09088713343650656196e-02, + 6.66107313738753120669e-02, + 4.97687799461593236017e-02, + 1.62858201153657823623e-02) +atan_q(w::Float64) = w*@horner(w, + -1.99999999998764832476e-01, + -1.11111104054623557880e-01, + -7.69187620504482999495e-02, + -5.83357013379057348645e-02, + -3.65315727442169155270e-02) +atan_p(z::Float32, w::Float32) = z*@horner(w, 3.3333328366f-01, 1.4253635705f-01, 6.1687607318f-02) +atan_q(w::Float32) = w*@horner(w, -1.9999158382f-01, -1.0648017377f-01) +@inline function atan_pq(x) + x² = x*x + x⁴ = x²*x² + # break sum from i=0 to 10 aT[i]z**(i+1) into odd and even poly + atan_p(x², x⁴), atan_q(x⁴) +end + +atan(x::Real) = atan(float(x)) +function atan(x::T) where T<:Union{Float32, Float64} + # Method + # 1. Reduce x to positive by atan(x) = -atan(-x). + # 2. According to the integer k=4t+0.25 chopped, t=x, the argument + # is further reduced to one of the following intervals and the + # arctangent of t is evaluated by the corresponding formula: + # + # [0,7/16] atan(x) = t-t^3*(a1+t^2*(a2+...(a10+t^2*a11)...) + # [7/16,11/16] atan(x) = atan(1/2) + atan( (t-0.5)/(1+t/2) ) + # [11/16.19/16] atan(x) = atan( 1 ) + atan( (t-1)/(1+t) ) + # [19/16,39/16] atan(x) = atan(3/2) + atan( (t-1.5)/(1+1.5t) ) + # [39/16,INF] atan(x) = atan(INF) + atan( -1/t ) + # + # If isnan(x) is true, then the nan value will eventually be passed to + # atan_pq(x) and return the appropriate nan value. + + absx = abs(x) + if absx >= ATAN_LARGE_X(T) + return copysign(T(1.5707963267948966), x) + end + if absx < T(7/16) + # no reduction needed + if absx < ATAN_SMALL_X(T) + return x + end + p, q = atan_pq(x) + return x - x*(p + q) + end + xsign = sign(x) + if absx < T(19/16) # 7/16 <= |x| < 19/16 + if absx < T(11/16) # 7/16 <= |x| <11/16 + hi = ATAN_1_O_2_HI(T) + lo = ATAN_1_O_2_LO(T) + x = (T(2.0)*absx - T(1.0))/(T(2.0) + absx) + else # 11/16 <= |x| < 19/16 + hi = ATAN_2_O_2_HI(T) + lo = ATAN_2_O_2_LO(T) + x = (absx - T(1.0))/(absx + T(1.0)) + end + else + if absx < T(39/16) # 19/16 <= |x| < 39/16 + hi = ATAN_3_O_2_HI(T) + lo = ATAN_3_O_2_LO(T) + x = (absx - T(1.5))/(T(1.0) + T(1.5)*absx) + else # 39/16 <= |x| < upper threshold (2.0^66 or 2.0f0^26) + hi = ATAN_INF_HI(T) + lo = ATAN_INF_LO(T) + x = -T(1.0)/absx + end + end + # end of argument reduction + p, q = atan_pq(x) + z = hi - ((x*(p + q) - lo) - x) + copysign(z, xsign) +end +# atan2 methods +ATAN2_PI_LO(::Type{Float32}) = -8.7422776573f-08 +ATAN2_RATIO_BIT_SHIFT(::Type{Float32}) = 23 +ATAN2_RATIO_THRESHOLD(::Type{Float32}) = 26 + +ATAN2_PI_LO(::Type{Float64}) = 1.2246467991473531772E-16 +ATAN2_RATIO_BIT_SHIFT(::Type{Float64}) = 20 +ATAN2_RATIO_THRESHOLD(::Type{Float64}) = 60 + +function atan(y::T, x::T) where T<:Union{Float32, Float64} + # Method : + # M1) Reduce y to positive by atan2(y,x)=-atan2(-y,x). + # M2) Reduce x to positive by (if x and y are unexceptional): + # ARG (x+iy) = arctan(y/x) ... if x > 0, + # ARG (x+iy) = pi - arctan[y/(-x)] ... if x < 0, + # + # Special cases: + # + # S1) ATAN2((anything), NaN ) is NaN; + # S2) ATAN2(NAN , (anything) ) is NaN; + # S3) ATAN2(+-0, +(anything but NaN)) is +-0 ; + # S4) ATAN2(+-0, -(anything but NaN)) is +-pi ; + # S5) ATAN2(+-(anything but 0 and NaN), 0) is +-pi/2; + # S6) ATAN2(+-(anything but INF and NaN), +INF) is +-0 ; + # S7) ATAN2(+-(anything but INF and NaN), -INF) is +-pi; + # S8) ATAN2(+-INF,+INF ) is +-pi/4 ; + # S9) ATAN2(+-INF,-INF ) is +-3pi/4; + # S10) ATAN2(+-INF, (anything but,0,NaN, and INF)) is +-pi/2; + if isnan(x) || isnan(y) # S1 or S2 + return T(NaN) + end + + if x == T(1.0) # then y/x = y and x > 0, see M2 + return atan(y) + end + # generate an m ∈ {0, 1, 2, 3} to branch off of + m = 2*signbit(x) + 1*signbit(y) + + if iszero(y) + if m == 0 || m == 1 + return y # atan(+-0, +anything) = +-0 + elseif m == 2 + return T(pi) # atan(+0, -anything) = pi + elseif m == 3 + return -T(pi) # atan(-0, -anything) =-pi + end + elseif iszero(x) + return flipsign(T(pi)/2, y) + end + + if isinf(x) + if isinf(y) + if m == 0 + return T(pi)/4 # atan(+Inf), +Inf)) + elseif m == 1 + return -T(pi)/4 # atan(-Inf), +Inf)) + elseif m == 2 + return 3*T(pi)/4 # atan(+Inf, -Inf) + elseif m == 3 + return -3*T(pi)/4 # atan(-Inf,-Inf) + end + else + if m == 0 + return zero(T) # atan(+...,+Inf) */ + elseif m == 1 + return -zero(T) # atan(-...,+Inf) */ + elseif m == 2 + return T(pi) # atan(+...,-Inf) */ + elseif m == 3 + return -T(pi) # atan(-...,-Inf) */ + end + end + end + + # x wasn't Inf, but y is + isinf(y) && return copysign(T(pi)/2, y) + + ypw = poshighword(y) + xpw = poshighword(x) + # compute y/x for Float32 + k = reinterpret(Int32, ypw-xpw)>>ATAN2_RATIO_BIT_SHIFT(T) + + if k > ATAN2_RATIO_THRESHOLD(T) # |y/x| > threshold + z=T(pi)/2+T(0.5)*ATAN2_PI_LO(T) + m&=1; + elseif x<0 && k < -ATAN2_RATIO_THRESHOLD(T) # 0 > |y|/x > threshold + z = zero(T) + else #safe to do y/x + z = atan(abs(y/x)) + end + + if m == 0 + return z # atan(+,+) + elseif m == 1 + return -z # atan(-,+) + elseif m == 2 + return T(pi)-(z-ATAN2_PI_LO(T)) # atan(+,-) + else # default case m == 3 + return (z-ATAN2_PI_LO(T))-T(pi) # atan(-,-) + end +end +# acos methods +ACOS_X_MIN_THRESHOLD(::Type{Float32}) = 2.0f0^-26 +ACOS_X_MIN_THRESHOLD(::Type{Float64}) = 2.0^-57 +PIO2_HI(::Type{Float32}) = 1.5707962513f+00 +PIO2_LO(::Type{Float32}) = 7.5497894159f-08 +PIO2_HI(::Type{Float64}) = 1.57079632679489655800e+00 +PIO2_LO(::Type{Float64}) = 6.12323399573676603587e-17 +ACOS_PI(::Type{Float32}) = 3.1415925026f+00 +ACOS_PI(::Type{Float64}) = 3.14159265358979311600e+00 +@inline ACOS_CORRECT_LOWWORD(::Type{Float32}, x) = reinterpret(Float32, (reinterpret(UInt32, x) & 0xfffff000)) +@inline ACOS_CORRECT_LOWWORD(::Type{Float64}, x) = reinterpret(Float64, (reinterpret(UInt64, x) >> 32) << 32) + +@noinline acos_domain_error(x) = throw(DomainError(x, "acos(x) not defined for |x| > 1")) +function acos(x::T) where T <: Union{Float32, Float64} + # Method : + # acos(x) = pi/2 - asin(x) + # acos(-x) = pi/2 + asin(x) + # As a result, we use the same rational approximation (arc_tRt) as in asin. + # See the comments in asin for more information about this approximation. + # 1) For |x| <= 0.5 + # acos(x) = pi/2 - (x + x*x^2*R(x^2)) + # 2) For x < -0.5 + # acos(x) = pi - 2asin(sqrt((1 - |x|)/2)) + # = pi - 0.5*(s+s*z*R(z)) + # where z=(1-|x|)/2, s=sqrt(z) + # 3) For x > 0.5 + # acos(x) = pi/2 - (pi/2 - 2asin(sqrt((1 - x)/2))) + # = 2asin(sqrt((1 - x)/2)) + # = 2s + 2s*z*R(z) ...z=(1 - x)/2, s=sqrt(z) + # = 2f + (2c + 2s*z*R(z)) + # where f=hi part of s, and c = (z - f*f)/(s + f) is the correction term + # for f so that f + c ~ sqrt(z). + + # Special cases: + # 4) if x is NaN, return x itself; + # 5) if |x|>1 throw warning. + + absx = abs(x) + if absx >= T(1.0) + # acos(-1) = π, acos(1) = 0 + absx == T(1.0) && return x > T(0.0) ? T(0.0) : T(pi) + # acos(x) is not defined for |x| > 1 + acos_domain_error(x) # see 5) above + elseif absx < T(1.0)/2 # see 1) above + # if |x| sufficiently small, acos(x) ≈ pi/2 + absx < ACOS_X_MIN_THRESHOLD(T) && return T(pi)/2 + # if |x| < 0.5 we have acos(x) = pi/2 - (x + x*x^2*R(x^2)) + return PIO2_HI(T) - (x - (PIO2_LO(T) - x*arc_tRt(x*x))) + end + z = (T(1.0) - absx)*T(0.5) + zRz = arc_tRt(z) + s = sqrt_llvm(z) + if x < T(0.0) # see 2) above + return ACOS_PI(T) - T(2.0)*(s + (zRz*s - PIO2_LO(T))) + else # see 3) above + # if x > 0.5 we have + # acos(x) = pi/2 - (pi/2 - 2asin(sqrt((1-x)/2))) + # = 2asin(sqrt((1-x)/2)) + # = 2s + 2s*z*R(z) ...z=(1-x)/2, s=sqrt(z) + # = 2f + (2c + 2s*z*R(z)) + # where f=hi part of s, and c = (z-f*f)/(s+f) is the correction term + # for f so that f+c ~ sqrt(z). + df = ACOS_CORRECT_LOWWORD(T, s) + c = (z - df*df)/(s + df) + return T(2.0)*(df + (zRz*s + c)) + end +end +acos(x::Real) = acos(float(x)) + +# multiply in extended precision +function mulpi_ext(x::Float64) + m = 3.141592653589793 + m_hi = 3.1415926218032837 + m_lo = 3.178650954705639e-8 + + x_hi = reinterpret(Float64, reinterpret(UInt64,x) & 0xffff_ffff_f800_0000) + x_lo = x-x_hi + + y_hi = m*x + y_lo = x_hi * m_lo + (x_lo* m_hi + ((x_hi*m_hi-y_hi) + x_lo*m_lo)) + + DoubleFloat64(y_hi,y_lo) +end +mulpi_ext(x::Float32) = DoubleFloat32(pi*Float64(x)) +mulpi_ext(x::Rational) = mulpi_ext(float(x)) +mulpi_ext(x::Real) = pi*x # Fallback + +""" + sinpi(x) + +Compute ``\\sin(\\pi x)`` more accurately than `sin(pi*x)`, especially for large `x`. +""" +function sinpi(x::T) where T<:AbstractFloat + if !isfinite(x) + isnan(x) && return x + throw(DomainError(x, "`x` cannot be infinite.")) + end + + ax = abs(x) + s = maxintfloat(T)/2 + ax >= s && return copysign(zero(T),x) # integer-valued + + # reduce to interval [-1,1] + # assumes RoundNearest rounding mode + t = 3*s + rx = x-((x+t)-t) # zeros may be incorrectly signed + arx = abs(rx) + + if (arx == 0) | (arx == 1) + copysign(zero(T),x) + elseif arx < 0.25 + sin_kernel(mulpi_ext(rx)) + elseif arx < 0.75 + y = mulpi_ext(T(0.5) - arx) + copysign(cos_kernel(y),rx) + else + y = mulpi_ext(copysign(one(T),rx) - rx) + sin_kernel(y) + end +end + +# Integers and Rationals +function sinpi(x::T) where T<:Union{Integer,Rational} + Tf = float(T) + if !isfinite(x) + throw(DomainError(x, "`x` must be finite.")) + end + + # until we get an IEEE remainder function (#9283) + rx = rem(x,2) + if rx > 1 + rx -= 2 + elseif rx < -1 + rx += 2 + end + arx = abs(rx) + + if (arx == 0) | (arx == 1) + copysign(zero(Tf),x) + elseif arx < 0.25 + sin_kernel(mulpi_ext(rx)) + elseif arx < 0.75 + y = mulpi_ext(T(0.5) - arx) + copysign(cos_kernel(y),rx) + else + y = mulpi_ext(copysign(one(T),rx) - rx) + sin_kernel(y) + end +end + +""" + cospi(x) + +Compute ``\\cos(\\pi x)`` more accurately than `cos(pi*x)`, especially for large `x`. +""" +function cospi(x::T) where T<:AbstractFloat + if !isfinite(x) + isnan(x) && return x + throw(DomainError(x, "`x` cannot be infinite.")) + end + + ax = abs(x) + s = maxintfloat(T) + ax >= s && return one(T) # even integer-valued + + # reduce to interval [-1,1], then [0,1] + # assumes RoundNearest rounding mode + rx = abs(ax-((ax+s)-s)) + + if rx <= 0.25 + cos_kernel(mulpi_ext(rx)) + elseif rx < 0.75 + y = mulpi_ext(T(0.5) - rx) + sin_kernel(y) + else + y = mulpi_ext(one(T) - rx) + -cos_kernel(y) + end +end + +# Integers and Rationals +function cospi(x::T) where T<:Union{Integer,Rational} + if !isfinite(x) + throw(DomainError(x, "`x` must be finite.")) + end + + ax = abs(x) + # until we get an IEEE remainder function (#9283) + rx = rem(ax,2) + if rx > 1 + rx = 2-rx + end + + if rx <= 0.25 + cos_kernel(mulpi_ext(rx)) + elseif rx < 0.75 + y = mulpi_ext(T(0.5) - rx) + sin_kernel(y) + else + y = mulpi_ext(one(T) - rx) + -cos_kernel(y) + end +end + +sinpi(x::Integer) = x >= 0 ? zero(float(x)) : -zero(float(x)) +cospi(x::Integer) = isodd(x) ? -one(float(x)) : one(float(x)) +sinpi(x::Real) = sinpi(float(x)) +cospi(x::Real) = cospi(float(x)) + +function sinpi(z::Complex{T}) where T + F = float(T) + zr, zi = reim(z) + if isinteger(zr) + # zr = ...,-2,-1,0,1,2,... + # sin(pi*zr) == ±0 + # cos(pi*zr) == ±1 + # cosh(pi*zi) > 0 + s = copysign(zero(F),zr) + c_pos = isa(zr,Integer) ? iseven(zr) : isinteger(zr/2) + sh = sinh(pi*zi) + Complex(s, c_pos ? sh : -sh) + elseif isinteger(2*zr) + # zr = ...,-1.5,-0.5,0.5,1.5,2.5,... + # sin(pi*zr) == ±1 + # cos(pi*zr) == +0 + # sign(sinh(pi*zi)) == sign(zi) + s_pos = isinteger((2*zr-1)/4) + ch = cosh(pi*zi) + Complex(s_pos ? ch : -ch, isnan(zi) ? zero(F) : copysign(zero(F),zi)) + elseif !isfinite(zr) + if zi == 0 || isinf(zi) + Complex(F(NaN), F(zi)) + else + Complex(F(NaN), F(NaN)) + end + else + pizi = pi*zi + Complex(sinpi(zr)*cosh(pizi), cospi(zr)*sinh(pizi)) + end +end + +function cospi(z::Complex{T}) where T + F = float(T) + zr, zi = reim(z) + if isinteger(zr) + # zr = ...,-2,-1,0,1,2,... + # sin(pi*zr) == ±0 + # cos(pi*zr) == ±1 + # sign(sinh(pi*zi)) == sign(zi) + # cosh(pi*zi) > 0 + s = copysign(zero(F),zr) + c_pos = isa(zr,Integer) ? iseven(zr) : isinteger(zr/2) + ch = cosh(pi*zi) + Complex(c_pos ? ch : -ch, isnan(zi) ? s : -flipsign(s,zi)) + elseif isinteger(2*zr) + # zr = ...,-1.5,-0.5,0.5,1.5,2.5,... + # sin(pi*zr) == ±1 + # cos(pi*zr) == +0 + # sign(sinh(pi*zi)) == sign(zi) + s_pos = isinteger((2*zr-1)/4) + sh = sinh(pi*zi) + Complex(zero(F), s_pos ? -sh : sh) + elseif !isfinite(zr) + if zi == 0 + Complex(F(NaN), isnan(zr) ? zero(F) : -flipsign(F(zi),zr)) + elseif isinf(zi) + Complex(F(Inf), F(NaN)) + else + Complex(F(NaN), F(NaN)) + end + else + pizi = pi*zi + Complex(cospi(zr)*cosh(pizi), -sinpi(zr)*sinh(pizi)) + end +end + +""" + sinc(x) + +Compute ``\\sin(\\pi x) / (\\pi x)`` if ``x \\neq 0``, and ``1`` if ``x = 0``. +""" +sinc(x::Number) = x==0 ? one(x) : oftype(x,sinpi(x)/(pi*x)) +sinc(x::Integer) = x==0 ? one(x) : zero(x) +sinc(x::Complex{<:AbstractFloat}) = x==0 ? one(x) : oftype(x, sinpi(x)/(pi*x)) +sinc(x::Complex) = sinc(float(x)) +sinc(x::Real) = x==0 ? one(x) : isinf(x) ? zero(x) : sinpi(x)/(pi*x) + +""" + cosc(x) + +Compute ``\\cos(\\pi x) / x - \\sin(\\pi x) / (\\pi x^2)`` if ``x \\neq 0``, and ``0`` if +``x = 0``. This is the derivative of `sinc(x)`. +""" +cosc(x::Number) = x==0 ? zero(x) : oftype(x,(cospi(x)-sinpi(x)/(pi*x))/x) +cosc(x::Integer) = cosc(float(x)) +cosc(x::Complex{<:AbstractFloat}) = x==0 ? zero(x) : oftype(x,(cospi(x)-sinpi(x)/(pi*x))/x) +cosc(x::Complex) = cosc(float(x)) +cosc(x::Real) = x==0 || isinf(x) ? zero(x) : (cospi(x)-sinpi(x)/(pi*x))/x + +for (finv, f, finvh, fh, finvd, fd, fn) in ((:sec, :cos, :sech, :cosh, :secd, :cosd, "secant"), + (:csc, :sin, :csch, :sinh, :cscd, :sind, "cosecant"), + (:cot, :tan, :coth, :tanh, :cotd, :tand, "cotangent")) + name = string(finv) + hname = string(finvh) + dname = string(finvd) + @eval begin + @doc """ + $($name)(x) + + Compute the $($fn) of `x`, where `x` is in radians. + """ ($finv)(z::Number) = inv(($f)(z)) + @doc """ + $($hname)(x) + + Compute the hyperbolic $($fn) of `x`. + """ ($finvh)(z::Number) = inv(($fh)(z)) + @doc """ + $($dname)(x) + + Compute the $($fn) of `x`, where `x` is in degrees. + """ ($finvd)(z::Number) = inv(($fd)(z)) + end +end + +for (tfa, tfainv, hfa, hfainv, fn) in ((:asec, :acos, :asech, :acosh, "secant"), + (:acsc, :asin, :acsch, :asinh, "cosecant"), + (:acot, :atan, :acoth, :atanh, "cotangent")) + tname = string(tfa) + hname = string(hfa) + @eval begin + @doc """ + $($tname)(x) + Compute the inverse $($fn) of `x`, where the output is in radians. """ ($tfa)(y::Number) = ($tfainv)(inv(y)) + @doc """ + $($hname)(x) + Compute the inverse hyperbolic $($fn) of `x`. """ ($hfa)(y::Number) = ($hfainv)(inv(y)) + end +end + + +# multiply in extended precision +function deg2rad_ext(x::Float64) + m = 0.017453292519943295 + m_hi = 0.01745329238474369 + m_lo = 1.3519960527851425e-10 + + u = 134217729.0*x # 0x1p27 + 1 + x_hi = u-(u-x) + x_lo = x-x_hi + + y_hi = m*x + y_lo = x_hi * m_lo + (x_lo* m_hi + ((x_hi*m_hi-y_hi) + x_lo*m_lo)) + + DoubleFloat64(y_hi,y_lo) +end +deg2rad_ext(x::Float32) = DoubleFloat32(deg2rad(Float64(x))) +deg2rad_ext(x::Real) = deg2rad(x) # Fallback + +function sind(x::Real) + if isinf(x) + return throw(DomainError(x, "`x` cannot be infinite.")) + elseif isnan(x) + return oftype(x,NaN) + end + + rx = copysign(float(rem(x,360)),x) + arx = abs(rx) + + if rx == zero(rx) + return rx + elseif arx < oftype(rx,45) + return sin_kernel(deg2rad_ext(rx)) + elseif arx <= oftype(rx,135) + y = deg2rad_ext(oftype(rx,90) - arx) + return copysign(cos_kernel(y),rx) + elseif arx == oftype(rx,180) + return copysign(zero(rx),rx) + elseif arx < oftype(rx,225) + y = deg2rad_ext((oftype(rx,180) - arx)*sign(rx)) + return sin_kernel(y) + elseif arx <= oftype(rx,315) + y = deg2rad_ext(oftype(rx,270) - arx) + return -copysign(cos_kernel(y),rx) + else + y = deg2rad_ext(rx - copysign(oftype(rx,360),rx)) + return sin_kernel(y) + end +end + +function cosd(x::Real) + if isinf(x) + return throw(DomainError(x, "`x` cannot be infinite.")) + elseif isnan(x) + return oftype(x,NaN) + end + + rx = abs(float(rem(x,360))) + + if rx <= oftype(rx,45) + return cos_kernel(deg2rad_ext(rx)) + elseif rx < oftype(rx,135) + y = deg2rad_ext(oftype(rx,90) - rx) + return sin_kernel(y) + elseif rx <= oftype(rx,225) + y = deg2rad_ext(oftype(rx,180) - rx) + return -cos_kernel(y) + elseif rx < oftype(rx,315) + y = deg2rad_ext(rx - oftype(rx,270)) + return sin_kernel(y) + else + y = deg2rad_ext(oftype(rx,360) - rx) + return cos_kernel(y) + end +end + +tand(x::Real) = sind(x) / cosd(x) + +""" + sincosd(x) + +Simultaneously compute the sine and cosine of `x`, where `x` is in degrees. + +!!! compat "Julia 1.3" + This function requires at least Julia 1.3. +""" +function sincosd(x::Real) + if isinf(x) + return throw(DomainError(x, "sincosd(x) is only defined for finite `x`.")) + elseif isnan(x) + return (oftype(x,NaN), oftype(x,NaN)) + end + + # It turns out that calling those functions separately yielded better + # performance than considering each case and calling `sincos_kernel`. + return (sind(x), cosd(x)) +end + +sincosd(::Missing) = (missing, missing) + +for (fd, f, fn) in ((:sind, :sin, "sine"), (:cosd, :cos, "cosine"), (:tand, :tan, "tangent")) + name = string(fd) + @eval begin + @doc """ + $($name)(x) + Compute $($fn) of `x`, where `x` is in degrees. """ ($fd)(z) = ($f)(deg2rad(z)) + end +end + +for (fd, f, fn) in ((:asind, :asin, "sine"), (:acosd, :acos, "cosine"), + (:asecd, :asec, "secant"), (:acscd, :acsc, "cosecant"), (:acotd, :acot, "cotangent")) + name = string(fd) + @eval begin + @doc """ + $($name)(x) + + Compute the inverse $($fn) of `x`, where the output is in degrees. """ ($fd)(y) = rad2deg(($f)(y)) + end +end + +""" + atand(y) + atand(y,x) + +Compute the inverse tangent of `y` or `y/x`, respectively, where the output is in degrees. +""" +atand(y) = rad2deg(atan(y)) +atand(y, x) = rad2deg(atan(y,x)) diff --git a/base/stacktraces.jl b/base/stacktraces.jl new file mode 100644 index 0000000..0fca8a8 --- /dev/null +++ b/base/stacktraces.jl @@ -0,0 +1,308 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" +Tools for collecting and manipulating stack traces. Mainly used for building errors. +""" +module StackTraces + + +import Base: hash, ==, show +using Base.Printf: @printf +using Base: something + +export StackTrace, StackFrame, stacktrace + +""" + StackFrame + +Stack information representing execution context, with the following fields: + +- `func::Symbol` + + The name of the function containing the execution context. + +- `linfo::Union{Core.MethodInstance, CodeInfo, Nothing}` + + The MethodInstance containing the execution context (if it could be found). + +- `file::Symbol` + + The path to the file containing the execution context. + +- `line::Int` + + The line number in the file containing the execution context. + +- `from_c::Bool` + + True if the code is from C. + +- `inlined::Bool` + + True if the code is from an inlined frame. + +- `pointer::UInt64` + + Representation of the pointer to the execution context as returned by `backtrace`. + +""" +struct StackFrame # this type should be kept platform-agnostic so that profiles can be dumped on one machine and read on another + "the name of the function containing the execution context" + func::Symbol + "the path to the file containing the execution context" + file::Symbol + "the line number in the file containing the execution context" + line::Int + "the MethodInstance or CodeInfo containing the execution context (if it could be found)" + linfo::Union{Core.MethodInstance, Core.CodeInfo, Nothing} + "true if the code is from C" + from_c::Bool + "true if the code is from an inlined frame" + inlined::Bool + "representation of the pointer to the execution context as returned by `backtrace`" + pointer::UInt64 # Large enough to be read losslessly on 32- and 64-bit machines. +end + +StackFrame(func, file, line) = StackFrame(Symbol(func), Symbol(file), line, + nothing, false, false, 0) + +""" + StackTrace + +An alias for `Vector{StackFrame}` provided for convenience; returned by calls to +`stacktrace`. +""" +const StackTrace = Vector{StackFrame} + +const empty_sym = Symbol("") +const UNKNOWN = StackFrame(empty_sym, empty_sym, -1, nothing, true, false, 0) # === lookup(C_NULL) + + +#= +If the StackFrame has function and line information, we consider two of them the same if +they share the same function/line information. +=# +function ==(a::StackFrame, b::StackFrame) + return a.line == b.line && a.from_c == b.from_c && a.func == b.func && a.file == b.file && a.inlined == b.inlined # excluding linfo and pointer +end + +function hash(frame::StackFrame, h::UInt) + h += 0xf4fbda67fe20ce88 % UInt + h = hash(frame.line, h) + h = hash(frame.file, h) + h = hash(frame.func, h) + h = hash(frame.from_c, h) + h = hash(frame.inlined, h) + return h +end + + +""" + lookup(pointer::Union{Ptr{Cvoid}, UInt}) -> Vector{StackFrame} + +Given a pointer to an execution context (usually generated by a call to `backtrace`), looks +up stack frame context information. Returns an array of frame information for all functions +inlined at that point, innermost function first. +""" +function lookup(pointer::Ptr{Cvoid}) + infos = ccall(:jl_lookup_code_address, Any, (Ptr{Cvoid}, Cint), pointer - 1, false) + isempty(infos) && return [StackFrame(empty_sym, empty_sym, -1, nothing, true, false, convert(UInt64, pointer))] + res = Vector{StackFrame}(undef, length(infos)) + for i in 1:length(infos) + info = infos[i] + @assert(length(info) == 7) + res[i] = StackFrame(info[1], info[2], info[3], info[4], info[5], info[6], info[7]) + end + return res +end + +lookup(pointer::UInt) = lookup(convert(Ptr{Cvoid}, pointer)) + +const top_level_scope_sym = Symbol("top-level scope") + +using Base.Meta +is_loc_meta(expr, kind) = isexpr(expr, :meta) && length(expr.args) >= 1 && expr.args[1] === kind +function lookup(ip::Base.InterpreterIP) + if ip.code isa Core.MethodInstance && ip.code.def isa Method + codeinfo = ip.code.uninferred + func = ip.code.def.name + file = ip.code.def.file + line = ip.code.def.line + elseif ip.code === nothing + # interpreted top-level expression with no CodeInfo + return [StackFrame(top_level_scope_sym, empty_sym, 0, nothing, false, false, 0)] + else + @assert ip.code isa Core.CodeInfo + codeinfo = ip.code + func = top_level_scope_sym + file = empty_sym + line = 0 + end + i = max(ip.stmt+1, 1) # ip.stmt is 0-indexed + if i > length(codeinfo.codelocs) || codeinfo.codelocs[i] == 0 + return [StackFrame(func, file, line, ip.code, false, false, 0)] + end + lineinfo = codeinfo.linetable[codeinfo.codelocs[i]] + scopes = StackFrame[] + while true + push!(scopes, StackFrame(lineinfo.method, lineinfo.file, lineinfo.line, ip.code, false, false, 0)) + if lineinfo.inlined_at == 0 + break + end + lineinfo = codeinfo.linetable[lineinfo.inlined_at] + end + return scopes +end + +# allow lookup on already-looked-up data for easier handling of pre-processed frames +lookup(s::StackFrame) = StackFrame[s] +lookup(s::Tuple{StackFrame,Int}) = StackFrame[s[1]] + +""" + backtrace() + +Get a backtrace object for the current program point. +""" +function Base.backtrace() + bt, bt2 = ccall(:jl_backtrace_from_here, Any, (Int32,), false) + if length(bt) > 2 + # remove frames for jl_backtrace_from_here and backtrace() + if bt[2] == Ptr{Cvoid}(-1%UInt) + # backtrace() is interpreted + # Note: win32 is missing the top frame (see https://bugs.chromium.org/p/crashpad/issues/detail?id=53) + @static if Base.Sys.iswindows() && Int === Int32 + deleteat!(bt, 1:2) + else + deleteat!(bt, 1:3) + end + pushfirst!(bt2) + else + @static if Base.Sys.iswindows() && Int === Int32 + deleteat!(bt, 1) + else + deleteat!(bt, 1:2) + end + end + end + return Base._reformat_bt(bt, bt2) +end + +""" + stacktrace([trace::Vector{Ptr{Cvoid}},] [c_funcs::Bool=false]) -> StackTrace + +Returns a stack trace in the form of a vector of `StackFrame`s. (By default stacktrace +doesn't return C functions, but this can be enabled.) When called without specifying a +trace, `stacktrace` first calls `backtrace`. +""" +function stacktrace(trace::Vector{<:Union{Base.InterpreterIP,Ptr{Cvoid}}}, c_funcs::Bool=false) + stack = vcat(StackTrace(), map(lookup, trace)...)::StackTrace + + # Remove frames that come from C calls. + if !c_funcs + filter!(frame -> !frame.from_c, stack) + end + + # Remove frame for this function (and any functions called by this function). + remove_frames!(stack, :stacktrace) + + # is there a better way? the func symbol has a number suffix which changes. + # it's possible that no test is needed and we could just popfirst! all the time. + # this line was added to PR #16213 because otherwise stacktrace() != stacktrace(false). + # not sure why. possibly b/c of re-ordering of base/sysimg.jl + !isempty(stack) && startswith(string(stack[1].func),"jlcall_stacktrace") && popfirst!(stack) + stack +end + +stacktrace(c_funcs::Bool=false) = stacktrace(backtrace(), c_funcs) + +""" + remove_frames!(stack::StackTrace, name::Symbol) + +Takes a `StackTrace` (a vector of `StackFrames`) and a function name (a `Symbol`) and +removes the `StackFrame` specified by the function name from the `StackTrace` (also removing +all frames above the specified function). Primarily used to remove `StackTraces` functions +from the `StackTrace` prior to returning it. +""" +function remove_frames!(stack::StackTrace, name::Symbol) + splice!(stack, 1:something(findlast(frame -> frame.func == name, stack), 0)) + return stack +end + +function remove_frames!(stack::StackTrace, names::Vector{Symbol}) + splice!(stack, 1:something(findlast(frame -> frame.func in names, stack), 0)) + return stack +end + +""" + remove_frames!(stack::StackTrace, m::Module) + +Returns the `StackTrace` with all `StackFrame`s from the provided `Module` removed. +""" +function remove_frames!(stack::StackTrace, m::Module) + filter!(f -> !from(f, m), stack) + return stack +end + +is_top_level_frame(f::StackFrame) = f.linfo isa Core.CodeInfo || (f.linfo === nothing && f.func === top_level_scope_sym) + +function show_spec_linfo(io::IO, frame::StackFrame) + if frame.linfo === nothing + if frame.func === empty_sym + @printf(io, "ip:%#x", frame.pointer) + elseif frame.func === top_level_scope_sym + print(io, "top-level scope") + else + color = get(io, :color, false) && get(io, :backtrace, false) ? + Base.stackframe_function_color() : + :nothing + printstyled(io, string(frame.func), color=color) + end + elseif frame.linfo isa Core.MethodInstance + if isa(frame.linfo.def, Method) + Base.show_tuple_as_call(io, frame.linfo.def.name, frame.linfo.specTypes) + else + Base.show(io, frame.linfo) + end + elseif frame.linfo isa Core.CodeInfo + print(io, "top-level scope") + end +end + +function show(io::IO, frame::StackFrame; full_path::Bool=false) + show_spec_linfo(io, frame) + if frame.file !== empty_sym + file_info = full_path ? string(frame.file) : basename(string(frame.file)) + print(io, " at ") + Base.with_output_color(get(io, :color, false) && get(io, :backtrace, false) ? Base.stackframe_lineinfo_color() : :nothing, io) do io + print(io, file_info, ":") + if frame.line >= 0 + print(io, frame.line) + else + print(io, "?") + end + end + end + if frame.inlined + print(io, " [inlined]") + end +end + +""" + from(frame::StackFrame, filter_mod::Module) -> Bool + +Returns whether the `frame` is from the provided `Module` +""" +function from(frame::StackFrame, m::Module) + finfo = frame.linfo + result = false + + if finfo isa Core.MethodInstance + frame_m = finfo.def + isa(frame_m, Method) && (frame_m = frame_m.module) + result = nameof(frame_m) === nameof(m) + end + + return result +end + +end diff --git a/base/stat.jl b/base/stat.jl new file mode 100644 index 0000000..15bbe0b --- /dev/null +++ b/base/stat.jl @@ -0,0 +1,346 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# filesystem operations + +export + ctime, + filemode, + filesize, + gperm, + isblockdev, + ischardev, + isdir, + isfifo, + isfile, + islink, + ismount, + ispath, + issetgid, + issetuid, + issocket, + issticky, + lstat, + mtime, + operm, + stat, + uperm + +struct StatStruct + device :: UInt + inode :: UInt + mode :: UInt + nlink :: Int + uid :: UInt + gid :: UInt + rdev :: UInt + size :: Int64 + blksize :: Int64 + blocks :: Int64 + mtime :: Float64 + ctime :: Float64 +end + +StatStruct() = StatStruct(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + +StatStruct(buf::Union{Vector{UInt8},Ptr{UInt8}}) = StatStruct( + ccall(:jl_stat_dev, UInt32, (Ptr{UInt8},), buf), + ccall(:jl_stat_ino, UInt32, (Ptr{UInt8},), buf), + ccall(:jl_stat_mode, UInt32, (Ptr{UInt8},), buf), + ccall(:jl_stat_nlink, UInt32, (Ptr{UInt8},), buf), + ccall(:jl_stat_uid, UInt32, (Ptr{UInt8},), buf), + ccall(:jl_stat_gid, UInt32, (Ptr{UInt8},), buf), + ccall(:jl_stat_rdev, UInt32, (Ptr{UInt8},), buf), + ccall(:jl_stat_size, UInt64, (Ptr{UInt8},), buf), + ccall(:jl_stat_blksize, UInt64, (Ptr{UInt8},), buf), + ccall(:jl_stat_blocks, UInt64, (Ptr{UInt8},), buf), + ccall(:jl_stat_mtime, Float64, (Ptr{UInt8},), buf), + ccall(:jl_stat_ctime, Float64, (Ptr{UInt8},), buf), +) + +show(io::IO, st::StatStruct) = print(io, "StatStruct(mode=0o$(string(filemode(st), base = 8, pad = 6)), size=$(filesize(st)))") + +# stat & lstat functions + +macro stat_call(sym, arg1type, arg) + return quote + stat_buf = zeros(UInt8, ccall(:jl_sizeof_stat, Int32, ())) + r = ccall($(Expr(:quote, sym)), Int32, ($(esc(arg1type)), Ptr{UInt8}), $(esc(arg)), stat_buf) + if !(r in (0, Base.UV_ENOENT, Base.UV_ENOTDIR, Base.UV_EINVAL)) + throw(_UVError("stat", r, "for file ", repr($(esc(arg))))) + end + st = StatStruct(stat_buf) + if ispath(st) != (r == 0) + error("stat returned zero type for a valid path") + end + return st + end +end + +stat(fd::OS_HANDLE) = @stat_call jl_fstat OS_HANDLE fd +stat(path::AbstractString) = @stat_call jl_stat Cstring path +lstat(path::AbstractString) = @stat_call jl_lstat Cstring path +if RawFD !== OS_HANDLE + global stat(fd::RawFD) = stat(Libc._get_osfhandle(fd)) +end +stat(fd::Integer) = stat(RawFD(fd)) + +""" + stat(file) + +Returns a structure whose fields contain information about the file. +The fields of the structure are: + +| Name | Description | +|:--------|:-------------------------------------------------------------------| +| size | The size (in bytes) of the file | +| device | ID of the device that contains the file | +| inode | The inode number of the file | +| mode | The protection mode of the file | +| nlink | The number of hard links to the file | +| uid | The user id of the owner of the file | +| gid | The group id of the file owner | +| rdev | If this file refers to a device, the ID of the device it refers to | +| blksize | The file-system preferred block size for the file | +| blocks | The number of such blocks allocated | +| mtime | Unix timestamp of when the file was last modified | +| ctime | Unix timestamp of when the file was created | + +""" +stat(path...) = stat(joinpath(path...)) + +""" + lstat(file) + +Like [`stat`](@ref), but for symbolic links gets the info for the link +itself rather than the file it refers to. +This function must be called on a file path rather than a file object or a file +descriptor. +""" +lstat(path...) = lstat(joinpath(path...)) + +# some convenience functions + +""" + filemode(file) + +Equivalent to `stat(file).mode`. +""" +filemode(st::StatStruct) = st.mode + +""" + filesize(path...) + +Equivalent to `stat(file).size`. +""" +filesize(st::StatStruct) = st.size + +""" + mtime(file) + +Equivalent to `stat(file).mtime`. +""" +mtime(st::StatStruct) = st.mtime + +""" + ctime(file) + +Equivalent to `stat(file).ctime`. +""" +ctime(st::StatStruct) = st.ctime + +# mode type predicates + +""" + ispath(path) -> Bool + +Return `true` if a valid filesystem entity exists at `path`, +otherwise returns `false`. +This is the generalization of [`isfile`](@ref), [`isdir`](@ref) etc. +""" +ispath(st::StatStruct) = filemode(st) & 0xf000 != 0x0000 + +""" + isfifo(path) -> Bool + +Return `true` if `path` is a FIFO, `false` otherwise. +""" +isfifo(st::StatStruct) = filemode(st) & 0xf000 == 0x1000 + +""" + ischardev(path) -> Bool + +Return `true` if `path` is a character device, `false` otherwise. +""" +ischardev(st::StatStruct) = filemode(st) & 0xf000 == 0x2000 + +""" + isdir(path) -> Bool + +Return `true` if `path` is a directory, `false` otherwise. + +# Examples +```jldoctest +julia> isdir(homedir()) +true + +julia> isdir("not/a/directory") +false +``` + +See also: [`isfile`](@ref) and [`ispath`](@ref). +""" +isdir(st::StatStruct) = filemode(st) & 0xf000 == 0x4000 + +""" + isblockdev(path) -> Bool + +Return `true` if `path` is a block device, `false` otherwise. +""" +isblockdev(st::StatStruct) = filemode(st) & 0xf000 == 0x6000 + +""" + isfile(path) -> Bool + +Return `true` if `path` is a regular file, `false` otherwise. + +# Examples +```jldoctest +julia> isfile(homedir()) +false + +julia> f = open("test_file.txt", "w"); + +julia> isfile(f) +true + +julia> close(f); rm("test_file.txt") +``` + +See also: [`isdir`](@ref) and [`ispath`](@ref). +""" +isfile(st::StatStruct) = filemode(st) & 0xf000 == 0x8000 + +""" + islink(path) -> Bool + +Return `true` if `path` is a symbolic link, `false` otherwise. +""" +islink(st::StatStruct) = filemode(st) & 0xf000 == 0xa000 + +""" + issocket(path) -> Bool + +Return `true` if `path` is a socket, `false` otherwise. +""" +issocket(st::StatStruct) = filemode(st) & 0xf000 == 0xc000 + +# mode permission predicates + +""" + issetuid(path) -> Bool + +Return `true` if `path` has the setuid flag set, `false` otherwise. +""" +issetuid(st::StatStruct) = (filemode(st) & 0o4000) > 0 + +""" + issetgid(path) -> Bool + +Return `true` if `path` has the setgid flag set, `false` otherwise. +""" +issetgid(st::StatStruct) = (filemode(st) & 0o2000) > 0 + +""" + issticky(path) -> Bool + +Return `true` if `path` has the sticky bit set, `false` otherwise. +""" +issticky(st::StatStruct) = (filemode(st) & 0o1000) > 0 + +""" + uperm(file) + +Get the permissions of the owner of the file as a bitfield of + +| Value | Description | +|:------|:-------------------| +| 01 | Execute Permission | +| 02 | Write Permission | +| 04 | Read Permission | + +For allowed arguments, see [`stat`](@ref). +""" +uperm(st::StatStruct) = UInt8((filemode(st) >> 6) & 0x7) + +""" + gperm(file) + +Like [`uperm`](@ref) but gets the permissions of the group owning the file. +""" +gperm(st::StatStruct) = UInt8((filemode(st) >> 3) & 0x7) + +""" + operm(file) + +Like [`uperm`](@ref) but gets the permissions for people who neither own the file nor are a member of +the group owning the file +""" +operm(st::StatStruct) = UInt8((filemode(st) ) & 0x7) + +# mode predicate methods for file names + +for f in Symbol[ + :ispath, + :isfifo, + :ischardev, + :isdir, + :isblockdev, + :isfile, + :issocket, + :issetuid, + :issetgid, + :issticky, + :uperm, + :gperm, + :operm, + :filemode, + :filesize, + :mtime, + :ctime, +] + @eval ($f)(path...) = ($f)(stat(path...)) +end + +islink(path...) = islink(lstat(path...)) + +# samefile can be used for files and directories: #11145#issuecomment-99511194 +samefile(a::StatStruct, b::StatStruct) = a.device==b.device && a.inode==b.inode +function samefile(a::AbstractString, b::AbstractString) + infoa = stat(a) + infob = stat(b) + if ispath(infoa) && ispath(infob) + samefile(infoa, infob) + else + return false + end +end + +""" + ismount(path) -> Bool + +Return `true` if `path` is a mount point, `false` otherwise. +""" +function ismount(path...) + path = joinpath(path...) + isdir(path) || return false + s1 = lstat(path) + # Symbolic links cannot be mount points + islink(s1) && return false + parent_path = joinpath(path, "..") + s2 = lstat(parent_path) + # If a directory and its parent are on different devices, then the + # directory must be a mount point + (s1.device != s2.device) && return true + (s1.inode == s2.inode) && return true + false +end diff --git a/base/stream.jl b/base/stream.jl new file mode 100644 index 0000000..933db06 --- /dev/null +++ b/base/stream.jl @@ -0,0 +1,1304 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +import .Libc: RawFD, dup +if Sys.iswindows() + import .Libc: WindowsRawSocket + const OS_HANDLE = WindowsRawSocket + const INVALID_OS_HANDLE = WindowsRawSocket(Ptr{Cvoid}(-1)) +else + const OS_HANDLE = RawFD + const INVALID_OS_HANDLE = RawFD(-1) +end + + +## types ## +abstract type IOServer end +abstract type LibuvServer <: IOServer end +abstract type LibuvStream <: IO end + + +# IO +# +- GenericIOBuffer{T<:AbstractArray{UInt8,1}} (not exported) +# +- AbstractPipe (not exported) +# . +- Pipe +# . +- Process (not exported) +# . +- ProcessChain (not exported) +# +- BufferStream +# +- DevNull (not exported) +# +- Filesystem.File +# +- LibuvStream (not exported) +# . +- PipeEndpoint (not exported) +# . +- TCPSocket +# . +- TTY (not exported) +# . +- UDPSocket +# +- IOBuffer = Base.GenericIOBuffer{Array{UInt8,1}} +# +- IOStream + +# IOServer +# +- LibuvServer +# . +- PipeServer +# . +- TCPServer + +# Redirectable = Union{IO, FileRedirect, Libc.RawFD} (not exported) + +bytesavailable(s::LibuvStream) = bytesavailable(s.buffer) + +function eof(s::LibuvStream) + bytesavailable(s) > 0 && return false + wait_readnb(s, 1) + # This function is race-y if used from multiple threads, but we guarantee + # it to never return false until the stream is definitively exhausted + # and that we won't return true if there's a readerror pending (it'll instead get thrown). + # This requires some careful ordering here (TODO: atomic loads) + bytesavailable(s) > 0 && return false + open = isopen(s) # must preceed readerror check + s.readerror === nothing || throw(s.readerror) + return !open +end + +# Limit our default maximum read and buffer size, +# to avoid DoS-ing ourself into an OOM situation +const DEFAULT_READ_BUFFER_SZ = 10485760 # 10 MB + +# manually limit our write size, if the OS doesn't support full-size writes +if Sys.iswindows() + const MAX_OS_WRITE = UInt(0x1FF0_0000) # 511 MB (determined semi-empirically, limited to 31 MB on XP) +else + const MAX_OS_WRITE = UInt(typemax(Csize_t)) +end + + +const StatusUninit = 0 # handle is allocated, but not initialized +const StatusInit = 1 # handle is valid, but not connected/active +const StatusConnecting = 2 # handle is in process of connecting +const StatusOpen = 3 # handle is usable +const StatusActive = 4 # handle is listening for read/write/connect events +const StatusClosing = 5 # handle is closing / being closed +const StatusClosed = 6 # handle is closed +const StatusEOF = 7 # handle is a TTY that has seen an EOF event (pretends to be closed until reseteof is called) +const StatusPaused = 8 # handle is Active, but not consuming events, and will transition to Open if it receives an event +function uv_status_string(x) + s = x.status + if x.handle == C_NULL + if s == StatusClosed + return "closed" + elseif s == StatusUninit + return "null" + end + return "invalid status" + elseif s == StatusUninit + return "uninit" + elseif s == StatusInit + return "init" + elseif s == StatusConnecting + return "connecting" + elseif s == StatusOpen + return "open" + elseif s == StatusActive + return "active" + elseif s == StatusPaused + return "paused" + elseif s == StatusClosing + return "closing" + elseif s == StatusClosed + return "closed" + elseif s == StatusEOF + return "eof" + end + return "invalid status" +end + +mutable struct PipeEndpoint <: LibuvStream + handle::Ptr{Cvoid} + status::Int + buffer::IOBuffer + cond::ThreadSynchronizer + readerror::Any + sendbuf::Union{IOBuffer, Nothing} + lock::ReentrantLock # advisory lock + throttle::Int + function PipeEndpoint(handle::Ptr{Cvoid}, status) + p = new(handle, + status, + PipeBuffer(), + ThreadSynchronizer(), + nothing, + nothing, + ReentrantLock(), + DEFAULT_READ_BUFFER_SZ) + associate_julia_struct(handle, p) + finalizer(uvfinalize, p) + return p + end +end + +function PipeEndpoint() + pipe = PipeEndpoint(Libc.malloc(_sizeof_uv_named_pipe), StatusUninit) + iolock_begin() + err = ccall(:uv_pipe_init, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Cint), eventloop(), pipe.handle, 0) + uv_error("failed to create pipe endpoint", err) + pipe.status = StatusInit + iolock_end() + return pipe +end + +function PipeEndpoint(fd::OS_HANDLE) + pipe = PipeEndpoint() + iolock_begin() + err = ccall(:uv_pipe_open, Int32, (Ptr{Cvoid}, OS_HANDLE), pipe.handle, fd) + uv_error("pipe_open", err) + pipe.status = StatusOpen + iolock_end() + return pipe +end +if OS_HANDLE != RawFD + PipeEndpoint(fd::RawFD) = PipeEndpoint(Libc._get_osfhandle(fd)) +end + + +mutable struct TTY <: LibuvStream + handle::Ptr{Cvoid} + status::Int + buffer::IOBuffer + cond::ThreadSynchronizer + readerror::Any + sendbuf::Union{IOBuffer, Nothing} + lock::ReentrantLock # advisory lock + throttle::Int + @static if Sys.iswindows(); ispty::Bool; end + function TTY(handle::Ptr{Cvoid}, status) + tty = new( + handle, + status, + PipeBuffer(), + ThreadSynchronizer(), + nothing, + nothing, + ReentrantLock(), + DEFAULT_READ_BUFFER_SZ) + associate_julia_struct(handle, tty) + finalizer(uvfinalize, tty) + @static if Sys.iswindows() + tty.ispty = ccall(:jl_ispty, Cint, (Ptr{Cvoid},), handle) != 0 + end + return tty + end +end + +function TTY(fd::OS_HANDLE) + tty = TTY(Libc.malloc(_sizeof_uv_tty), StatusUninit) + iolock_begin() + err = ccall(:uv_tty_init, Int32, (Ptr{Cvoid}, Ptr{Cvoid}, OS_HANDLE, Int32), + eventloop(), tty.handle, fd, 0) + uv_error("TTY", err) + tty.status = StatusOpen + iolock_end() + return tty +end +if OS_HANDLE != RawFD + TTY(fd::RawFD) = TTY(Libc._get_osfhandle(fd)) +end + +show(io::IO, stream::LibuvServer) = print(io, typeof(stream), "(", + _fd(stream), " ", + uv_status_string(stream), ")") +show(io::IO, stream::LibuvStream) = print(io, typeof(stream), "(", + _fd(stream), " ", + uv_status_string(stream), ", ", + bytesavailable(stream.buffer), " bytes waiting)") + +# Shared LibuvStream object interface + +function isreadable(io::LibuvStream) + bytesavailable(io) > 0 && return true + isopen(io) || return false + return ccall(:uv_is_readable, Cint, (Ptr{Cvoid},), io.handle) != 0 +end + +function iswritable(io::LibuvStream) + isopen(io) || return false + io.status == StatusClosing && return false + return ccall(:uv_is_writable, Cint, (Ptr{Cvoid},), io.handle) != 0 +end + +lock(s::LibuvStream) = lock(s.lock) +unlock(s::LibuvStream) = unlock(s.lock) + +rawhandle(stream::LibuvStream) = stream.handle +unsafe_convert(::Type{Ptr{Cvoid}}, s::Union{LibuvStream, LibuvServer}) = s.handle + +function init_stdio(handle::Ptr{Cvoid}) + iolock_begin() + t = ccall(:jl_uv_handle_type, Int32, (Ptr{Cvoid},), handle) + local io + if t == UV_FILE + fd = ccall(:jl_uv_file_handle, OS_HANDLE, (Ptr{Cvoid},), handle) + # TODO: Replace ios.c file with libuv fs? + # return File(fd) + @static if Sys.iswindows() + # TODO: Get ios.c to understand native handles + fd = ccall(:_open_osfhandle, RawFD, (WindowsRawSocket, Int32), fd, 0) + end + # TODO: Get fdio to work natively with file descriptors instead of integers + io = fdio(cconvert(Cint, fd)) + elseif t == UV_TTY + io = TTY(handle, StatusOpen) + elseif t == UV_TCP + Sockets = require(PkgId(UUID((0x6462fe0b_24de_5631, 0x8697_dd941f90decc)), "Sockets")) + io = Sockets.TCPSocket(handle, StatusOpen) + elseif t == UV_NAMED_PIPE + io = PipeEndpoint(handle, StatusOpen) + else + throw(ArgumentError("invalid stdio type: $t")) + end + iolock_end() + return io +end + +""" + open(fd::OS_HANDLE) -> IO + +Take a raw file descriptor wrap it in a Julia-aware IO type, +and take ownership of the fd handle. +Call `open(Libc.dup(fd))` to avoid the ownership capture +of the original handle. + +!!! warn + Do not call this on a handle that's already owned by some + other part of the system. +""" +function open(h::OS_HANDLE) + iolock_begin() + t = ccall(:uv_guess_handle, Cint, (OS_HANDLE,), h) + local io + if t == UV_FILE + @static if Sys.iswindows() + # TODO: Get ios.c to understand native handles + h = ccall(:_open_osfhandle, RawFD, (WindowsRawSocket, Int32), h, 0) + end + # TODO: Get fdio to work natively with file descriptors instead of integers + io = fdio(cconvert(Cint, h)) + elseif t == UV_TTY + io = TTY(h) + elseif t == UV_TCP + Sockets = require(PkgId(UUID((0x6462fe0b_24de_5631, 0x8697_dd941f90decc)), "Sockets")) + io = Sockets.TCPSocket(h) + elseif t == UV_NAMED_PIPE + io = PipeEndpoint(h) + @static if Sys.iswindows() + if ccall(:jl_ispty, Cint, (Ptr{Cvoid},), io.handle) != 0 + # replace the Julia `PipeEndpoint` type with a `TTY` type, + # if we detect that this is a cygwin pty object + pipe_handle, pipe_status = io.handle, pipe.status + io.status = StatusClosed + io.handle = C_NULL + io = TTY(pipe_handle, pipe_status) + end + end + else + throw(ArgumentError("invalid stdio type: $t")) + end + iolock_end() + return io +end + +if OS_HANDLE != RawFD + function open(fd::RawFD) + h = Libc.dup(Libc._get_osfhandle(fd)) # make a dup to steal ownership away from msvcrt + try + io = open(h) + ccall(:_close, Cint, (RawFD,), fd) # on success, destroy the old libc handle + return io + catch ex + ccall(:CloseHandle, stdcall, Cint, (OS_HANDLE,), h) # on failure, destroy the new nt handle + rethrow(ex) + end + end +end + +function isopen(x::Union{LibuvStream, LibuvServer}) + if x.status == StatusUninit || x.status == StatusInit + throw(ArgumentError("$x is not initialized")) + end + return x.status != StatusClosed && x.status != StatusEOF +end + +function check_open(x::Union{LibuvStream, LibuvServer}) + if !isopen(x) || x.status == StatusClosing + throw(IOError("stream is closed or unusable", 0)) + end +end + +function wait_readnb(x::LibuvStream, nb::Int) + # fast path before iolock acquire + bytesavailable(x.buffer) >= nb && return + open = isopen(x) # must preceed readerror check + x.readerror === nothing || throw(x.readerror) + open || return + iolock_begin() + # repeat fast path after iolock acquire, before other expensive work + bytesavailable(x.buffer) >= nb && (iolock_end(); return) + open = isopen(x) + x.readerror === nothing || throw(x.readerror) + open || (iolock_end(); return) + # now do the "real" work + oldthrottle = x.throttle + preserve_handle(x) + lock(x.cond) + try + while bytesavailable(x.buffer) < nb + x.readerror === nothing || throw(x.readerror) + isopen(x) || break + x.throttle = max(nb, x.throttle) + start_reading(x) # ensure we are reading + iolock_end() + wait(x.cond) + unlock(x.cond) + iolock_begin() + lock(x.cond) + end + finally + if isempty(x.cond) + stop_reading(x) # stop reading iff there are currently no other read clients of the stream + end + if oldthrottle <= x.throttle <= nb + # if we're interleaving readers, we might not get back to the "original" throttle + # but we consider that an acceptable "risk", since we can't be quite sure what the intended value is now + x.throttle = oldthrottle + end + unpreserve_handle(x) + unlock(x.cond) + end + iolock_end() + nothing +end + +function wait_close(x::Union{LibuvStream, LibuvServer}) + preserve_handle(x) + lock(x.cond) + try + while isopen(x) + wait(x.cond) + end + finally + unlock(x.cond) + unpreserve_handle(x) + end + nothing +end + +function close(stream::Union{LibuvStream, LibuvServer}) + iolock_begin() + should_wait = false + if stream.status == StatusInit + ccall(:jl_forceclose_uv, Cvoid, (Ptr{Cvoid},), stream.handle) + stream.status = StatusClosing + elseif isopen(stream) || stream.status == StatusEOF + should_wait = uv_handle_data(stream) != C_NULL + if stream.status != StatusClosing + ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), stream.handle) + stream.status = StatusClosing + end + end + iolock_end() + should_wait && wait_close(stream) + nothing +end + +function uvfinalize(uv::Union{LibuvStream, LibuvServer}) + uv.handle == C_NULL && return + iolock_begin() + if uv.handle != C_NULL + disassociate_julia_struct(uv.handle) # not going to call the usual close hooks + if uv.status != StatusUninit + close(uv) + else + Libc.free(uv.handle) + end + uv.status = StatusClosed + uv.handle = C_NULL + end + iolock_end() + nothing +end + +if Sys.iswindows() + ispty(s::TTY) = s.ispty + ispty(s::IO) = false +end + +""" + displaysize([io::IO]) -> (lines, columns) + +Return the nominal size of the screen that may be used for rendering output to +this `IO` object. +If no input is provided, the environment variables `LINES` and `COLUMNS` are read. +If those are not set, a default size of `(24, 80)` is returned. + +# Examples +```jldoctest +julia> withenv("LINES" => 30, "COLUMNS" => 100) do + displaysize() + end +(30, 100) +``` + +To get your TTY size, + +```julia +julia> displaysize(stdout) +(34, 147) +``` +""" +displaysize(io::IO) = displaysize() +displaysize() = (parse(Int, get(ENV, "LINES", "24")), + parse(Int, get(ENV, "COLUMNS", "80")))::Tuple{Int, Int} + +function displaysize(io::TTY) + local h::Int, w::Int + default_size = displaysize() + + @static if Sys.iswindows() + if ispty(io) + # io is actually a libuv pipe but a cygwin/msys2 pty + try + h, w = parse.(Int, split(read(open(Base.Cmd(String["stty", "size"]), "r", io).out, String))) + h > 0 || (h = default_size[1]) + w > 0 || (w = default_size[2]) + return h, w + catch + return default_size + end + end + end + + s1 = Ref{Int32}(0) + s2 = Ref{Int32}(0) + iolock_begin() + Base.uv_error("size (TTY)", ccall(:uv_tty_get_winsize, + Int32, (Ptr{Cvoid}, Ptr{Int32}, Ptr{Int32}), + io, s1, s2) != 0) + iolock_end() + w, h = s1[], s2[] + h > 0 || (h = default_size[1]) + w > 0 || (w = default_size[2]) + return h, w +end + +in(key_value::Pair{Symbol,Bool}, ::TTY) = key_value.first === :color && key_value.second === have_color +haskey(::TTY, key::Symbol) = key === :color +getindex(::TTY, key::Symbol) = key === :color ? have_color : throw(KeyError(key)) +get(::TTY, key::Symbol, default) = key === :color ? have_color : default + +### Libuv callbacks ### + +## BUFFER ## +## Allocate space in buffer (for immediate use) +function alloc_request(buffer::IOBuffer, recommended_size::UInt) + ensureroom(buffer, Int(recommended_size)) + ptr = buffer.append ? buffer.size + 1 : buffer.ptr + nb = length(buffer.data) - ptr + 1 + return (pointer(buffer.data, ptr), nb) +end + +notify_filled(buffer::IOBuffer, nread::Int, base::Ptr{Cvoid}, len::UInt) = notify_filled(buffer, nread) + +function notify_filled(buffer::IOBuffer, nread::Int) + if buffer.append + buffer.size += nread + else + buffer.ptr += nread + end + nothing +end + +function alloc_buf_hook(stream::LibuvStream, size::UInt) + throttle = UInt(stream.throttle) + return alloc_request(stream.buffer, (size > throttle) ? throttle : size) +end + +function uv_alloc_buf(handle::Ptr{Cvoid}, size::Csize_t, buf::Ptr{Cvoid}) + hd = uv_handle_data(handle) + if hd == C_NULL + ccall(:jl_uv_buf_set_len, Cvoid, (Ptr{Cvoid}, Csize_t), buf, 0) + return nothing + end + stream = unsafe_pointer_to_objref(hd)::LibuvStream + + local data::Ptr{Cvoid}, newsize::Csize_t + if stream.status != StatusActive + data = C_NULL + newsize = 0 + else + (data, newsize) = alloc_buf_hook(stream, UInt(size)) + if data == C_NULL + newsize = 0 + end + # avoid aliasing of `nread` with `errno` in uv_readcb + # or exceeding the Win32 maximum uv_buf_t len + maxsize = @static Sys.iswindows() ? typemax(Cint) : typemax(Cssize_t) + newsize > maxsize && (newsize = maxsize) + end + + ccall(:jl_uv_buf_set_base, Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}), buf, data) + ccall(:jl_uv_buf_set_len, Cvoid, (Ptr{Cvoid}, Csize_t), buf, newsize) + nothing +end + +function uv_readcb(handle::Ptr{Cvoid}, nread::Cssize_t, buf::Ptr{Cvoid}) + stream_unknown_type = @handle_as handle LibuvStream + nrequested = ccall(:jl_uv_buf_len, Csize_t, (Ptr{Cvoid},), buf) + function readcb_specialized(stream::LibuvStream, nread::Int, nrequested::UInt) + lock(stream.cond) + try + if nread < 0 + if nread == UV_ENOBUFS && nrequested == 0 + # remind the client that stream.buffer is full + notify(stream.cond) + elseif nread == UV_EOF + if isa(stream, TTY) + stream.status = StatusEOF # libuv called uv_stop_reading already + notify(stream.cond) + elseif stream.status != StatusClosing + # begin shutdown of the stream + ccall(:jl_close_uv, Cvoid, (Ptr{Cvoid},), stream.handle) + stream.status = StatusClosing + end + else + stream.readerror = _UVError("read", nread) + # This is a fatal connection error. Shutdown requests as per the usual + # close function won't work and libuv will fail with an assertion failure + ccall(:jl_forceclose_uv, Cvoid, (Ptr{Cvoid},), stream) + stream.status = StatusClosing + notify(stream.cond) + end + else + notify_filled(stream.buffer, nread) + notify(stream.cond) + end + finally + unlock(stream.cond) + end + + # Stop background reading when + # 1) there's nobody paying attention to the data we are reading + # 2) we have accumulated a lot of unread data OR + # 3) we have an alternate buffer that has reached its limit. + if stream.status == StatusPaused || + (stream.status == StatusActive && + ((bytesavailable(stream.buffer) >= stream.throttle) || + (bytesavailable(stream.buffer) >= stream.buffer.maxsize))) + # save cycles by stopping kernel notifications from arriving + ccall(:uv_read_stop, Cint, (Ptr{Cvoid},), stream) + stream.status = StatusOpen + end + nothing + end + readcb_specialized(stream_unknown_type, Int(nread), UInt(nrequested)) +end + +function reseteof(x::TTY) + iolock_begin() + if x.status == StatusEOF + x.status = StatusOpen + end + iolock_end() + nothing +end + +function _uv_hook_close(uv::Union{LibuvStream, LibuvServer}) + lock(uv.cond) + try + uv.handle = C_NULL + uv.status = StatusClosed + # notify any listeners that exist on this libuv stream type + notify(uv.cond) + finally + unlock(uv.cond) + end + nothing +end + + +########################################## +# Pipe Abstraction +# (composed of two half-pipes: .in and .out) +########################################## + +mutable struct Pipe <: AbstractPipe + in::PipeEndpoint # writable + out::PipeEndpoint # readable +end + +""" +Construct an uninitialized Pipe object. + +The appropriate end of the pipe will be automatically initialized if +the object is used in process spawning. This can be useful to easily +obtain references in process pipelines, e.g.: + +``` +julia> err = Pipe() + +# After this `err` will be initialized and you may read `foo`'s +# stderr from the `err` pipe. +julia> run(pipeline(pipeline(`foo`, stderr=err), `cat`), wait=false) +``` +""" +Pipe() = Pipe(PipeEndpoint(), PipeEndpoint()) +pipe_reader(p::Pipe) = p.out +pipe_writer(p::Pipe) = p.in + +function link_pipe!(pipe::Pipe; + reader_supports_async = false, + writer_supports_async = false) + link_pipe!(pipe.out, reader_supports_async, pipe.in, writer_supports_async) + return pipe +end + +show(io::IO, stream::Pipe) = print(io, + "Pipe(", + _fd(stream.in), " ", + uv_status_string(stream.in), " => ", + _fd(stream.out), " ", + uv_status_string(stream.out), ", ", + bytesavailable(stream), " bytes waiting)") + + +## Functions for PipeEndpoint and PipeServer ## + +function open_pipe!(p::PipeEndpoint, handle::OS_HANDLE) + iolock_begin() + if p.status != StatusInit + error("pipe is already in use or has been closed") + end + err = ccall(:uv_pipe_open, Int32, (Ptr{Cvoid}, OS_HANDLE), p.handle, handle) + uv_error("pipe_open", err) + p.status = StatusOpen + iolock_end() + return p +end + + +function link_pipe!(read_end::PipeEndpoint, reader_supports_async::Bool, + write_end::PipeEndpoint, writer_supports_async::Bool) + rd, wr = link_pipe(reader_supports_async, writer_supports_async) + try + try + open_pipe!(read_end, rd) + catch + close_pipe_sync(rd) + rethrow() + end + open_pipe!(write_end, wr) + catch + close_pipe_sync(wr) + rethrow() + end + nothing +end + +function link_pipe(reader_supports_async::Bool, writer_supports_async::Bool) + UV_NONBLOCK_PIPE = 0x40 + fildes = Ref{Pair{OS_HANDLE, OS_HANDLE}}(INVALID_OS_HANDLE => INVALID_OS_HANDLE) # read (in) => write (out) + err = ccall(:uv_pipe, Int32, (Ptr{Pair{OS_HANDLE, OS_HANDLE}}, Cint, Cint), + fildes, + reader_supports_async * UV_NONBLOCK_PIPE, + writer_supports_async * UV_NONBLOCK_PIPE) + uv_error("pipe", err) + return fildes[] +end + +if Sys.iswindows() + function close_pipe_sync(handle::WindowsRawSocket) + ccall(:CloseHandle, stdcall, Cint, (WindowsRawSocket,), handle) + nothing + end +else + function close_pipe_sync(handle::RawFD) + ccall(:close, Cint, (RawFD,), handle) + nothing + end +end + +## Functions for any LibuvStream ## + +# flow control + +function start_reading(stream::LibuvStream) + iolock_begin() + if stream.status == StatusOpen + if !isreadable(stream) + error("tried to read a stream that is not readable") + end + # libuv may call the alloc callback immediately + # for a TTY on Windows, so ensure the status is set first + stream.status = StatusActive + ret = ccall(:uv_read_start, Cint, (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}), + stream, uv_jl_alloc_buf::Ptr{Cvoid}, uv_jl_readcb::Ptr{Cvoid}) + elseif stream.status == StatusPaused + stream.status = StatusActive + ret = Int32(0) + elseif stream.status == StatusActive + ret = Int32(0) + else + ret = Int32(-1) + end + iolock_end() + return ret +end + +if Sys.iswindows() + # the low performance version of stop_reading is required + # on Windows due to a NT kernel bug that we can't use a blocking + # stream for non-blocking (overlapped) calls, + # and a ReadFile call blocking on one thread + # causes all other operations on that stream to lockup + function stop_reading(stream::LibuvStream) + iolock_begin() + if stream.status == StatusActive + stream.status = StatusOpen + ccall(:uv_read_stop, Cint, (Ptr{Cvoid},), stream) + end + iolock_end() + nothing + end +else + function stop_reading(stream::LibuvStream) + iolock_begin() + if stream.status == StatusActive + stream.status = StatusPaused + end + iolock_end() + nothing + end +end + +# bulk read / write + +readbytes!(s::LibuvStream, a::Vector{UInt8}, nb = length(a)) = readbytes!(s, a, Int(nb)) +function readbytes!(s::LibuvStream, a::Vector{UInt8}, nb::Int) + iolock_begin() + sbuf = s.buffer + @assert sbuf.seekable == false + @assert sbuf.maxsize >= nb + + function wait_locked(s, buf, nb) + while bytesavailable(buf) < nb + s.readerror === nothing || throw(s.readerror) + isopen(s) || break + iolock_end() + wait_readnb(s, nb) + iolock_begin() + end + end + + if nb <= SZ_UNBUFFERED_IO # Under this limit we are OK with copying the array from the stream's buffer + wait_locked(s, sbuf, nb) + end + if bytesavailable(sbuf) >= nb + nread = readbytes!(sbuf, a, nb) + else + newbuf = PipeBuffer(a, maxsize=nb) + newbuf.size = 0 # reset the write pointer to the beginning + nread = try + s.buffer = newbuf + write(newbuf, sbuf) + wait_locked(s, newbuf, nb) + bytesavailable(newbuf) + finally + s.buffer = sbuf + end + compact(newbuf) + end + iolock_end() + return nread +end + +function read(stream::LibuvStream) + wait_readnb(stream, typemax(Int)) + iolock_begin() + bytes = take!(stream.buffer) + iolock_end() + return bytes +end + +function unsafe_read(s::LibuvStream, p::Ptr{UInt8}, nb::UInt) + iolock_begin() + sbuf = s.buffer + @assert sbuf.seekable == false + @assert sbuf.maxsize >= nb + + function wait_locked(s, buf, nb) + while bytesavailable(buf) < nb + s.readerror === nothing || throw(s.readerror) + isopen(s) || throw(EOFError()) + iolock_end() + wait_readnb(s, nb) + iolock_begin() + end + end + + if nb <= SZ_UNBUFFERED_IO # Under this limit we are OK with copying the array from the stream's buffer + wait_locked(s, sbuf, Int(nb)) + end + if bytesavailable(sbuf) >= nb + unsafe_read(sbuf, p, nb) + else + newbuf = PipeBuffer(unsafe_wrap(Array, p, nb), maxsize=Int(nb)) + newbuf.size = 0 # reset the write pointer to the beginning + try + s.buffer = newbuf + write(newbuf, sbuf) + wait_locked(s, newbuf, Int(nb)) + finally + s.buffer = sbuf + end + end + iolock_end() + nothing +end + +function read(this::LibuvStream, ::Type{UInt8}) + iolock_begin() + sbuf = this.buffer + @assert sbuf.seekable == false + while bytesavailable(sbuf) < 1 + iolock_end() + eof(this) && throw(EOFError()) + iolock_begin() + end + c = read(sbuf, UInt8) + iolock_end() + return c +end + +function readavailable(this::LibuvStream) + wait_readnb(this, 1) # unlike the other `read` family of functions, this one doesn't guarantee error reporting + iolock_begin() + buf = this.buffer + @assert buf.seekable == false + bytes = take!(buf) + iolock_end() + return bytes +end + +function readuntil(x::LibuvStream, c::UInt8; keep::Bool=false) + iolock_begin() + buf = x.buffer + @assert buf.seekable == false + if !occursin(c, buf) # fast path checks first + x.readerror === nothing || throw(x.readerror) + if isopen(x) + preserve_handle(x) + lock(x.cond) + try + while !occursin(c, x.buffer) + x.readerror === nothing || throw(x.readerror) + isopen(x) || break + start_reading(x) # ensure we are reading + iolock_end() + wait(x.cond) + unlock(x.cond) + iolock_begin() + lock(x.cond) + end + finally + if isempty(x.cond) + stop_reading(x) # stop reading iff there are currently no other read clients of the stream + end + unlock(x.cond) + unpreserve_handle(x) + end + end + end + bytes = readuntil(buf, c, keep=keep) + iolock_end() + return bytes +end + +uv_write(s::LibuvStream, p::Vector{UInt8}) = GC.@preserve p uv_write(s, pointer(p), UInt(sizeof(p))) + +# caller must have acquired the iolock +function uv_write(s::LibuvStream, p::Ptr{UInt8}, n::UInt) + uvw = uv_write_async(s, p, n) + ct = current_task() + preserve_handle(ct) + sigatomic_begin() + uv_req_set_data(uvw, ct) + iolock_end() + status = try + sigatomic_end() + # wait for the last chunk to complete (or error) + # assume that any errors would be sticky, + # (so we don't need to monitor the error status of the intermediate writes) + wait()::Cint + finally + # try-finally unwinds the sigatomic level, so need to repeat sigatomic_end + sigatomic_end() + iolock_begin() + ct.queue === nothing || list_deletefirst!(ct.queue, ct) + if uv_req_data(uvw) != C_NULL + # uvw is still alive, + # so make sure we won't get spurious notifications later + uv_req_set_data(uvw, C_NULL) + else + # done with uvw + Libc.free(uvw) + end + iolock_end() + unpreserve_handle(ct) + end + if status < 0 + throw(_UVError("write", status)) + end + return Int(n) +end + +# helper function for uv_write that returns the uv_write_t struct for the write +# rather than waiting on it, caller must hold the iolock +function uv_write_async(s::LibuvStream, p::Ptr{UInt8}, n::UInt) + check_open(s) + while true + uvw = Libc.malloc(_sizeof_uv_write) + uv_req_set_data(uvw, C_NULL) # in case we get interrupted before arriving at the wait call + nwrite = min(n, MAX_OS_WRITE) # split up the write into chunks the OS can handle. + # TODO: use writev instead of a loop + err = ccall(:jl_uv_write, + Int32, + (Ptr{Cvoid}, Ptr{Cvoid}, UInt, Ptr{Cvoid}, Ptr{Cvoid}), + s, p, nwrite, uvw, + uv_jl_writecb_task::Ptr{Cvoid}) + if err < 0 + Libc.free(uvw) + uv_error("write", err) + end + n -= nwrite + p += nwrite + if n == 0 + return uvw + end + end +end + + +# Optimized send +# - smaller writes are buffered, final uv write on flush or when buffer full +# - large isbits arrays are unbuffered and written directly + +function unsafe_write(s::LibuvStream, p::Ptr{UInt8}, n::UInt) + while true + # try to add to the send buffer + iolock_begin() + buf = s.sendbuf + buf === nothing && break + totb = bytesavailable(buf) + n + if totb < buf.maxsize + nb = unsafe_write(buf, p, n) + iolock_end() + return nb + end + bytesavailable(buf) == 0 && break + # perform flush(s) + arr = take!(buf) + uv_write(s, arr) + end + # perform the output to the kernel + return uv_write(s, p, n) +end + +function flush(s::LibuvStream) + iolock_begin() + buf = s.sendbuf + if buf !== nothing + if bytesavailable(buf) > 0 + arr = take!(buf) + uv_write(s, arr) + return + end + end + uv_write(s, Ptr{UInt8}(Base.eventloop()), UInt(0)) # zero write from a random pointer to flush current queue + return +end + +function buffer_writes(s::LibuvStream, bufsize) + sendbuf = PipeBuffer(bufsize) + iolock_begin() + s.sendbuf = sendbuf + iolock_end() + return s +end + +## low-level calls to libuv ## + +function write(s::LibuvStream, b::UInt8) + buf = s.sendbuf + if buf !== nothing + iolock_begin() + if bytesavailable(buf) + 1 < buf.maxsize + n = write(buf, b) + iolock_end() + return n + end + iolock_end() + end + return write(s, Ref{UInt8}(b)) +end + +function uv_writecb_task(req::Ptr{Cvoid}, status::Cint) + d = uv_req_data(req) + if d != C_NULL + uv_req_set_data(req, C_NULL) # let the Task know we got the writecb + t = unsafe_pointer_to_objref(d)::Task + schedule(t, status) + else + # no owner for this req, safe to just free it + Libc.free(req) + end + nothing +end + +_fd(x::IOStream) = RawFD(fd(x)) +_fd(x::Union{OS_HANDLE, RawFD}) = x + +function _fd(x::Union{LibuvStream, LibuvServer}) + fd = Ref{OS_HANDLE}(INVALID_OS_HANDLE) + if x.status != StatusUninit && x.status != StatusClosed + err = ccall(:uv_fileno, Int32, (Ptr{Cvoid}, Ptr{OS_HANDLE}), x.handle, fd) + # handle errors by returning INVALID_OS_HANDLE + end + return fd[] +end + +for (x, writable, unix_fd, c_symbol) in + ((:stdin, false, 0, :jl_uv_stdin), + (:stdout, true, 1, :jl_uv_stdout), + (:stderr, true, 2, :jl_uv_stderr)) + f = Symbol("redirect_", lowercase(string(x))) + _f = Symbol("_", f) + @eval begin + function ($_f)(stream) + global $x + posix_fd = _fd(stream) + @static if Sys.iswindows() + ccall(:SetStdHandle, stdcall, Int32, (Int32, OS_HANDLE), + $(-10 - unix_fd), Libc._get_osfhandle(posix_fd)) + end + dup(posix_fd, RawFD($unix_fd)) + $x = stream + nothing + end + function ($f)(handle::Union{LibuvStream, IOStream}) + $(_f)(handle) + unsafe_store!(cglobal($(Expr(:quote, c_symbol)), Ptr{Cvoid}), + handle.handle) + return handle + end + function ($f)() + p = link_pipe!(Pipe()) + read, write = p.out, p.in + ($f)($(writable ? :write : :read)) + return (read, write) + end + end +end + +""" + redirect_stdout([stream]) -> (rd, wr) + +Create a pipe to which all C and Julia level [`stdout`](@ref) output +will be redirected. +Returns a tuple `(rd, wr)` representing the pipe ends. +Data written to [`stdout`](@ref) may now be read from the `rd` end of +the pipe. The `wr` end is given for convenience in case the old +[`stdout`](@ref) object was cached by the user and needs to be replaced +elsewhere. + +If called with the optional `stream` argument, then returns `stream` itself. + +!!! note + `stream` must be a `TTY`, a `Pipe`, or a socket. +""" +redirect_stdout + +""" + redirect_stderr([stream]) -> (rd, wr) + +Like [`redirect_stdout`](@ref), but for [`stderr`](@ref). + +!!! note + `stream` must be a `TTY`, a `Pipe`, or a socket. +""" +redirect_stderr + +""" + redirect_stdin([stream]) -> (rd, wr) + +Like [`redirect_stdout`](@ref), but for [`stdin`](@ref). +Note that the order of the return tuple is still `(rd, wr)`, +i.e. data to be read from [`stdin`](@ref) may be written to `wr`. + +!!! note + `stream` must be a `TTY`, a `Pipe`, or a socket. +""" +redirect_stdin + +for (F,S) in ((:redirect_stdin, :stdin), (:redirect_stdout, :stdout), (:redirect_stderr, :stderr)) + @eval function $F(f::Function, stream) + STDOLD = $S + local ret + $F(stream) + try + ret = f() + finally + $F(STDOLD) + end + ret + end +end + +""" + redirect_stdout(f::Function, stream) + +Run the function `f` while redirecting [`stdout`](@ref) to `stream`. +Upon completion, [`stdout`](@ref) is restored to its prior setting. + +!!! note + `stream` must be a `TTY`, a `Pipe`, or a socket. +""" +redirect_stdout(f::Function, stream) + +""" + redirect_stderr(f::Function, stream) + +Run the function `f` while redirecting [`stderr`](@ref) to `stream`. +Upon completion, [`stderr`](@ref) is restored to its prior setting. + +!!! note + `stream` must be a `TTY`, a `Pipe`, or a socket. +""" +redirect_stderr(f::Function, stream) + +""" + redirect_stdin(f::Function, stream) + +Run the function `f` while redirecting [`stdin`](@ref) to `stream`. +Upon completion, [`stdin`](@ref) is restored to its prior setting. + +!!! note + `stream` must be a `TTY`, a `Pipe`, or a socket. +""" +redirect_stdin(f::Function, stream) + +mark(x::LibuvStream) = mark(x.buffer) +unmark(x::LibuvStream) = unmark(x.buffer) +reset(x::LibuvStream) = reset(x.buffer) +ismarked(x::LibuvStream) = ismarked(x.buffer) + +function peek(s::LibuvStream) + mark(s) + try read(s, UInt8) + finally + reset(s) + end +end + +# BufferStream's are non-OS streams, backed by a regular IOBuffer +mutable struct BufferStream <: LibuvStream + buffer::IOBuffer + cond::Threads.Condition + is_open::Bool + buffer_writes::Bool + lock::ReentrantLock # advisory lock + + BufferStream() = new(PipeBuffer(), Threads.Condition(), true, false, ReentrantLock()) +end + +isopen(s::BufferStream) = s.is_open + +function close(s::BufferStream) + lock(s.cond) do + s.is_open = false + notify(s.cond) + nothing + end +end +uvfinalize(s::BufferStream) = nothing + +function read(s::BufferStream, ::Type{UInt8}) + nread = lock(s.cond) do + wait_readnb(s, 1) + read(s.buffer, UInt8) + end + return nread +end +function unsafe_read(s::BufferStream, a::Ptr{UInt8}, nb::UInt) + lock(s.cond) do + wait_readnb(s, Int(nb)) + unsafe_read(s.buffer, a, nb) + nothing + end +end +bytesavailable(s::BufferStream) = bytesavailable(s.buffer) + +isreadable(s::BufferStream) = s.buffer.readable +iswritable(s::BufferStream) = s.buffer.writable + +function wait_readnb(s::BufferStream, nb::Int) + lock(s.cond) do + while isopen(s) && bytesavailable(s.buffer) < nb + wait(s.cond) + end + end +end + +show(io::IO, s::BufferStream) = print(io, "BufferStream() bytes waiting:", bytesavailable(s.buffer), ", isopen:", s.is_open) + +function readuntil(s::BufferStream, c::UInt8; keep::Bool=false) + bytes = lock(s.cond) do + while isopen(s) && !occursin(c, s.buffer) + wait(s.cond) + end + readuntil(s.buffer, c, keep=keep) + end + return bytes +end + +function wait_close(s::BufferStream) + lock(s.cond) do + while isopen(s) + wait(s.cond) + end + end +end + +start_reading(s::BufferStream) = Int32(0) +stop_reading(s::BufferStream) = nothing + +write(s::BufferStream, b::UInt8) = write(s, Ref{UInt8}(b)) +function unsafe_write(s::BufferStream, p::Ptr{UInt8}, nb::UInt) + nwrite = lock(s.cond) do + rv = unsafe_write(s.buffer, p, nb) + s.buffer_writes || notify(s.cond) + rv + end + return nwrite +end + +function eof(s::BufferStream) + bytesavailable(s) > 0 && return false + iseof = lock(s.cond) do + wait_readnb(s, 1) + return !isopen(s) && bytesavailable(s) <= 0 + end + return iseof +end + +# If buffer_writes is called, it will delay notifying waiters till a flush is called. +buffer_writes(s::BufferStream, bufsize=0) = (s.buffer_writes = true; s) +function flush(s::BufferStream) + lock(s.cond) do + notify(s.cond) + nothing + end +end diff --git a/base/strings/basic.jl b/base/strings/basic.jl new file mode 100644 index 0000000..98709d6 --- /dev/null +++ b/base/strings/basic.jl @@ -0,0 +1,713 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" +The `AbstractString` type is the supertype of all string implementations in +Julia. Strings are encodings of sequences of [Unicode](https://unicode.org/) +code points as represented by the `AbstractChar` type. Julia makes a few assumptions +about strings: + +* Strings are encoded in terms of fixed-size "code units" + * Code units can be extracted with `codeunit(s, i)` + * The first code unit has index `1` + * The last code unit has index `ncodeunits(s)` + * Any index `i` such that `1 ≤ i ≤ ncodeunits(s)` is in bounds +* String indexing is done in terms of these code units: + * Characters are extracted by `s[i]` with a valid string index `i` + * Each `AbstractChar` in a string is encoded by one or more code units + * Only the index of the first code unit of an `AbstractChar` is a valid index + * The encoding of an `AbstractChar` is independent of what precedes or follows it + * String encodings are [self-synchronizing] – i.e. `isvalid(s, i)` is O(1) + +[self-synchronizing]: https://en.wikipedia.org/wiki/Self-synchronizing_code + +Some string functions that extract code units, characters or substrings from +strings error if you pass them out-of-bounds or invalid string indices. This +includes `codeunit(s, i)` and `s[i]`. Functions that do string +index arithmetic take a more relaxed approach to indexing and give you the +closest valid string index when in-bounds, or when out-of-bounds, behave as if +there were an infinite number of characters padding each side of the string. +Usually these imaginary padding characters have code unit length `1` but string +types may choose different "imaginary" character sizes as makes sense for their +implementations (e.g. substrings may pass index arithmetic through to the +underlying string they provide a view into). Relaxed indexing functions include +those intended for index arithmetic: `thisind`, `nextind` and `prevind`. This +model allows index arithmetic to work with out-of- bounds indices as +intermediate values so long as one never uses them to retrieve a character, +which often helps avoid needing to code around edge cases. + +See also: [`codeunit`](@ref), [`ncodeunits`](@ref), [`thisind`](@ref), +[`nextind`](@ref), [`prevind`](@ref) +""" +AbstractString + +## required string functions ## + +""" + ncodeunits(s::AbstractString) -> Int + +Return the number of code units in a string. Indices that are in bounds to +access this string must satisfy `1 ≤ i ≤ ncodeunits(s)`. Not all such indices +are valid – they may not be the start of a character, but they will return a +code unit value when calling `codeunit(s,i)`. + +See also: [`codeunit`](@ref), [`checkbounds`](@ref), [`sizeof`](@ref), +[`length`](@ref), [`lastindex`](@ref) +""" +ncodeunits(s::AbstractString) + +""" + codeunit(s::AbstractString) -> Type{<:Union{UInt8, UInt16, UInt32}} + +Return the code unit type of the given string object. For ASCII, Latin-1, or +UTF-8 encoded strings, this would be `UInt8`; for UCS-2 and UTF-16 it would be +`UInt16`; for UTF-32 it would be `UInt32`. The unit code type need not be +limited to these three types, but it's hard to think of widely used string +encodings that don't use one of these units. `codeunit(s)` is the same as +`typeof(codeunit(s,1))` when `s` is a non-empty string. + +See also: [`ncodeunits`](@ref) +""" +codeunit(s::AbstractString) + +""" + codeunit(s::AbstractString, i::Integer) -> Union{UInt8, UInt16, UInt32} + +Return the code unit value in the string `s` at index `i`. Note that + + codeunit(s, i) :: codeunit(s) + +I.e. the value returned by `codeunit(s, i)` is of the type returned by +`codeunit(s)`. + +See also: [`ncodeunits`](@ref), [`checkbounds`](@ref) +""" +@propagate_inbounds codeunit(s::AbstractString, i::Integer) = typeof(i) === Int ? + throw(MethodError(codeunit, (s, i))) : codeunit(s, Int(i)) + +""" + isvalid(s::AbstractString, i::Integer) -> Bool + +Predicate indicating whether the given index is the start of the encoding of a +character in `s` or not. If `isvalid(s, i)` is true then `s[i]` will return the +character whose encoding starts at that index, if it's false, then `s[i]` will +raise an invalid index error or a bounds error depending on if `i` is in bounds. +In order for `isvalid(s, i)` to be an O(1) function, the encoding of `s` must be +[self-synchronizing](https://en.wikipedia.org/wiki/Self-synchronizing_code) this +is a basic assumption of Julia's generic string support. + +See also: [`getindex`](@ref), [`iterate`](@ref), [`thisind`](@ref), +[`nextind`](@ref), [`prevind`](@ref), [`length`](@ref) + +# Examples + +```jldoctest +julia> str = "αβγdef"; + +julia> isvalid(str, 1) +true + +julia> str[1] +'α': Unicode U+03b1 (category Ll: Letter, lowercase) + +julia> isvalid(str, 2) +false + +julia> str[2] +ERROR: StringIndexError("αβγdef", 2) +Stacktrace: +[...] +``` +""" +@propagate_inbounds isvalid(s::AbstractString, i::Integer) = typeof(i) === Int ? + throw(MethodError(isvalid, (s, i))) : isvalid(s, Int(i)) + +""" + iterate(s::AbstractString, i::Integer) -> Union{Tuple{<:AbstractChar, Int}, Nothing} + +Return a tuple of the character in `s` at index `i` with the index of the start +of the following character in `s`. This is the key method that allows strings to +be iterated, yielding a sequences of characters. If `i` is out of bounds in `s` +then a bounds error is raised. The `iterate` function, as part of the iteration +protocol may assume that `i` is the start of a character in `s`. + +See also: [`getindex`](@ref), [`checkbounds`](@ref) +""" +@propagate_inbounds iterate(s::AbstractString, i::Integer) = typeof(i) === Int ? + throw(MethodError(iterate, (s, i))) : iterate(s, Int(i)) + +## basic generic definitions ## + +eltype(::Type{<:AbstractString}) = Char # some string types may use another AbstractChar + +""" + sizeof(str::AbstractString) + +Size, in bytes, of the string `str`. Equal to the number of code units in `str` multiplied by +the size, in bytes, of one code unit in `str`. + +# Examples +```jldoctest +julia> sizeof("") +0 + +julia> sizeof("∀") +3 +``` +""" +sizeof(s::AbstractString) = ncodeunits(s) * sizeof(codeunit(s)) +firstindex(s::AbstractString) = 1 +lastindex(s::AbstractString) = thisind(s, ncodeunits(s)) + +function getindex(s::AbstractString, i::Integer) + @boundscheck checkbounds(s, i) + @inbounds return isvalid(s, i) ? iterate(s, i)[1] : string_index_err(s, i) +end + +getindex(s::AbstractString, i::Colon) = s +# TODO: handle other ranges with stride ±1 specially? +# TODO: add more @propagate_inbounds annotations? +getindex(s::AbstractString, v::AbstractVector{<:Integer}) = + sprint(io->(for i in v; write(io, s[i]) end), sizehint=length(v)) +getindex(s::AbstractString, v::AbstractVector{Bool}) = + throw(ArgumentError("logical indexing not supported for strings")) + +function get(s::AbstractString, i::Integer, default) +# TODO: use ternary once @inbounds is expression-like + if checkbounds(Bool, s, i) + @inbounds return s[i] + else + return default + end +end + +## bounds checking ## + +checkbounds(::Type{Bool}, s::AbstractString, i::Integer) = + 1 ≤ i ≤ ncodeunits(s) +checkbounds(::Type{Bool}, s::AbstractString, r::AbstractRange{<:Integer}) = + isempty(r) || (1 ≤ minimum(r) && maximum(r) ≤ ncodeunits(s)) +checkbounds(::Type{Bool}, s::AbstractString, I::AbstractArray{<:Real}) = + all(i -> checkbounds(Bool, s, i), I) +checkbounds(::Type{Bool}, s::AbstractString, I::AbstractArray{<:Integer}) = + all(i -> checkbounds(Bool, s, i), I) +checkbounds(s::AbstractString, I::Union{Integer,AbstractArray}) = + checkbounds(Bool, s, I) ? nothing : throw(BoundsError(s, I)) + +## construction, conversion, promotion ## + +string() = "" +string(s::AbstractString) = s + +(::Type{Vector{UInt8}})(s::AbstractString) = unsafe_wrap(Vector{UInt8}, String(s)) +(::Type{Array{UInt8}})(s::AbstractString) = unsafe_wrap(Vector{UInt8}, String(s)) +(::Type{Vector{T}})(s::AbstractString) where {T<:AbstractChar} = collect(T, s) + +Symbol(s::AbstractString) = Symbol(String(s)) +Symbol(x...) = Symbol(string(x...)) + +convert(::Type{T}, s::T) where {T<:AbstractString} = s +convert(::Type{T}, s::AbstractString) where {T<:AbstractString} = T(s) + +## string & character concatenation ## + +""" + *(s::Union{AbstractString, AbstractChar}, t::Union{AbstractString, AbstractChar}...) -> AbstractString + +Concatenate strings and/or characters, producing a [`String`](@ref). This is equivalent +to calling the [`string`](@ref) function on the arguments. Concatenation of built-in +string types always produces a value of type `String` but other string types may choose +to return a string of a different type as appropriate. + +# Examples +```jldoctest +julia> "Hello " * "world" +"Hello world" + +julia> 'j' * "ulia" +"julia" +``` +""" +(*)(s1::Union{AbstractChar, AbstractString}, ss::Union{AbstractChar, AbstractString}...) = string(s1, ss...) + +one(::Union{T,Type{T}}) where {T<:AbstractString} = convert(T, "") + +## generic string comparison ## + +""" + cmp(a::AbstractString, b::AbstractString) -> Int + +Compare two strings. Return `0` if both strings have the same length and the character +at each index is the same in both strings. Return `-1` if `a` is a prefix of `b`, or if +`a` comes before `b` in alphabetical order. Return `1` if `b` is a prefix of `a`, or if +`b` comes before `a` in alphabetical order (technically, lexicographical order by Unicode +code points). + +# Examples +```jldoctest +julia> cmp("abc", "abc") +0 + +julia> cmp("ab", "abc") +-1 + +julia> cmp("abc", "ab") +1 + +julia> cmp("ab", "ac") +-1 + +julia> cmp("ac", "ab") +1 + +julia> cmp("α", "a") +1 + +julia> cmp("b", "β") +-1 +``` +""" +function cmp(a::AbstractString, b::AbstractString) + a === b && return 0 + a, b = Iterators.Stateful(a), Iterators.Stateful(b) + for (c, d) in zip(a, b) + c ≠ d && return ifelse(c < d, -1, 1) + end + isempty(a) && return ifelse(isempty(b), 0, -1) + return 1 +end + +""" + ==(a::AbstractString, b::AbstractString) -> Bool + +Test whether two strings are equal character by character (technically, Unicode +code point by code point). + +# Examples +```jldoctest +julia> "abc" == "abc" +true + +julia> "abc" == "αβγ" +false +``` +""" +==(a::AbstractString, b::AbstractString) = cmp(a, b) == 0 + +""" + isless(a::AbstractString, b::AbstractString) -> Bool + +Test whether string `a` comes before string `b` in alphabetical order +(technically, in lexicographical order by Unicode code points). + +# Examples +```jldoctest +julia> isless("a", "b") +true + +julia> isless("β", "α") +false + +julia> isless("a", "a") +false +``` +""" +isless(a::AbstractString, b::AbstractString) = cmp(a, b) < 0 + +# faster comparisons for symbols + +cmp(a::Symbol, b::Symbol) = Int(sign(ccall(:strcmp, Int32, (Cstring, Cstring), a, b))) + +isless(a::Symbol, b::Symbol) = cmp(a, b) < 0 + +## character index arithmetic ## + +""" + length(s::AbstractString) -> Int + length(s::AbstractString, i::Integer, j::Integer) -> Int + +The number of characters in string `s` from indices `i` through `j`. This is +computed as the number of code unit indices from `i` to `j` which are valid +character indices. With only a single string argument, this computes the +number of characters in the entire string. With `i` and `j` arguments it +computes the number of indices between `i` and `j` inclusive that are valid +indices in the string `s`. In addition to in-bounds values, `i` may take the +out-of-bounds value `ncodeunits(s) + 1` and `j` may take the out-of-bounds +value `0`. + +See also: [`isvalid`](@ref), [`ncodeunits`](@ref), [`lastindex`](@ref), +[`thisind`](@ref), [`nextind`](@ref), [`prevind`](@ref) + +# Examples +```jldoctest +julia> length("jμΛIα") +5 +``` +""" +length(s::AbstractString) = @inbounds return length(s, 1, ncodeunits(s)) + +function length(s::AbstractString, i::Int, j::Int) + @boundscheck begin + 0 < i ≤ ncodeunits(s)+1 || throw(BoundsError(s, i)) + 0 ≤ j < ncodeunits(s)+1 || throw(BoundsError(s, j)) + end + n = 0 + for k = i:j + @inbounds n += isvalid(s, k) + end + return n +end + +@propagate_inbounds length(s::AbstractString, i::Integer, j::Integer) = + length(s, Int(i), Int(j)) + +""" + thisind(s::AbstractString, i::Integer) -> Int + +If `i` is in bounds in `s` return the index of the start of the character whose +encoding code unit `i` is part of. In other words, if `i` is the start of a +character, return `i`; if `i` is not the start of a character, rewind until the +start of a character and return that index. If `i` is equal to 0 or `ncodeunits(s)+1` +return `i`. In all other cases throw `BoundsError`. + +# Examples +```jldoctest +julia> thisind("α", 0) +0 + +julia> thisind("α", 1) +1 + +julia> thisind("α", 2) +1 + +julia> thisind("α", 3) +3 + +julia> thisind("α", 4) +ERROR: BoundsError: attempt to access String + at index [4] +[...] + +julia> thisind("α", -1) +ERROR: BoundsError: attempt to access String + at index [-1] +[...] +``` +""" +thisind(s::AbstractString, i::Integer) = thisind(s, Int(i)) + +function thisind(s::AbstractString, i::Int) + z = ncodeunits(s) + 1 + i == z && return i + @boundscheck 0 ≤ i ≤ z || throw(BoundsError(s, i)) + @inbounds while 1 < i && !isvalid(s, i) + i -= 1 + end + return i +end + +""" + prevind(str::AbstractString, i::Integer, n::Integer=1) -> Int + +* Case `n == 1` + + If `i` is in bounds in `s` return the index of the start of the character whose + encoding starts before index `i`. In other words, if `i` is the start of a + character, return the start of the previous character; if `i` is not the start + of a character, rewind until the start of a character and return that index. + If `i` is equal to `1` return `0`. + If `i` is equal to `ncodeunits(str)+1` return `lastindex(str)`. + Otherwise throw `BoundsError`. + +* Case `n > 1` + + Behaves like applying `n` times `prevind` for `n==1`. The only difference + is that if `n` is so large that applying `prevind` would reach `0` then each remaining + iteration decreases the returned value by `1`. + This means that in this case `prevind` can return a negative value. + +* Case `n == 0` + + Return `i` only if `i` is a valid index in `str` or is equal to `ncodeunits(str)+1`. + Otherwise `StringIndexError` or `BoundsError` is thrown. + +# Examples +```jldoctest +julia> prevind("α", 3) +1 + +julia> prevind("α", 1) +0 + +julia> prevind("α", 0) +ERROR: BoundsError: attempt to access String + at index [0] +[...] + +julia> prevind("α", 2, 2) +0 + +julia> prevind("α", 2, 3) +-1 +``` +""" +prevind(s::AbstractString, i::Integer, n::Integer) = prevind(s, Int(i), Int(n)) +prevind(s::AbstractString, i::Integer) = prevind(s, Int(i)) +prevind(s::AbstractString, i::Int) = prevind(s, i, 1) + +function prevind(s::AbstractString, i::Int, n::Int) + n < 0 && throw(ArgumentError("n cannot be negative: $n")) + z = ncodeunits(s) + 1 + @boundscheck 0 < i ≤ z || throw(BoundsError(s, i)) + n == 0 && return thisind(s, i) == i ? i : string_index_err(s, i) + while n > 0 && 1 < i + @inbounds n -= isvalid(s, i -= 1) + end + return i - n +end + +""" + nextind(str::AbstractString, i::Integer, n::Integer=1) -> Int + +* Case `n == 1` + + If `i` is in bounds in `s` return the index of the start of the character whose + encoding starts after index `i`. In other words, if `i` is the start of a + character, return the start of the next character; if `i` is not the start + of a character, move forward until the start of a character and return that index. + If `i` is equal to `0` return `1`. + If `i` is in bounds but greater or equal to `lastindex(str)` return `ncodeunits(str)+1`. + Otherwise throw `BoundsError`. + +* Case `n > 1` + + Behaves like applying `n` times `nextind` for `n==1`. The only difference + is that if `n` is so large that applying `nextind` would reach `ncodeunits(str)+1` then + each remaining iteration increases the returned value by `1`. This means that in this + case `nextind` can return a value greater than `ncodeunits(str)+1`. + +* Case `n == 0` + + Return `i` only if `i` is a valid index in `s` or is equal to `0`. + Otherwise `StringIndexError` or `BoundsError` is thrown. + +# Examples +```jldoctest +julia> nextind("α", 0) +1 + +julia> nextind("α", 1) +3 + +julia> nextind("α", 3) +ERROR: BoundsError: attempt to access String + at index [3] +[...] + +julia> nextind("α", 0, 2) +3 + +julia> nextind("α", 1, 2) +4 +``` +""" +nextind(s::AbstractString, i::Integer, n::Integer) = nextind(s, Int(i), Int(n)) +nextind(s::AbstractString, i::Integer) = nextind(s, Int(i)) +nextind(s::AbstractString, i::Int) = nextind(s, i, 1) + +function nextind(s::AbstractString, i::Int, n::Int) + n < 0 && throw(ArgumentError("n cannot be negative: $n")) + z = ncodeunits(s) + @boundscheck 0 ≤ i ≤ z || throw(BoundsError(s, i)) + n == 0 && return thisind(s, i) == i ? i : string_index_err(s, i) + while n > 0 && i < z + @inbounds n -= isvalid(s, i += 1) + end + return i + n +end + +## string index iteration type ## + +struct EachStringIndex{T<:AbstractString} + s::T +end +keys(s::AbstractString) = EachStringIndex(s) + +length(e::EachStringIndex) = length(e.s) +first(::EachStringIndex) = 1 +last(e::EachStringIndex) = lastindex(e.s) +iterate(e::EachStringIndex, state=firstindex(e.s)) = state > ncodeunits(e.s) ? nothing : (state, nextind(e.s, state)) +eltype(::Type{<:EachStringIndex}) = Int + +""" + isascii(c::Union{AbstractChar,AbstractString}) -> Bool + +Test whether a character belongs to the ASCII character set, or whether this is true for +all elements of a string. + +# Examples +```jldoctest +julia> isascii('a') +true + +julia> isascii('α') +false + +julia> isascii("abc") +true + +julia> isascii("αβγ") +false +``` +""" +isascii(c::Char) = bswap(reinterpret(UInt32, c)) < 0x80 +isascii(s::AbstractString) = all(isascii, s) +isascii(c::AbstractChar) = UInt32(c) < 0x80 + +## string map, filter, has ## + +function map(f, s::AbstractString) + out = IOBuffer(sizehint=sizeof(s)) + for c in s + c′ = f(c) + isa(c′, AbstractChar) || throw(ArgumentError( + "map(f, s::AbstractString) requires f to return AbstractChar; try map(f, collect(s)) or a comprehension instead")) + write(out, c′::AbstractChar) + end + String(take!(out)) +end + +function filter(f, s::AbstractString) + out = IOBuffer(sizehint=sizeof(s)) + for c in s + f(c) && write(out, c) + end + String(take!(out)) +end + +## string first and last ## + +""" + first(s::AbstractString, n::Integer) + +Get a string consisting of the first `n` characters of `s`. + +```jldoctest +julia> first("∀ϵ≠0: ϵ²>0", 0) +"" + +julia> first("∀ϵ≠0: ϵ²>0", 1) +"∀" + +julia> first("∀ϵ≠0: ϵ²>0", 3) +"∀ϵ≠" +``` +""" +first(s::AbstractString, n::Integer) = @inbounds s[1:min(end, nextind(s, 0, n))] + +""" + last(s::AbstractString, n::Integer) + +Get a string consisting of the last `n` characters of `s`. + +```jldoctest +julia> last("∀ϵ≠0: ϵ²>0", 0) +"" + +julia> last("∀ϵ≠0: ϵ²>0", 1) +"0" + +julia> last("∀ϵ≠0: ϵ²>0", 3) +"²>0" +``` +""" +last(s::AbstractString, n::Integer) = @inbounds s[max(1, prevind(s, ncodeunits(s)+1, n)):end] + +""" + reverseind(v, i) + +Given an index `i` in [`reverse(v)`](@ref), return the corresponding index in +`v` so that `v[reverseind(v,i)] == reverse(v)[i]`. (This can be nontrivial in +cases where `v` contains non-ASCII characters.) + +# Examples +```jldoctest +julia> r = reverse("Julia") +"ailuJ" + +julia> for i in 1:length(r) + print(r[reverseind("Julia", i)]) + end +Julia +``` +""" +reverseind(s::AbstractString, i::Integer) = thisind(s, ncodeunits(s)-i+1) + +""" + repeat(s::AbstractString, r::Integer) + +Repeat a string `r` times. This can be written as `s^r`. + +See also: [`^`](@ref) + +# Examples +```jldoctest +julia> repeat("ha", 3) +"hahaha" +``` +""" +repeat(s::AbstractString, r::Integer) = repeat(String(s), r) + +""" + ^(s::Union{AbstractString,AbstractChar}, n::Integer) + +Repeat a string or character `n` times. This can also be written as `repeat(s, n)`. + +See also: [`repeat`](@ref) + +# Examples +```jldoctest +julia> "Test "^3 +"Test Test Test " +``` +""" +(^)(s::Union{AbstractString,AbstractChar}, r::Integer) = repeat(s, r) + +# reverse-order iteration for strings and indices thereof +iterate(r::Iterators.Reverse{<:AbstractString}, i=lastindex(r.itr)) = i < firstindex(r.itr) ? nothing : (r.itr[i], prevind(r.itr, i)) +iterate(r::Iterators.Reverse{<:EachStringIndex}, i=lastindex(r.itr.s)) = i < firstindex(r.itr.s) ? nothing : (i, prevind(r.itr.s, i)) + +## code unit access ## + +""" + CodeUnits(s::AbstractString) + +Wrap a string (without copying) in an immutable vector-like object that accesses the code units +of the string's representation. +""" +struct CodeUnits{T,S<:AbstractString} <: DenseVector{T} + s::S + CodeUnits(s::S) where {S<:AbstractString} = new{codeunit(s),S}(s) +end + +length(s::CodeUnits) = ncodeunits(s.s) +sizeof(s::CodeUnits{T}) where {T} = ncodeunits(s.s) * sizeof(T) +size(s::CodeUnits) = (length(s),) +elsize(s::CodeUnits{T}) where {T} = sizeof(T) +@propagate_inbounds getindex(s::CodeUnits, i::Int) = codeunit(s.s, i) +IndexStyle(::Type{<:CodeUnits}) = IndexLinear() +iterate(s::CodeUnits, i=1) = (@_propagate_inbounds_meta; i == length(s)+1 ? nothing : (s[i], i+1)) + +write(io::IO, s::CodeUnits) = write(io, s.s) + +unsafe_convert(::Type{Ptr{T}}, s::CodeUnits{T}) where {T} = unsafe_convert(Ptr{T}, s.s) +unsafe_convert(::Type{Ptr{Int8}}, s::CodeUnits{UInt8}) = unsafe_convert(Ptr{Int8}, s.s) + +""" + codeunits(s::AbstractString) + +Obtain a vector-like object containing the code units of a string. +Returns a `CodeUnits` wrapper by default, but `codeunits` may optionally be defined +for new string types if necessary. +""" +codeunits(s::AbstractString) = CodeUnits(s) diff --git a/base/strings/io.jl b/base/strings/io.jl new file mode 100644 index 0000000..18cb027 --- /dev/null +++ b/base/strings/io.jl @@ -0,0 +1,651 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## core text I/O ## + +""" + print([io::IO], xs...) + +Write to `io` (or to the default output stream [`stdout`](@ref) +if `io` is not given) a canonical (un-decorated) text representation. +The representation used by `print` includes minimal formatting and tries to +avoid Julia-specific details. + +`print` falls back to calling `show`, so most types should just define +`show`. Define `print` if your type has a separate "plain" representation. +For example, `show` displays strings with quotes, and `print` displays strings +without quotes. + +[`string`](@ref) returns the output of `print` as a string. + +# Examples +```jldoctest +julia> print("Hello World!") +Hello World! +julia> io = IOBuffer(); + +julia> print(io, "Hello", ' ', :World!) + +julia> String(take!(io)) +"Hello World!" +``` +""" +function print(io::IO, x) + lock(io) + try + show(io, x) + finally + unlock(io) + end + return nothing +end + +function print(io::IO, xs...) + lock(io) + try + for x in xs + print(io, x) + end + finally + unlock(io) + end + return nothing +end + +""" + println([io::IO], xs...) + +Print (using [`print`](@ref)) `xs` followed by a newline. +If `io` is not supplied, prints to [`stdout`](@ref). + +# Examples +```jldoctest +julia> println("Hello, world") +Hello, world + +julia> io = IOBuffer(); + +julia> println(io, "Hello, world") + +julia> String(take!(io)) +"Hello, world\\n" +``` +""" +println(io::IO, xs...) = print(io, xs..., '\n') + +## conversion of general objects to strings ## + +""" + sprint(f::Function, args...; context=nothing, sizehint=0) + +Call the given function with an I/O stream and the supplied extra arguments. +Everything written to this I/O stream is returned as a string. +`context` can be either an [`IOContext`](@ref) whose properties will be used, +or a `Pair` specifying a property and its value. `sizehint` suggests the capacity +of the buffer (in bytes). + +The optional keyword argument `context` can be set to `:key=>value` pair +or an `IO` or [`IOContext`](@ref) object whose attributes are used for the I/O +stream passed to `f`. The optional `sizehint` is a suggested size (in bytes) +to allocate for the buffer used to write the string. + +# Examples +```jldoctest +julia> sprint(show, 66.66666; context=:compact => true) +"66.6667" + +julia> sprint(showerror, BoundsError([1], 100)) +"BoundsError: attempt to access 1-element Array{Int64,1} at index [100]" +``` +""" +function sprint(f::Function, args...; context=nothing, sizehint::Integer=0) + s = IOBuffer(sizehint=sizehint) + if context !== nothing + f(IOContext(s, context), args...) + else + f(s, args...) + end + String(resize!(s.data, s.size)) +end + +# CategoricalArrays extends this +function tostr_sizehint end + +function _str_sizehint(x) + if x isa Float64 + return 20 + elseif x isa Float32 + return 12 + elseif x isa String || x isa SubString{String} + return sizeof(x) + elseif x isa Char + return ncodeunits(x) + else + return 8 + end +end + +function print_to_string(xs...) + if isempty(xs) + return "" + end + siz::Int = 0 + for x in xs + siz += _str_sizehint(x) + end + # specialized for performance reasons + s = IOBuffer(sizehint=siz) + for x in xs + print(s, x) + end + String(resize!(s.data, s.size)) +end + +function string_with_env(env, xs...) + if isempty(xs) + return "" + end + siz::Int = 0 + for x in xs + siz += _str_sizehint(x) + end + # specialized for performance reasons + s = IOBuffer(sizehint=siz) + env_io = IOContext(s, env) + for x in xs + print(env_io, x) + end + String(resize!(s.data, s.size)) +end + +""" + string(xs...) + +Create a string from any values, except `nothing`, using the [`print`](@ref) function. + +`string` should usually not be defined directly. Instead, define a method +`print(io::IO, x::MyType)`. If `string(x)` for a certain type needs to be +highly efficient, then it may make sense to add a method to `string` and +define `print(io::IO, x::MyType) = print(io, string(x))` to ensure the +functions are consistent. + +# Examples +```jldoctest +julia> string("a", 1, true) +"a1true" +``` +""" +string(xs...) = print_to_string(xs...) + +# note: print uses an encoding determined by `io` (defaults to UTF-8), whereas +# write uses an encoding determined by `s` (UTF-8 for `String`) +print(io::IO, s::AbstractString) = for c in s; print(io, c); end +write(io::IO, s::AbstractString) = (len = 0; for c in s; len += write(io, c); end; len) +show(io::IO, s::AbstractString) = print_quoted(io, s) + +# optimized methods to avoid iterating over chars +write(io::IO, s::Union{String,SubString{String}}) = + GC.@preserve s unsafe_write(io, pointer(s), reinterpret(UInt, sizeof(s))) +print(io::IO, s::Union{String,SubString{String}}) = (write(io, s); nothing) + +## printing literal quoted string data ## + +# this is the inverse of print_unescaped_chars(io, s, "\\\") + +function print_quoted_literal(io, s::AbstractString) + print(io, '"') + for c = s; c == '"' ? print(io, "\\\"") : print(io, c); end + print(io, '"') +end + +""" + repr(x; context=nothing) + +Create a string from any value using the [`show`](@ref) function. +You should not add methods to `repr`; define a `show` method instead. + +The optional keyword argument `context` can be set to an `IO` or [`IOContext`](@ref) +object whose attributes are used for the I/O stream passed to `show`. + +Note that `repr(x)` is usually similar to how the value of `x` would +be entered in Julia. See also [`repr(MIME("text/plain"), x)`](@ref) to instead +return a "pretty-printed" version of `x` designed more for human consumption, +equivalent to the REPL display of `x`. + +# Examples +```jldoctest +julia> repr(1) +"1" + +julia> repr(zeros(3)) +"[0.0, 0.0, 0.0]" + +julia> repr(big(1/3)) +"0.333333333333333314829616256247390992939472198486328125" + +julia> repr(big(1/3), context=:compact => true) +"0.333333" + +``` +""" +repr(x; context=nothing) = sprint(show, x; context=context) + +limitrepr(x) = repr(x, context = :limit=>true) + +# IOBuffer views of a (byte)string: + +""" + IOBuffer(string::String) + +Create a read-only `IOBuffer` on the data underlying the given string. + +# Examples +```jldoctest +julia> io = IOBuffer("Haho"); + +julia> String(take!(io)) +"Haho" + +julia> String(take!(io)) +"Haho" +``` +""" +IOBuffer(str::String) = IOBuffer(unsafe_wrap(Vector{UInt8}, str)) +IOBuffer(s::SubString{String}) = IOBuffer(view(unsafe_wrap(Vector{UInt8}, s.string), s.offset + 1 : s.offset + sizeof(s))) + +# join is implemented using IO + +""" + join([io::IO,] strings [, delim [, last]]) + +Join an array of `strings` into a single string, inserting the given delimiter (if any) between +adjacent strings. If `last` is given, it will be used instead of `delim` between the last +two strings. If `io` is given, the result is written to `io` rather than returned as +as a `String`. + +`strings` can be any iterable over elements `x` which are convertible to strings +via `print(io::IOBuffer, x)`. `strings` will be printed to `io`. + +# Examples +```jldoctest +julia> join(["apples", "bananas", "pineapples"], ", ", " and ") +"apples, bananas and pineapples" + +julia> join([1,2,3,4,5]) +"12345" +``` +""" +function join(io::IO, strings, delim, last) + first = true + local prev + for str in strings + if @isdefined prev + first ? (first = false) : print(io, delim) + print(io, prev) + end + prev = str + end + if @isdefined prev + first || print(io, last) + print(io, prev) + end + nothing +end +function join(io::IO, strings, delim="") + # Specialization of the above code when delim==last, + # which lets us emit (compile) less code + first = true + for str in strings + first ? (first = false) : print(io, delim) + print(io, str) + end +end + +join(strings) = sprint(join, strings) +join(strings, delim) = sprint(join, strings, delim) +join(strings, delim, last) = sprint(join, strings, delim, last) + +## string escaping & unescaping ## + +need_full_hex(c::Union{Nothing, AbstractChar}) = c !== nothing && isxdigit(c) +escape_nul(c::Union{Nothing, AbstractChar}) = + (c !== nothing && '0' <= c <= '7') ? "\\x00" : "\\0" + +""" + escape_string(str::AbstractString[, esc])::AbstractString + escape_string(io, str::AbstractString[, esc::])::Nothing + +General escaping of traditional C and Unicode escape sequences. The first form returns the +escaped string, the second prints the result to `io`. + +Backslashes (`\\`) are escaped with a double-backslash (`"\\\\"`). Non-printable +characters are escaped either with their standard C escape codes, `"\\0"` for NUL (if +unambiguous), unicode code point (`"\\u"` prefix) or hex (`"\\x"` prefix). + +The optional `esc` argument specifies any additional characters that should also be +escaped by a prepending backslash (`\"` is also escaped by default in the first form). + +# Examples +```jldoctest +julia> escape_string("aaa\\nbbb") +"aaa\\\\nbbb" + +julia> escape_string("\\xfe\\xff") # invalid utf-8 +"\\\\xfe\\\\xff" + +julia> escape_string(string('\\u2135','\\0')) # unambiguous +"ℵ\\\\0" + +julia> escape_string(string('\\u2135','\\0','0')) # \\0 would be ambiguous +"ℵ\\\\x000" +``` + +## See also +[`unescape_string`](@ref) for the reverse operation. +""" +function escape_string(io::IO, s::AbstractString, esc="") + a = Iterators.Stateful(s) + for c in a + if c in esc + print(io, '\\', c) + elseif isascii(c) + c == '\0' ? print(io, escape_nul(peek(a))) : + c == '\e' ? print(io, "\\e") : + c == '\\' ? print(io, "\\\\") : + '\a' <= c <= '\r' ? print(io, '\\', "abtnvfr"[Int(c)-6]) : + isprint(c) ? print(io, c) : + print(io, "\\x", string(UInt32(c), base = 16, pad = 2)) + elseif !isoverlong(c) && !ismalformed(c) + isprint(c) ? print(io, c) : + c <= '\x7f' ? print(io, "\\x", string(UInt32(c), base = 16, pad = 2)) : + c <= '\uffff' ? print(io, "\\u", string(UInt32(c), base = 16, pad = need_full_hex(peek(a)) ? 4 : 2)) : + print(io, "\\U", string(UInt32(c), base = 16, pad = need_full_hex(peek(a)) ? 8 : 4)) + else # malformed or overlong + u = bswap(reinterpret(UInt32, c)) + while true + print(io, "\\x", string(u % UInt8, base = 16, pad = 2)) + (u >>= 8) == 0 && break + end + end + end +end + +escape_string(s::AbstractString, esc=('\"',)) = sprint(escape_string, s, esc, sizehint=lastindex(s)) + +function print_quoted(io, s::AbstractString) + print(io, '"') + escape_string(io, s, ('\"','$')) #"# work around syntax highlighting problem + print(io, '"') +end + +# general unescaping of traditional C and Unicode escape sequences + +# TODO: handle unescaping invalid UTF-8 sequences +""" + unescape_string(str::AbstractString, keep = ())::AbstractString + unescape_string(io, s::AbstractString, keep = ())::Nothing + +General unescaping of traditional C and Unicode escape sequences. The first form returns +the escaped string, the second prints the result to `io`. +The argument `keep` specifies a collection of characters which (along with backlashes) are +to be kept as they are. + +The following escape sequences are recognised: + - Escaped backslash (`\\\\`) + - Escaped double-quote (`\\\"`) + - Standard C escape sequences (`\\a`, `\\b`, `\\t`, `\\n`, `\\v`, `\\f`, `\\r`, `\\e`) + - Unicode code points (`\\u` or `\\U` prefixes with 1-4 trailing hex digits) + - Hex bytes (`\\x` with 1-2 trailing hex digits) + - Octal bytes (`\\` with 1-3 trailing octal digits) + +# Examples +```jldoctest +julia> unescape_string("aaa\\\\nbbb") # C escape sequence +"aaa\\nbbb" + +julia> unescape_string("\\\\u03c0") # unicode +"π" + +julia> unescape_string("\\\\101") # octal +"A" + +julia> unescape_string("aaa \\\\g \\\\n", ['g']) # using `keep` argument +"aaa \\\\g \\n" +``` + +## See also +[`escape_string`](@ref). +""" +function unescape_string(io::IO, s::AbstractString, keep = ()) + a = Iterators.Stateful(s) + for c in a + if !isempty(a) && c == '\\' + c = popfirst!(a) + if c in keep + print(io, '\\', c) + elseif c == 'x' || c == 'u' || c == 'U' + n = k = 0 + m = c == 'x' ? 2 : + c == 'u' ? 4 : 8 + while (k += 1) <= m && !isempty(a) + nc = peek(a) + n = '0' <= nc <= '9' ? n<<4 + (nc-'0') : + 'a' <= nc <= 'f' ? n<<4 + (nc-'a'+10) : + 'A' <= nc <= 'F' ? n<<4 + (nc-'A'+10) : break + popfirst!(a) + end + if k == 1 || n > 0x10ffff + u = m == 4 ? 'u' : 'U' + throw(ArgumentError("invalid $(m == 2 ? "hex (\\x)" : + "unicode (\\$u)") escape sequence")) + end + if m == 2 # \x escape sequence + write(io, UInt8(n)) + else + print(io, Char(n)) + end + elseif '0' <= c <= '7' + k = 1 + n = c-'0' + while (k += 1) <= 3 && !isempty(a) + c = peek(a) + n = ('0' <= c <= '7') ? n<<3 + c-'0' : break + popfirst!(a) + end + if n > 255 + throw(ArgumentError("octal escape sequence out of range")) + end + write(io, UInt8(n)) + else + print(io, c == 'a' ? '\a' : + c == 'b' ? '\b' : + c == 't' ? '\t' : + c == 'n' ? '\n' : + c == 'v' ? '\v' : + c == 'f' ? '\f' : + c == 'r' ? '\r' : + c == 'e' ? '\e' : + (c == '\\' || c == '"') ? c : + throw(ArgumentError("invalid escape sequence \\$c"))) + end + else + print(io, c) + end + end +end +unescape_string(s::AbstractString, keep = ()) = + sprint(unescape_string, s, keep; sizehint=lastindex(s)) + +""" + @b_str + +Create an immutable byte (`UInt8`) vector using string syntax. + +# Examples +```jldoctest +julia> v = b"12\\x01\\x02" +4-element Base.CodeUnits{UInt8,String}: + 0x31 + 0x32 + 0x01 + 0x02 + +julia> v[2] +0x32 +``` +""" +macro b_str(s) + v = codeunits(unescape_string(s)) + QuoteNode(v) +end + +""" + @raw_str -> String + +Create a raw string without interpolation and unescaping. +The exception is that quotation marks still must be escaped. Backslashes +escape both quotation marks and other backslashes, but only when a sequence +of backslashes precedes a quote character. Thus, 2n backslashes followed by +a quote encodes n backslashes and the end of the literal while 2n+1 backslashes +followed by a quote encodes n backslashes followed by a quote character. + +# Examples +```jldoctest +julia> println(raw"\\ \$x") +\\ \$x + +julia> println(raw"\\"") +" + +julia> println(raw"\\\\\\"") +\\" + +julia> println(raw"\\\\x \\\\\\"") +\\\\x \\" +``` +""" +macro raw_str(s); s; end + +## multiline strings ## + +""" + indentation(str::AbstractString; tabwidth=8) -> (Int, Bool) + +Calculate the width of leading white space. Return the width and a flag to indicate +if the string is empty. + +# Examples +```jldoctest +julia> Base.indentation("") +(0, true) + +julia> Base.indentation(" a") +(2, false) + +julia> Base.indentation("\\ta"; tabwidth=3) +(3, false) +``` +""" +function indentation(str::AbstractString; tabwidth=8) + count = 0 + for ch in str + if ch == ' ' + count += 1 + elseif ch == '\t' + count = div(count + tabwidth, tabwidth) * tabwidth + else + return count, false + end + end + count, true +end + +""" + unindent(str::AbstractString, indent::Int; tabwidth=8) + +Remove leading indentation from string. + +# Examples +```jldoctest +julia> Base.unindent(" a\\n b", 2) +" a\\n b" + +julia> Base.unindent("\\ta\\n\\tb", 2, tabwidth=8) +" a\\n b" +``` +""" +function unindent(str::AbstractString, indent::Int; tabwidth=8) + indent == 0 && return str + # Note: this loses the type of the original string + buf = IOBuffer(sizehint=sizeof(str)) + cutting = true + col = 0 # current column (0 based) + for ch in str + if cutting + if ch == ' ' + col += 1 + elseif ch == '\t' + col = div(col + tabwidth, tabwidth) * tabwidth + elseif ch == '\n' + # Now we need to output enough indentation + for i = 1:col-indent + print(buf, ' ') + end + col = 0 + print(buf, '\n') + else + cutting = false + # Now we need to output enough indentation to get to + # correct place + for i = 1:col-indent + print(buf, ' ') + end + col += 1 + print(buf, ch) + end + elseif ch == '\t' # Handle internal tabs + upd = div(col + tabwidth, tabwidth) * tabwidth + # output the number of spaces that would have been seen + # with original indentation + for i = 1:(upd-col) + print(buf, ' ') + end + col = upd + elseif ch == '\n' + cutting = true + col = 0 + print(buf, '\n') + else + col += 1 + print(buf, ch) + end + end + # If we were still "cutting" when we hit the end of the string, + # we need to output the right number of spaces for the indentation + if cutting + for i = 1:col-indent + print(buf, ' ') + end + end + String(take!(buf)) +end + +function String(a::AbstractVector{Char}) + n = 0 + for v in a + n += ncodeunits(v) + end + out = _string_n(n) + offs = 1 + for v in a + offs += __unsafe_string!(out, v, offs) + end + return out +end + +function String(chars::AbstractVector{<:AbstractChar}) + sprint(sizehint=length(chars)) do io + for c in chars + print(io, c) + end + end +end diff --git a/base/strings/search.jl b/base/strings/search.jl new file mode 100644 index 0000000..15d0a96 --- /dev/null +++ b/base/strings/search.jl @@ -0,0 +1,533 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +nothing_sentinel(i) = i == 0 ? nothing : i + +function findnext(pred::Fix2{<:Union{typeof(isequal),typeof(==)},<:AbstractChar}, + s::String, i::Integer) + if i < 1 || i > sizeof(s) + i == sizeof(s) + 1 && return nothing + throw(BoundsError(s, i)) + end + @inbounds isvalid(s, i) || string_index_err(s, i) + c = pred.x + c ≤ '\x7f' && return nothing_sentinel(_search(s, c % UInt8, i)) + while true + i = _search(s, first_utf8_byte(c), i) + i == 0 && return nothing + pred(s[i]) && return i + i = nextind(s, i) + end +end + +findfirst(pred::Fix2{<:Union{typeof(isequal),typeof(==)},<:Union{Int8,UInt8}}, a::ByteArray) = + nothing_sentinel(_search(a, pred.x)) + +findnext(pred::Fix2{<:Union{typeof(isequal),typeof(==)},<:Union{Int8,UInt8}}, a::ByteArray, i::Integer) = + nothing_sentinel(_search(a, pred.x, i)) + +function _search(a::Union{String,ByteArray}, b::Union{Int8,UInt8}, i::Integer = 1) + if i < 1 + throw(BoundsError(a, i)) + end + n = sizeof(a) + if i > n + return i == n+1 ? 0 : throw(BoundsError(a, i)) + end + p = pointer(a) + q = GC.@preserve a ccall(:memchr, Ptr{UInt8}, (Ptr{UInt8}, Int32, Csize_t), p+i-1, b, n-i+1) + return q == C_NULL ? 0 : Int(q-p+1) +end + +function _search(a::ByteArray, b::AbstractChar, i::Integer = 1) + if isascii(b) + _search(a,UInt8(b),i) + else + _search(a,unsafe_wrap(Vector{UInt8},string(b)),i).start + end +end + +function findprev(pred::Fix2{<:Union{typeof(isequal),typeof(==)},<:AbstractChar}, + s::String, i::Integer) + c = pred.x + c ≤ '\x7f' && return nothing_sentinel(_rsearch(s, c % UInt8, i)) + b = first_utf8_byte(c) + while true + i = _rsearch(s, b, i) + i == 0 && return nothing + pred(s[i]) && return i + i = prevind(s, i) + end +end + +findlast(pred::Fix2{<:Union{typeof(isequal),typeof(==)},<:Union{Int8,UInt8}}, a::ByteArray) = + nothing_sentinel(_rsearch(a, pred.x)) + +findprev(pred::Fix2{<:Union{typeof(isequal),typeof(==)},<:Union{Int8,UInt8}}, a::ByteArray, i::Integer) = + nothing_sentinel(_rsearch(a, pred.x, i)) + +function _rsearch(a::Union{String,ByteArray}, b::Union{Int8,UInt8}, i::Integer = sizeof(a)) + if i < 1 + return i == 0 ? 0 : throw(BoundsError(a, i)) + end + n = sizeof(a) + if i > n + return i == n+1 ? 0 : throw(BoundsError(a, i)) + end + p = pointer(a) + q = GC.@preserve a ccall(:memrchr, Ptr{UInt8}, (Ptr{UInt8}, Int32, Csize_t), p, b, i) + return q == C_NULL ? 0 : Int(q-p+1) +end + +function _rsearch(a::ByteArray, b::AbstractChar, i::Integer = length(a)) + if isascii(b) + _rsearch(a,UInt8(b),i) + else + _rsearch(a,unsafe_wrap(Vector{UInt8},string(b)),i).start + end +end + +""" + findfirst(pattern::AbstractString, string::AbstractString) + findfirst(pattern::Regex, string::String) + +Find the first occurrence of `pattern` in `string`. Equivalent to +[`findnext(pattern, string, firstindex(s))`](@ref). + +# Examples +```jldoctest +julia> findfirst("z", "Hello to the world") # returns nothing, but not printed in the REPL + +julia> findfirst("Julia", "JuliaLang") +1:5 +``` +""" +findfirst(pattern::AbstractString, string::AbstractString) = + findnext(pattern, string, firstindex(string)) + +""" + findfirst(ch::AbstractChar, string::AbstractString) + +Find the first occurrence of character `ch` in `string`. + +!!! compat "Julia 1.3" + This method requires at least Julia 1.3. + +# Examples +```jldoctest +julia> findfirst('a', "happy") +2 + +julia> findfirst('z', "happy") === nothing +true +``` +""" +findfirst(ch::AbstractChar, string::AbstractString) = findfirst(==(ch), string) + +# AbstractString implementation of the generic findnext interface +function findnext(testf::Function, s::AbstractString, i::Integer) + z = ncodeunits(s) + 1 + 1 ≤ i ≤ z || throw(BoundsError(s, i)) + @inbounds i == z || isvalid(s, i) || string_index_err(s, i) + for (j, d) in pairs(SubString(s, i)) + if testf(d) + return i + j - 1 + end + end + return nothing +end + +in(c::AbstractChar, s::AbstractString) = (findfirst(isequal(c),s)!==nothing) + +function _searchindex(s::Union{AbstractString,ByteArray}, + t::Union{AbstractString,AbstractChar,Int8,UInt8}, + i::Integer) + if isempty(t) + return 1 <= i <= nextind(s,lastindex(s)) ? i : + throw(BoundsError(s, i)) + end + t1, trest = Iterators.peel(t) + while true + i = findnext(isequal(t1),s,i) + if i === nothing return 0 end + ii = nextind(s, i) + a = Iterators.Stateful(trest) + matched = all(splat(==), zip(SubString(s, ii), a)) + (isempty(a) && matched) && return i + i = ii + end +end + +_searchindex(s::AbstractString, t::AbstractChar, i::Integer) = something(findnext(isequal(t), s, i), 0) + +function _search_bloom_mask(c) + UInt64(1) << (c & 63) +end + +_nthbyte(s::String, i) = codeunit(s, i) +_nthbyte(a::Union{AbstractVector{UInt8},AbstractVector{Int8}}, i) = a[i] + +function _searchindex(s::String, t::String, i::Integer) + # Check for fast case of a single byte + lastindex(t) == 1 && return something(findnext(isequal(t[1]), s, i), 0) + _searchindex(unsafe_wrap(Vector{UInt8},s), unsafe_wrap(Vector{UInt8},t), i) +end + +function _searchindex(s::ByteArray, t::ByteArray, i::Integer) + n = sizeof(t) + m = sizeof(s) + + if n == 0 + return 1 <= i <= m+1 ? max(1, i) : 0 + elseif m == 0 + return 0 + elseif n == 1 + return something(findnext(isequal(_nthbyte(t,1)), s, i), 0) + end + + w = m - n + if w < 0 || i - 1 > w + return 0 + end + + bloom_mask = UInt64(0) + skip = n - 1 + tlast = _nthbyte(t,n) + for j in 1:n + bloom_mask |= _search_bloom_mask(_nthbyte(t,j)) + if _nthbyte(t,j) == tlast && j < n + skip = n - j - 1 + end + end + + i -= 1 + while i <= w + if _nthbyte(s,i+n) == tlast + # check candidate + j = 0 + while j < n - 1 + if _nthbyte(s,i+j+1) != _nthbyte(t,j+1) + break + end + j += 1 + end + + # match found + if j == n - 1 + return i+1 + end + + # no match, try to rule out the next character + if i < w && bloom_mask & _search_bloom_mask(_nthbyte(s,i+n+1)) == 0 + i += n + else + i += skip + end + elseif i < w + if bloom_mask & _search_bloom_mask(_nthbyte(s,i+n+1)) == 0 + i += n + end + end + i += 1 + end + + 0 +end + +function _search(s::Union{AbstractString,ByteArray}, + t::Union{AbstractString,AbstractChar,Int8,UInt8}, + i::Integer) + idx = _searchindex(s,t,i) + if isempty(t) + idx:idx-1 + elseif idx > 0 + idx:(idx + lastindex(t) - 1) + else + nothing + end +end + +""" + findnext(pattern::AbstractString, string::AbstractString, start::Integer) + findnext(pattern::Regex, string::String, start::Integer) + +Find the next occurrence of `pattern` in `string` starting at position `start`. +`pattern` can be either a string, or a regular expression, in which case `string` +must be of type `String`. + +The return value is a range of indices where the matching sequence is found, such that +`s[findnext(x, s, i)] == x`: + +`findnext("substring", string, i)` == `start:stop` such that +`string[start:stop] == "substring"` and `i <= start`, or `nothing` if unmatched. + +# Examples +```jldoctest +julia> findnext("z", "Hello to the world", 1) === nothing +true + +julia> findnext("o", "Hello to the world", 6) +8:8 + +julia> findnext("Lang", "JuliaLang", 2) +6:9 +``` +""" +findnext(t::AbstractString, s::AbstractString, i::Integer) = _search(s, t, i) + +""" + findnext(ch::AbstractChar, string::AbstractString, start::Integer) + +Find the next occurrence of character `ch` in `string` starting at position `start`. + +!!! compat "Julia 1.3" + This method requires at least Julia 1.3. + +# Examples +```jldoctest +julia> findnext('z', "Hello to the world", 1) === nothing +true + +julia> findnext('o', "Hello to the world", 6) +8 +``` +""" +findnext(ch::AbstractChar, string::AbstractString, ind::Integer) = + findnext(==(ch), string, ind) + +""" + findlast(pattern::AbstractString, string::AbstractString) + +Find the last occurrence of `pattern` in `string`. Equivalent to +[`findprev(pattern, string, lastindex(string))`](@ref). + +# Examples +```jldoctest +julia> findlast("o", "Hello to the world") +15:15 + +julia> findfirst("Julia", "JuliaLang") +1:5 +``` +""" +findlast(pattern::AbstractString, string::AbstractString) = + findprev(pattern, string, lastindex(string)) + +""" + findlast(ch::AbstractChar, string::AbstractString) + +Find the last occurrence of character `ch` in `string`. + +!!! compat "Julia 1.3" + This method requires at least Julia 1.3. + +# Examples +```jldoctest +julia> findlast('p', "happy") +4 + +julia> findlast('z', "happy") === nothing +true +``` +""" +findlast(ch::AbstractChar, string::AbstractString) = findlast(==(ch), string) + +# AbstractString implementation of the generic findprev interface +function findprev(testf::Function, s::AbstractString, i::Integer) + if i < 1 + return i == 0 ? nothing : throw(BoundsError(s, i)) + end + n = ncodeunits(s) + if i > n + return i == n+1 ? nothing : throw(BoundsError(s, i)) + end + # r[reverseind(r,i)] == reverse(r)[i] == s[i] + # s[reverseind(s,j)] == reverse(s)[j] == r[j] + r = reverse(s) + j = findnext(testf, r, reverseind(r, i)) + j === nothing ? nothing : reverseind(s, j) +end + +function _rsearchindex(s::AbstractString, + t::Union{AbstractString,AbstractChar,Int8,UInt8}, + i::Integer) + if isempty(t) + return 1 <= i <= nextind(s, lastindex(s)) ? i : + throw(BoundsError(s, i)) + end + t1, trest = Iterators.peel(Iterators.reverse(t)) + while true + i = findprev(isequal(t1), s, i) + i === nothing && return 0 + ii = prevind(s, i) + a = Iterators.Stateful(trest) + b = Iterators.Stateful(Iterators.reverse( + pairs(SubString(s, 1, ii)))) + matched = all(splat(==), zip(a, (x[2] for x in b))) + if matched && isempty(a) + isempty(b) && return firstindex(s) + return nextind(s, popfirst!(b)[1]) + end + i = ii + end +end + +function _rsearchindex(s::String, t::String, i::Integer) + # Check for fast case of a single byte + if lastindex(t) == 1 + return something(findprev(isequal(t[1]), s, i), 0) + elseif lastindex(t) != 0 + j = i ≤ ncodeunits(s) ? nextind(s, i)-1 : i + return _rsearchindex(unsafe_wrap(Vector{UInt8}, s), unsafe_wrap(Vector{UInt8}, t), j) + elseif i > sizeof(s) + return 0 + elseif i == 0 + return 1 + else + return i + end +end + +function _rsearchindex(s::ByteArray, t::ByteArray, k::Integer) + n = sizeof(t) + m = sizeof(s) + + if n == 0 + return 0 <= k <= m ? max(k, 1) : 0 + elseif m == 0 + return 0 + elseif n == 1 + return something(findprev(isequal(_nthbyte(t,1)), s, k), 0) + end + + w = m - n + if w < 0 || k <= 0 + return 0 + end + + bloom_mask = UInt64(0) + skip = n - 1 + tfirst = _nthbyte(t,1) + for j in n:-1:1 + bloom_mask |= _search_bloom_mask(_nthbyte(t,j)) + if _nthbyte(t,j) == tfirst && j > 1 + skip = j - 2 + end + end + + i = min(k - n + 1, w + 1) + while i > 0 + if _nthbyte(s,i) == tfirst + # check candidate + j = 1 + while j < n + if _nthbyte(s,i+j) != _nthbyte(t,j+1) + break + end + j += 1 + end + + # match found + if j == n + return i + end + + # no match, try to rule out the next character + if i > 1 && bloom_mask & _search_bloom_mask(_nthbyte(s,i-1)) == 0 + i -= n + else + i -= skip + end + elseif i > 1 + if bloom_mask & _search_bloom_mask(_nthbyte(s,i-1)) == 0 + i -= n + end + end + i -= 1 + end + + 0 +end + +function _rsearch(s::Union{AbstractString,ByteArray}, + t::Union{AbstractString,AbstractChar,Int8,UInt8}, + i::Integer) + idx = _rsearchindex(s,t,i) + if isempty(t) + idx:idx-1 + elseif idx > 0 + idx:(idx + lastindex(t) - 1) + else + nothing + end +end + +""" + findprev(pattern::AbstractString, string::AbstractString, start::Integer) + +Find the previous occurrence of `pattern` in `string` starting at position `start`. + +The return value is a range of indices where the matching sequence is found, such that +`s[findprev(x, s, i)] == x`: + +`findprev("substring", string, i)` == `start:stop` such that +`string[start:stop] == "substring"` and `stop <= i`, or `nothing` if unmatched. + +# Examples +```jldoctest +julia> findprev("z", "Hello to the world", 18) === nothing +true + +julia> findprev("o", "Hello to the world", 18) +15:15 + +julia> findprev("Julia", "JuliaLang", 6) +1:5 +``` +""" +findprev(t::AbstractString, s::AbstractString, i::Integer) = _rsearch(s, t, i) + +""" + findprev(ch::AbstractChar, string::AbstractString, start::Integer) + +Find the previous occurrence of character `ch` in `string` starting at position `start`. + +!!! compat "Julia 1.3" + This method requires at least Julia 1.3. + +# Examples +```jldoctest +julia> findprev('z', "Hello to the world", 18) === nothing +true + +julia> findprev('o', "Hello to the world", 18) +15 +``` +""" +findprev(ch::AbstractChar, string::AbstractString, ind::Integer) = + findprev(==(ch), string, ind) + +""" + occursin(needle::Union{AbstractString,Regex,AbstractChar}, haystack::AbstractString) + +Determine whether the first argument is a substring of the second. If `needle` +is a regular expression, checks whether `haystack` contains a match. + +# Examples +```jldoctest +julia> occursin("Julia", "JuliaLang is pretty cool!") +true + +julia> occursin('a', "JuliaLang is pretty cool!") +true + +julia> occursin(r"a.a", "aba") +true + +julia> occursin(r"a.a", "abba") +false +``` +""" +occursin(needle::Union{AbstractString,AbstractChar}, haystack::AbstractString) = + _searchindex(haystack, needle, firstindex(haystack)) != 0 + +in(::AbstractString, ::AbstractString) = error("use occursin(x, y) for string containment") diff --git a/base/strings/string.jl b/base/strings/string.jl new file mode 100644 index 0000000..a65e02f --- /dev/null +++ b/base/strings/string.jl @@ -0,0 +1,363 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" + StringIndexError(str, i) + +An error occurred when trying to access `str` at index `i` that is not valid. +""" +struct StringIndexError <: Exception + string::AbstractString + index::Integer +end +@noinline string_index_err(s::AbstractString, i::Integer) = + throw(StringIndexError(s, Int(i))) + +const ByteArray = Union{Vector{UInt8},Vector{Int8}} + +@inline between(b::T, lo::T, hi::T) where {T<:Integer} = (lo ≤ b) & (b ≤ hi) + +## constructors and conversions ## + +# String constructor docstring from boot.jl, workaround for #16730 +# and the unavailability of @doc in boot.jl context. +""" + String(v::AbstractVector{UInt8}) + +Create a new `String` object from a byte vector `v` containing UTF-8 encoded +characters. If `v` is `Vector{UInt8}` it will be truncated to zero length and +future modification of `v` cannot affect the contents of the resulting string. +To avoid truncation use `String(copy(v))`. + +When possible, the memory of `v` will be used without copying when the `String` +object is created. This is guaranteed to be the case for byte vectors returned +by [`take!`](@ref) on a writable [`IOBuffer`](@ref) and by calls to +[`read(io, nb)`](@ref). This allows zero-copy conversion of I/O data to strings. +In other cases, `Vector{UInt8}` data may be copied, but `v` is truncated anyway +to guarantee consistent behavior. +""" +String(v::AbstractVector{UInt8}) = String(copyto!(StringVector(length(v)), v)) +String(v::Vector{UInt8}) = ccall(:jl_array_to_string, Ref{String}, (Any,), v) + +""" + unsafe_string(p::Ptr{UInt8}, [length::Integer]) + +Copy a string from the address of a C-style (NUL-terminated) string encoded as UTF-8. +(The pointer can be safely freed afterwards.) If `length` is specified +(the length of the data in bytes), the string does not have to be NUL-terminated. + +This function is labeled "unsafe" because it will crash if `p` is not +a valid memory address to data of the requested length. +""" +function unsafe_string(p::Union{Ptr{UInt8},Ptr{Int8}}, len::Integer) + p == C_NULL && throw(ArgumentError("cannot convert NULL to string")) + ccall(:jl_pchar_to_string, Ref{String}, (Ptr{UInt8}, Int), p, len) +end +function unsafe_string(p::Union{Ptr{UInt8},Ptr{Int8}}) + p == C_NULL && throw(ArgumentError("cannot convert NULL to string")) + ccall(:jl_cstr_to_string, Ref{String}, (Ptr{UInt8},), p) +end + +_string_n(n::Integer) = ccall(:jl_alloc_string, Ref{String}, (Csize_t,), n) + +""" + String(s::AbstractString) + +Convert a string to a contiguous byte array representation encoded as UTF-8 bytes. +This representation is often appropriate for passing strings to C. +""" +String(s::AbstractString) = print_to_string(s) +String(s::Symbol) = unsafe_string(unsafe_convert(Ptr{UInt8}, s)) + +unsafe_wrap(::Type{Vector{UInt8}}, s::String) = ccall(:jl_string_to_array, Ref{Vector{UInt8}}, (Any,), s) + +(::Type{Vector{UInt8}})(s::CodeUnits{UInt8,String}) = copyto!(Vector{UInt8}(undef, length(s)), s) +(::Type{Vector{UInt8}})(s::String) = Vector{UInt8}(codeunits(s)) +(::Type{Array{UInt8}})(s::String) = Vector{UInt8}(codeunits(s)) + +String(s::CodeUnits{UInt8,String}) = s.s + +## low-level functions ## + +pointer(s::String) = unsafe_convert(Ptr{UInt8}, s) +pointer(s::String, i::Integer) = pointer(s)+(i-1) + +ncodeunits(s::String) = Core.sizeof(s) +sizeof(s::String) = Core.sizeof(s) +codeunit(s::String) = UInt8 + +@inline function codeunit(s::String, i::Integer) + @boundscheck checkbounds(s, i) + b = GC.@preserve s unsafe_load(pointer(s, i)) + return b +end + +## comparison ## + +_memcmp(a::Union{Ptr{UInt8},AbstractString}, b::Union{Ptr{UInt8},AbstractString}, len) = + ccall(:memcmp, Cint, (Ptr{UInt8}, Ptr{UInt8}, Csize_t), a, b, len % Csize_t) % Int + +function cmp(a::String, b::String) + al, bl = sizeof(a), sizeof(b) + c = _memcmp(a, b, min(al,bl)) + return c < 0 ? -1 : c > 0 ? +1 : cmp(al,bl) +end + +function ==(a::String, b::String) + pointer_from_objref(a) == pointer_from_objref(b) && return true + al = sizeof(a) + return al == sizeof(b) && 0 == _memcmp(a, b, al) +end + +typemin(::Type{String}) = "" +typemin(::String) = typemin(String) + +## thisind, nextind ## + +@propagate_inbounds thisind(s::String, i::Int) = _thisind_str(s, i) + +# s should be String or SubString{String} +@inline function _thisind_str(s, i::Int) + i == 0 && return 0 + n = ncodeunits(s) + i == n + 1 && return i + @boundscheck between(i, 1, n) || throw(BoundsError(s, i)) + @inbounds b = codeunit(s, i) + (b & 0xc0 == 0x80) & (i-1 > 0) || return i + @inbounds b = codeunit(s, i-1) + between(b, 0b11000000, 0b11110111) && return i-1 + (b & 0xc0 == 0x80) & (i-2 > 0) || return i + @inbounds b = codeunit(s, i-2) + between(b, 0b11100000, 0b11110111) && return i-2 + (b & 0xc0 == 0x80) & (i-3 > 0) || return i + @inbounds b = codeunit(s, i-3) + between(b, 0b11110000, 0b11110111) && return i-3 + return i +end + +@propagate_inbounds nextind(s::String, i::Int) = _nextind_str(s, i) + +# s should be String or SubString{String} +@inline function _nextind_str(s, i::Int) + i == 0 && return 1 + n = ncodeunits(s) + @boundscheck between(i, 1, n) || throw(BoundsError(s, i)) + @inbounds l = codeunit(s, i) + (l < 0x80) | (0xf8 ≤ l) && return i+1 + if l < 0xc0 + i′ = thisind(s, i) + return i′ < i ? nextind(s, i′) : i+1 + end + # first continuation byte + (i += 1) > n && return i + @inbounds b = codeunit(s, i) + b & 0xc0 ≠ 0x80 && return i + ((i += 1) > n) | (l < 0xe0) && return i + # second continuation byte + @inbounds b = codeunit(s, i) + b & 0xc0 ≠ 0x80 && return i + ((i += 1) > n) | (l < 0xf0) && return i + # third continuation byte + @inbounds b = codeunit(s, i) + ifelse(b & 0xc0 ≠ 0x80, i, i+1) +end + +## checking UTF-8 & ACSII validity ## + +byte_string_classify(s::Union{String,Vector{UInt8}}) = + ccall(:u8_isvalid, Int32, (Ptr{UInt8}, Int), s, sizeof(s)) + # 0: neither valid ASCII nor UTF-8 + # 1: valid ASCII + # 2: valid UTF-8 + +isvalid(::Type{String}, s::Union{Vector{UInt8},String}) = byte_string_classify(s) ≠ 0 +isvalid(s::String) = isvalid(String, s) + +is_valid_continuation(c) = c & 0xc0 == 0x80 + +## required core functionality ## + +@propagate_inbounds function iterate(s::String, i::Int=firstindex(s)) + i > ncodeunits(s) && return nothing + b = codeunit(s, i) + u = UInt32(b) << 24 + between(b, 0x80, 0xf7) || return reinterpret(Char, u), i+1 + return iterate_continued(s, i, u) +end + +function iterate_continued(s::String, i::Int, u::UInt32) + u < 0xc0000000 && (i += 1; @goto ret) + n = ncodeunits(s) + # first continuation byte + (i += 1) > n && @goto ret + @inbounds b = codeunit(s, i) + b & 0xc0 == 0x80 || @goto ret + u |= UInt32(b) << 16 + # second continuation byte + ((i += 1) > n) | (u < 0xe0000000) && @goto ret + @inbounds b = codeunit(s, i) + b & 0xc0 == 0x80 || @goto ret + u |= UInt32(b) << 8 + # third continuation byte + ((i += 1) > n) | (u < 0xf0000000) && @goto ret + @inbounds b = codeunit(s, i) + b & 0xc0 == 0x80 || @goto ret + u |= UInt32(b); i += 1 +@label ret + return reinterpret(Char, u), i +end + +@propagate_inbounds function getindex(s::String, i::Int) + b = codeunit(s, i) + u = UInt32(b) << 24 + between(b, 0x80, 0xf7) || return reinterpret(Char, u) + return getindex_continued(s, i, u) +end + +function getindex_continued(s::String, i::Int, u::UInt32) + if u < 0xc0000000 + # called from `getindex` which checks bounds + @inbounds isvalid(s, i) && @goto ret + string_index_err(s, i) + end + n = ncodeunits(s) + + (i += 1) > n && @goto ret + @inbounds b = codeunit(s, i) # cont byte 1 + b & 0xc0 == 0x80 || @goto ret + u |= UInt32(b) << 16 + + ((i += 1) > n) | (u < 0xe0000000) && @goto ret + @inbounds b = codeunit(s, i) # cont byte 2 + b & 0xc0 == 0x80 || @goto ret + u |= UInt32(b) << 8 + + ((i += 1) > n) | (u < 0xf0000000) && @goto ret + @inbounds b = codeunit(s, i) # cont byte 3 + b & 0xc0 == 0x80 || @goto ret + u |= UInt32(b) +@label ret + return reinterpret(Char, u) +end + +getindex(s::String, r::UnitRange{<:Integer}) = s[Int(first(r)):Int(last(r))] + +@inline function getindex(s::String, r::UnitRange{Int}) + isempty(r) && return "" + i, j = first(r), last(r) + @boundscheck begin + checkbounds(s, r) + @inbounds isvalid(s, i) || string_index_err(s, i) + @inbounds isvalid(s, j) || string_index_err(s, j) + end + j = nextind(s, j) - 1 + n = j - i + 1 + ss = _string_n(n) + GC.@preserve s ss unsafe_copyto!(pointer(ss), pointer(s, i), n) + return ss +end + +length(s::String) = length_continued(s, 1, ncodeunits(s), ncodeunits(s)) + +@inline function length(s::String, i::Int, j::Int) + @boundscheck begin + 0 < i ≤ ncodeunits(s)+1 || throw(BoundsError(s, i)) + 0 ≤ j < ncodeunits(s)+1 || throw(BoundsError(s, j)) + end + j < i && return 0 + @inbounds i, k = thisind(s, i), i + c = j - i + (i == k) + length_continued(s, i, j, c) +end + +@inline function length_continued(s::String, i::Int, n::Int, c::Int) + i < n || return c + @inbounds b = codeunit(s, i) + @inbounds while true + while true + (i += 1) ≤ n || return c + 0xc0 ≤ b ≤ 0xf7 && break + b = codeunit(s, i) + end + l = b + b = codeunit(s, i) # cont byte 1 + c -= (x = b & 0xc0 == 0x80) + x & (l ≥ 0xe0) || continue + + (i += 1) ≤ n || return c + b = codeunit(s, i) # cont byte 2 + c -= (x = b & 0xc0 == 0x80) + x & (l ≥ 0xf0) || continue + + (i += 1) ≤ n || return c + b = codeunit(s, i) # cont byte 3 + c -= (b & 0xc0 == 0x80) + end +end + +## overload methods for efficiency ## + +isvalid(s::String, i::Int) = checkbounds(Bool, s, i) && thisind(s, i) == i + +function isascii(s::String) + @inbounds for i = 1:ncodeunits(s) + codeunit(s, i) >= 0x80 && return false + end + return true +end + +""" + repeat(c::AbstractChar, r::Integer) -> String + +Repeat a character `r` times. This can equivalently be accomplished by calling [`c^r`](@ref ^). + +# Examples +```jldoctest +julia> repeat('A', 3) +"AAA" +``` +""" +repeat(c::AbstractChar, r::Integer) = repeat(Char(c), r) # fallback +function repeat(c::Char, r::Integer) + r == 0 && return "" + r < 0 && throw(ArgumentError("can't repeat a character $r times")) + u = bswap(reinterpret(UInt32, c)) + n = 4 - (leading_zeros(u | 0xff) >> 3) + s = _string_n(n*r) + p = pointer(s) + GC.@preserve s if n == 1 + ccall(:memset, Ptr{Cvoid}, (Ptr{UInt8}, Cint, Csize_t), p, u % UInt8, r) + elseif n == 2 + p16 = reinterpret(Ptr{UInt16}, p) + for i = 1:r + unsafe_store!(p16, u % UInt16, i) + end + elseif n == 3 + b1 = (u >> 0) % UInt8 + b2 = (u >> 8) % UInt8 + b3 = (u >> 16) % UInt8 + for i = 0:r-1 + unsafe_store!(p, b1, 3i + 1) + unsafe_store!(p, b2, 3i + 2) + unsafe_store!(p, b3, 3i + 3) + end + elseif n == 4 + p32 = reinterpret(Ptr{UInt32}, p) + for i = 1:r + unsafe_store!(p32, u, i) + end + end + return s +end + +function filter(f, s::String) + out = StringVector(sizeof(s)) + offset = 1 + for c in s + if f(c) + offset += __unsafe_string!(out, c, offset) + end + end + resize!(out, offset-1) + sizehint!(out, offset-1) + return String(out) +end diff --git a/base/strings/strings.jl b/base/strings/strings.jl new file mode 100644 index 0000000..07e4367 --- /dev/null +++ b/base/strings/strings.jl @@ -0,0 +1,10 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +include("strings/search.jl") +include("strings/unicode.jl") + +import .Unicode: textwidth, islowercase, isuppercase, isletter, isdigit, isnumeric, iscntrl, ispunct, + isspace, isprint, isxdigit, lowercase, uppercase, titlecase, lowercasefirst, uppercasefirst + +include("strings/util.jl") +include("strings/io.jl") diff --git a/base/strings/substring.jl b/base/strings/substring.jl new file mode 100644 index 0000000..cfebe99 --- /dev/null +++ b/base/strings/substring.jl @@ -0,0 +1,215 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" + SubString(s::AbstractString, i::Integer, j::Integer=lastindex(s)) + SubString(s::AbstractString, r::UnitRange{<:Integer}) + +Like [`getindex`](@ref), but returns a view into the parent string `s` +within range `i:j` or `r` respectively instead of making a copy. + +# Examples +```jldoctest +julia> SubString("abc", 1, 2) +"ab" + +julia> SubString("abc", 1:2) +"ab" + +julia> SubString("abc", 2) +"bc" +``` +""" +struct SubString{T<:AbstractString} <: AbstractString + string::T + offset::Int + ncodeunits::Int + + function SubString{T}(s::T, i::Int, j::Int) where T<:AbstractString + i ≤ j || return new(s, 0, 0) + @boundscheck begin + checkbounds(s, i:j) + @inbounds isvalid(s, i) || string_index_err(s, i) + @inbounds isvalid(s, j) || string_index_err(s, j) + end + return new(s, i-1, nextind(s,j)-i) + end +end + +@propagate_inbounds SubString(s::T, i::Int, j::Int) where {T<:AbstractString} = SubString{T}(s, i, j) +@propagate_inbounds SubString(s::AbstractString, i::Integer, j::Integer=lastindex(s)) = SubString(s, Int(i), Int(j)) +@propagate_inbounds SubString(s::AbstractString, r::UnitRange{<:Integer}) = SubString(s, first(r), last(r)) + +@propagate_inbounds function SubString(s::SubString, i::Int, j::Int) + @boundscheck i ≤ j && checkbounds(s, i:j) + SubString(s.string, s.offset+i, s.offset+j) +end + +SubString(s::AbstractString) = SubString(s, 1, lastindex(s)) +SubString{T}(s::T) where {T<:AbstractString} = SubString{T}(s, 1, lastindex(s)) + +convert(::Type{SubString{S}}, s::AbstractString) where {S<:AbstractString} = + SubString(convert(S, s)) +convert(::Type{T}, s::T) where {T<:SubString} = s + +function String(s::SubString{String}) + parent = s.string + copy = GC.@preserve parent unsafe_string(pointer(parent, s.offset+1), s.ncodeunits) + return copy +end + +ncodeunits(s::SubString) = s.ncodeunits +codeunit(s::SubString) = codeunit(s.string) +length(s::SubString) = length(s.string, s.offset+1, s.offset+s.ncodeunits) + +function codeunit(s::SubString, i::Integer) + @boundscheck checkbounds(s, i) + @inbounds return codeunit(s.string, s.offset + i) +end + +function iterate(s::SubString, i::Integer=firstindex(s)) + i == ncodeunits(s)+1 && return nothing + @boundscheck checkbounds(s, i) + y = iterate(s.string, s.offset + i) + y === nothing && return nothing + c, i = y + return c, i - s.offset +end + +function getindex(s::SubString, i::Integer) + @boundscheck checkbounds(s, i) + @inbounds return getindex(s.string, s.offset + i) +end + +function isvalid(s::SubString, i::Integer) + ib = true + @boundscheck ib = checkbounds(Bool, s, i) + @inbounds return ib && isvalid(s.string, s.offset + i) +end + +byte_string_classify(s::SubString{String}) = + ccall(:u8_isvalid, Int32, (Ptr{UInt8}, Int), s, sizeof(s)) + +isvalid(::Type{String}, s::SubString{String}) = byte_string_classify(s) ≠ 0 +isvalid(s::SubString{String}) = isvalid(String, s) + +thisind(s::SubString{String}, i::Int) = _thisind_str(s, i) +nextind(s::SubString{String}, i::Int) = _nextind_str(s, i) + +function cmp(a::SubString{String}, b::SubString{String}) + na = sizeof(a) + nb = sizeof(b) + c = _memcmp(a, b, min(na, nb)) + return c < 0 ? -1 : c > 0 ? +1 : cmp(na, nb) +end + +# don't make unnecessary copies when passing substrings to C functions +cconvert(::Type{Ptr{UInt8}}, s::SubString{String}) = s +cconvert(::Type{Ptr{Int8}}, s::SubString{String}) = s + +function unsafe_convert(::Type{Ptr{R}}, s::SubString{String}) where R<:Union{Int8, UInt8} + convert(Ptr{R}, pointer(s.string)) + s.offset +end + +pointer(x::SubString{String}) = pointer(x.string) + x.offset +pointer(x::SubString{String}, i::Integer) = pointer(x.string) + x.offset + (i-1) + +""" + reverse(s::AbstractString) -> AbstractString + +Reverses a string. Technically, this function reverses the codepoints in a string and its +main utility is for reversed-order string processing, especially for reversed +regular-expression searches. See also [`reverseind`](@ref) to convert indices in `s` to +indices in `reverse(s)` and vice-versa, and `graphemes` from module `Unicode` to +operate on user-visible "characters" (graphemes) rather than codepoints. +See also [`Iterators.reverse`](@ref) for +reverse-order iteration without making a copy. Custom string types must implement the +`reverse` function themselves and should typically return a string with the same type +and encoding. If they return a string with a different encoding, they must also override +`reverseind` for that string type to satisfy `s[reverseind(s,i)] == reverse(s)[i]`. + +# Examples +```jldoctest +julia> reverse("JuliaLang") +"gnaLailuJ" + +julia> reverse("ax̂e") # combining characters can lead to surprising results +"êxa" + +julia> using Unicode + +julia> join(reverse(collect(graphemes("ax̂e")))) # reverses graphemes +"ex̂a" +``` +""" +function reverse(s::Union{String,SubString{String}})::String + # Read characters forwards from `s` and write backwards to `out` + out = _string_n(sizeof(s)) + offs = sizeof(s) + 1 + for c in s + offs -= ncodeunits(c) + __unsafe_string!(out, c, offs) + end + return out +end + +string(a::String) = String(a) +string(a::SubString{String}) = String(a) + +@inline function __unsafe_string!(out, c::Char, offs::Integer) # out is a (new) String (or StringVector) + x = bswap(reinterpret(UInt32, c)) + n = ncodeunits(c) + GC.@preserve out begin + unsafe_store!(pointer(out, offs), x % UInt8) + n == 1 && return n + x >>= 8 + unsafe_store!(pointer(out, offs+1), x % UInt8) + n == 2 && return n + x >>= 8 + unsafe_store!(pointer(out, offs+2), x % UInt8) + n == 3 && return n + x >>= 8 + unsafe_store!(pointer(out, offs+3), x % UInt8) + end + return n +end + +@inline function __unsafe_string!(out, s::Union{String, SubString{String}}, offs::Integer) + n = sizeof(s) + GC.@preserve s out unsafe_copyto!(pointer(out, offs), pointer(s), n) + return n +end + +function string(a::Union{Char, String, SubString{String}}...) + n = 0 + for v in a + if v isa Char + n += ncodeunits(v) + else + n += sizeof(v) + end + end + out = _string_n(n) + offs = 1 + for v in a + offs += __unsafe_string!(out, v, offs) + end + return out +end + +function repeat(s::Union{String, SubString{String}}, r::Integer) + r < 0 && throw(ArgumentError("can't repeat a string $r times")) + r == 1 && return String(s) + n = sizeof(s) + out = _string_n(n*r) + if n == 1 # common case: repeating a single-byte string + @inbounds b = codeunit(s, 1) + ccall(:memset, Ptr{Cvoid}, (Ptr{UInt8}, Cint, Csize_t), out, b, r) + else + for i = 0:r-1 + GC.@preserve s out unsafe_copyto!(pointer(out, i*n+1), pointer(s), n) + end + end + return out +end + +getindex(s::AbstractString, r::UnitRange{<:Integer}) = SubString(s, r) diff --git a/base/strings/unicode.jl b/base/strings/unicode.jl new file mode 100644 index 0000000..5d5bc93 --- /dev/null +++ b/base/strings/unicode.jl @@ -0,0 +1,684 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Various Unicode functionality from the utf8proc library +module Unicode + +import Base: show, ==, hash, string, Symbol, isless, length, eltype, + convert, isvalid, ismalformed, isoverlong, iterate + +# whether codepoints are valid Unicode scalar values, i.e. 0-0xd7ff, 0xe000-0x10ffff + +""" + isvalid(value) -> Bool + +Returns `true` if the given value is valid for its type, which currently can be either +`AbstractChar` or `String` or `SubString{String}`. + +# Examples +```jldoctest +julia> isvalid(Char(0xd800)) +false + +julia> isvalid(SubString(String(UInt8[0xfe,0x80,0x80,0x80,0x80,0x80]),1,2)) +false + +julia> isvalid(Char(0xd799)) +true +``` +""" +isvalid(value) + +""" + isvalid(T, value) -> Bool + +Returns `true` if the given value is valid for that type. Types currently can +be either `AbstractChar` or `String`. Values for `AbstractChar` can be of type `AbstractChar` or [`UInt32`](@ref). +Values for `String` can be of that type, or `Vector{UInt8}` or `SubString{String}`. + +# Examples +```jldoctest +julia> isvalid(Char, 0xd800) +false + +julia> isvalid(String, SubString("thisisvalid",1,5)) +true + +julia> isvalid(Char, 0xd799) +true +``` +""" +isvalid(T,value) + +isvalid(c::AbstractChar) = !ismalformed(c) & !isoverlong(c) & ((c ≤ '\ud7ff') | ('\ue000' ≤ c) & (c ≤ '\U10ffff')) +isvalid(::Type{<:AbstractChar}, c::Unsigned) = ((c ≤ 0xd7ff ) | ( 0xe000 ≤ c) & (c ≤ 0x10ffff )) +isvalid(::Type{T}, c::Integer) where {T<:AbstractChar} = isvalid(T, Unsigned(c)) +isvalid(::Type{<:AbstractChar}, c::AbstractChar) = isvalid(c) + +# utf8 category constants +const UTF8PROC_CATEGORY_CN = 0 +const UTF8PROC_CATEGORY_LU = 1 +const UTF8PROC_CATEGORY_LL = 2 +const UTF8PROC_CATEGORY_LT = 3 +const UTF8PROC_CATEGORY_LM = 4 +const UTF8PROC_CATEGORY_LO = 5 +const UTF8PROC_CATEGORY_MN = 6 +const UTF8PROC_CATEGORY_MC = 7 +const UTF8PROC_CATEGORY_ME = 8 +const UTF8PROC_CATEGORY_ND = 9 +const UTF8PROC_CATEGORY_NL = 10 +const UTF8PROC_CATEGORY_NO = 11 +const UTF8PROC_CATEGORY_PC = 12 +const UTF8PROC_CATEGORY_PD = 13 +const UTF8PROC_CATEGORY_PS = 14 +const UTF8PROC_CATEGORY_PE = 15 +const UTF8PROC_CATEGORY_PI = 16 +const UTF8PROC_CATEGORY_PF = 17 +const UTF8PROC_CATEGORY_PO = 18 +const UTF8PROC_CATEGORY_SM = 19 +const UTF8PROC_CATEGORY_SC = 20 +const UTF8PROC_CATEGORY_SK = 21 +const UTF8PROC_CATEGORY_SO = 22 +const UTF8PROC_CATEGORY_ZS = 23 +const UTF8PROC_CATEGORY_ZL = 24 +const UTF8PROC_CATEGORY_ZP = 25 +const UTF8PROC_CATEGORY_CC = 26 +const UTF8PROC_CATEGORY_CF = 27 +const UTF8PROC_CATEGORY_CS = 28 +const UTF8PROC_CATEGORY_CO = 29 + +# strings corresponding to the category constants +const category_strings = [ + "Other, not assigned", + "Letter, uppercase", + "Letter, lowercase", + "Letter, titlecase", + "Letter, modifier", + "Letter, other", + "Mark, nonspacing", + "Mark, spacing combining", + "Mark, enclosing", + "Number, decimal digit", + "Number, letter", + "Number, other", + "Punctuation, connector", + "Punctuation, dash", + "Punctuation, open", + "Punctuation, close", + "Punctuation, initial quote", + "Punctuation, final quote", + "Punctuation, other", + "Symbol, math", + "Symbol, currency", + "Symbol, modifier", + "Symbol, other", + "Separator, space", + "Separator, line", + "Separator, paragraph", + "Other, control", + "Other, format", + "Other, surrogate", + "Other, private use", + "Invalid, too high", + "Malformed, bad data", +] + +const UTF8PROC_STABLE = (1<<1) +const UTF8PROC_COMPAT = (1<<2) +const UTF8PROC_COMPOSE = (1<<3) +const UTF8PROC_DECOMPOSE = (1<<4) +const UTF8PROC_IGNORE = (1<<5) +const UTF8PROC_REJECTNA = (1<<6) +const UTF8PROC_NLF2LS = (1<<7) +const UTF8PROC_NLF2PS = (1<<8) +const UTF8PROC_NLF2LF = (UTF8PROC_NLF2LS | UTF8PROC_NLF2PS) +const UTF8PROC_STRIPCC = (1<<9) +const UTF8PROC_CASEFOLD = (1<<10) +const UTF8PROC_CHARBOUND = (1<<11) +const UTF8PROC_LUMP = (1<<12) +const UTF8PROC_STRIPMARK = (1<<13) + +############################################################################ + +utf8proc_error(result) = error(unsafe_string(ccall(:utf8proc_errmsg, Cstring, (Cssize_t,), result))) + +function utf8proc_map(str::String, options::Integer) + nwords = ccall(:utf8proc_decompose, Int, (Ptr{UInt8}, Int, Ptr{UInt8}, Int, Cint), + str, sizeof(str), C_NULL, 0, options) + nwords < 0 && utf8proc_error(nwords) + buffer = Base.StringVector(nwords*4) + nwords = ccall(:utf8proc_decompose, Int, (Ptr{UInt8}, Int, Ptr{UInt8}, Int, Cint), + str, sizeof(str), buffer, nwords, options) + nwords < 0 && utf8proc_error(nwords) + nbytes = ccall(:utf8proc_reencode, Int, (Ptr{UInt8}, Int, Cint), buffer, nwords, options) + nbytes < 0 && utf8proc_error(nbytes) + return String(resize!(buffer, nbytes)) +end + +utf8proc_map(s::AbstractString, flags::Integer) = utf8proc_map(String(s), flags) + +# Documented in Unicode module +function normalize( + s::AbstractString; + stable::Bool=false, + compat::Bool=false, + compose::Bool=true, + decompose::Bool=false, + stripignore::Bool=false, + rejectna::Bool=false, + newline2ls::Bool=false, + newline2ps::Bool=false, + newline2lf::Bool=false, + stripcc::Bool=false, + casefold::Bool=false, + lump::Bool=false, + stripmark::Bool=false, +) + flags = 0 + stable && (flags = flags | UTF8PROC_STABLE) + compat && (flags = flags | UTF8PROC_COMPAT) + # TODO: error if compose & decompose? + if decompose + flags = flags | UTF8PROC_DECOMPOSE + elseif compose + flags = flags | UTF8PROC_COMPOSE + elseif compat || stripmark + throw(ArgumentError("compat=true or stripmark=true require compose=true or decompose=true")) + end + stripignore && (flags = flags | UTF8PROC_IGNORE) + rejectna && (flags = flags | UTF8PROC_REJECTNA) + newline2ls + newline2ps + newline2lf > 1 && throw(ArgumentError("only one newline conversion may be specified")) + newline2ls && (flags = flags | UTF8PROC_NLF2LS) + newline2ps && (flags = flags | UTF8PROC_NLF2PS) + newline2lf && (flags = flags | UTF8PROC_NLF2LF) + stripcc && (flags = flags | UTF8PROC_STRIPCC) + casefold && (flags = flags | UTF8PROC_CASEFOLD) + lump && (flags = flags | UTF8PROC_LUMP) + stripmark && (flags = flags | UTF8PROC_STRIPMARK) + utf8proc_map(s, flags) +end + +function normalize(s::AbstractString, nf::Symbol) + utf8proc_map(s, nf === :NFC ? (UTF8PROC_STABLE | UTF8PROC_COMPOSE) : + nf === :NFD ? (UTF8PROC_STABLE | UTF8PROC_DECOMPOSE) : + nf === :NFKC ? (UTF8PROC_STABLE | UTF8PROC_COMPOSE + | UTF8PROC_COMPAT) : + nf === :NFKD ? (UTF8PROC_STABLE | UTF8PROC_DECOMPOSE + | UTF8PROC_COMPAT) : + throw(ArgumentError(":$nf is not one of :NFC, :NFD, :NFKC, :NFKD"))) +end + +############################################################################ + +## character column width function ## +""" + textwidth(c) + +Give the number of columns needed to print a character. + +# Examples +```jldoctest +julia> textwidth('α') +1 + +julia> textwidth('⛵') +2 +``` +""" +function textwidth(c::AbstractChar) + ismalformed(c) && return 1 + Int(ccall(:utf8proc_charwidth, Cint, (UInt32,), c)) +end + +""" + textwidth(s::AbstractString) + +Give the number of columns needed to print a string. + +# Examples +```jldoctest +julia> textwidth("March") +5 +``` +""" +textwidth(s::AbstractString) = mapreduce(textwidth, +, s; init=0) + +lowercase(c::T) where {T<:AbstractChar} = isascii(c) ? ('A' <= c <= 'Z' ? c + 0x20 : c) : + T(ccall(:utf8proc_tolower, UInt32, (UInt32,), c)) +uppercase(c::T) where {T<:AbstractChar} = isascii(c) ? ('a' <= c <= 'z' ? c - 0x20 : c) : + T(ccall(:utf8proc_toupper, UInt32, (UInt32,), c)) +titlecase(c::T) where {T<:AbstractChar} = isascii(c) ? ('a' <= c <= 'z' ? c - 0x20 : c) : + T(ccall(:utf8proc_totitle, UInt32, (UInt32,), c)) + +############################################################################ + +# returns UTF8PROC_CATEGORY code in 0:30 giving Unicode category +function category_code(c::AbstractChar) + !ismalformed(c) ? category_code(UInt32(c)) : Cint(31) +end + +function category_code(x::Integer) + x ≤ 0x10ffff ? ccall(:utf8proc_category, Cint, (UInt32,), x) : Cint(30) +end + +# more human-readable representations of the category code +function category_abbrev(c::AbstractChar) + ismalformed(c) && return "Ma" + c ≤ '\U10ffff' || return "In" + unsafe_string(ccall(:utf8proc_category_string, Cstring, (UInt32,), c)) +end + +category_string(c) = category_strings[category_code(c)+1] + +isassigned(c) = UTF8PROC_CATEGORY_CN < category_code(c) <= UTF8PROC_CATEGORY_CO + +## libc character class predicates ## + +""" + islowercase(c::AbstractChar) -> Bool + +Tests whether a character is a lowercase letter. +A character is classified as lowercase if it belongs to Unicode category Ll, +Letter: Lowercase. + +# Examples +```jldoctest +julia> islowercase('α') +true + +julia> islowercase('Γ') +false + +julia> islowercase('❤') +false +``` +""" +islowercase(c::AbstractChar) = category_code(c) == UTF8PROC_CATEGORY_LL + +# true for Unicode upper and mixed case + +""" + isuppercase(c::AbstractChar) -> Bool + +Tests whether a character is an uppercase letter. +A character is classified as uppercase if it belongs to Unicode category Lu, +Letter: Uppercase, or Lt, Letter: Titlecase. + +# Examples +```jldoctest +julia> isuppercase('γ') +false + +julia> isuppercase('Γ') +true + +julia> isuppercase('❤') +false +``` +""" +function isuppercase(c::AbstractChar) + cat = category_code(c) + cat == UTF8PROC_CATEGORY_LU || cat == UTF8PROC_CATEGORY_LT +end + +""" + iscased(c::AbstractChar) -> Bool + +Tests whether a character is cased, i.e. is lower-, upper- or title-cased. +""" +function iscased(c::AbstractChar) + cat = category_code(c) + return cat == UTF8PROC_CATEGORY_LU || + cat == UTF8PROC_CATEGORY_LT || + cat == UTF8PROC_CATEGORY_LL +end + + +""" + isdigit(c::AbstractChar) -> Bool + +Tests whether a character is a decimal digit (0-9). + +# Examples +```jldoctest +julia> isdigit('❤') +false + +julia> isdigit('9') +true + +julia> isdigit('α') +false +``` +""" +isdigit(c::AbstractChar) = (c >= '0') & (c <= '9') + +""" + isletter(c::AbstractChar) -> Bool + +Test whether a character is a letter. +A character is classified as a letter if it belongs to the Unicode general +category Letter, i.e. a character whose category code begins with 'L'. + +# Examples +```jldoctest +julia> isletter('❤') +false + +julia> isletter('α') +true + +julia> isletter('9') +false +``` +""" +isletter(c::AbstractChar) = UTF8PROC_CATEGORY_LU <= category_code(c) <= UTF8PROC_CATEGORY_LO + +""" + isnumeric(c::AbstractChar) -> Bool + +Tests whether a character is numeric. +A character is classified as numeric if it belongs to the Unicode general category Number, +i.e. a character whose category code begins with 'N'. + +Note that this broad category includes characters such as ¾ and ௰. +Use [`isdigit`](@ref) to check whether a character a decimal digit between 0 and 9. + +# Examples +```jldoctest +julia> isnumeric('௰') +true + +julia> isnumeric('9') +true + +julia> isnumeric('α') +false + +julia> isnumeric('❤') +false +``` +""" +isnumeric(c::AbstractChar) = UTF8PROC_CATEGORY_ND <= category_code(c) <= UTF8PROC_CATEGORY_NO + +# following C++ only control characters from the Latin-1 subset return true + +""" + iscntrl(c::AbstractChar) -> Bool + +Tests whether a character is a control character. +Control characters are the non-printing characters of the Latin-1 subset of Unicode. + +# Examples +```jldoctest +julia> iscntrl('\\x01') +true + +julia> iscntrl('a') +false +``` +""" +iscntrl(c::AbstractChar) = c <= '\x1f' || '\x7f' <= c <= '\u9f' + +""" + ispunct(c::AbstractChar) -> Bool + +Tests whether a character belongs to the Unicode general category Punctuation, i.e. a +character whose category code begins with 'P'. + +# Examples +```jldoctest +julia> ispunct('α') +false + +julia> ispunct('/') +true + +julia> ispunct(';') +true +``` +""" +ispunct(c::AbstractChar) = UTF8PROC_CATEGORY_PC <= category_code(c) <= UTF8PROC_CATEGORY_PO + +# \u85 is the Unicode Next Line (NEL) character + +""" + isspace(c::AbstractChar) -> Bool + +Tests whether a character is any whitespace character. Includes ASCII characters '\\t', +'\\n', '\\v', '\\f', '\\r', and ' ', Latin-1 character U+0085, and characters in Unicode +category Zs. + +# Examples +```jldoctest +julia> isspace('\\n') +true + +julia> isspace('\\r') +true + +julia> isspace(' ') +true + +julia> isspace('\\x20') +true +``` +""" +@inline isspace(c::AbstractChar) = + c == ' ' || '\t' <= c <= '\r' || c == '\u85' || + '\ua0' <= c && category_code(c) == UTF8PROC_CATEGORY_ZS + +""" + isprint(c::AbstractChar) -> Bool + +Tests whether a character is printable, including spaces, but not a control character. + +# Examples +```jldoctest +julia> isprint('\\x01') +false + +julia> isprint('A') +true +``` +""" +isprint(c::AbstractChar) = UTF8PROC_CATEGORY_LU <= category_code(c) <= UTF8PROC_CATEGORY_ZS + +# true in principal if a printer would use ink + +""" + isxdigit(c::AbstractChar) -> Bool + +Test whether a character is a valid hexadecimal digit. Note that this does not +include `x` (as in the standard `0x` prefix). + +# Examples +```jldoctest +julia> isxdigit('a') +true + +julia> isxdigit('x') +false +``` +""" +isxdigit(c::AbstractChar) = '0'<=c<='9' || 'a'<=c<='f' || 'A'<=c<='F' + +## uppercase, lowercase, and titlecase transformations ## + +""" + uppercase(s::AbstractString) + +Return `s` with all characters converted to uppercase. + +# Examples +```jldoctest +julia> uppercase("Julia") +"JULIA" +``` +""" +uppercase(s::AbstractString) = map(uppercase, s) + +""" + lowercase(s::AbstractString) + +Return `s` with all characters converted to lowercase. + +# Examples +```jldoctest +julia> lowercase("STRINGS AND THINGS") +"strings and things" +``` +""" +lowercase(s::AbstractString) = map(lowercase, s) + +""" + titlecase(s::AbstractString; [wordsep::Function], strict::Bool=true) -> String + +Capitalize the first character of each word in `s`; +if `strict` is true, every other character is +converted to lowercase, otherwise they are left unchanged. +By default, all non-letters are considered as word separators; +a predicate can be passed as the `wordsep` keyword to determine +which characters should be considered as word separators. +See also [`uppercasefirst`](@ref) to capitalize only the first +character in `s`. + +# Examples +```jldoctest +julia> titlecase("the JULIA programming language") +"The Julia Programming Language" + +julia> titlecase("ISS - international space station", strict=false) +"ISS - International Space Station" + +julia> titlecase("a-a b-b", wordsep = c->c==' ') +"A-a B-b" +``` +""" +function titlecase(s::AbstractString; wordsep::Function = !iscased, strict::Bool=true) + startword = true + b = IOBuffer() + for c in s + if wordsep(c) + print(b, c) + startword = true + else + print(b, startword ? titlecase(c) : strict ? lowercase(c) : c) + startword = false + end + end + return String(take!(b)) +end + +""" + uppercasefirst(s::AbstractString) -> String + +Return `s` with the first character converted to uppercase (technically "title +case" for Unicode). See also [`titlecase`](@ref) to capitalize the first +character of every word in `s`. + +See also: [`lowercasefirst`](@ref), [`uppercase`](@ref), [`lowercase`](@ref), +[`titlecase`](@ref) + +# Examples +```jldoctest +julia> uppercasefirst("python") +"Python" +``` +""" +function uppercasefirst(s::AbstractString) + isempty(s) && return "" + c = s[1] + c′ = titlecase(c) + c == c′ ? convert(String, s) : + string(c′, SubString(s, nextind(s, 1))) +end + +""" + lowercasefirst(s::AbstractString) + +Return `s` with the first character converted to lowercase. + +See also: [`uppercasefirst`](@ref), [`uppercase`](@ref), [`lowercase`](@ref), +[`titlecase`](@ref) + +# Examples +```jldoctest +julia> lowercasefirst("Julia") +"julia" +``` +""" +function lowercasefirst(s::AbstractString) + isempty(s) && return "" + c = s[1] + c′ = lowercase(c) + c == c′ ? convert(String, s) : + string(c′, SubString(s, nextind(s, 1))) +end + +############################################################################ +# iterators for grapheme segmentation + +isgraphemebreak(c1::AbstractChar, c2::AbstractChar) = + ismalformed(c1) || ismalformed(c2) || + ccall(:utf8proc_grapheme_break, Bool, (UInt32, UInt32), c1, c2) + +# Stateful grapheme break required by Unicode-9 rules: the string +# must be processed in sequence, with state initialized to Ref{Int32}(0). +# Requires utf8proc v2.0 or later. +function isgraphemebreak!(state::Ref{Int32}, c1::AbstractChar, c2::AbstractChar) + if ismalformed(c1) || ismalformed(c2) + state[] = 0 + return true + end + ccall(:utf8proc_grapheme_break_stateful, Bool, + (UInt32, UInt32, Ref{Int32}), c1, c2, state) +end + +struct GraphemeIterator{S<:AbstractString} + s::S # original string (for generation of SubStrings) +end + +# Documented in Unicode module +graphemes(s::AbstractString) = GraphemeIterator{typeof(s)}(s) + +eltype(::Type{GraphemeIterator{S}}) where {S} = SubString{S} +eltype(::Type{GraphemeIterator{SubString{S}}}) where {S} = SubString{S} + +function length(g::GraphemeIterator{S}) where {S} + c0 = eltype(S)(0x00000000) + n = 0 + state = Ref{Int32}(0) + for c in g.s + n += isgraphemebreak!(state, c0, c) + c0 = c + end + return n +end + +function iterate(g::GraphemeIterator, i_=(Int32(0),firstindex(g.s))) + s = g.s + statei, i = i_ + state = Ref{Int32}(statei) + j = i + y = iterate(s, i) + y === nothing && return nothing + c0, k = y + while k <= ncodeunits(s) # loop until next grapheme is s[i:j] + c, ℓ = iterate(s, k) + isgraphemebreak!(state, c0, c) && break + j = k + k = ℓ + c0 = c + end + return (SubString(s, i, j), (state[], k)) +end + +==(g1::GraphemeIterator, g2::GraphemeIterator) = g1.s == g2.s +hash(g::GraphemeIterator, h::UInt) = hash(g.s, h) +isless(g1::GraphemeIterator, g2::GraphemeIterator) = isless(g1.s, g2.s) + +show(io::IO, g::GraphemeIterator{S}) where {S} = print(io, "length-$(length(g)) GraphemeIterator{$S} for \"$(g.s)\"") + +############################################################################ + +end # module diff --git a/base/strings/util.jl b/base/strings/util.jl new file mode 100644 index 0000000..1d0acde --- /dev/null +++ b/base/strings/util.jl @@ -0,0 +1,636 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +const Chars = Union{AbstractChar,Tuple{Vararg{<:AbstractChar}},AbstractVector{<:AbstractChar},Set{<:AbstractChar}} + +# starts with and ends with predicates + +""" + startswith(s::AbstractString, prefix::AbstractString) + +Return `true` if `s` starts with `prefix`. If `prefix` is a vector or set +of characters, test whether the first character of `s` belongs to that set. + +See also [`endswith`](@ref). + +# Examples +```jldoctest +julia> startswith("JuliaLang", "Julia") +true +``` +""" +function startswith(a::AbstractString, b::AbstractString) + a, b = Iterators.Stateful(a), Iterators.Stateful(b) + all(splat(==), zip(a, b)) && isempty(b) +end +startswith(str::AbstractString, chars::Chars) = !isempty(str) && first(str) in chars + +""" + endswith(s::AbstractString, suffix::AbstractString) + +Return `true` if `s` ends with `suffix`. If `suffix` is a vector or set of +characters, test whether the last character of `s` belongs to that set. + +See also [`startswith`](@ref). + +# Examples +```jldoctest +julia> endswith("Sunday", "day") +true +``` +""" +function endswith(a::AbstractString, b::AbstractString) + a = Iterators.Stateful(Iterators.reverse(a)) + b = Iterators.Stateful(Iterators.reverse(b)) + all(splat(==), zip(a, b)) && isempty(b) +end +endswith(str::AbstractString, chars::Chars) = !isempty(str) && last(str) in chars + +function startswith(a::Union{String, SubString{String}}, + b::Union{String, SubString{String}}) + cub = ncodeunits(b) + if ncodeunits(a) < cub + false + elseif _memcmp(a, b, sizeof(b)) == 0 + nextind(a, cub) == cub + 1 + else + false + end +end + +function endswith(a::Union{String, SubString{String}}, + b::Union{String, SubString{String}}) + cub = ncodeunits(b) + astart = ncodeunits(a) - ncodeunits(b) + 1 + if astart < 1 + false + elseif GC.@preserve(a, _memcmp(pointer(a, astart), b, sizeof(b))) == 0 + thisind(a, astart) == astart + else + false + end +end + +""" + chop(s::AbstractString; head::Integer = 0, tail::Integer = 1) + +Remove the first `head` and the last `tail` characters from `s`. +The call `chop(s)` removes the last character from `s`. +If it is requested to remove more characters than `length(s)` +then an empty string is returned. + +# Examples +```jldoctest +julia> a = "March" +"March" + +julia> chop(a) +"Marc" + +julia> chop(a, head = 1, tail = 2) +"ar" + +julia> chop(a, head = 5, tail = 5) +"" +``` +""" +function chop(s::AbstractString; head::Integer = 0, tail::Integer = 1) + if isempty(s) + return SubString(s) + end + SubString(s, nextind(s, firstindex(s), head), prevind(s, lastindex(s), tail)) +end + +# TODO: optimization for the default case based on +# chop(s::AbstractString) = SubString(s, firstindex(s), prevind(s, lastindex(s))) + +""" + chomp(s::AbstractString) + +Remove a single trailing newline from a string. + +# Examples +```jldoctest +julia> chomp("Hello\\n") +"Hello" +``` +""" +function chomp(s::AbstractString) + i = lastindex(s) + (i < 1 || s[i] != '\n') && (return SubString(s, 1, i)) + j = prevind(s,i) + (j < 1 || s[j] != '\r') && (return SubString(s, 1, j)) + return SubString(s, 1, prevind(s,j)) +end +function chomp(s::String) + i = lastindex(s) + if i < 1 || codeunit(s,i) != 0x0a + return @inbounds SubString(s, 1, i) + elseif i < 2 || codeunit(s,i-1) != 0x0d + return @inbounds SubString(s, 1, prevind(s, i)) + else + return @inbounds SubString(s, 1, prevind(s, i-1)) + end +end + +""" + lstrip([pred=isspace,] str::AbstractString) + lstrip(str::AbstractString, chars) + +Remove leading characters from `str`, either those specified by `chars` or those for +which the function `pred` returns `true`. + +The default behaviour is to remove leading whitespace and delimiters: see +[`isspace`](@ref) for precise details. + +The optional `chars` argument specifies which characters to remove: it can be a single +character, or a vector or set of characters. + +# Examples +```jldoctest +julia> a = lpad("March", 20) +" March" + +julia> lstrip(a) +"March" +``` +""" +function lstrip(f, s::AbstractString) + e = lastindex(s) + for (i, c) in pairs(s) + !f(c) && return @inbounds SubString(s, i, e) + end + SubString(s, e+1, e) +end +lstrip(s::AbstractString) = lstrip(isspace, s) +lstrip(s::AbstractString, chars::Chars) = lstrip(in(chars), s) + +""" + rstrip([pred=isspace,] str::AbstractString) + rstrip(str::AbstractString, chars) + +Remove trailing characters from `str`, either those specified by `chars` or those for +which the function `pred` returns `true`. + +The default behaviour is to remove trailing whitespace and delimiters: see +[`isspace`](@ref) for precise details. + +The optional `chars` argument specifies which characters to remove: it can be a single +character, or a vector or set of characters. + +# Examples +```jldoctest +julia> a = rpad("March", 20) +"March " + +julia> rstrip(a) +"March" +``` +""" +function rstrip(f, s::AbstractString) + for (i, c) in Iterators.reverse(pairs(s)) + f(c) || return @inbounds SubString(s, 1, i) + end + SubString(s, 1, 0) +end +rstrip(s::AbstractString) = rstrip(isspace, s) +rstrip(s::AbstractString, chars::Chars) = rstrip(in(chars), s) + +""" + strip([pred=isspace,] str::AbstractString) + strip(str::AbstractString, chars) + +Remove leading and trailing characters from `str`, either those specified by `chars` or +those for which the function `pred` returns `true`. + +The default behaviour is to remove leading whitespace and delimiters: see +[`isspace`](@ref) for precise details. + +The optional `chars` argument specifies which characters to remove: it can be a single +character, vector or set of characters. + +!!! compat "Julia 1.2" + The method which accepts a predicate function requires Julia 1.2 or later. + +# Examples +```jldoctest +julia> strip("{3, 5}\\n", ['{', '}', '\\n']) +"3, 5" +``` +""" +strip(s::AbstractString) = lstrip(rstrip(s)) +strip(s::AbstractString, chars::Chars) = lstrip(rstrip(s, chars), chars) +strip(f, s::AbstractString) = lstrip(f, rstrip(f, s)) + +## string padding functions ## + +""" + lpad(s, n::Integer, p::Union{AbstractChar,AbstractString}=' ') -> String + +Stringify `s` and pad the resulting string on the left with `p` to make it `n` +characters (code points) long. If `s` is already `n` characters long, an equal +string is returned. Pad with spaces by default. + +# Examples +```jldoctest +julia> lpad("March", 10) +" March" +``` +""" +lpad(s, n::Integer, p::Union{AbstractChar,AbstractString}=' ') = lpad(string(s), n, string(p)) + +function lpad( + s::Union{AbstractChar,AbstractString}, + n::Integer, + p::Union{AbstractChar,AbstractString}=' ', +) :: String + m = signed(n) - length(s) + m ≤ 0 && return string(s) + l = length(p) + q, r = divrem(m, l) + r == 0 ? string(p^q, s) : string(p^q, first(p, r), s) +end + +""" + rpad(s, n::Integer, p::Union{AbstractChar,AbstractString}=' ') -> String + +Stringify `s` and pad the resulting string on the right with `p` to make it `n` +characters (code points) long. If `s` is already `n` characters long, an equal +string is returned. Pad with spaces by default. + +# Examples +```jldoctest +julia> rpad("March", 20) +"March " +``` +""" +rpad(s, n::Integer, p::Union{AbstractChar,AbstractString}=' ') = rpad(string(s), n, string(p)) + +function rpad( + s::Union{AbstractChar,AbstractString}, + n::Integer, + p::Union{AbstractChar,AbstractString}=' ', +) :: String + m = signed(n) - length(s) + m ≤ 0 && return string(s) + l = length(p) + q, r = divrem(m, l) + r == 0 ? string(s, p^q) : string(s, p^q, first(p, r)) +end + +""" + split(str::AbstractString, dlm; limit::Integer=0, keepempty::Bool=true) + split(str::AbstractString; limit::Integer=0, keepempty::Bool=false) + +Split `str` into an array of substrings on occurrences of the delimiter(s) `dlm`. `dlm` +can be any of the formats allowed by [`findnext`](@ref)'s first argument (i.e. as a +string, regular expression or a function), or as a single character or collection of +characters. + +If `dlm` is omitted, it defaults to [`isspace`](@ref). + +The optional keyword arguments are: + - `limit`: the maximum size of the result. `limit=0` implies no maximum (default) + - `keepempty`: whether empty fields should be kept in the result. Default is `false` without + a `dlm` argument, `true` with a `dlm` argument. + +See also [`rsplit`](@ref). + +# Examples +```jldoctest +julia> a = "Ma.rch" +"Ma.rch" + +julia> split(a,".") +2-element Array{SubString{String},1}: + "Ma" + "rch" +``` +""" +function split end + +function split(str::T, splitter; + limit::Integer=0, keepempty::Bool=true) where {T<:AbstractString} + _split(str, splitter, limit, keepempty, T <: SubString ? T[] : SubString{T}[]) +end +function split(str::T, splitter::Union{Tuple{Vararg{<:AbstractChar}},AbstractVector{<:AbstractChar},Set{<:AbstractChar}}; + limit::Integer=0, keepempty::Bool=true) where {T<:AbstractString} + _split(str, in(splitter), limit, keepempty, T <: SubString ? T[] : SubString{T}[]) +end +function split(str::T, splitter::AbstractChar; + limit::Integer=0, keepempty::Bool=true) where {T<:AbstractString} + _split(str, isequal(splitter), limit, keepempty, T <: SubString ? T[] : SubString{T}[]) +end + +function _split(str::AbstractString, splitter, limit::Integer, keepempty::Bool, strs::Array) + i = 1 # firstindex(str) + n = lastindex(str) + r = something(findfirst(splitter,str), 0) + if r != 0:-1 + j, k = first(r), nextind(str,last(r)) + while 0 < j <= n && length(strs) != limit-1 + if i < k + if keepempty || i < j + push!(strs, @inbounds SubString(str,i,prevind(str,j))) + end + i = k + end + (k <= j) && (k = nextind(str,j)) + r = something(findnext(splitter,str,k), 0) + r == 0:-1 && break + j, k = first(r), nextind(str,last(r)) + end + end + if keepempty || i <= ncodeunits(str) + push!(strs, @inbounds SubString(str,i)) + end + return strs +end + +# a bit oddball, but standard behavior in Perl, Ruby & Python: +split(str::AbstractString; + limit::Integer=0, keepempty::Bool=false) = + split(str, isspace; limit=limit, keepempty=keepempty) + +""" + rsplit(s::AbstractString; limit::Integer=0, keepempty::Bool=false) + rsplit(s::AbstractString, chars; limit::Integer=0, keepempty::Bool=true) + +Similar to [`split`](@ref), but starting from the end of the string. + +# Examples +```jldoctest +julia> a = "M.a.r.c.h" +"M.a.r.c.h" + +julia> rsplit(a,".") +5-element Array{SubString{String},1}: + "M" + "a" + "r" + "c" + "h" + +julia> rsplit(a,".";limit=1) +1-element Array{SubString{String},1}: + "M.a.r.c.h" + +julia> rsplit(a,".";limit=2) +2-element Array{SubString{String},1}: + "M.a.r.c" + "h" +``` +""" +function rsplit end + +function rsplit(str::T, splitter; + limit::Integer=0, keepempty::Bool=true) where {T<:AbstractString} + _rsplit(str, splitter, limit, keepempty, T <: SubString ? T[] : SubString{T}[]) +end +function rsplit(str::T, splitter::Union{Tuple{Vararg{<:AbstractChar}},AbstractVector{<:AbstractChar},Set{<:AbstractChar}}; + limit::Integer=0, keepempty::Bool=true) where {T<:AbstractString} + _rsplit(str, in(splitter), limit, keepempty, T <: SubString ? T[] : SubString{T}[]) +end +function rsplit(str::T, splitter::AbstractChar; + limit::Integer=0, keepempty::Bool=true) where {T<:AbstractString} + _rsplit(str, isequal(splitter), limit, keepempty, T <: SubString ? T[] : SubString{T}[]) +end + +function _rsplit(str::AbstractString, splitter, limit::Integer, keepempty::Bool, strs::Array) + n = lastindex(str) + r = something(findlast(splitter, str), 0) + j, k = first(r), last(r) + while j > 0 && k > 0 && length(strs) != limit-1 + (keepempty || k < n) && pushfirst!(strs, @inbounds SubString(str,nextind(str,k),n)) + n = prevind(str, j) + r = something(findprev(splitter,str,n), 0) + j, k = first(r), last(r) + end + (keepempty || n > 0) && pushfirst!(strs, SubString(str,1,n)) + return strs +end +rsplit(str::AbstractString; + limit::Integer=0, keepempty::Bool=false) = + rsplit(str, isspace; limit=limit, keepempty=keepempty) + +_replace(io, repl, str, r, pattern) = print(io, repl) +_replace(io, repl::Function, str, r, pattern) = + print(io, repl(SubString(str, first(r), last(r)))) +_replace(io, repl::Function, str, r, pattern::Function) = + print(io, repl(str[first(r)])) + +replace(str::String, pat_repl::Pair{<:AbstractChar}; count::Integer=typemax(Int)) = + replace(str, isequal(first(pat_repl)) => last(pat_repl); count=count) + +replace(str::String, pat_repl::Pair{<:Union{Tuple{Vararg{<:AbstractChar}}, + AbstractVector{<:AbstractChar},Set{<:AbstractChar}}}; + count::Integer=typemax(Int)) = + replace(str, in(first(pat_repl)) => last(pat_repl), count=count) + +_pat_replacer(x) = x +_free_pat_replacer(x) = nothing + +function replace(str::String, pat_repl::Pair; count::Integer=typemax(Int)) + pattern, repl = pat_repl + count == 0 && return str + count < 0 && throw(DomainError(count, "`count` must be non-negative.")) + n = 1 + e = lastindex(str) + i = a = firstindex(str) + pattern = _pat_replacer(pattern) + r = something(findnext(pattern,str,i), 0) + j, k = first(r), last(r) + out = IOBuffer(sizehint=floor(Int, 1.2sizeof(str))) + while j != 0 + if i == a || i <= k + GC.@preserve str unsafe_write(out, pointer(str, i), UInt(j-i)) + _replace(out, repl, str, r, pattern) + end + if k < j + i = j + j > e && break + k = nextind(str, j) + else + i = k = nextind(str, k) + end + r = something(findnext(pattern,str,k), 0) + r == 0:-1 || n == count && break + j, k = first(r), last(r) + n += 1 + end + _free_pat_replacer(pattern) + write(out, SubString(str,i)) + String(take!(out)) +end + +""" + replace(s::AbstractString, pat=>r; [count::Integer]) + +Search for the given pattern `pat` in `s`, and replace each occurrence with `r`. +If `count` is provided, replace at most `count` occurrences. +`pat` may be a single character, a vector or a set of characters, a string, +or a regular expression. +If `r` is a function, each occurrence is replaced with `r(s)` +where `s` is the matched substring (when `pat`is a `Regex` or `AbstractString`) or +character (when `pat` is an `AbstractChar` or a collection of `AbstractChar`). +If `pat` is a regular expression and `r` is a [`SubstitutionString`](@ref), then capture group +references in `r` are replaced with the corresponding matched text. +To remove instances of `pat` from `string`, set `r` to the empty `String` (`""`). + +# Examples +```jldoctest +julia> replace("Python is a programming language.", "Python" => "Julia") +"Julia is a programming language." + +julia> replace("The quick foxes run quickly.", "quick" => "slow", count=1) +"The slow foxes run quickly." + +julia> replace("The quick foxes run quickly.", "quick" => "", count=1) +"The foxes run quickly." + +julia> replace("The quick foxes run quickly.", r"fox(es)?" => s"bus\\1") +"The quick buses run quickly." +``` +""" +replace(s::AbstractString, pat_f::Pair; count=typemax(Int)) = + replace(String(s), pat_f, count=count) + +# TODO: allow transform as the first argument to replace? + +# hex <-> bytes conversion + +""" + hex2bytes(s::Union{AbstractString,AbstractVector{UInt8}}) + +Given a string or array `s` of ASCII codes for a sequence of hexadecimal digits, returns a +`Vector{UInt8}` of bytes corresponding to the binary representation: each successive pair +of hexadecimal digits in `s` gives the value of one byte in the return vector. + +The length of `s` must be even, and the returned array has half of the length of `s`. +See also [`hex2bytes!`](@ref) for an in-place version, and [`bytes2hex`](@ref) for the inverse. + +# Examples +```jldoctest +julia> s = string(12345, base = 16) +"3039" + +julia> hex2bytes(s) +2-element Array{UInt8,1}: + 0x30 + 0x39 + +julia> a = b"01abEF" +6-element Base.CodeUnits{UInt8,String}: + 0x30 + 0x31 + 0x61 + 0x62 + 0x45 + 0x46 + +julia> hex2bytes(a) +3-element Array{UInt8,1}: + 0x01 + 0xab + 0xef +``` +""" +function hex2bytes end + +hex2bytes(s::AbstractString) = hex2bytes(String(s)) +hex2bytes(s::Union{String,AbstractVector{UInt8}}) = hex2bytes!(Vector{UInt8}(undef, length(s) >> 1), s) + +_firstbyteidx(s::String) = 1 +_firstbyteidx(s::AbstractVector{UInt8}) = first(eachindex(s)) +_lastbyteidx(s::String) = sizeof(s) +_lastbyteidx(s::AbstractVector{UInt8}) = lastindex(s) + +""" + hex2bytes!(d::AbstractVector{UInt8}, s::Union{String,AbstractVector{UInt8}}) + +Convert an array `s` of bytes representing a hexadecimal string to its binary +representation, similar to [`hex2bytes`](@ref) except that the output is written in-place +in `d`. The length of `s` must be exactly twice the length of `d`. +""" +function hex2bytes!(d::AbstractVector{UInt8}, s::Union{String,AbstractVector{UInt8}}) + if 2length(d) != sizeof(s) + isodd(sizeof(s)) && throw(ArgumentError("input hex array must have even length")) + throw(ArgumentError("output array must be half length of input array")) + end + j = first(eachindex(d)) - 1 + for i = _firstbyteidx(s):2:_lastbyteidx(s) + @inbounds d[j += 1] = number_from_hex(_nthbyte(s,i)) << 4 + number_from_hex(_nthbyte(s,i+1)) + end + return d +end + +@inline number_from_hex(c) = + (UInt8('0') <= c <= UInt8('9')) ? c - UInt8('0') : + (UInt8('A') <= c <= UInt8('F')) ? c - (UInt8('A') - 0x0a) : + (UInt8('a') <= c <= UInt8('f')) ? c - (UInt8('a') - 0x0a) : + throw(ArgumentError("byte is not an ASCII hexadecimal digit")) + +""" + bytes2hex(a::AbstractArray{UInt8}) -> String + bytes2hex(io::IO, a::AbstractArray{UInt8}) + +Convert an array `a` of bytes to its hexadecimal string representation, either +returning a `String` via `bytes2hex(a)` or writing the string to an `io` stream +via `bytes2hex(io, a)`. The hexadecimal characters are all lowercase. + +# Examples +```jldoctest +julia> a = string(12345, base = 16) +"3039" + +julia> b = hex2bytes(a) +2-element Array{UInt8,1}: + 0x30 + 0x39 + +julia> bytes2hex(b) +"3039" +``` +""" +function bytes2hex end + +function bytes2hex(a::AbstractArray{UInt8}) + b = Base.StringVector(2*length(a)) + @inbounds for (i, x) in enumerate(a) + b[2i - 1] = hex_chars[1 + x >> 4] + b[2i ] = hex_chars[1 + x & 0xf] + end + return String(b) +end + +bytes2hex(io::IO, a::AbstractArray{UInt8}) = + for x in a + print(io, Char(hex_chars[1 + x >> 4]), Char(hex_chars[1 + x & 0xf])) + end + +# check for pure ASCII-ness +function ascii(s::String) + for i in 1:sizeof(s) + @inbounds codeunit(s, i) < 0x80 || __throw_invalid_ascii(s, i) + end + return s +end +@noinline __throw_invalid_ascii(s::String, i::Int) = throw(ArgumentError("invalid ASCII at index $i in $(repr(s))")) + +""" + ascii(s::AbstractString) + +Convert a string to `String` type and check that it contains only ASCII data, otherwise +throwing an `ArgumentError` indicating the position of the first non-ASCII byte. + +# Examples +```jldoctest +julia> ascii("abcdeγfgh") +ERROR: ArgumentError: invalid ASCII at index 6 in "abcdeγfgh" +Stacktrace: +[...] + +julia> ascii("abcdefgh") +"abcdefgh" +``` +""" +ascii(x::AbstractString) = ascii(String(x)) diff --git a/base/subarray.jl b/base/subarray.jl new file mode 100644 index 0000000..59cc8cb --- /dev/null +++ b/base/subarray.jl @@ -0,0 +1,422 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +abstract type AbstractCartesianIndex{N} end # This is a hacky forward declaration for CartesianIndex +const ViewIndex = Union{Real, AbstractArray} +const ScalarIndex = Real + +# L is true if the view itself supports fast linear indexing +struct SubArray{T,N,P,I,L} <: AbstractArray{T,N} + parent::P + indices::I + offset1::Int # for linear indexing and pointer, only valid when L==true + stride1::Int # used only for linear indexing + function SubArray{T,N,P,I,L}(parent, indices, offset1, stride1) where {T,N,P,I,L} + @_inline_meta + check_parent_index_match(parent, indices) + new(parent, indices, offset1, stride1) + end +end +# Compute the linear indexability of the indices, and combine it with the linear indexing of the parent +function SubArray(parent::AbstractArray, indices::Tuple) + @_inline_meta + SubArray(IndexStyle(viewindexing(indices), IndexStyle(parent)), parent, ensure_indexable(indices), index_dimsum(indices...)) +end +function SubArray(::IndexCartesian, parent::P, indices::I, ::NTuple{N,Any}) where {P,I,N} + @_inline_meta + SubArray{eltype(P), N, P, I, false}(parent, indices, 0, 0) +end +function SubArray(::IndexLinear, parent::P, indices::I, ::NTuple{N,Any}) where {P,I,N} + @_inline_meta + # Compute the stride and offset + stride1 = compute_stride1(parent, indices) + SubArray{eltype(P), N, P, I, true}(parent, indices, compute_offset1(parent, stride1, indices), stride1) +end + +check_parent_index_match(parent, indices) = check_parent_index_match(parent, index_ndims(indices...)) +check_parent_index_match(parent::AbstractArray{T,N}, ::NTuple{N, Bool}) where {T,N} = nothing +check_parent_index_match(parent, ::NTuple{N, Bool}) where {N} = + throw(ArgumentError("number of indices ($N) must match the parent dimensionality ($(ndims(parent)))")) + +# This makes it possible to elide view allocation in cases where the +# view is indexed with a boundscheck but otherwise all its uses +# are inlined +@inline Base.throw_boundserror(A::SubArray, I) = + __subarray_throw_boundserror(typeof(A), A.parent, A.indices, A.offset1, A.stride1, I) +@noinline __subarray_throw_boundserror(::Type{T}, parent, indices, offset1, stride1, I) where {T} = + throw(BoundsError(T(parent, indices, offset1, stride1), I)) + +# This computes the linear indexing compatibility for a given tuple of indices +viewindexing(I::Tuple{}) = IndexLinear() +# Leading scalar indices simply increase the stride +viewindexing(I::Tuple{ScalarIndex, Vararg{Any}}) = (@_inline_meta; viewindexing(tail(I))) +# Slices may begin a section which may be followed by any number of Slices +viewindexing(I::Tuple{Slice, Slice, Vararg{Any}}) = (@_inline_meta; viewindexing(tail(I))) +# A UnitRange can follow Slices, but only if all other indices are scalar +viewindexing(I::Tuple{Slice, AbstractUnitRange, Vararg{ScalarIndex}}) = IndexLinear() +viewindexing(I::Tuple{Slice, Slice, Vararg{ScalarIndex}}) = IndexLinear() # disambiguate +# In general, ranges are only fast if all other indices are scalar +viewindexing(I::Tuple{AbstractRange, Vararg{ScalarIndex}}) = IndexLinear() +# All other index combinations are slow +viewindexing(I::Tuple{Vararg{Any}}) = IndexCartesian() +# Of course, all other array types are slow +viewindexing(I::Tuple{AbstractArray, Vararg{Any}}) = IndexCartesian() + +# Simple utilities +size(V::SubArray) = (@_inline_meta; map(n->Int(unsafe_length(n)), axes(V))) + +similar(V::SubArray, T::Type, dims::Dims) = similar(V.parent, T, dims) + +sizeof(V::SubArray) = length(V) * sizeof(eltype(V)) + +copy(V::SubArray) = V.parent[V.indices...] + +parent(V::SubArray) = V.parent +parentindices(V::SubArray) = V.indices + +""" + parentindices(A) + +Return the indices in the [`parent`](@ref) which correspond to the array view `A`. + +# Examples +```jldoctest +julia> A = [1 2; 3 4]; + +julia> V = view(A, 1, :) +2-element view(::Array{Int64,2}, 1, :) with eltype Int64: + 1 + 2 + +julia> parentindices(V) +(1, Base.Slice(Base.OneTo(2))) +``` +""" +parentindices(a::AbstractArray) = map(OneTo, size(a)) + +## Aliasing detection +dataids(A::SubArray) = (dataids(A.parent)..., _splatmap(dataids, A.indices)...) +_splatmap(f, ::Tuple{}) = () +_splatmap(f, t::Tuple) = (f(t[1])..., _splatmap(f, tail(t))...) +unaliascopy(A::SubArray) = typeof(A)(unaliascopy(A.parent), map(unaliascopy, A.indices), A.offset1, A.stride1) + +# When the parent is an Array we can trim the size down a bit. In the future this +# could possibly be extended to any mutable array. +function unaliascopy(V::SubArray{T,N,A,I,LD}) where {T,N,A<:Array,I<:Tuple{Vararg{Union{Real,AbstractRange,Array}}},LD} + dest = Array{T}(undef, index_lengths(V.indices...)) + copyto!(dest, V) + SubArray{T,N,A,I,LD}(dest, map(_trimmedindex, V.indices), 0, Int(LD)) +end +# Transform indices to be "dense" +_trimmedindex(i::Real) = oftype(i, 1) +_trimmedindex(i::AbstractUnitRange) = oftype(i, OneTo(length(i))) +_trimmedindex(i::AbstractArray) = oftype(i, reshape(eachindex(IndexLinear(), i), axes(i))) + +## SubArray creation +# We always assume that the dimensionality of the parent matches the number of +# indices that end up getting passed to it, so we store the parent as a +# ReshapedArray view if necessary. The trouble is that arrays of `CartesianIndex` +# can make the number of effective indices not equal to length(I). +_maybe_reshape_parent(A::AbstractArray, ::NTuple{1, Bool}) = reshape(A, Val(1)) +_maybe_reshape_parent(A::AbstractArray{<:Any,1}, ::NTuple{1, Bool}) = reshape(A, Val(1)) +_maybe_reshape_parent(A::AbstractArray{<:Any,N}, ::NTuple{N, Bool}) where {N} = A +_maybe_reshape_parent(A::AbstractArray, ::NTuple{N, Bool}) where {N} = reshape(A, Val(N)) +""" + view(A, inds...) + +Like [`getindex`](@ref), but returns a view into the parent array `A` with the +given indices instead of making a copy. Calling [`getindex`](@ref) or +[`setindex!`](@ref) on the returned `SubArray` computes the +indices to the parent array on the fly without checking bounds. + +# Examples +```jldoctest +julia> A = [1 2; 3 4] +2×2 Array{Int64,2}: + 1 2 + 3 4 + +julia> b = view(A, :, 1) +2-element view(::Array{Int64,2}, :, 1) with eltype Int64: + 1 + 3 + +julia> fill!(b, 0) +2-element view(::Array{Int64,2}, :, 1) with eltype Int64: + 0 + 0 + +julia> A # Note A has changed even though we modified b +2×2 Array{Int64,2}: + 0 2 + 0 4 +``` +""" +function view(A::AbstractArray, I::Vararg{Any,N}) where {N} + @_inline_meta + J = map(i->unalias(A,i), to_indices(A, I)) + @boundscheck checkbounds(A, J...) + unsafe_view(_maybe_reshape_parent(A, index_ndims(J...)), J...) +end + +function unsafe_view(A::AbstractArray, I::Vararg{ViewIndex,N}) where {N} + @_inline_meta + SubArray(A, I) +end +# When we take the view of a view, it's often possible to "reindex" the parent +# view's indices such that we can "pop" the parent view and keep just one layer +# of indirection. But we can't always do this because arrays of `CartesianIndex` +# might span multiple parent indices, making the reindex calculation very hard. +# So we use _maybe_reindex to figure out if there are any arrays of +# `CartesianIndex`, and if so, we punt and keep two layers of indirection. +unsafe_view(V::SubArray, I::Vararg{ViewIndex,N}) where {N} = + (@_inline_meta; _maybe_reindex(V, I)) +_maybe_reindex(V, I) = (@_inline_meta; _maybe_reindex(V, I, I)) +_maybe_reindex(V, I, ::Tuple{AbstractArray{<:AbstractCartesianIndex}, Vararg{Any}}) = + (@_inline_meta; SubArray(V, I)) +# But allow arrays of CartesianIndex{1}; they behave just like arrays of Ints +_maybe_reindex(V, I, A::Tuple{AbstractArray{<:AbstractCartesianIndex{1}}, Vararg{Any}}) = + (@_inline_meta; _maybe_reindex(V, I, tail(A))) +_maybe_reindex(V, I, A::Tuple{Any, Vararg{Any}}) = (@_inline_meta; _maybe_reindex(V, I, tail(A))) +function _maybe_reindex(V, I, ::Tuple{}) + @_inline_meta + @inbounds idxs = to_indices(V.parent, reindex(V.indices, I)) + SubArray(V.parent, idxs) +end + +## Re-indexing is the heart of a view, transforming A[i, j][x, y] to A[i[x], j[y]] +# +# Recursively look through the heads of the parent- and sub-indices, considering +# the following cases: +# * Parent index is array -> re-index that with one or more sub-indices (one per dimension) +# * Parent index is Colon -> just use the sub-index as provided +# * Parent index is scalar -> that dimension was dropped, so skip the sub-index and use the index as is + +AbstractZeroDimArray{T} = AbstractArray{T, 0} + +reindex(::Tuple{}, ::Tuple{}) = () + +# Skip dropped scalars, so simply peel them off the parent indices and continue +reindex(idxs::Tuple{ScalarIndex, Vararg{Any}}, subidxs::Tuple{Vararg{Any}}) = + (@_propagate_inbounds_meta; (idxs[1], reindex(tail(idxs), subidxs)...)) + +# Slices simply pass their subindices straight through +reindex(idxs::Tuple{Slice, Vararg{Any}}, subidxs::Tuple{Any, Vararg{Any}}) = + (@_propagate_inbounds_meta; (subidxs[1], reindex(tail(idxs), tail(subidxs))...)) + +# Re-index into parent vectors with one subindex +reindex(idxs::Tuple{AbstractVector, Vararg{Any}}, subidxs::Tuple{Any, Vararg{Any}}) = + (@_propagate_inbounds_meta; (idxs[1][subidxs[1]], reindex(tail(idxs), tail(subidxs))...)) + +# Parent matrices are re-indexed with two sub-indices +reindex(idxs::Tuple{AbstractMatrix, Vararg{Any}}, subidxs::Tuple{Any, Any, Vararg{Any}}) = + (@_propagate_inbounds_meta; (idxs[1][subidxs[1], subidxs[2]], reindex(tail(idxs), tail(tail(subidxs)))...)) + +# In general, we index N-dimensional parent arrays with N indices +@generated function reindex(idxs::Tuple{AbstractArray{T,N}, Vararg{Any}}, subidxs::Tuple{Vararg{Any}}) where {T,N} + if length(subidxs.parameters) >= N + subs = [:(subidxs[$d]) for d in 1:N] + tail = [:(subidxs[$d]) for d in N+1:length(subidxs.parameters)] + :(@_propagate_inbounds_meta; (idxs[1][$(subs...)], reindex(tail(idxs), ($(tail...),))...)) + else + :(throw(ArgumentError("cannot re-index SubArray with fewer indices than dimensions\nThis should not occur; please submit a bug report."))) + end +end + +# In general, we simply re-index the parent indices by the provided ones +SlowSubArray{T,N,P,I} = SubArray{T,N,P,I,false} +function getindex(V::SubArray{T,N}, I::Vararg{Int,N}) where {T,N} + @_inline_meta + @boundscheck checkbounds(V, I...) + @inbounds r = V.parent[reindex(V.indices, I)...] + r +end + +# But SubArrays with fast linear indexing pre-compute a stride and offset +FastSubArray{T,N,P,I} = SubArray{T,N,P,I,true} +function getindex(V::FastSubArray, i::Int) + @_inline_meta + @boundscheck checkbounds(V, i) + @inbounds r = V.parent[V.offset1 + V.stride1*i] + r +end +# We can avoid a multiplication if the first parent index is a Colon or AbstractUnitRange, +# or if all the indices are scalars, i.e. the view is for a single value only +FastContiguousSubArray{T,N,P,I<:Union{Tuple{Union{Slice, AbstractUnitRange}, Vararg{Any}}, + Tuple{Vararg{ScalarIndex}}}} = SubArray{T,N,P,I,true} +function getindex(V::FastContiguousSubArray, i::Int) + @_inline_meta + @boundscheck checkbounds(V, i) + @inbounds r = V.parent[V.offset1 + i] + r +end +# For vector views with linear indexing, we disambiguate to favor the stride/offset +# computation as that'll generally be faster than (or just as fast as) re-indexing into a range. +function getindex(V::FastSubArray{<:Any, 1}, i::Int) + @_inline_meta + @boundscheck checkbounds(V, i) + @inbounds r = V.parent[V.offset1 + V.stride1*i] + r +end +function getindex(V::FastContiguousSubArray{<:Any, 1}, i::Int) + @_inline_meta + @boundscheck checkbounds(V, i) + @inbounds r = V.parent[V.offset1 + i] + r +end + +# Indexed assignment follows the same pattern as `getindex` above +function setindex!(V::SubArray{T,N}, x, I::Vararg{Int,N}) where {T,N} + @_inline_meta + @boundscheck checkbounds(V, I...) + @inbounds V.parent[reindex(V.indices, I)...] = x + V +end +function setindex!(V::FastSubArray, x, i::Int) + @_inline_meta + @boundscheck checkbounds(V, i) + @inbounds V.parent[V.offset1 + V.stride1*i] = x + V +end +function setindex!(V::FastContiguousSubArray, x, i::Int) + @_inline_meta + @boundscheck checkbounds(V, i) + @inbounds V.parent[V.offset1 + i] = x + V +end +function setindex!(V::FastSubArray{<:Any, 1}, x, i::Int) + @_inline_meta + @boundscheck checkbounds(V, i) + @inbounds V.parent[V.offset1 + V.stride1*i] = x + V +end +function setindex!(V::FastContiguousSubArray{<:Any, 1}, x, i::Int) + @_inline_meta + @boundscheck checkbounds(V, i) + @inbounds V.parent[V.offset1 + i] = x + V +end + +IndexStyle(::Type{<:FastSubArray}) = IndexLinear() +IndexStyle(::Type{<:SubArray}) = IndexCartesian() + +# Strides are the distance in memory between adjacent elements in a given dimension +# which we determine from the strides of the parent +strides(V::SubArray) = substrides(strides(V.parent), V.indices) + +substrides(strds::Tuple{}, ::Tuple{}) = () +substrides(strds::NTuple{N,Int}, I::Tuple{ScalarIndex, Vararg{Any}}) where N = (substrides(tail(strds), tail(I))...,) +substrides(strds::NTuple{N,Int}, I::Tuple{Slice, Vararg{Any}}) where N = (first(strds), substrides(tail(strds), tail(I))...) +substrides(strds::NTuple{N,Int}, I::Tuple{AbstractRange, Vararg{Any}}) where N = (first(strds)*step(I[1]), substrides(tail(strds), tail(I))...) +substrides(strds, I::Tuple{Any, Vararg{Any}}) = throw(ArgumentError("strides is invalid for SubArrays with indices of type $(typeof(I[1]))")) + +stride(V::SubArray, d::Integer) = d <= ndims(V) ? strides(V)[d] : strides(V)[end] * size(V)[end] + +compute_stride1(parent::AbstractArray, I::NTuple{N,Any}) where {N} = + (@_inline_meta; compute_stride1(1, fill_to_length(axes(parent), OneTo(1), Val(N)), I)) +compute_stride1(s, inds, I::Tuple{}) = s +compute_stride1(s, inds, I::Tuple{Vararg{ScalarIndex}}) = s +compute_stride1(s, inds, I::Tuple{ScalarIndex, Vararg{Any}}) = + (@_inline_meta; compute_stride1(s*unsafe_length(inds[1]), tail(inds), tail(I))) +compute_stride1(s, inds, I::Tuple{AbstractRange, Vararg{Any}}) = s*step(I[1]) +compute_stride1(s, inds, I::Tuple{Slice, Vararg{Any}}) = s +compute_stride1(s, inds, I::Tuple{Any, Vararg{Any}}) = throw(ArgumentError("invalid strided index type $(typeof(I[1]))")) + +elsize(::Type{<:SubArray{<:Any,<:Any,P}}) where {P} = elsize(P) + +iscontiguous(A::SubArray) = iscontiguous(typeof(A)) +iscontiguous(::Type{<:SubArray}) = false +iscontiguous(::Type{<:FastContiguousSubArray}) = true + +first_index(V::FastSubArray) = V.offset1 + V.stride1 # cached for fast linear SubArrays +function first_index(V::SubArray) + P, I = parent(V), V.indices + s1 = compute_stride1(P, I) + s1 + compute_offset1(P, s1, I) +end + +# Computing the first index simply steps through the indices, accumulating the +# sum of index each multiplied by the parent's stride. +# The running sum is `f`; the cumulative stride product is `s`. +# If the parent is a vector, then we offset the parent's own indices with parameters of I +compute_offset1(parent::AbstractVector, stride1::Integer, I::Tuple{AbstractRange}) = + (@_inline_meta; first(I[1]) - first(axes1(I[1]))*stride1) +# If the result is one-dimensional and it's a Colon, then linear +# indexing uses the indices along the given dimension. Otherwise +# linear indexing always starts with 1. +compute_offset1(parent, stride1::Integer, I::Tuple) = + (@_inline_meta; compute_offset1(parent, stride1, find_extended_dims(1, I...), find_extended_inds(I...), I)) +compute_offset1(parent, stride1::Integer, dims::Tuple{Int}, inds::Tuple{Union{Slice, IdentityUnitRange}}, I::Tuple) = + (@_inline_meta; compute_linindex(parent, I) - stride1*first(axes(parent, dims[1]))) # index-preserving case +compute_offset1(parent, stride1::Integer, dims, inds, I::Tuple) = + (@_inline_meta; compute_linindex(parent, I) - stride1) # linear indexing starts with 1 + +function compute_linindex(parent, I::NTuple{N,Any}) where N + @_inline_meta + IP = fill_to_length(axes(parent), OneTo(1), Val(N)) + compute_linindex(1, 1, IP, I) +end +function compute_linindex(f, s, IP::Tuple, I::Tuple{ScalarIndex, Vararg{Any}}) + @_inline_meta + Δi = I[1]-first(IP[1]) + compute_linindex(f + Δi*s, s*unsafe_length(IP[1]), tail(IP), tail(I)) +end +function compute_linindex(f, s, IP::Tuple, I::Tuple{Any, Vararg{Any}}) + @_inline_meta + Δi = first(I[1])-first(IP[1]) + compute_linindex(f + Δi*s, s*unsafe_length(IP[1]), tail(IP), tail(I)) +end +compute_linindex(f, s, IP::Tuple, I::Tuple{}) = f + +find_extended_dims(dim, ::ScalarIndex, I...) = (@_inline_meta; find_extended_dims(dim + 1, I...)) +find_extended_dims(dim, i1, I...) = (@_inline_meta; (dim, find_extended_dims(dim + 1, I...)...)) +find_extended_dims(dim) = () +find_extended_inds(::ScalarIndex, I...) = (@_inline_meta; find_extended_inds(I...)) +find_extended_inds(i1, I...) = (@_inline_meta; (i1, find_extended_inds(I...)...)) +find_extended_inds() = () + +unsafe_convert(::Type{Ptr{T}}, V::SubArray{T,N,P,<:Tuple{Vararg{RangeIndex}}}) where {T,N,P} = + unsafe_convert(Ptr{T}, V.parent) + (first_index(V)-1)*sizeof(T) + +pointer(V::FastSubArray, i::Int) = pointer(V.parent, V.offset1 + V.stride1*i) +pointer(V::FastContiguousSubArray, i::Int) = pointer(V.parent, V.offset1 + i) +pointer(V::SubArray, i::Int) = _pointer(V, i) +_pointer(V::SubArray{<:Any,1}, i::Int) = pointer(V, (i,)) +_pointer(V::SubArray, i::Int) = pointer(V, Base._ind2sub(axes(V), i)) + +function pointer(V::SubArray{T,N,<:Array,<:Tuple{Vararg{RangeIndex}}}, is::Tuple{Vararg{Int}}) where {T,N} + index = first_index(V) + strds = strides(V) + for d = 1:length(is) + index += (is[d]-1)*strds[d] + end + return pointer(V.parent, index) +end + +# indices are taken from the range/vector +# Since bounds-checking is performance-critical and uses +# indices, it's worth optimizing these implementations thoroughly +axes(S::SubArray) = (@_inline_meta; _indices_sub(S.indices...)) +_indices_sub(::Real, I...) = (@_inline_meta; _indices_sub(I...)) +_indices_sub() = () +function _indices_sub(i1::AbstractArray, I...) + @_inline_meta + (unsafe_indices(i1)..., _indices_sub(I...)...) +end + +## Compatibility +# deprecate? +function parentdims(s::SubArray) + nd = ndims(s) + dimindex = Vector{Int}(undef, nd) + sp = strides(s.parent) + sv = strides(s) + j = 1 + for i = 1:ndims(s.parent) + r = s.indices[i] + if j <= nd && (isa(r,AbstractRange) ? sp[i]*step(r) : sp[i]) == sv[j] + dimindex[j] = i + j += 1 + end + end + dimindex +end diff --git a/base/summarysize.jl b/base/summarysize.jl new file mode 100644 index 0000000..69aa791 --- /dev/null +++ b/base/summarysize.jl @@ -0,0 +1,181 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +struct SummarySize + seen::IdDict{Any,Any} + frontier_x::Vector{Any} + frontier_i::Vector{Int} + exclude::Any + chargeall::Any +end + +""" + Base.summarysize(obj; exclude=Union{...}, chargeall=Union{...}) -> Int + +Compute the amount of memory, in bytes, used by all unique objects reachable from the argument. + +# Keyword Arguments +- `exclude`: specifies the types of objects to exclude from the traversal. +- `chargeall`: specifies the types of objects to always charge the size of all of their + fields, even if those fields would normally be excluded. +""" +function summarysize(obj; + exclude = Union{DataType, Core.TypeName, Core.MethodInstance}, + chargeall = Union{Core.TypeMapEntry, Method}) + @nospecialize obj exclude chargeall + ss = SummarySize(IdDict(), Any[], Int[], exclude, chargeall) + size::Int = ss(obj) + while !isempty(ss.frontier_x) + # DFS heap traversal of everything without a specialization + # BFS heap traversal of anything with a specialization + x = ss.frontier_x[end] + i = ss.frontier_i[end] + val = nothing + if isa(x, SimpleVector) + nf = length(x) + if isassigned(x, i) + val = x[i] + end + elseif isa(x, Array) + nf = length(x) + if ccall(:jl_array_isassigned, Cint, (Any, UInt), x, i - 1) != 0 + val = x[i] + end + else + nf = nfields(x) + ft = typeof(x).types + if !isbitstype(ft[i]) && isdefined(x, i) + val = getfield(x, i) + end + end + if nf > i + ss.frontier_i[end] = i + 1 + else + pop!(ss.frontier_x) + pop!(ss.frontier_i) + end + if val !== nothing && !isa(val, Module) && (!isa(val, ss.exclude) || isa(x, ss.chargeall)) + size += ss(val)::Int + end + end + return size +end + +(ss::SummarySize)(@nospecialize obj) = _summarysize(ss, obj) +# define the general case separately to make sure it is not specialized for every type +@noinline function _summarysize(ss::SummarySize, @nospecialize obj) + isdefined(typeof(obj), :instance) && return 0 + # NOTE: this attempts to discover multiple copies of the same immutable value, + # and so is somewhat approximate. + key = ccall(:jl_value_ptr, Ptr{Cvoid}, (Any,), obj) + haskey(ss.seen, key) ? (return 0) : (ss.seen[key] = true) + if nfields(obj) > 0 + push!(ss.frontier_x, obj) + push!(ss.frontier_i, 1) + end + if isa(obj, UnionAll) || isa(obj, Union) + # black-list of items that don't have a Core.sizeof + sz = 2 * sizeof(Int) + else + sz = Core.sizeof(obj) + end + if sz == 0 + # 0-field mutable structs are not unique + return gc_alignment(0) + end + return sz +end + +(::SummarySize)(obj::Symbol) = 0 +(::SummarySize)(obj::SummarySize) = 0 + +function (ss::SummarySize)(obj::String) + key = ccall(:jl_value_ptr, Ptr{Cvoid}, (Any,), obj) + haskey(ss.seen, key) ? (return 0) : (ss.seen[key] = true) + return Core.sizeof(Int) + Core.sizeof(obj) +end + +function (ss::SummarySize)(obj::DataType) + key = pointer_from_objref(obj) + haskey(ss.seen, key) ? (return 0) : (ss.seen[key] = true) + size::Int = 7 * Core.sizeof(Int) + 6 * Core.sizeof(Int32) + size += 4 * nfields(obj) + ifelse(Sys.WORD_SIZE == 64, 4, 0) + size += ss(obj.parameters)::Int + if isdefined(obj, :types) + size += ss(obj.types)::Int + end + return size +end + +function (ss::SummarySize)(obj::Core.TypeName) + key = pointer_from_objref(obj) + haskey(ss.seen, key) ? (return 0) : (ss.seen[key] = true) + return Core.sizeof(obj) + (isdefined(obj, :mt) ? ss(obj.mt) : 0) +end + +function (ss::SummarySize)(obj::Array) + haskey(ss.seen, obj) ? (return 0) : (ss.seen[obj] = true) + headersize = 4*sizeof(Int) + 8 + max(0, ndims(obj)-2)*sizeof(Int) + size::Int = headersize + datakey = unsafe_convert(Ptr{Cvoid}, obj) + if !haskey(ss.seen, datakey) + ss.seen[datakey] = true + dsize = Core.sizeof(obj) + if isbitsunion(eltype(obj)) + # add 1 union selector byte for each element + dsize += length(obj) + end + size += dsize + if !isempty(obj) && !Base.allocatedinline(eltype(obj)) + push!(ss.frontier_x, obj) + push!(ss.frontier_i, 1) + end + end + return size +end + +function (ss::SummarySize)(obj::SimpleVector) + key = pointer_from_objref(obj) + haskey(ss.seen, key) ? (return 0) : (ss.seen[key] = true) + size::Int = Core.sizeof(obj) + if !isempty(obj) + push!(ss.frontier_x, obj) + push!(ss.frontier_i, 1) + end + return size +end + +function (ss::SummarySize)(obj::Module) + haskey(ss.seen, obj) ? (return 0) : (ss.seen[obj] = true) + size::Int = Core.sizeof(obj) + for binding in names(obj, all = true) + if isdefined(obj, binding) && !isdeprecated(obj, binding) + value = getfield(obj, binding) + if !isa(value, Module) || parentmodule(value) === obj + size += ss(value)::Int + if isa(value, UnionAll) + value = unwrap_unionall(value) + end + if isa(value, DataType) && value.name.module === obj && value.name.name === binding + # charge a TypeName to its module (but not to the type) + size += ss(value.name)::Int + end + end + end + end + return size +end + +function (ss::SummarySize)(obj::Task) + haskey(ss.seen, obj) ? (return 0) : (ss.seen[obj] = true) + size::Int = Core.sizeof(obj) + if isdefined(obj, :code) + size += ss(obj.code)::Int + end + size += ss(obj.storage)::Int + size += ss(obj.backtrace)::Int + size += ss(obj.donenotify)::Int + size += ss(obj.exception)::Int + size += ss(obj.result)::Int + # TODO: add stack size, and possibly traverse stack roots + return size +end diff --git a/base/sysimg.jl b/base/sysimg.jl new file mode 100644 index 0000000..bfd57c2 --- /dev/null +++ b/base/sysimg.jl @@ -0,0 +1,97 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +Core.include(Main, "Base.jl") + +using .Base + +# Ensure this file is also tracked +pushfirst!(Base._included_files, (@__MODULE__, joinpath(@__DIR__, "Base.jl"))) +pushfirst!(Base._included_files, (@__MODULE__, joinpath(@__DIR__, "sysimg.jl"))) + +# set up depot & load paths to be able to find stdlib packages +@eval Base creating_sysimg = true +Base.init_depot_path() +Base.init_load_path() + +if Base.is_primary_base_module +# load some stdlib packages but don't put their names in Main +let + # Stdlibs manually sorted in top down order + stdlibs = [ + # No deps + :Base64, + :CRC32c, + :SHA, + :FileWatching, + :Unicode, + :Mmap, + :Serialization, + :Libdl, + :Markdown, + :LibGit2, + :Logging, + :Sockets, + :Printf, + :Profile, + :Dates, + :DelimitedFiles, + :Random, + :UUIDs, + :Future, + :LinearAlgebra, + :SparseArrays, + :SuiteSparse, + :Distributed, + :SharedArrays, + :Pkg, + :Test, + :REPL, + :Statistics, + ] + + maxlen = maximum(textwidth.(string.(stdlibs))) + + print_time = (mod, t) -> (print(rpad(string(mod) * " ", maxlen + 3, "─")); Base.time_print(t * 10^9); println()) + print_time(Base, (Base.end_base_include - Base.start_base_include) * 10^(-9)) + + Base._track_dependencies[] = true + Base.tot_time_stdlib[] = @elapsed for stdlib in stdlibs + tt = @elapsed Base.require(Base, stdlib) + print_time(stdlib, tt) + end + for dep in Base._require_dependencies + dep[3] == 0.0 && continue + push!(Base._included_files, dep[1:2]) + end + empty!(Base._require_dependencies) + Base._track_dependencies[] = false + + print_time("Stdlibs total", Base.tot_time_stdlib[]) +end +end + +# Clear global state +empty!(Core.ARGS) +empty!(Base.ARGS) +empty!(LOAD_PATH) +@eval Base creating_sysimg = false +Base.init_load_path() # want to be able to find external packages in userimg.jl + +let +tot_time_userimg = @elapsed (Base.isfile("userimg.jl") && Base.include(Main, "userimg.jl")) + + +tot_time_base = (Base.end_base_include - Base.start_base_include) * 10.0^(-9) +tot_time = tot_time_base + Base.tot_time_stdlib[] + tot_time_userimg + +println("Sysimage built. Summary:") +print("Total ─────── "); Base.time_print(tot_time * 10^9); print(" \n"); +print("Base: ─────── "); Base.time_print(tot_time_base * 10^9); print(" "); show(IOContext(stdout, :compact=>true), (tot_time_base / tot_time) * 100); println("%") +print("Stdlibs: ──── "); Base.time_print(Base.tot_time_stdlib[] * 10^9); print(" "); show(IOContext(stdout, :compact=>true), (Base.tot_time_stdlib[] / tot_time) * 100); println("%") +if isfile("userimg.jl") +print("Userimg: ──── "); Base.time_print(tot_time_userimg * 10^9); print(" "); show(IOContext(stdout, :compact=>true), (tot_time_userimg / tot_time) * 100); println("%") +end +end + +empty!(LOAD_PATH) +empty!(DEPOT_PATH) diff --git a/base/sysinfo.jl b/base/sysinfo.jl new file mode 100644 index 0000000..df71537 --- /dev/null +++ b/base/sysinfo.jl @@ -0,0 +1,518 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +module Sys +@doc """ +Provide methods for retrieving information about hardware and the operating system. +""" Sys + +export BINDIR, + STDLIB, + CPU_THREADS, + CPU_NAME, + WORD_SIZE, + ARCH, + MACHINE, + KERNEL, + JIT, + cpu_info, + cpu_summary, + uptime, + loadavg, + free_memory, + total_memory, + isapple, + isbsd, + isdragonfly, + isfreebsd, + islinux, + isnetbsd, + isopenbsd, + isunix, + iswindows, + isjsvm, + isexecutable, + which + +import ..Base: show + +global BINDIR = ccall(:jl_get_julia_bindir, Any, ())::String +""" + Sys.BINDIR + +A string containing the full path to the directory containing the `julia` executable. +""" +:BINDIR + +""" + Sys.STDLIB + +A string containing the full path to the directory containing the `stdlib` packages. +""" +STDLIB = "$BINDIR/../share/julia/stdlib/v$(VERSION.major).$(VERSION.minor)" # for bootstrap + +# helper to avoid triggering precompile warnings + +""" + Sys.CPU_THREADS + +The number of logical CPU cores available in the system, i.e. the number of threads +that the CPU can run concurrently. Note that this is not necessarily the number of +CPU cores, for example, in the presence of +[hyper-threading](https://en.wikipedia.org/wiki/Hyper-threading). + +See Hwloc.jl or CpuId.jl for extended information, including number of physical cores. +""" +CPU_THREADS = 1 # for bootstrap, changed on startup + +""" + Sys.ARCH + +A symbol representing the architecture of the build configuration. +""" +const ARCH = ccall(:jl_get_ARCH, Any, ()) + + +""" + Sys.KERNEL + +A symbol representing the name of the operating system, as returned by `uname` of the build configuration. +""" +const KERNEL = ccall(:jl_get_UNAME, Any, ()) + +""" + Sys.MACHINE + +A string containing the build triple. +""" +const MACHINE = Base.MACHINE + +""" + Sys.WORD_SIZE + +Standard word size on the current machine, in bits. +""" +const WORD_SIZE = Core.sizeof(Int) * 8 + +function __init__() + env_threads = nothing + if haskey(ENV, "JULIA_CPU_THREADS") + env_threads = ENV["JULIA_CPU_THREADS"] + end + global CPU_THREADS = if env_threads !== nothing + env_threads = tryparse(Int, env_threads) + if !(env_threads isa Int && env_threads > 0) + env_threads = Int(ccall(:jl_cpu_threads, Int32, ())) + Core.print(Core.stderr, "WARNING: couldn't parse `JULIA_CPU_THREADS` environment variable. Defaulting Sys.CPU_THREADS to $env_threads.\n") + end + env_threads + else + Int(ccall(:jl_cpu_threads, Int32, ())) + end + global SC_CLK_TCK = ccall(:jl_SC_CLK_TCK, Clong, ()) + global CPU_NAME = ccall(:jl_get_cpu_name, Ref{String}, ()) + global JIT = ccall(:jl_get_JIT, Ref{String}, ()) + global BINDIR = ccall(:jl_get_julia_bindir, Any, ())::String + vers = "v$(VERSION.major).$(VERSION.minor)" + global STDLIB = abspath(BINDIR, "..", "share", "julia", "stdlib", vers) + nothing +end + +mutable struct UV_cpu_info_t + model::Ptr{UInt8} + speed::Int32 + cpu_times!user::UInt64 + cpu_times!nice::UInt64 + cpu_times!sys::UInt64 + cpu_times!idle::UInt64 + cpu_times!irq::UInt64 +end +mutable struct CPUinfo + model::String + speed::Int32 + cpu_times!user::UInt64 + cpu_times!nice::UInt64 + cpu_times!sys::UInt64 + cpu_times!idle::UInt64 + cpu_times!irq::UInt64 + CPUinfo(model,speed,u,n,s,id,ir)=new(model,speed,u,n,s,id,ir) +end +CPUinfo(info::UV_cpu_info_t) = CPUinfo(unsafe_string(info.model), info.speed, + info.cpu_times!user, info.cpu_times!nice, info.cpu_times!sys, + info.cpu_times!idle, info.cpu_times!irq) + +function _show_cpuinfo(io::IO, info::Sys.CPUinfo, header::Bool=true, prefix::AbstractString=" ") + tck = SC_CLK_TCK + if header + println(io, info.model, ": ") + print(io, " "^length(prefix)) + println(io, " ", lpad("speed", 5), " ", lpad("user", 9), " ", lpad("nice", 9), " ", + lpad("sys", 9), " ", lpad("idle", 9), " ", lpad("irq", 9)) + end + print(io, prefix) + unit = tck > 0 ? " s " : " " + tc = max(tck, 1) + d(i, unit=unit) = lpad(string(round(Int64,i)), 9) * unit + print(io, + lpad(string(info.speed), 5), " MHz ", + d(info.cpu_times!user / tc), d(info.cpu_times!nice / tc), d(info.cpu_times!sys / tc), + d(info.cpu_times!idle / tc), d(info.cpu_times!irq / tc, tck > 0 ? " s" : " ")) + if tck <= 0 + print(io, "ticks") + end +end + +show(io::IO, info::CPUinfo) = _show_cpuinfo(io, info, true, " ") + +function _cpu_summary(io::IO, cpu::AbstractVector{CPUinfo}, i, j) + if j-i < 9 + header = true + for x = i:j + header || println(io) + _show_cpuinfo(io, cpu[x], header, "#$(x-i+1) ") + header = false + end + else + summary = CPUinfo(cpu[i].model,0,0,0,0,0,0) + count = j - i + 1 + for x = i:j + summary.speed += cpu[i].speed + summary.cpu_times!user += cpu[x].cpu_times!user + summary.cpu_times!nice += cpu[x].cpu_times!nice + summary.cpu_times!sys += cpu[x].cpu_times!sys + summary.cpu_times!idle += cpu[x].cpu_times!idle + summary.cpu_times!irq += cpu[x].cpu_times!irq + end + summary.speed = div(summary.speed,count) + _show_cpuinfo(io, summary, true, "#1-$(count) ") + end + println(io) +end + +function cpu_summary(io::IO=stdout, cpu::AbstractVector{CPUinfo} = cpu_info()) + model = cpu[1].model + first = 1 + for i = 2:length(cpu) + if model != cpu[i].model + _cpu_summary(io, cpu, first, i-1) + first = i + end + end + _cpu_summary(io, cpu, first, length(cpu)) +end + +function cpu_info() + UVcpus = Ref{Ptr{UV_cpu_info_t}}() + count = Ref{Int32}() + err = ccall(:uv_cpu_info, Int32, (Ptr{Ptr{UV_cpu_info_t}}, Ptr{Int32}), UVcpus, count) + Base.uv_error("uv_cpu_info", err) + cpus = Vector{CPUinfo}(undef, count[]) + for i = 1:length(cpus) + cpus[i] = CPUinfo(unsafe_load(UVcpus[], i)) + end + ccall(:uv_free_cpu_info, Cvoid, (Ptr{UV_cpu_info_t}, Int32), UVcpus[], count[]) + return cpus +end + +""" + Sys.uptime() + +Gets the current system uptime in seconds. +""" +function uptime() + uptime_ = Ref{Float64}() + err = ccall(:uv_uptime, Int32, (Ptr{Float64},), uptime_) + Base.uv_error("uv_uptime", err) + return uptime_[] +end + +""" + Sys.loadavg() + +Get the load average. See: https://en.wikipedia.org/wiki/Load_(computing). +""" +function loadavg() + loadavg_ = Vector{Float64}(undef, 3) + ccall(:uv_loadavg, Cvoid, (Ptr{Float64},), loadavg_) + return loadavg_ +end + +""" + Sys.free_memory() + +Get the total free memory in RAM in kilobytes. +""" +free_memory() = ccall(:uv_get_free_memory, UInt64, ()) + +""" + Sys.total_memory() + +Get the total memory in RAM (including that which is currently used) in kilobytes. +""" +total_memory() = ccall(:uv_get_total_memory, UInt64, ()) + +""" + Sys.get_process_title() + +Get the process title. On some systems, will always return an empty string. +""" +function get_process_title() + buf = Vector{UInt8}(undef, 512) + err = ccall(:uv_get_process_title, Cint, (Ptr{UInt8}, Cint), buf, 512) + Base.uv_error("get_process_title", err) + return unsafe_string(pointer(buf)) +end + +""" + Sys.set_process_title(title::AbstractString) + +Set the process title. No-op on some operating systems. +""" +function set_process_title(title::AbstractString) + err = ccall(:uv_set_process_title, Cint, (Cstring,), title) + Base.uv_error("set_process_title", err) +end + +""" + Sys.maxrss() + +Get the maximum resident set size utilized in bytes. +See also: + - man page of getrusage(2) on Linux and FreeBSD. + - windows api `GetProcessMemoryInfo` +""" +maxrss() = ccall(:jl_maxrss, Csize_t, ()) + +""" + Sys.isunix([os]) + +Predicate for testing if the OS provides a Unix-like interface. +See documentation in [Handling Operating System Variation](@ref). +""" +function isunix(os::Symbol) + if iswindows(os) + return false + elseif islinux(os) || isbsd(os) + return true + elseif os === :Emscripten + # Emscripten implements the POSIX ABI and provides traditional + # Unix-style operating system functions such as file system support. + # Therefor, we consider it a unix, even though this need not be + # generally true for a jsvm embedding. + return true + else + throw(ArgumentError("unknown operating system \"$os\"")) + end +end + +""" + Sys.islinux([os]) + +Predicate for testing if the OS is a derivative of Linux. +See documentation in [Handling Operating System Variation](@ref). +""" +islinux(os::Symbol) = (os === :Linux) + +""" + Sys.isbsd([os]) + +Predicate for testing if the OS is a derivative of BSD. +See documentation in [Handling Operating System Variation](@ref). + +!!! note + The Darwin kernel descends from BSD, which means that `Sys.isbsd()` is + `true` on macOS systems. To exclude macOS from a predicate, use + `Sys.isbsd() && !Sys.isapple()`. +""" +isbsd(os::Symbol) = (isfreebsd(os) || isopenbsd(os) || isnetbsd(os) || isdragonfly(os) || isapple(os)) + +""" + Sys.isfreebsd([os]) + +Predicate for testing if the OS is a derivative of FreeBSD. +See documentation in [Handling Operating System Variation](@ref). + +!!! note + Not to be confused with `Sys.isbsd()`, which is `true` on FreeBSD but also on + other BSD-based systems. `Sys.isfreebsd()` refers only to FreeBSD. +!!! compat "Julia 1.1" + This function requires at least Julia 1.1. +""" +isfreebsd(os::Symbol) = (os === :FreeBSD) + +""" + Sys.isopenbsd([os]) + +Predicate for testing if the OS is a derivative of OpenBSD. +See documentation in [Handling Operating System Variation](@ref). + +!!! note + Not to be confused with `Sys.isbsd()`, which is `true` on OpenBSD but also on + other BSD-based systems. `Sys.isopenbsd()` refers only to OpenBSD. +!!! compat "Julia 1.1" + This function requires at least Julia 1.1. +""" +isopenbsd(os::Symbol) = (os === :OpenBSD) + +""" + Sys.isnetbsd([os]) + +Predicate for testing if the OS is a derivative of NetBSD. +See documentation in [Handling Operating System Variation](@ref). + +!!! note + Not to be confused with `Sys.isbsd()`, which is `true` on NetBSD but also on + other BSD-based systems. `Sys.isnetbsd()` refers only to NetBSD. +!!! compat "Julia 1.1" + This function requires at least Julia 1.1. +""" +isnetbsd(os::Symbol) = (os === :NetBSD) + +""" + Sys.isdragonfly([os]) + +Predicate for testing if the OS is a derivative of DragonFly BSD. +See documentation in [Handling Operating System Variation](@ref). + +!!! note + Not to be confused with `Sys.isbsd()`, which is `true` on DragonFly but also on + other BSD-based systems. `Sys.isdragonfly()` refers only to DragonFly. +!!! compat "Julia 1.1" + This function requires at least Julia 1.1. +""" +isdragonfly(os::Symbol) = (os === :DragonFly) + +""" + Sys.iswindows([os]) + +Predicate for testing if the OS is a derivative of Microsoft Windows NT. +See documentation in [Handling Operating System Variation](@ref). +""" +iswindows(os::Symbol) = (os === :Windows || os === :NT) + +""" + Sys.isapple([os]) + +Predicate for testing if the OS is a derivative of Apple Macintosh OS X or Darwin. +See documentation in [Handling Operating System Variation](@ref). +""" +isapple(os::Symbol) = (os === :Apple || os === :Darwin) + +""" + Sys.isjsvm([os]) + +Predicate for testing if Julia is running in a JavaScript VM (JSVM), +including e.g. a WebAssembly JavaScript embedding in a web browser. + +!!! compat "Julia 1.2" + This function requires at least Julia 1.2. +""" +isjsvm(os::Symbol) = (os === :Emscripten) + +for f in (:isunix, :islinux, :isbsd, :isapple, :iswindows, :isfreebsd, :isopenbsd, :isnetbsd, :isdragonfly, :isjsvm) + @eval $f() = $(getfield(@__MODULE__, f)(KERNEL)) +end + +if iswindows() + function windows_version() + verinfo = ccall(:GetVersion, UInt32, ()) + VersionNumber(verinfo & 0xFF, (verinfo >> 8) & 0xFF, verinfo >> 16) + end +else + windows_version() = v"0.0" +end + +""" + Sys.windows_version() + +Return the version number for the Windows NT Kernel as a `VersionNumber`, +i.e. `v"major.minor.build"`, or `v"0.0.0"` if this is not running on Windows. +""" +windows_version + +const WINDOWS_VISTA_VER = v"6.0" + +""" + Sys.isexecutable(path::String) + +Return `true` if the given `path` has executable permissions. +""" +function isexecutable(path::String) + if iswindows() + return isfile(path) + else + # We use `access()` and `X_OK` to determine if a given path is + # executable by the current user. `X_OK` comes from `unistd.h`. + X_OK = 0x01 + ccall(:access, Cint, (Ptr{UInt8}, Cint), path, X_OK) == 0 + end +end +isexecutable(path::AbstractString) = isexecutable(String(path)) + +""" + Sys.which(program_name::String) + +Given a program name, search the current `PATH` to find the first binary with +the proper executable permissions that can be run and return an absolute path +to it, or return `nothing` if no such program is available. If a path with +a directory in it is passed in for `program_name`, tests that exact path +for executable permissions only (with `.exe` and `.com` extensions added on +Windows platforms); no searching of `PATH` is performed. +""" +function which(program_name::String) + if isempty(program_name) + return nothing + end + # Build a list of program names that we're going to try + program_names = String[] + base_pname = basename(program_name) + if iswindows() + # If the file already has an extension, try that name first + if !isempty(splitext(base_pname)[2]) + push!(program_names, base_pname) + end + + # But also try appending .exe and .com` + for pe in (".exe", ".com") + push!(program_names, string(base_pname, pe)) + end + else + # On non-windows, we just always search for what we've been given + push!(program_names, base_pname) + end + + path_dirs = String[] + program_dirname = dirname(program_name) + # If we've been given a path that has a directory name in it, then we + # check to see if that path exists. Otherwise, we search the PATH. + if isempty(program_dirname) + # If we have been given just a program name (not a relative or absolute + # path) then we should search `PATH` for it here: + pathsep = iswindows() ? ';' : ':' + path_dirs = abspath.(split(get(ENV, "PATH", ""), pathsep)) + + # On windows we always check the current directory as well + if iswindows() + pushfirst!(path_dirs, pwd()) + end + else + push!(path_dirs, abspath(program_dirname)) + end + + # Here we combine our directories with our program names, searching for the + # first match among all combinations. + for path_dir in path_dirs + for pname in program_names + program_path = joinpath(path_dir, pname) + # If we find something that matches our name and we can execute + if isfile(program_path) && isexecutable(program_path) + return realpath(program_path) + end + end + end + + # If we couldn't find anything, don't return anything + nothing +end +which(program_name::AbstractString) = which(String(program_name)) + +end # module Sys diff --git a/base/task.jl b/base/task.jl new file mode 100644 index 0000000..bfa2194 --- /dev/null +++ b/base/task.jl @@ -0,0 +1,678 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## basic task functions and TLS + +Core.Task(@nospecialize(f), reserved_stack::Int=0) = Core._Task(f, reserved_stack, ThreadSynchronizer()) + +# Container for a captured exception and its backtrace. Can be serialized. +struct CapturedException <: Exception + ex::Any + processed_bt::Vector{Any} + + function CapturedException(ex, bt_raw::Vector) + # bt_raw MUST be a vector that can be processed by StackTraces.stacktrace + # Typically the result of a catch_backtrace() + + # Process bt_raw so that it can be safely serialized + bt_lines = process_backtrace(bt_raw, 100) # Limiting this to 100 lines. + CapturedException(ex, bt_lines) + end + + CapturedException(ex, processed_bt::Vector{Any}) = new(ex, processed_bt) +end + +function showerror(io::IO, ce::CapturedException) + showerror(io, ce.ex, ce.processed_bt, backtrace=true) +end + +""" + CompositeException + +Wrap a `Vector` of exceptions thrown by a [`Task`](@ref) (e.g. generated from a remote worker over a channel +or an asynchronously executing local I/O write or a remote worker under `pmap`) with information about the series of exceptions. +For example, if a group of workers are executing several tasks, and multiple workers fail, the resulting `CompositeException` will +contain a "bundle" of information from each worker indicating where and why the exception(s) occurred. +""" +struct CompositeException <: Exception + exceptions::Vector{Any} + CompositeException() = new(Any[]) + CompositeException(exceptions) = new(exceptions) +end +length(c::CompositeException) = length(c.exceptions) +push!(c::CompositeException, ex) = push!(c.exceptions, ex) +isempty(c::CompositeException) = isempty(c.exceptions) +iterate(c::CompositeException, state...) = iterate(c.exceptions, state...) +eltype(::Type{CompositeException}) = Any + +function showerror(io::IO, ex::CompositeException) + if !isempty(ex) + showerror(io, ex.exceptions[1]) + remaining = length(ex) - 1 + if remaining > 0 + print(io, string("\n\n...and ", remaining, " more exception(s).\n")) + end + else + print(io, "CompositeException()\n") + end +end + +""" + TaskFailedException + +This exception is thrown by a `wait(t)` call when task `t` fails. +`TaskFailedException` wraps the failed task `t`. +""" +struct TaskFailedException <: Exception + task::Task +end + +function showerror(io::IO, ex::TaskFailedException) + stacks = [] + while isa(ex.task.exception, TaskFailedException) + pushfirst!(stacks, ex.task.backtrace) + ex = ex.task.exception + end + println(io, "TaskFailedException:") + showerror(io, ex.task.exception, ex.task.backtrace) + if !isempty(stacks) + for bt in stacks + show_backtrace(io, bt) + end + end +end + +function show(io::IO, t::Task) + print(io, "Task ($(t.state)) @0x$(string(convert(UInt, pointer_from_objref(t)), base = 16, pad = Sys.WORD_SIZE>>2))") +end + +""" + @task + +Wrap an expression in a [`Task`](@ref) without executing it, and return the [`Task`](@ref). This only +creates a task, and does not run it. + +# Examples +```jldoctest +julia> a1() = sum(i for i in 1:1000); + +julia> b = @task a1(); + +julia> istaskstarted(b) +false + +julia> schedule(b); + +julia> yield(); + +julia> istaskdone(b) +true +``` +""" +macro task(ex) + :(Task(()->$(esc(ex)))) +end + +""" + current_task() + +Get the currently running [`Task`](@ref). +""" +current_task() = ccall(:jl_get_current_task, Ref{Task}, ()) + +""" + istaskdone(t::Task) -> Bool + +Determine whether a task has exited. + +# Examples +```jldoctest +julia> a2() = sum(i for i in 1:1000); + +julia> b = Task(a2); + +julia> istaskdone(b) +false + +julia> schedule(b); + +julia> yield(); + +julia> istaskdone(b) +true +``` +""" +istaskdone(t::Task) = ((t.state === :done) | istaskfailed(t)) + +""" + istaskstarted(t::Task) -> Bool + +Determine whether a task has started executing. + +# Examples +```jldoctest +julia> a3() = sum(i for i in 1:1000); + +julia> b = Task(a3); + +julia> istaskstarted(b) +false +``` +""" +istaskstarted(t::Task) = ccall(:jl_is_task_started, Cint, (Any,), t) != 0 + +""" + istaskfailed(t::Task) -> Bool + +Determine whether a task has exited because an exception was thrown. + +# Examples +```jldoctest +julia> a4() = error("task failed"); + +julia> b = Task(a4); + +julia> istaskfailed(b) +false + +julia> schedule(b); + +julia> yield(); + +julia> istaskfailed(b) +true +``` +""" +istaskfailed(t::Task) = (t.state === :failed) + +Threads.threadid(t::Task) = Int(ccall(:jl_get_task_tid, Int16, (Any,), t)+1) + +task_result(t::Task) = t.result + +task_local_storage() = get_task_tls(current_task()) +function get_task_tls(t::Task) + if t.storage === nothing + t.storage = IdDict() + end + return (t.storage)::IdDict{Any,Any} +end + +""" + task_local_storage(key) + +Look up the value of a key in the current task's task-local storage. +""" +task_local_storage(key) = task_local_storage()[key] + +""" + task_local_storage(key, value) + +Assign a value to a key in the current task's task-local storage. +""" +task_local_storage(key, val) = (task_local_storage()[key] = val) + +""" + task_local_storage(body, key, value) + +Call the function `body` with a modified task-local storage, in which `value` is assigned to +`key`; the previous value of `key`, or lack thereof, is restored afterwards. Useful +for emulating dynamic scoping. +""" +function task_local_storage(body::Function, key, val) + tls = task_local_storage() + hadkey = haskey(tls, key) + old = get(tls, key, nothing) + tls[key] = val + try + return body() + finally + hadkey ? (tls[key] = old) : delete!(tls, key) + end +end + +# just wait for a task to be done, no error propagation +function _wait(t::Task) + if !istaskdone(t) + lock(t.donenotify) + try + while !istaskdone(t) + wait(t.donenotify) + end + finally + unlock(t.donenotify) + end + end + nothing +end + +function wait(t::Task) + t === current_task() && error("deadlock detected: cannot wait on current task") + _wait(t) + if istaskfailed(t) + throw(TaskFailedException(t)) + end + nothing +end + +fetch(@nospecialize x) = x + +""" + fetch(t::Task) + +Wait for a Task to finish, then return its result value. +If the task fails with an exception, a `TaskFailedException` (which wraps the failed task) +is thrown. +""" +function fetch(t::Task) + wait(t) + return task_result(t) +end + + +## lexically-scoped waiting for multiple items + +function sync_end(refs) + local c_ex + defined = false + for r in refs + if isa(r, Task) + _wait(r) + if istaskfailed(r) + if !defined + defined = true + c_ex = CompositeException() + end + push!(c_ex, TaskFailedException(r)) + end + else + try + wait(r) + catch e + if !defined + defined = true + c_ex = CompositeException() + end + push!(c_ex, e) + end + end + end + + if defined + throw(c_ex) + end + nothing +end + +const sync_varname = gensym(:sync) + +""" + @sync + +Wait until all lexically-enclosed uses of `@async`, `@spawn`, `@spawnat` and `@distributed` +are complete. All exceptions thrown by enclosed async operations are collected and thrown as +a `CompositeException`. +""" +macro sync(block) + var = esc(sync_varname) + quote + let $var = Any[] + v = $(esc(block)) + sync_end($var) + v + end + end +end + +# schedule an expression to run asynchronously + +""" + @async + +Wrap an expression in a [`Task`](@ref) and add it to the local machine's scheduler queue. +""" +macro async(expr) + thunk = esc(:(()->($expr))) + var = esc(sync_varname) + quote + local task = Task($thunk) + if $(Expr(:isdefined, var)) + push!($var, task) + end + schedule(task) + task + end +end + +# add a wait-able object to the sync pool +macro sync_add(expr) + var = esc(sync_varname) + quote + local ref = $(esc(expr)) + push!($var, ref) + ref + end +end + +function register_taskdone_hook(t::Task, hook) + tls = get_task_tls(t) + push!(get!(tls, :TASKDONE_HOOKS, []), hook) + return t +end + +# runtime system hook called when a task finishes +function task_done_hook(t::Task) + # `finish_task` sets `sigatomic` before entering this function + err = istaskfailed(t) + result = task_result(t) + handled = false + if err + t.backtrace = catch_backtrace() + end + + donenotify = t.donenotify + if isa(donenotify, ThreadSynchronizer) + lock(donenotify) + try + if !isempty(donenotify.waitq) + handled = true + notify(donenotify) + end + finally + unlock(donenotify) + end + end + + # Execute any other hooks registered in the TLS + if isa(t.storage, IdDict) && haskey(t.storage, :TASKDONE_HOOKS) + foreach(hook -> hook(t), t.storage[:TASKDONE_HOOKS]) + delete!(t.storage, :TASKDONE_HOOKS) + handled = true + end + + if err && !handled && Threads.threadid() == 1 + if isa(result, InterruptException) && isdefined(Base, :active_repl_backend) && + active_repl_backend.backend_task.state === :runnable && isempty(Workqueue) && + active_repl_backend.in_eval + throwto(active_repl_backend.backend_task, result) # this terminates the task + end + end + # Clear sigatomic before waiting + sigatomic_end() + try + wait() # this will not return + catch e + # If an InterruptException happens while blocked in the event loop, try handing + # the exception to the REPL task since the current task is done. + # issue #19467 + if Threads.threadid() == 1 && + isa(e, InterruptException) && isdefined(Base, :active_repl_backend) && + active_repl_backend.backend_task.state === :runnable && isempty(Workqueue) && + active_repl_backend.in_eval + throwto(active_repl_backend.backend_task, e) + else + rethrow() + end + end +end + + +## scheduler and work queue + +struct InvasiveLinkedListSynchronized{T} + queue::InvasiveLinkedList{T} + lock::Threads.SpinLock + InvasiveLinkedListSynchronized{T}() where {T} = new(InvasiveLinkedList{T}(), Threads.SpinLock()) +end +isempty(W::InvasiveLinkedListSynchronized) = isempty(W.queue) +length(W::InvasiveLinkedListSynchronized) = length(W.queue) +function push!(W::InvasiveLinkedListSynchronized{T}, t::T) where T + lock(W.lock) + try + push!(W.queue, t) + finally + unlock(W.lock) + end + return W +end +function pushfirst!(W::InvasiveLinkedListSynchronized{T}, t::T) where T + lock(W.lock) + try + pushfirst!(W.queue, t) + finally + unlock(W.lock) + end + return W +end +function pop!(W::InvasiveLinkedListSynchronized) + lock(W.lock) + try + return pop!(W.queue) + finally + unlock(W.lock) + end +end +function popfirst!(W::InvasiveLinkedListSynchronized) + lock(W.lock) + try + return popfirst!(W.queue) + finally + unlock(W.lock) + end +end +function list_deletefirst!(W::InvasiveLinkedListSynchronized{T}, t::T) where T + lock(W.lock) + try + list_deletefirst!(W.queue, t) + finally + unlock(W.lock) + end + return W +end + +const StickyWorkqueue = InvasiveLinkedListSynchronized{Task} +global const Workqueues = [StickyWorkqueue()] +global const Workqueue = Workqueues[1] # default work queue is thread 1 +function __preinit_threads__() + if length(Workqueues) < Threads.nthreads() + resize!(Workqueues, Threads.nthreads()) + for i = 2:length(Workqueues) + Workqueues[i] = StickyWorkqueue() + end + end + nothing +end + +function enq_work(t::Task) + (t.state === :runnable && t.queue === nothing) || error("schedule: Task not runnable") + tid = Threads.threadid(t) + # Note there are three reasons a Task might be put into a sticky queue + # even if t.sticky == false: + # 1. The Task's stack is currently being used by the scheduler for a certain thread. + # 2. There is only 1 thread. + # 3. The multiq is full (can be fixed by making it growable). + if t.sticky || tid != 0 || Threads.nthreads() == 1 + if tid == 0 + tid = Threads.threadid() + ccall(:jl_set_task_tid, Cvoid, (Any, Cint), t, tid-1) + end + push!(Workqueues[tid], t) + else + tid = 0 + if ccall(:jl_enqueue_task, Cint, (Any,), t) != 0 + # if multiq is full, give to a random thread (TODO fix) + tid = mod(time_ns() % Int, Threads.nthreads()) + 1 + ccall(:jl_set_task_tid, Cvoid, (Any, Cint), t, tid-1) + push!(Workqueues[tid], t) + end + end + ccall(:jl_wakeup_thread, Cvoid, (Int16,), (tid - 1) % Int16) + return t +end + +schedule(t::Task) = enq_work(t) + +""" + schedule(t::Task, [val]; error=false) + +Add a [`Task`](@ref) to the scheduler's queue. This causes the task to run constantly when the system +is otherwise idle, unless the task performs a blocking operation such as [`wait`](@ref). + +If a second argument `val` is provided, it will be passed to the task (via the return value of +[`yieldto`](@ref)) when it runs again. If `error` is `true`, the value is raised as an exception in +the woken task. + +# Examples +```jldoctest +julia> a5() = sum(i for i in 1:1000); + +julia> b = Task(a5); + +julia> istaskstarted(b) +false + +julia> schedule(b); + +julia> yield(); + +julia> istaskstarted(b) +true + +julia> istaskdone(b) +true +``` +""" +function schedule(t::Task, @nospecialize(arg); error=false) + # schedule a task to be (re)started with the given value or exception + t.state === :runnable || Base.error("schedule: Task not runnable") + if error + t.queue === nothing || Base.list_deletefirst!(t.queue, t) + setfield!(t, :exception, arg) + else + t.queue === nothing || Base.error("schedule: Task not runnable") + setfield!(t, :result, arg) + end + enq_work(t) + return t +end + +""" + yield() + +Switch to the scheduler to allow another scheduled task to run. A task that calls this +function is still runnable, and will be restarted immediately if there are no other runnable +tasks. +""" +function yield() + ct = current_task() + enq_work(ct) + try + wait() + catch + ct.queue === nothing || list_deletefirst!(ct.queue, ct) + rethrow() + end +end + +""" + yield(t::Task, arg = nothing) + +A fast, unfair-scheduling version of `schedule(t, arg); yield()` which +immediately yields to `t` before calling the scheduler. +""" +function yield(t::Task, @nospecialize(x=nothing)) + t.result = x + enq_work(current_task()) + return try_yieldto(ensure_rescheduled, Ref(t)) +end + +""" + yieldto(t::Task, arg = nothing) + +Switch to the given task. The first time a task is switched to, the task's function is +called with no arguments. On subsequent switches, `arg` is returned from the task's last +call to `yieldto`. This is a low-level call that only switches tasks, not considering states +or scheduling in any way. Its use is discouraged. +""" +function yieldto(t::Task, @nospecialize(x=nothing)) + t.result = x + return try_yieldto(identity, Ref(t)) +end + +function try_yieldto(undo, reftask::Ref{Task}) + try + ccall(:jl_switchto, Cvoid, (Any,), reftask) + catch + undo(reftask[]) + rethrow() + end + ct = current_task() + exc = ct.exception + if exc !== nothing + ct.exception = nothing + throw(exc) + end + result = ct.result + ct.result = nothing + return result +end + +# yield to a task, throwing an exception in it +function throwto(t::Task, @nospecialize exc) + t.exception = exc + return yieldto(t) +end + +function ensure_rescheduled(othertask::Task) + ct = current_task() + W = Workqueues[Threads.threadid()] + if ct !== othertask && othertask.state === :runnable + # we failed to yield to othertask + # return it to the head of a queue to be retried later + tid = Threads.threadid(othertask) + Wother = tid == 0 ? W : Workqueues[tid] + pushfirst!(Wother, othertask) + end + # if the current task was queued, + # also need to return it to the runnable state + # before throwing an error + list_deletefirst!(W, ct) + nothing +end + +function trypoptask(W::StickyWorkqueue) + isempty(W) && return + t = popfirst!(W) + if t.state !== :runnable + # assume this somehow got queued twice, + # probably broken now, but try discarding this switch and keep going + # can't throw here, because it's probably not the fault of the caller to wait + # and don't want to use print() here, because that may try to incur a task switch + ccall(:jl_safe_printf, Cvoid, (Ptr{UInt8}, Int32...), + "\nWARNING: Workqueue inconsistency detected: popfirst!(Workqueue).state != :runnable\n") + return + end + return t +end + +@noinline function poptaskref(W::StickyWorkqueue) + task = trypoptask(W) + if !(task isa Task) + gettask = () -> trypoptask(W) + task = ccall(:jl_task_get_next, Any, (Any,), gettask)::Task + end + return Ref(task) +end + +function wait() + W = Workqueues[Threads.threadid()] + reftask = poptaskref(W) + result = try_yieldto(ensure_rescheduled, reftask) + Sys.isjsvm() || process_events() + # return when we come out of the queue + return result +end + +if Sys.iswindows() + pause() = ccall(:Sleep, stdcall, Cvoid, (UInt32,), 0xffffffff) +else + pause() = ccall(:pause, Cvoid, ()) +end diff --git a/base/threadcall.jl b/base/threadcall.jl new file mode 100644 index 0000000..2267e4e --- /dev/null +++ b/base/threadcall.jl @@ -0,0 +1,100 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +const max_ccall_threads = parse(Int, get(ENV, "UV_THREADPOOL_SIZE", "4")) +const thread_notifiers = Union{Base.Condition, Nothing}[nothing for i in 1:max_ccall_threads] +const threadcall_restrictor = Semaphore(max_ccall_threads) + +""" + @threadcall((cfunc, clib), rettype, (argtypes...), argvals...) + +The `@threadcall` macro is called in the same way as [`ccall`](@ref) but does the work +in a different thread. This is useful when you want to call a blocking C +function without causing the main `julia` thread to become blocked. Concurrency +is limited by size of the libuv thread pool, which defaults to 4 threads but +can be increased by setting the `UV_THREADPOOL_SIZE` environment variable and +restarting the `julia` process. + +Note that the called function should never call back into Julia. +""" +macro threadcall(f, rettype, argtypes, argvals...) + # check for usage errors + isa(argtypes,Expr) && argtypes.head === :tuple || + error("threadcall: argument types must be a tuple") + length(argtypes.args) == length(argvals) || + error("threadcall: wrong number of arguments to C function") + + # hygiene escape arguments + f = esc(f) + rettype = esc(rettype) + argtypes = map(esc, argtypes.args) + argvals = map(esc, argvals) + + # construct non-allocating wrapper to call C function + wrapper = :(function (args_ptr::Ptr{Cvoid}, retval_ptr::Ptr{Cvoid}) + p = args_ptr + # the rest of the body is created below + end) + body = wrapper.args[2].args + args = Symbol[] + for (i, T) in enumerate(argtypes) + arg = Symbol("arg", i) + push!(body, :($arg = unsafe_load(convert(Ptr{$T}, p)))) + push!(body, :(p += Core.sizeof($T))) + push!(args, arg) + end + push!(body, :(ret = ccall($f, $rettype, ($(argtypes...),), $(args...)))) + push!(body, :(unsafe_store!(convert(Ptr{$rettype}, retval_ptr), ret))) + push!(body, :(return Int(Core.sizeof($rettype)))) + + # return code to generate wrapper function and send work request thread queue + wrapper = Expr(Symbol("hygienic-scope"), wrapper, @__MODULE__) + return :(let fun_ptr = @cfunction($wrapper, Int, (Ptr{Cvoid}, Ptr{Cvoid})) + do_threadcall(fun_ptr, $rettype, Any[$(argtypes...)], Any[$(argvals...)]) + end) +end + +function do_threadcall(fun_ptr::Ptr{Cvoid}, rettype::Type, argtypes::Vector, argvals::Vector) + # generate function pointer + c_notify_fun = @cfunction( + function notify_fun(idx) + global thread_notifiers + notify(thread_notifiers[idx]) + return + end, Cvoid, (Cint,)) + + # cconvert, root and unsafe_convert arguments + roots = Any[] + args_size = isempty(argtypes) ? 0 : sum(Core.sizeof, argtypes) + args_arr = Vector{UInt8}(undef, args_size) + ptr = pointer(args_arr) + for (T, x) in zip(argtypes, argvals) + isbitstype(T) || throw(ArgumentError("threadcall requires isbits argument types")) + y = cconvert(T, x) + push!(roots, y) + unsafe_store!(convert(Ptr{T}, ptr), unsafe_convert(T, y)::T) + ptr += Core.sizeof(T) + end + + # create return buffer + ret_arr = Vector{UInt8}(undef, Core.sizeof(rettype)) + + # wait for a worker thread to be available + acquire(threadcall_restrictor) + idx = findfirst(isequal(nothing), thread_notifiers)::Int + thread_notifiers[idx] = Base.Condition() + + GC.@preserve args_arr ret_arr roots begin + # queue up the work to be done + ccall(:jl_queue_work, Cvoid, + (Ptr{Cvoid}, Ptr{UInt8}, Ptr{UInt8}, Ptr{Cvoid}, Cint), + fun_ptr, args_arr, ret_arr, c_notify_fun, idx) + + # wait for a result & return it + wait(thread_notifiers[idx]) + thread_notifiers[idx] = nothing + release(threadcall_restrictor) + + r = unsafe_load(convert(Ptr{rettype}, pointer(ret_arr))) + end + return r +end diff --git a/base/threadingconstructs.jl b/base/threadingconstructs.jl new file mode 100644 index 0000000..b557e8a --- /dev/null +++ b/base/threadingconstructs.jl @@ -0,0 +1,124 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +export threadid, nthreads, @threads + +""" + Threads.threadid() + +Get the ID number of the current thread of execution. The master thread has ID `1`. +""" +threadid() = Int(ccall(:jl_threadid, Int16, ())+1) + +# Inclusive upper bound on threadid() +""" + Threads.nthreads() + +Get the number of threads available to the Julia process. This is the inclusive upper bound +on `threadid()`. +""" +nthreads() = Int(unsafe_load(cglobal(:jl_n_threads, Cint))) + +function _threadsfor(iter,lbody) + lidx = iter.args[1] # index + range = iter.args[2] + quote + local threadsfor_fun + let range = $(esc(range)) + function threadsfor_fun(onethread=false) + r = range # Load into local variable + lenr = length(r) + # divide loop iterations among threads + if onethread + tid = 1 + len, rem = lenr, 0 + else + tid = threadid() + len, rem = divrem(lenr, nthreads()) + end + # not enough iterations for all the threads? + if len == 0 + if tid > rem + return + end + len, rem = 1, 0 + end + # compute this thread's iterations + f = 1 + ((tid-1) * len) + l = f + len - 1 + # distribute remaining iterations evenly + if rem > 0 + if tid <= rem + f = f + (tid-1) + l = l + tid + else + f = f + rem + l = l + rem + end + end + # run this thread's iterations + for i = f:l + local $(esc(lidx)) = Base.unsafe_getindex(r,i) + $(esc(lbody)) + end + end + end + if threadid() != 1 + # only thread 1 can enter/exit _threadedregion + Base.invokelatest(threadsfor_fun, true) + else + ccall(:jl_threading_run, Cvoid, (Any,), threadsfor_fun) + end + nothing + end +end + +""" + Threads.@threads + +A macro to parallelize a for-loop to run with multiple threads. This spawns `nthreads()` +number of threads, splits the iteration space amongst them, and iterates in parallel. +A barrier is placed at the end of the loop which waits for all the threads to finish +execution, and the loop returns. +""" +macro threads(args...) + na = length(args) + if na != 1 + throw(ArgumentError("wrong number of arguments in @threads")) + end + ex = args[1] + if !isa(ex, Expr) + throw(ArgumentError("need an expression argument to @threads")) + end + if ex.head === :for + return _threadsfor(ex.args[1], ex.args[2]) + else + throw(ArgumentError("unrecognized argument to @threads")) + end +end + +""" + Threads.@spawn expr + +Create and run a [`Task`](@ref) on any available thread. To wait for the task to +finish, call [`wait`](@ref) on the result of this macro, or call [`fetch`](@ref) +to wait and then obtain its return value. + +!!! note + This feature is currently considered experimental. + +!!! compat "Julia 1.3" + This macro is available as of Julia 1.3. +""" +macro spawn(expr) + thunk = esc(:(()->($expr))) + var = esc(Base.sync_varname) + quote + local task = Task($thunk) + task.sticky = false + if $(Expr(:isdefined, var)) + push!($var, task) + end + schedule(task) + task + end +end diff --git a/base/threads.jl b/base/threads.jl new file mode 100644 index 0000000..2925134 --- /dev/null +++ b/base/threads.jl @@ -0,0 +1,35 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" +Experimental multithreading support. +""" +module Threads + +global Condition # we'll define this later, make sure we don't import Base.Condition + +include("threadingconstructs.jl") +include("atomics.jl") +include("locks-mt.jl") + + +""" + resize_nthreads!(A, copyvalue=A[1]) + +Resize the array `A` to length [`nthreads()`](@ref). Any new +elements that are allocated are initialized to `deepcopy(copyvalue)`, +where `copyvalue` defaults to `A[1]`. + +This is typically used to allocate per-thread variables, and +should be called in `__init__` if `A` is a global constant. +""" +function resize_nthreads!(A::AbstractVector, copyvalue=A[1]) + nthr = nthreads() + nold = length(A) + resize!(A, nthr) + for i = nold+1:nthr + A[i] = deepcopy(copyvalue) + end + return A +end + +end diff --git a/base/traits.jl b/base/traits.jl new file mode 100644 index 0000000..3c6a801 --- /dev/null +++ b/base/traits.jl @@ -0,0 +1,59 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## numeric/object traits +# trait for objects that have an ordering +abstract type OrderStyle end +struct Ordered <: OrderStyle end +struct Unordered <: OrderStyle end + +OrderStyle(instance) = OrderStyle(typeof(instance)) +OrderStyle(::Type{<:Real}) = Ordered() +OrderStyle(::Type{<:Any}) = Unordered() + +# trait for objects that support arithmetic +abstract type ArithmeticStyle end +struct ArithmeticRounds <: ArithmeticStyle end # least significant bits can be lost +struct ArithmeticWraps <: ArithmeticStyle end # most significant bits can be lost +struct ArithmeticUnknown <: ArithmeticStyle end + +ArithmeticStyle(instance) = ArithmeticStyle(typeof(instance)) +ArithmeticStyle(::Type{<:AbstractFloat}) = ArithmeticRounds() +ArithmeticStyle(::Type{<:Integer}) = ArithmeticWraps() +ArithmeticStyle(::Type{<:Any}) = ArithmeticUnknown() + +# trait for objects that support ranges with regular step +""" + RangeStepStyle(instance) + RangeStepStyle(T::Type) + +Indicate whether an instance or a type supports constructing a range with +a perfectly regular step or not. A regular step means that +[`step`](@ref) will always be exactly equal to the difference between two +subsequent elements in a range, i.e. for a range `r::AbstractRange{T}`: +```julia +all(diff(r) .== step(r)) +``` + +When a type `T` always leads to ranges with regular steps, it should +define the following method: +```julia +Base.RangeStepStyle(::Type{<:AbstractRange{<:T}}) = Base.RangeStepRegular() +``` +This will allow [`hash`](@ref) to use an O(1) algorithm for `AbstractRange{T}` +objects instead of the default O(N) algorithm (with N the length of the range). + +In some cases, whether the step will be regular depends not only on the +element type `T`, but also on the type of the step `S`. In that case, more +specific methods should be defined: +```julia +Base.RangeStepStyle(::Type{<:OrdinalRange{<:T, <:S}}) = Base.RangeStepRegular() +``` + +By default, all range types are assumed to be `RangeStepIrregular`, except +ranges with an element type which is a subtype of `Integer`. +""" +abstract type RangeStepStyle end +struct RangeStepRegular <: RangeStepStyle end # range with regular step +struct RangeStepIrregular <: RangeStepStyle end # range with rounding error + +RangeStepStyle(instance) = RangeStepStyle(typeof(instance)) diff --git a/base/tuple.jl b/base/tuple.jl new file mode 100644 index 0000000..7e8c680 --- /dev/null +++ b/base/tuple.jl @@ -0,0 +1,411 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Document NTuple here where we have everything needed for the doc system +""" + NTuple{N, T} + +A compact way of representing the type for a tuple of length `N` where all elements are of type `T`. + +# Examples +```jldoctest +julia> isa((1, 2, 3, 4, 5, 6), NTuple{6, Int}) +true +``` +""" +NTuple + +## indexing ## + +length(@nospecialize t::Tuple) = nfields(t) +firstindex(@nospecialize t::Tuple) = 1 +lastindex(@nospecialize t::Tuple) = length(t) +size(@nospecialize(t::Tuple), d::Integer) = (d == 1) ? length(t) : throw(ArgumentError("invalid tuple dimension $d")) +axes(@nospecialize t::Tuple) = (OneTo(length(t)),) +@eval getindex(@nospecialize(t::Tuple), i::Int) = getfield(t, i, $(Expr(:boundscheck))) +@eval getindex(@nospecialize(t::Tuple), i::Real) = getfield(t, convert(Int, i), $(Expr(:boundscheck))) +getindex(t::Tuple, r::AbstractArray{<:Any,1}) = ([t[ri] for ri in r]...,) +getindex(t::Tuple, b::AbstractArray{Bool,1}) = length(b) == length(t) ? getindex(t, findall(b)) : throw(BoundsError(t, b)) +getindex(t::Tuple, c::Colon) = t + +# returns new tuple; N.B.: becomes no-op if i is out-of-bounds +setindex(x::Tuple, v, i::Integer) = (@_inline_meta; _setindex(v, i, x...)) +function _setindex(v, i::Integer, first, tail...) + @_inline_meta + return (ifelse(i == 1, v, first), _setindex(v, i - 1, tail...)...) +end +_setindex(v, i::Integer) = () + + +## iterating ## + +function iterate(@nospecialize(t::Tuple), i::Int=1) + @_inline_meta + return (1 <= i <= length(t)) ? (@inbounds t[i], i + 1) : nothing +end + +keys(@nospecialize t::Tuple) = OneTo(length(t)) + +prevind(@nospecialize(t::Tuple), i::Integer) = Int(i)-1 +nextind(@nospecialize(t::Tuple), i::Integer) = Int(i)+1 + +function keys(t::Tuple, t2::Tuple...) + @_inline_meta + OneTo(_maxlength(t, t2...)) +end +_maxlength(t::Tuple) = length(t) +function _maxlength(t::Tuple, t2::Tuple, t3::Tuple...) + @_inline_meta + max(length(t), _maxlength(t2, t3...)) +end + +# this allows partial evaluation of bounded sequences of next() calls on tuples, +# while reducing to plain next() for arbitrary iterables. +indexed_iterate(t::Tuple, i::Int, state=1) = (@_inline_meta; (getfield(t, i), i+1)) +indexed_iterate(a::Array, i::Int, state=1) = (@_inline_meta; (a[i], i+1)) +function indexed_iterate(I, i) + x = iterate(I) + x === nothing && throw(BoundsError(I, i)) + x +end +function indexed_iterate(I, i, state) + x = iterate(I, state) + x === nothing && throw(BoundsError(I, i)) + x +end + +# Use dispatch to avoid a branch in first +first(::Tuple{}) = throw(ArgumentError("tuple must be non-empty")) +first(t::Tuple) = t[1] + +# eltype + +eltype(::Type{Tuple{}}) = Bottom +function eltype(t::Type{<:Tuple{Vararg{E}}}) where {E} + if @isdefined(E) + return E + else + # TODO: need to guard against E being miscomputed by subtyping (ref #23017) + # and compute the result manually in this case + return _compute_eltype(t) + end +end +eltype(t::Type{<:Tuple}) = _compute_eltype(t) +function _compute_eltype(t::Type{<:Tuple}) + @_pure_meta + t isa Union && return promote_typejoin(eltype(t.a), eltype(t.b)) + t´ = unwrap_unionall(t) + r = Union{} + for ti in t´.parameters + r = promote_typejoin(r, rewrap_unionall(unwrapva(ti), t)) + end + return r +end + +# version of tail that doesn't throw on empty tuples (used in array indexing) +safe_tail(t::Tuple) = tail(t) +safe_tail(t::Tuple{}) = () + +# front (the converse of tail: it skips the last entry) + +""" + front(x::Tuple)::Tuple + +Return a `Tuple` consisting of all but the last component of `x`. + +# Examples +```jldoctest +julia> Base.front((1,2,3)) +(1, 2) + +julia> Base.front(()) +ERROR: ArgumentError: Cannot call front on an empty tuple. +``` +""" +function front(t::Tuple) + @_inline_meta + _front(t...) +end +_front() = throw(ArgumentError("Cannot call front on an empty tuple.")) +_front(v) = () +function _front(v, t...) + @_inline_meta + (v, _front(t...)...) +end + +## mapping ## + +# 1 argument function +map(f, t::Tuple{}) = () +map(f, t::Tuple{Any,}) = (f(t[1]),) +map(f, t::Tuple{Any, Any}) = (f(t[1]), f(t[2])) +map(f, t::Tuple{Any, Any, Any}) = (f(t[1]), f(t[2]), f(t[3])) +map(f, t::Tuple) = (@_inline_meta; (f(t[1]), map(f,tail(t))...)) +# stop inlining after some number of arguments to avoid code blowup +const Any16{N} = Tuple{Any,Any,Any,Any,Any,Any,Any,Any, + Any,Any,Any,Any,Any,Any,Any,Any,Vararg{Any,N}} +const All16{T,N} = Tuple{T,T,T,T,T,T,T,T, + T,T,T,T,T,T,T,T,Vararg{T,N}} +function map(f, t::Any16) + n = length(t) + A = Vector{Any}(undef, n) + for i=1:n + A[i] = f(t[i]) + end + (A...,) +end +# 2 argument function +map(f, t::Tuple{}, s::Tuple{}) = () +map(f, t::Tuple{Any,}, s::Tuple{Any,}) = (f(t[1],s[1]),) +map(f, t::Tuple{Any,Any}, s::Tuple{Any,Any}) = (f(t[1],s[1]), f(t[2],s[2])) +function map(f, t::Tuple, s::Tuple) + @_inline_meta + (f(t[1],s[1]), map(f, tail(t), tail(s))...) +end +function map(f, t::Any16, s::Any16) + n = length(t) + A = Vector{Any}(undef, n) + for i = 1:n + A[i] = f(t[i], s[i]) + end + (A...,) +end +# n argument function +heads(ts::Tuple...) = map(t -> t[1], ts) +tails(ts::Tuple...) = map(tail, ts) +map(f, ::Tuple{}...) = () +function map(f, t1::Tuple, t2::Tuple, ts::Tuple...) + @_inline_meta + (f(heads(t1, t2, ts...)...), map(f, tails(t1, t2, ts...)...)...) +end +function map(f, t1::Any16, t2::Any16, ts::Any16...) + n = length(t1) + A = Vector{Any}(undef, n) + for i = 1:n + A[i] = f(t1[i], t2[i], map(t -> t[i], ts)...) + end + (A...,) +end + +# mapafoldl, based on afold in operators.jl +mapafoldl(F,op,a) = a +mapafoldl(F,op,a,b) = op(a,F(b)) +mapafoldl(F,op,a,b,c...) = mapafoldl(F, op, op(a,F(b)), c...) +function mapafoldl(F,op,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,qs...) + y = op(op(op(op(op(op(op(op(op(op(op(op(op(op(op(a,F(b)),F(c)),F(d)),F(e)),F(f)),F(g)),F(h)),F(i)),F(j)),F(k)),F(l)),F(m)),F(n)),F(o)),F(p)) + for x in qs; y = op(y,F(x)); end + y +end +mapfoldl_impl(f, op, nt::NamedTuple{(:init,)}, t::Tuple) = mapafoldl(f, op, nt.init, t...) +mapfoldl_impl(f, op, nt::NamedTuple{()}, t::Tuple) = mapafoldl(f, op, f(t[1]), tail(t)...) +mapfoldl_impl(f, op, nt::NamedTuple{()}, t::Tuple{}) = mapreduce_empty_iter(f, op, t, IteratorEltype(t)) + +# type-stable padding +fill_to_length(t::NTuple{N,Any}, val, ::Val{N}) where {N} = t +fill_to_length(t::Tuple{}, val, ::Val{1}) = (val,) +fill_to_length(t::Tuple{Any}, val, ::Val{2}) = (t..., val) +fill_to_length(t::Tuple{}, val, ::Val{2}) = (val, val) +#function fill_to_length(t::Tuple, val, ::Val{N}) where {N} +# @_inline_meta +# return (t..., ntuple(i -> val, N - length(t))...) +#end + +# constructing from an iterator + +# only define these in Base, to avoid overwriting the constructors +# NOTE: this means this constructor must be avoided in Core.Compiler! +if nameof(@__MODULE__) === :Base + +(::Type{T})(x::Tuple) where {T<:Tuple} = convert(T, x) # still use `convert` for tuples + +(::Type{T})(itr) where {T<:Tuple} = _totuple(T, itr) + +_totuple(::Type{Tuple{}}, itr, s...) = () + +function _totuple_err(@nospecialize T) + @_noinline_meta + throw(ArgumentError("too few elements for tuple type $T")) +end + +function _totuple(T, itr, s...) + @_inline_meta + y = iterate(itr, s...) + y === nothing && _totuple_err(T) + (convert(tuple_type_head(T), y[1]), _totuple(tuple_type_tail(T), itr, y[2])...) +end + +# use iterative algorithm for long tuples +function _totuple(T::Type{All16{E,N}}, itr) where {E,N} + len = N+16 + elts = collect(E, Iterators.take(itr,len)) + if length(elts) != len + _totuple_err(T) + end + (elts...,) +end + +_totuple(::Type{Tuple{Vararg{E}}}, itr, s...) where {E} = (collect(E, Iterators.rest(itr,s...))...,) + +_totuple(::Type{Tuple}, itr, s...) = (collect(Iterators.rest(itr,s...))...,) + +end + +## comparison ## + +isequal(t1::Tuple, t2::Tuple) = (length(t1) == length(t2)) && _isequal(t1, t2) +_isequal(t1::Tuple{}, t2::Tuple{}) = true +_isequal(t1::Tuple{Any}, t2::Tuple{Any}) = isequal(t1[1], t2[1]) +_isequal(t1::Tuple, t2::Tuple) = isequal(t1[1], t2[1]) && _isequal(tail(t1), tail(t2)) +function _isequal(t1::Any16, t2::Any16) + for i = 1:length(t1) + if !isequal(t1[i], t2[i]) + return false + end + end + return true +end + +==(t1::Tuple, t2::Tuple) = (length(t1) == length(t2)) && _eq(t1, t2) +_eq(t1::Tuple{}, t2::Tuple{}) = true +_eq_missing(t1::Tuple{}, t2::Tuple{}) = missing +function _eq(t1::Tuple, t2::Tuple) + eq = t1[1] == t2[1] + if eq === false + return false + elseif ismissing(eq) + return _eq_missing(tail(t1), tail(t2)) + else + return _eq(tail(t1), tail(t2)) + end +end +function _eq_missing(t1::Tuple, t2::Tuple) + eq = t1[1] == t2[1] + if eq === false + return false + else + return _eq_missing(tail(t1), tail(t2)) + end +end +function _eq(t1::Any16, t2::Any16) + anymissing = false + for i = 1:length(t1) + eq = (t1[i] == t2[i]) + if ismissing(eq) + anymissing = true + elseif !eq + return false + end + end + return anymissing ? missing : true +end + +const tuplehash_seed = UInt === UInt64 ? 0x77cfa1eef01bca90 : 0xf01bca90 +hash(::Tuple{}, h::UInt) = h + tuplehash_seed +hash(t::Tuple, h::UInt) = hash(t[1], hash(tail(t), h)) +function hash(t::Any16, h::UInt) + out = h + tuplehash_seed + for i = length(t):-1:1 + out = hash(t[i], out) + end + return out +end + +<(::Tuple{}, ::Tuple{}) = false +<(::Tuple{}, ::Tuple) = true +<(::Tuple, ::Tuple{}) = false +function <(t1::Tuple, t2::Tuple) + a, b = t1[1], t2[1] + eq = (a == b) + if ismissing(eq) + return missing + elseif !eq + return a < b + end + return tail(t1) < tail(t2) +end +function <(t1::Any16, t2::Any16) + n1, n2 = length(t1), length(t2) + for i = 1:min(n1, n2) + a, b = t1[i], t2[i] + eq = (a == b) + if ismissing(eq) + return missing + elseif !eq + return a < b + end + end + return n1 < n2 +end + +isless(::Tuple{}, ::Tuple{}) = false +isless(::Tuple{}, ::Tuple) = true +isless(::Tuple, ::Tuple{}) = false + +""" + isless(t1::Tuple, t2::Tuple) + +Returns true when t1 is less than t2 in lexicographic order. +""" +function isless(t1::Tuple, t2::Tuple) + a, b = t1[1], t2[1] + isless(a, b) || (isequal(a, b) && isless(tail(t1), tail(t2))) +end +function isless(t1::Any16, t2::Any16) + n1, n2 = length(t1), length(t2) + for i = 1:min(n1, n2) + a, b = t1[i], t2[i] + if !isequal(a, b) + return isless(a, b) + end + end + return n1 < n2 +end + +## functions ## + +isempty(x::Tuple{}) = true +isempty(@nospecialize x::Tuple) = false + +revargs() = () +revargs(x, r...) = (revargs(r...)..., x) + +reverse(t::Tuple) = revargs(t...) + +## specialized reduction ## + +# TODO: these definitions cannot yet be combined, since +(x...) +# where x might be any tuple matches too many methods. +# TODO: this is inconsistent with the regular sum in cases where the arguments +# require size promotion to system size. +sum(x::Tuple{Any, Vararg{Any}}) = +(x...) + +# NOTE: should remove, but often used on array sizes +# TODO: this is inconsistent with the regular prod in cases where the arguments +# require size promotion to system size. +prod(x::Tuple{}) = 1 +prod(x::Tuple{Any, Vararg{Any}}) = *(x...) + +all(x::Tuple{}) = true +all(x::Tuple{Bool}) = x[1] +all(x::Tuple{Bool, Bool}) = x[1]&x[2] +all(x::Tuple{Bool, Bool, Bool}) = x[1]&x[2]&x[3] +# use generic reductions for the rest + +any(x::Tuple{}) = false +any(x::Tuple{Bool}) = x[1] +any(x::Tuple{Bool, Bool}) = x[1]|x[2] +any(x::Tuple{Bool, Bool, Bool}) = x[1]|x[2]|x[3] + +# equivalent to any(f, t), to be used only in bootstrap +_tuple_any(f::Function, t::Tuple) = _tuple_any(f, false, t...) +function _tuple_any(f::Function, tf::Bool, a, b...) + @_inline_meta + _tuple_any(f, tf | f(a), b...) +end +_tuple_any(f::Function, tf::Bool) = tf + +""" + empty(x::Tuple) + +Returns an empty tuple, `()`. +""" +empty(@nospecialize x::Tuple) = () diff --git a/base/twiceprecision.jl b/base/twiceprecision.jl new file mode 100644 index 0000000..ebaae7c --- /dev/null +++ b/base/twiceprecision.jl @@ -0,0 +1,710 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Twice-precision arithmetic. + +# Necessary for creating nicely-behaved ranges like r = 0.1:0.1:0.3 +# that return r[3] == 0.3. Otherwise, we have roundoff error due to +# 0.1 + 2*0.1 = 0.30000000000000004 + +""" + hi, lo = splitprec(F::Type{<:AbstractFloat}, i::Integer) + +Represent an integer `i` as a pair of floating-point numbers `hi` and +`lo` (of type `F`) such that: +- `widen(hi) + widen(lo) ≈ i`. It is exact if 1.5 * (number of precision bits in `F`) is greater than the number of bits in `i`. +- all bits in `hi` are more significant than any of the bits in `lo` +- `hi` can be exactly multiplied by the `hi` component of another call to `splitprec`. + +In particular, while `convert(Float64, i)` can be lossy since Float64 +has only 53 bits of precision, `splitprec(Float64, i)` is exact for +any Int64/UInt64. +""" +function splitprec(::Type{F}, i::Integer) where {F<:AbstractFloat} + hi = truncbits(F(i), cld(precision(F), 2)) + ihi = oftype(i, hi) + hi, F(i - ihi) +end + +function truncmask(x::F, mask) where {F<:IEEEFloat} + reinterpret(F, mask & reinterpret(uinttype(F), x)) +end +truncmask(x, mask) = x + +function truncbits(x::F, nb) where {F<:IEEEFloat} + truncmask(x, typemax(uinttype(F)) << nb) +end +truncbits(x, nb) = x + + +## Dekker arithmetic + +""" + hi, lo = canonicalize2(big, little) + +Generate a representation where all the nonzero bits in `hi` are more +significant than any of the nonzero bits in `lo`. `big` must be larger +in absolute value than `little`. +""" +function canonicalize2(big, little) + h = big+little + h, (big - h) + little +end + +""" + zhi, zlo = add12(x, y) + +A high-precision representation of `x + y` for floating-point +numbers. Mathematically, `zhi + zlo = x + y`, where `zhi` contains the +most significant bits and `zlo` the least significant. + +Because of the way floating-point numbers are printed, `lo` may not +look the way you might expect from the standpoint of decimal +representation, even though it is exact from the standpoint of binary +representation. + +Example: +```julia +julia> 1.0 + 1.0001e-15 +1.000000000000001 + +julia> big(1.0) + big(1.0001e-15) +1.000000000000001000100000000000020165767380775934141445417482375879192346701529 + +julia> hi, lo = Base.add12(1.0, 1.0001e-15) +(1.000000000000001, -1.1012302462515652e-16) + +julia> big(hi) + big(lo) +1.000000000000001000100000000000020165767380775934141445417482375879192346701529 +``` + +`lo` differs from 1.0e-19 because `hi` is not exactly equal to +the first 16 decimal digits of the answer. +""" +function add12(x::T, y::T) where {T} + x, y = ifelse(abs(y) > abs(x), (y, x), (x, y)) + canonicalize2(x, y) +end +add12(x, y) = add12(promote(x, y)...) + +""" + zhi, zlo = mul12(x, y) + +A high-precision representation of `x * y` for floating-point +numbers. Mathematically, `zhi + zlo = x * y`, where `zhi` contains the +most significant bits and `zlo` the least significant. + +Example: +```julia +julia> x = Float32(π) +3.1415927f0 + +julia> x * x +9.869605f0 + +julia> Float64(x) * Float64(x) +9.869604950382893 + +julia> hi, lo = Base.mul12(x, x) +(9.869605f0, -1.140092f-7) + +julia> Float64(hi) + Float64(lo) +9.869604950382893 +``` +""" +function mul12(x::T, y::T) where {T<:AbstractFloat} + h = x * y + ifelse(iszero(h) | !isfinite(h), (h, h), canonicalize2(h, fma(x, y, -h))) +end +mul12(x::T, y::T) where {T} = (p = x * y; (p, zero(p))) +mul12(x, y) = mul12(promote(x, y)...) + +""" + zhi, zlo = div12(x, y) + +A high-precision representation of `x / y` for floating-point +numbers. Mathematically, `zhi + zlo ≈ x / y`, where `zhi` contains the +most significant bits and `zlo` the least significant. + +Example: +```julia +julia> x, y = Float32(π), 3.1f0 +(3.1415927f0, 3.1f0) + +julia> x / y +1.013417f0 + +julia> Float64(x) / Float64(y) +1.0134170444063078 + +julia> hi, lo = Base.div12(x, y) +(1.013417f0, 3.8867366f-8) + +julia> Float64(hi) + Float64(lo) +1.0134170444063066 +""" +function div12(x::T, y::T) where {T<:AbstractFloat} + # We lose precision if any intermediate calculation results in a subnormal. + # To prevent this from happening, standardize the values. + xs, xe = frexp(x) + ys, ye = frexp(y) + r = xs / ys + rh, rl = canonicalize2(r, -fma(r, ys, -xs)/ys) + ifelse(iszero(r) | !isfinite(r), (r, r), (ldexp(rh, xe-ye), ldexp(rl, xe-ye))) +end +div12(x::T, y::T) where {T} = (p = x / y; (p, zero(p))) +div12(x, y) = div12(promote(x, y)...) + + +## TwicePrecision + +""" + TwicePrecision{T}(hi::T, lo::T) + TwicePrecision{T}((num, denom)) + +A number with twice the precision of `T`, e.g., quad-precision if `T = +Float64`. `hi` represents the high bits (most significant bits) and +`lo` the low bits (least significant bits). Rational values +`num//denom` can be approximated conveniently using the syntax +`TwicePrecision{T}((num, denom))`. + +When used with `T<:Union{Float16,Float32,Float64}` to construct an "exact" +`StepRangeLen`, `ref` should be the range element with smallest +magnitude and `offset` set to the corresponding index. For +efficiency, multiplication of `step` by the index is not performed at +twice precision: `step.hi` should have enough trailing zeros in its +`bits` representation that `(0:len-1)*step.hi` is exact (has no +roundoff error). If `step` has an exact rational representation +`num//denom`, then you can construct `step` using + + step = TwicePrecision{T}((num, denom), nb) + +where `nb` is the number of trailing zero bits of `step.hi`. For +ranges, you can set `nb = ceil(Int, log2(len-1))`. +""" +struct TwicePrecision{T} + hi::T # most significant bits + lo::T # least significant bits +end + +TwicePrecision{T}(x::T) where {T} = TwicePrecision{T}(x, zero(T)) + +function TwicePrecision{T}(x) where {T} + xT = convert(T, x) + Δx = x - xT + TwicePrecision{T}(xT, T(Δx)) +end + +TwicePrecision{T}(i::Integer) where {T<:AbstractFloat} = + TwicePrecision{T}(canonicalize2(splitprec(T, i)...)...) + +TwicePrecision(x) = TwicePrecision{typeof(x)}(x) + +# Numerator/Denominator constructors +function TwicePrecision{T}(nd::Tuple{Integer,Integer}) where {T<:Union{Float16,Float32}} + n, d = nd + TwicePrecision{T}(n/d) +end + +function TwicePrecision{T}(nd::Tuple{Any,Any}) where {T} + n, d = nd + TwicePrecision{T}(n) / d +end + +function TwicePrecision{T}(nd::Tuple{I,I}, nb::Integer) where {T,I} + twiceprecision(TwicePrecision{T}(nd), nb) +end + +# Truncating constructors. Useful for generating values that can be +# exactly multiplied by small integers. +function twiceprecision(val::T, nb::Integer) where {T<:IEEEFloat} + hi = truncbits(val, nb) + TwicePrecision{T}(hi, val - hi) +end + +function twiceprecision(val::TwicePrecision{T}, nb::Integer) where {T<:IEEEFloat} + hi = truncbits(val.hi, nb) + TwicePrecision{T}(hi, (val.hi - hi) + val.lo) +end + +nbitslen(r::StepRangeLen) = nbitslen(eltype(r), length(r), r.offset) +nbitslen(::Type{T}, len, offset) where {T<:IEEEFloat} = + min(cld(precision(T), 2), nbitslen(len, offset)) +# The +1 here is for safety, because the precision of the significand +# is 1 bit higher than the number that are explicitly stored. +nbitslen(len, offset) = len < 2 ? 0 : ceil(Int, log2(max(offset-1, len-offset))) + 1 + +eltype(::Type{TwicePrecision{T}}) where {T} = T + +promote_rule(::Type{TwicePrecision{R}}, ::Type{TwicePrecision{S}}) where {R,S} = + TwicePrecision{promote_type(R,S)} +promote_rule(::Type{TwicePrecision{R}}, ::Type{S}) where {R,S<:Number} = + TwicePrecision{promote_type(R,S)} + +(::Type{T})(x::TwicePrecision) where {T<:Number} = T(x.hi + x.lo)::T +TwicePrecision{T}(x::Number) where {T} = TwicePrecision{T}(T(x), zero(T)) + +convert(::Type{TwicePrecision{T}}, x::TwicePrecision{T}) where {T} = x +convert(::Type{TwicePrecision{T}}, x::TwicePrecision) where {T} = + TwicePrecision{T}(convert(T, x.hi), convert(T, x.lo)) + +convert(::Type{T}, x::TwicePrecision) where {T<:Number} = T(x) +convert(::Type{TwicePrecision{T}}, x::Number) where {T} = TwicePrecision{T}(x) + +float(x::TwicePrecision{<:AbstractFloat}) = x +float(x::TwicePrecision) = TwicePrecision(float(x.hi), float(x.lo)) + +big(x::TwicePrecision) = big(x.hi) + big(x.lo) + +-(x::TwicePrecision) = TwicePrecision(-x.hi, -x.lo) + +zero(::Type{TwicePrecision{T}}) where {T} = TwicePrecision{T}(0, 0) + +# Arithmetic + +function +(x::TwicePrecision, y::Number) + s_hi, s_lo = add12(x.hi, y) + TwicePrecision(canonicalize2(s_hi, s_lo+x.lo)...) +end ++(x::Number, y::TwicePrecision) = y+x + +function +(x::TwicePrecision{T}, y::TwicePrecision{T}) where T + r = x.hi + y.hi + s = abs(x.hi) > abs(y.hi) ? (((x.hi - r) + y.hi) + y.lo) + x.lo : (((y.hi - r) + x.hi) + x.lo) + y.lo + TwicePrecision(canonicalize2(r, s)...) +end ++(x::TwicePrecision, y::TwicePrecision) = +(promote(x, y)...) + +-(x::TwicePrecision, y::TwicePrecision) = x + (-y) +-(x::TwicePrecision, y::Number) = x + (-y) +-(x::Number, y::TwicePrecision) = x + (-y) + +function *(x::TwicePrecision, v::Number) + v == 0 && return TwicePrecision(x.hi*v, x.lo*v) + x * TwicePrecision(oftype(x.hi*v, v)) +end +function *(x::TwicePrecision{<:IEEEFloat}, v::Integer) + v == 0 && return TwicePrecision(x.hi*v, x.lo*v) + nb = ceil(Int, log2(abs(v))) + u = truncbits(x.hi, nb) + TwicePrecision(canonicalize2(u*v, ((x.hi-u) + x.lo)*v)...) +end +*(v::Number, x::TwicePrecision) = x*v + +function *(x::TwicePrecision{T}, y::TwicePrecision{T}) where {T} + zh, zl = mul12(x.hi, y.hi) + ret = TwicePrecision{T}(canonicalize2(zh, (x.hi * y.lo + x.lo * y.hi) + zl)...) + ifelse(iszero(zh) | !isfinite(zh), TwicePrecision{T}(zh, zh), ret) +end +*(x::TwicePrecision, y::TwicePrecision) = *(promote(x, y)...) + +function /(x::TwicePrecision, v::Number) + x / TwicePrecision(oftype(x.hi/v, v)) +end + +function /(x::TwicePrecision, y::TwicePrecision) + hi = x.hi / y.hi + uh, ul = mul12(hi, y.hi) + lo = ((((x.hi - uh) - ul) + x.lo) - hi*y.lo)/y.hi + ret = TwicePrecision(canonicalize2(hi, lo)...) + ifelse(iszero(hi) | !isfinite(hi), TwicePrecision(hi, hi), ret) +end + +## StepRangeLen + +# Use TwicePrecision only for Float64; use Float64 for T<:Union{Float16,Float32} +# See also _linspace1 +# Ratio-of-integers constructors +function steprangelen_hp(::Type{Float64}, ref::Tuple{Integer,Integer}, + step::Tuple{Integer,Integer}, nb::Integer, + len::Integer, offset::Integer) + StepRangeLen(TwicePrecision{Float64}(ref), + TwicePrecision{Float64}(step, nb), Int(len), offset) +end + +function steprangelen_hp(::Type{T}, ref::Tuple{Integer,Integer}, + step::Tuple{Integer,Integer}, nb::Integer, + len::Integer, offset::Integer) where {T<:IEEEFloat} + StepRangeLen{T}(ref[1]/ref[2], step[1]/step[2], Int(len), offset) +end + +# AbstractFloat constructors (can supply a single number or a 2-tuple +const F_or_FF = Union{AbstractFloat, Tuple{AbstractFloat,AbstractFloat}} +asF64(x::AbstractFloat) = Float64(x) +asF64(x::Tuple{AbstractFloat,AbstractFloat}) = Float64(x[1]) + Float64(x[2]) + +# Defined to prevent splatting in the function below which here has a performance impact +_TP(x) = TwicePrecision{Float64}(x) +_TP(x::Tuple{Any, Any}) = TwicePrecision{Float64}(x[1], x[2]) +function steprangelen_hp(::Type{Float64}, ref::F_or_FF, + step::F_or_FF, nb::Integer, + len::Integer, offset::Integer) + StepRangeLen(_TP(ref), + twiceprecision(_TP(step), nb), Int(len), offset) +end + +function steprangelen_hp(::Type{T}, ref::F_or_FF, + step::F_or_FF, nb::Integer, + len::Integer, offset::Integer) where {T<:IEEEFloat} + StepRangeLen{T}(asF64(ref), + asF64(step), Int(len), offset) +end + + + +StepRangeLen(ref::TwicePrecision{T}, step::TwicePrecision{T}, + len::Integer, offset::Integer=1) where {T} = + StepRangeLen{T,TwicePrecision{T},TwicePrecision{T}}(ref, step, len, offset) + +# Construct range for rational start=start_n/den, step=step_n/den +function floatrange(::Type{T}, start_n::Integer, step_n::Integer, len::Integer, den::Integer) where T + if len < 2 || step_n == 0 + return steprangelen_hp(T, (start_n, den), (step_n, den), 0, Int(len), 1) + end + # index of smallest-magnitude value + imin = clamp(round(Int, -start_n/step_n+1), 1, Int(len)) + # Compute smallest-magnitude element to 2x precision + ref_n = start_n+(imin-1)*step_n # this shouldn't overflow, so don't check + nb = nbitslen(T, len, imin) + steprangelen_hp(T, (ref_n, den), (step_n, den), nb, Int(len), imin) +end + +function floatrange(a::AbstractFloat, st::AbstractFloat, len::Real, divisor::AbstractFloat) + T = promote_type(typeof(a), typeof(st), typeof(divisor)) + m = maxintfloat(T, Int) + if abs(a) <= m && abs(st) <= m && abs(divisor) <= m + ia, ist, idivisor = round(Int, a), round(Int, st), round(Int, divisor) + if ia == a && ist == st && idivisor == divisor + # We can return the high-precision range + return floatrange(T, ia, ist, Int(len), idivisor) + end + end + # Fallback (misses the opportunity to set offset different from 1, + # but otherwise this is still high-precision) + steprangelen_hp(T, (a,divisor), (st,divisor), nbitslen(T, len, 1), Int(len), 1) +end + +function (:)(start::T, step::T, stop::T) where T<:Union{Float16,Float32,Float64} + step == 0 && throw(ArgumentError("range step cannot be zero")) + # see if the inputs have exact rational approximations (and if so, + # perform all computations in terms of the rationals) + step_n, step_d = rat(step) + if step_d != 0 && T(step_n/step_d) == step + start_n, start_d = rat(start) + stop_n, stop_d = rat(stop) + if start_d != 0 && stop_d != 0 && + T(start_n/start_d) == start && T(stop_n/stop_d) == stop + den = lcm_unchecked(start_d, step_d) # use same denominator for start and step + m = maxintfloat(T, Int) + if den != 0 && abs(start*den) <= m && abs(step*den) <= m && # will round succeed? + rem(den, start_d) == 0 && rem(den, step_d) == 0 # check lcm overflow + start_n = round(Int, start*den) + step_n = round(Int, step*den) + len = max(0, div(den*stop_n - stop_d*start_n + step_n*stop_d, step_n*stop_d)) + # Integer ops could overflow, so check that this makes sense + if isbetween(start, start + (len-1)*step, stop + step/2) && + !isbetween(start, start + len*step, stop) + # Return a 2x precision range + return floatrange(T, start_n, step_n, len, den) + end + end + end + end + # Fallback, taking start and step literally + lf = (stop-start)/step + if lf < 0 + len = 0 + elseif lf == 0 + len = 1 + else + len = round(Int, lf) + 1 + stop′ = start + (len-1)*step + # if we've overshot the end, subtract one: + len -= (start < stop < stop′) + (start > stop > stop′) + end + steprangelen_hp(T, start, step, 0, len, 1) +end + +function _range(a::T, st::T, ::Nothing, len::Integer) where T<:Union{Float16,Float32,Float64} + start_n, start_d = rat(a) + step_n, step_d = rat(st) + if start_d != 0 && step_d != 0 && + T(start_n/start_d) == a && T(step_n/step_d) == st + den = lcm_unchecked(start_d, step_d) + m = maxintfloat(T, Int) + if abs(den*a) <= m && abs(den*st) <= m && + rem(den, start_d) == 0 && rem(den, step_d) == 0 + start_n = round(Int, den*a) + step_n = round(Int, den*st) + return floatrange(T, start_n, step_n, len, den) + end + end + steprangelen_hp(T, a, st, 0, len, 1) +end + +# This assumes that r.step has already been split so that (0:len-1)*r.step.hi is exact +function unsafe_getindex(r::StepRangeLen{T,<:TwicePrecision,<:TwicePrecision}, i::Integer) where T + # Very similar to _getindex_hiprec, but optimized to avoid a 2nd call to add12 + @_inline_meta + u = i - r.offset + shift_hi, shift_lo = u*r.step.hi, u*r.step.lo + x_hi, x_lo = add12(r.ref.hi, shift_hi) + T(x_hi + (x_lo + (shift_lo + r.ref.lo))) +end + +function _getindex_hiprec(r::StepRangeLen{<:Any,<:TwicePrecision,<:TwicePrecision}, i::Integer) + u = i - r.offset + shift_hi, shift_lo = u*r.step.hi, u*r.step.lo + x_hi, x_lo = add12(r.ref.hi, shift_hi) + x_hi, x_lo = add12(x_hi, x_lo + (shift_lo + r.ref.lo)) + TwicePrecision(x_hi, x_lo) +end + +function getindex(r::StepRangeLen{T,<:TwicePrecision,<:TwicePrecision}, s::OrdinalRange{<:Integer}) where T + @boundscheck checkbounds(r, s) + soffset = 1 + round(Int, (r.offset - first(s))/step(s)) + soffset = clamp(soffset, 1, length(s)) + ioffset = first(s) + (soffset-1)*step(s) + if step(s) == 1 || length(s) < 2 + newstep = r.step + else + newstep = twiceprecision(r.step*step(s), nbitslen(T, length(s), soffset)) + end + if ioffset == r.offset + StepRangeLen(r.ref, newstep, length(s), max(1,soffset)) + else + StepRangeLen(r.ref + (ioffset-r.offset)*r.step, newstep, length(s), max(1,soffset)) + end +end + +*(x::Real, r::StepRangeLen{<:Real,<:TwicePrecision}) = + StepRangeLen(x*r.ref, twiceprecision(x*r.step, nbitslen(r)), length(r), r.offset) +*(r::StepRangeLen{<:Real,<:TwicePrecision}, x::Real) = x*r +/(r::StepRangeLen{<:Real,<:TwicePrecision}, x::Real) = + StepRangeLen(r.ref/x, twiceprecision(r.step/x, nbitslen(r)), length(r), r.offset) + +StepRangeLen{T,R,S}(r::StepRangeLen{T,R,S}) where {T<:AbstractFloat,R<:TwicePrecision,S<:TwicePrecision} = r + +StepRangeLen{T,R,S}(r::StepRangeLen) where {T<:AbstractFloat,R<:TwicePrecision,S<:TwicePrecision} = + _convertSRL(StepRangeLen{T,R,S}, r) + +(::Type{StepRangeLen{Float64}})(r::StepRangeLen) = + _convertSRL(StepRangeLen{Float64,TwicePrecision{Float64},TwicePrecision{Float64}}, r) +StepRangeLen{T}(r::StepRangeLen) where {T<:IEEEFloat} = + _convertSRL(StepRangeLen{T,Float64,Float64}, r) + +(::Type{StepRangeLen{Float64}})(r::AbstractRange) = + _convertSRL(StepRangeLen{Float64,TwicePrecision{Float64},TwicePrecision{Float64}}, r) +StepRangeLen{T}(r::AbstractRange) where {T<:IEEEFloat} = + _convertSRL(StepRangeLen{T,Float64,Float64}, r) + +function _convertSRL(::Type{StepRangeLen{T,R,S}}, r::StepRangeLen{<:Integer}) where {T,R,S} + StepRangeLen{T,R,S}(R(r.ref), S(r.step), length(r), r.offset) +end + +function _convertSRL(::Type{StepRangeLen{T,R,S}}, r::AbstractRange{<:Integer}) where {T,R,S} + StepRangeLen{T,R,S}(R(first(r)), S(step(r)), length(r)) +end + +function _convertSRL(::Type{StepRangeLen{T,R,S}}, r::AbstractRange{U}) where {T,R,S,U} + # if start and step have a rational approximation in the old type, + # then we transfer that rational approximation to the new type + f, s = first(r), step(r) + start_n, start_d = rat(f) + step_n, step_d = rat(s) + if start_d != 0 && step_d != 0 && + U(start_n/start_d) == f && U(step_n/step_d) == s + den = lcm_unchecked(start_d, step_d) + m = maxintfloat(T, Int) + if den != 0 && abs(f*den) <= m && abs(s*den) <= m && + rem(den, start_d) == 0 && rem(den, step_d) == 0 + start_n = round(Int, f*den) + step_n = round(Int, s*den) + return floatrange(T, start_n, step_n, length(r), den) + end + end + __convertSRL(StepRangeLen{T,R,S}, r) +end + +function __convertSRL(::Type{StepRangeLen{T,R,S}}, r::StepRangeLen{U}) where {T,R,S,U} + StepRangeLen{T,R,S}(R(r.ref), S(r.step), length(r), r.offset) +end +function __convertSRL(::Type{StepRangeLen{T,R,S}}, r::AbstractRange{U}) where {T,R,S,U} + StepRangeLen{T,R,S}(R(first(r)), S(step(r)), length(r)) +end + +function sum(r::StepRangeLen) + l = length(r) + # Compute the contribution of step over all indices. + # Indexes on opposite side of r.offset contribute with opposite sign, + # r.step * (sum(1:np) - sum(1:nn)) + np, nn = l - r.offset, r.offset - 1 # positive, negative + # To prevent overflow in sum(1:n), multiply its factors by the step + sp, sn = sumpair(np), sumpair(nn) + tp = _tp_prod(r.step, sp[1], sp[2]) + tn = _tp_prod(r.step, sn[1], sn[2]) + s_hi, s_lo = add12(tp.hi, -tn.hi) + s_lo += tp.lo - tn.lo + # Add in contributions of ref + ref = r.ref * l + sm_hi, sm_lo = add12(s_hi, ref.hi) + add12(sm_hi, sm_lo + ref.lo)[1] +end + +# sum(1:n) as a product of two integers +sumpair(n::Integer) = iseven(n) ? (n+1, n>>1) : (n, (n+1)>>1) + +function +(r1::StepRangeLen{T,R}, r2::StepRangeLen{T,R}) where T where R<:TwicePrecision + len = length(r1) + (len == length(r2) || + throw(DimensionMismatch("argument dimensions must match"))) + if r1.offset == r2.offset + imid = r1.offset + ref = r1.ref + r2.ref + else + imid = round(Int, (r1.offset+r2.offset)/2) + ref1mid = _getindex_hiprec(r1, imid) + ref2mid = _getindex_hiprec(r2, imid) + ref = ref1mid + ref2mid + end + step = twiceprecision(r1.step + r2.step, nbitslen(T, len, imid)) + StepRangeLen{T,typeof(ref),typeof(step)}(ref, step, len, imid) +end + +## LinRange + +# For Float16, Float32, and Float64, this returns a StepRangeLen +function _range(start::T, ::Nothing, stop::T, len::Integer) where {T<:IEEEFloat} + len < 2 && return _linspace1(T, start, stop, len) + if start == stop + return steprangelen_hp(T, start, zero(T), 0, len, 1) + end + # Attempt to find exact rational approximations + start_n, start_d = rat(start) + stop_n, stop_d = rat(stop) + if start_d != 0 && stop_d != 0 + den = lcm_unchecked(start_d, stop_d) + m = maxintfloat(T, Int) + if den != 0 && abs(den*start) <= m && abs(den*stop) <= m + start_n = round(Int, den*start) + stop_n = round(Int, den*stop) + if T(start_n/den) == start && T(stop_n/den) == stop + return _linspace(T, start_n, stop_n, len, den) + end + end + end + _linspace(start, stop, len) +end + +function _linspace(start::T, stop::T, len::Integer) where {T<:IEEEFloat} + (isfinite(start) && isfinite(stop)) || throw(ArgumentError("start and stop must be finite, got $start and $stop")) + # Find the index that returns the smallest-magnitude element + Δ, Δfac = stop-start, 1 + if !isfinite(Δ) # handle overflow for large endpoints + Δ, Δfac = stop/len - start/len, Int(len) + end + tmin = -(start/Δ)/Δfac # t such that (1-t)*start + t*stop == 0 + imin = round(Int, tmin*(len-1)+1) # index approximately corresponding to t + if 1 < imin < len + # The smallest-magnitude element is in the interior + t = (imin-1)/(len-1) + ref = T((1-t)*start + t*stop) + step = imin-1 < len-imin ? (ref-start)/(imin-1) : (stop-ref)/(len-imin) + elseif imin <= 1 + imin = 1 + ref = start + step = (Δ/(len-1))*Δfac + else + imin = Int(len) + ref = stop + step = (Δ/(len-1))*Δfac + end + if len == 2 && !isfinite(step) + # For very large endpoints where step overflows, exploit the + # split-representation to handle the overflow + return steprangelen_hp(T, start, (-start, stop), 0, 2, 1) + end + # 2x calculations to get high precision endpoint matching while also + # preventing overflow in ref_hi+(i-offset)*step_hi + m, k = prevfloat(floatmax(T)), max(imin-1, len-imin) + step_hi_pre = clamp(step, max(-(m+ref)/k, (-m+ref)/k), min((m-ref)/k, (m+ref)/k)) + nb = nbitslen(T, len, imin) + step_hi = truncbits(step_hi_pre, nb) + x1_hi, x1_lo = add12((1-imin)*step_hi, ref) + x2_hi, x2_lo = add12((len-imin)*step_hi, ref) + a, b = (start - x1_hi) - x1_lo, (stop - x2_hi) - x2_lo + step_lo = (b - a)/(len - 1) + ref_lo = a - (1 - imin)*step_lo + steprangelen_hp(T, (ref, ref_lo), (step_hi, step_lo), 0, Int(len), imin) +end + +# range for rational numbers, start = start_n/den, stop = stop_n/den +# Note this returns a StepRangeLen +_linspace(::Type{T}, start::Integer, stop::Integer, len::Integer) where {T<:IEEEFloat} = _linspace(T, start, stop, len, 1) +function _linspace(::Type{T}, start_n::Integer, stop_n::Integer, len::Integer, den::Integer) where T<:IEEEFloat + len < 2 && return _linspace1(T, start_n/den, stop_n/den, len) + start_n == stop_n && return steprangelen_hp(T, (start_n, den), (zero(start_n), den), 0, len, 1) + tmin = -start_n/(Float64(stop_n) - Float64(start_n)) + imin = round(Int, tmin*(len-1)+1) + imin = clamp(imin, 1, Int(len)) + ref_num = Int128(len-imin) * start_n + Int128(imin-1) * stop_n + ref_denom = Int128(len-1) * den + ref = (ref_num, ref_denom) + step_full = (Int128(stop_n) - Int128(start_n), ref_denom) + steprangelen_hp(T, ref, step_full, nbitslen(T, len, imin), Int(len), imin) +end + +# For len < 2 +function _linspace1(::Type{T}, start, stop, len::Integer) where T<:IEEEFloat + len >= 0 || throw(ArgumentError("range($start, stop=$stop, length=$len): negative length")) + if len <= 1 + len == 1 && (start == stop || throw(ArgumentError("range($start, stop=$stop, length=$len): endpoints differ"))) + # Ensure that first(r)==start and last(r)==stop even for len==0 + # The output type must be consistent with steprangelen_hp + if T<:Union{Float32,Float16} + return StepRangeLen{T}(Float64(start), Float64(start) - Float64(stop), len, 1) + else + return StepRangeLen(TwicePrecision(start, zero(T)), TwicePrecision(start, -stop), len, 1) + end + end + throw(ArgumentError("should only be called for len < 2, got $len")) +end + +### Numeric utilities + +# Approximate x with a rational representation. Guaranteed to return, +# but not guaranteed to return a precise answer. +# https://en.wikipedia.org/wiki/Continued_fraction#Best_rational_approximations +function rat(x) + y = x + a = d = 1 + b = c = 0 + m = maxintfloat(narrow(typeof(x)), Int) + while abs(y) <= m + f = trunc(Int,y) + y -= f + a, c = f*a + c, a + b, d = f*b + d, b + max(abs(a), abs(b)) <= convert(Int,m) || return c, d + oftype(x,a)/oftype(x,b) == x && break + y = inv(y) + end + return a, b +end + +# This version of lcm does not check for overflows +lcm_unchecked(a::T, b::T) where T<:Integer = a * div(b, gcd(a, b)) + +narrow(::Type{T}) where {T<:AbstractFloat} = Float64 +narrow(::Type{Float64}) = Float32 +narrow(::Type{Float32}) = Float16 +narrow(::Type{Float16}) = Float16 + +function _tp_prod(t::TwicePrecision, x, y...) + @_inline_meta + _tp_prod(t * x, y...) +end +_tp_prod(t::TwicePrecision) = t +<(x::TwicePrecision{T}, y::TwicePrecision{T}) where {T} = + x.hi < y.hi || ((x.hi == y.hi) & (x.lo < y.lo)) + +isbetween(a, x, b) = a <= x <= b || b <= x <= a diff --git a/base/util.jl b/base/util.jl new file mode 100644 index 0000000..3fc6700 --- /dev/null +++ b/base/util.jl @@ -0,0 +1,821 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + + +# This type must be kept in sync with the C struct in src/gc.h +struct GC_Num + allocd ::Int64 # GC internal + deferred_alloc::Int64 # GC internal + freed ::Int64 # GC internal + malloc ::UInt64 + realloc ::UInt64 + poolalloc ::UInt64 + bigalloc ::UInt64 + freecall ::UInt64 + total_time ::UInt64 + total_allocd::UInt64 # GC internal + since_sweep ::UInt64 # GC internal + collect ::Csize_t # GC internal + pause ::Cint + full_sweep ::Cint +end + +gc_num() = ccall(:jl_gc_num, GC_Num, ()) + +# This type is to represent differences in the counters, so fields may be negative +struct GC_Diff + allocd ::Int64 # Bytes allocated + malloc ::Int64 # Number of GC aware malloc() + realloc ::Int64 # Number of GC aware realloc() + poolalloc ::Int64 # Number of pool allocation + bigalloc ::Int64 # Number of big (non-pool) allocation + freecall ::Int64 # Number of GC aware free() + total_time ::Int64 # Time spent in garbage collection + pause ::Int64 # Number of GC pauses + full_sweep ::Int64 # Number of GC full collection +end + +gc_total_bytes(gc_num::GC_Num) = + (gc_num.allocd + gc_num.deferred_alloc + Int64(gc_num.total_allocd)) + +function GC_Diff(new::GC_Num, old::GC_Num) + # logic from `src/gc.c:jl_gc_total_bytes` + old_allocd = gc_total_bytes(old) + new_allocd = gc_total_bytes(new) + return GC_Diff(new_allocd - old_allocd, + Int64(new.malloc - old.malloc), + Int64(new.realloc - old.realloc), + Int64(new.poolalloc - old.poolalloc), + Int64(new.bigalloc - old.bigalloc), + Int64(new.freecall - old.freecall), + Int64(new.total_time - old.total_time), + new.pause - old.pause, + new.full_sweep - old.full_sweep) +end + +function gc_alloc_count(diff::GC_Diff) + diff.malloc + diff.realloc + diff.poolalloc + diff.bigalloc +end + + +# total time spend in garbage collection, in nanoseconds +gc_time_ns() = ccall(:jl_gc_total_hrtime, UInt64, ()) + +# total number of bytes allocated so far +gc_bytes() = ccall(:jl_gc_total_bytes, Int64, ()) + +# print elapsed time, return expression value +const _mem_units = ["byte", "KiB", "MiB", "GiB", "TiB", "PiB"] +const _cnt_units = ["", " k", " M", " G", " T", " P"] +function prettyprint_getunits(value, numunits, factor) + if value == 0 || value == 1 + return (value, 1) + end + unit = ceil(Int, log(value) / log(factor)) + unit = min(numunits, unit) + number = value/factor^(unit-1) + return number, unit +end + +function padded_nonzero_print(value,str) + if value != 0 + blanks = " "[1:18-length(str)] + println("$str:$blanks$value") + end +end + +function format_bytes(bytes) + bytes, mb = prettyprint_getunits(bytes, length(_mem_units), Int64(1024)) + if mb == 1 + Printf.@sprintf("%d %s%s", bytes, _mem_units[mb], bytes==1 ? "" : "s") + else + Printf.@sprintf("%.3f %s", bytes, _mem_units[mb]) + end +end + +function time_print(elapsedtime, bytes=0, gctime=0, allocs=0) + Printf.@printf("%10.6f seconds", elapsedtime/1e9) + if bytes != 0 || allocs != 0 + allocs, ma = prettyprint_getunits(allocs, length(_cnt_units), Int64(1000)) + if ma == 1 + Printf.@printf(" (%d%s allocation%s: ", allocs, _cnt_units[ma], allocs==1 ? "" : "s") + else + Printf.@printf(" (%.2f%s allocations: ", allocs, _cnt_units[ma]) + end + print(format_bytes(bytes)) + if gctime > 0 + Printf.@printf(", %.2f%% gc time", 100*gctime/elapsedtime) + end + print(")") + elseif gctime > 0 + Printf.@printf(", %.2f%% gc time", 100*gctime/elapsedtime) + end +end + +function timev_print(elapsedtime, diff::GC_Diff) + allocs = gc_alloc_count(diff) + time_print(elapsedtime, diff.allocd, diff.total_time, allocs) + print("\nelapsed time (ns): $elapsedtime\n") + padded_nonzero_print(diff.total_time, "gc time (ns)") + padded_nonzero_print(diff.allocd, "bytes allocated") + padded_nonzero_print(diff.poolalloc, "pool allocs") + padded_nonzero_print(diff.bigalloc, "non-pool GC allocs") + padded_nonzero_print(diff.malloc, "malloc() calls") + padded_nonzero_print(diff.realloc, "realloc() calls") + padded_nonzero_print(diff.freecall, "free() calls") + padded_nonzero_print(diff.pause, "GC pauses") + padded_nonzero_print(diff.full_sweep, "full collections") +end + +""" + @time + +A macro to execute an expression, printing the time it took to execute, the number of +allocations, and the total number of bytes its execution caused to be allocated, before +returning the value of the expression. + +See also [`@timev`](@ref), [`@timed`](@ref), [`@elapsed`](@ref), and +[`@allocated`](@ref). + +```julia-repl +julia> @time rand(10^6); + 0.001525 seconds (7 allocations: 7.630 MiB) + +julia> @time begin + sleep(0.3) + 1+1 + end + 0.301395 seconds (8 allocations: 336 bytes) +2 +``` +""" +macro time(ex) + quote + local stats = gc_num() + local elapsedtime = time_ns() + local val = $(esc(ex)) + elapsedtime = time_ns() - elapsedtime + local diff = GC_Diff(gc_num(), stats) + time_print(elapsedtime, diff.allocd, diff.total_time, + gc_alloc_count(diff)) + println() + val + end +end + +""" + @timev + +This is a verbose version of the `@time` macro. It first prints the same information as +`@time`, then any non-zero memory allocation counters, and then returns the value of the +expression. + +See also [`@time`](@ref), [`@timed`](@ref), [`@elapsed`](@ref), and +[`@allocated`](@ref). + +```julia-repl +julia> @timev rand(10^6); + 0.001006 seconds (7 allocations: 7.630 MiB) +elapsed time (ns): 1005567 +bytes allocated: 8000256 +pool allocs: 6 +malloc() calls: 1 +``` +""" +macro timev(ex) + quote + local stats = gc_num() + local elapsedtime = time_ns() + local val = $(esc(ex)) + elapsedtime = time_ns() - elapsedtime + timev_print(elapsedtime, GC_Diff(gc_num(), stats)) + val + end +end + +""" + @elapsed + +A macro to evaluate an expression, discarding the resulting value, instead returning the +number of seconds it took to execute as a floating-point number. + +See also [`@time`](@ref), [`@timev`](@ref), [`@timed`](@ref), +and [`@allocated`](@ref). + +```julia-repl +julia> @elapsed sleep(0.3) +0.301391426 +``` +""" +macro elapsed(ex) + quote + local t0 = time_ns() + local val = $(esc(ex)) + (time_ns()-t0)/1e9 + end +end + +# measure bytes allocated without *most* contamination from compilation +# Note: This reports a different value from the @time macros, because +# it wraps the call in a function, however, this means that things +# like: @allocated y = foo() +# will not work correctly, because it will set y in the context of +# the local function made by the macro, not the current function +""" + @allocated + +A macro to evaluate an expression, discarding the resulting value, instead returning the +total number of bytes allocated during evaluation of the expression. Note: the expression is +evaluated inside a local function, instead of the current context, in order to eliminate the +effects of compilation, however, there still may be some allocations due to JIT compilation. +This also makes the results inconsistent with the `@time` macros, which do not try to adjust +for the effects of compilation. + +See also [`@time`](@ref), [`@timev`](@ref), [`@timed`](@ref), +and [`@elapsed`](@ref). + +```julia-repl +julia> @allocated rand(10^6) +8000080 +``` +""" +macro allocated(ex) + quote + let + local f + function f() + b0 = gc_bytes() + $(esc(ex)) + gc_bytes() - b0 + end + f() + end + end +end + +""" + @timed + +A macro to execute an expression, and return the value of the expression, elapsed time, +total bytes allocated, garbage collection time, and an object with various memory allocation +counters. + +See also [`@time`](@ref), [`@timev`](@ref), [`@elapsed`](@ref), and +[`@allocated`](@ref). + +```julia-repl +julia> val, t, bytes, gctime, memallocs = @timed rand(10^6); + +julia> t +0.006634834 + +julia> bytes +8000256 + +julia> gctime +0.0055765 + +julia> fieldnames(typeof(memallocs)) +(:allocd, :malloc, :realloc, :poolalloc, :bigalloc, :freecall, :total_time, :pause, :full_sweep) + +julia> memallocs.total_time +5576500 +``` +""" +macro timed(ex) + quote + local stats = gc_num() + local elapsedtime = time_ns() + local val = $(esc(ex)) + elapsedtime = time_ns() - elapsedtime + local diff = GC_Diff(gc_num(), stats) + val, elapsedtime/1e9, diff.allocd, diff.total_time/1e9, diff + end +end + + +## printing with color ## + +const text_colors = Dict{Union{Symbol,Int},String}( + :black => "\033[30m", + :red => "\033[31m", + :green => "\033[32m", + :yellow => "\033[33m", + :blue => "\033[34m", + :magenta => "\033[35m", + :cyan => "\033[36m", + :white => "\033[37m", + :light_black => "\033[90m", # gray + :light_red => "\033[91m", + :light_green => "\033[92m", + :light_yellow => "\033[93m", + :light_blue => "\033[94m", + :light_magenta => "\033[95m", + :light_cyan => "\033[96m", + :normal => "\033[0m", + :default => "\033[39m", + :bold => "\033[1m", + :underline => "\033[4m", + :blink => "\033[5m", + :reverse => "\033[7m", + :hidden => "\033[8m", + :nothing => "", +) + +for i in 0:255 + text_colors[i] = "\033[38;5;$(i)m" +end + +const disable_text_style = Dict{Symbol,String}( + :bold => "\033[22m", + :underline => "\033[24m", + :blink => "\033[25m", + :reverse => "\033[27m", + :hidden => "\033[28m", + :normal => "", + :default => "", + :nothing => "", +) + +# Create a docstring with an automatically generated list +# of colors. +available_text_colors = collect(Iterators.filter(x -> !isa(x, Integer), keys(text_colors))) +const possible_formatting_symbols = [:normal, :bold, :default] +available_text_colors = cat( + sort!(intersect(available_text_colors, possible_formatting_symbols), rev=true), + sort!(setdiff( available_text_colors, possible_formatting_symbols)); + dims=1) + +const available_text_colors_docstring = + string(join([string("`:", key,"`") + for key in available_text_colors], ",\n", ", or \n")) + +"""Dictionary of color codes for the terminal. + +Available colors are: $available_text_colors_docstring as well as the integers 0 to 255 inclusive. + +The color `:default` will print text in the default color while the color `:normal` +will print text with all text properties (like boldness) reset. +Printing with the color `:nothing` will print the string without modifications. +""" +text_colors + +function with_output_color(f::Function, color::Union{Int, Symbol}, io::IO, args...; bold::Bool = false) + buf = IOBuffer() + iscolor = get(io, :color, false) + try f(IOContext(buf, io), args...) + finally + str = String(take!(buf)) + if !iscolor + print(io, str) + else + bold && color === :bold && (color = :nothing) + enable_ansi = get(text_colors, color, text_colors[:default]) * + (bold ? text_colors[:bold] : "") + disable_ansi = (bold ? disable_text_style[:bold] : "") * + get(disable_text_style, color, text_colors[:default]) + first = true + for line in split(str, '\n') + first || print(buf, '\n') + first = false + isempty(line) && continue + print(buf, enable_ansi, line, disable_ansi) + end + print(io, String(take!(buf))) + end + end +end + +""" + printstyled([io], xs...; bold::Bool=false, color::Union{Symbol,Int}=:normal) + +Print `xs` in a color specified as a symbol or integer, optionally in bold. + +`color` may take any of the values $(Base.available_text_colors_docstring) +or an integer between 0 and 255 inclusive. Note that not all terminals support 256 colors. +If the keyword `bold` is given as `true`, the result will be printed in bold. +""" +printstyled(io::IO, msg...; bold::Bool=false, color::Union{Int,Symbol}=:normal) = + with_output_color(print, color, io, msg...; bold=bold) +printstyled(msg...; bold::Bool=false, color::Union{Int,Symbol}=:normal) = + printstyled(stdout, msg...; bold=bold, color=color) + +""" + Base.julia_cmd(juliapath=joinpath(Sys.BINDIR::String, julia_exename())) + +Return a julia command similar to the one of the running process. +Propagates any of the `--cpu-target`, `--sysimage`, `--compile`, `--sysimage-native-code`, +`--compiled-modules`, `--inline`, `--check-bounds`, `--optimize`, `-g`, +`--code-coverage`, and `--depwarn` +command line arguments that are not at their default values. + +Among others, `--math-mode`, `--warn-overwrite`, and `--trace-compile` are notably not propagated currently. + +!!! compat "Julia 1.1" + Only the `--cpu-target`, `--sysimage`, `--depwarn`, `--compile` and `--check-bounds` flags were propagated before Julia 1.1. +""" +function julia_cmd(julia=joinpath(Sys.BINDIR::String, julia_exename())) + opts = JLOptions() + cpu_target = unsafe_string(opts.cpu_target) + image_file = unsafe_string(opts.image_file) + addflags = String[] + let compile = if opts.compile_enabled == 0 + "no" + elseif opts.compile_enabled == 2 + "all" + elseif opts.compile_enabled == 3 + "min" + else + "" # default = "yes" + end + isempty(compile) || push!(addflags, "--compile=$compile") + end + let depwarn = if opts.depwarn == 0 + "no" + elseif opts.depwarn == 2 + "error" + else + "" # default = "yes" + end + isempty(depwarn) || push!(addflags, "--depwarn=$depwarn") + end + let check_bounds = if opts.check_bounds == 1 + "yes" # on + elseif opts.check_bounds == 2 + "no" # off + else + "" # "default" + end + isempty(check_bounds) || push!(addflags, "--check-bounds=$check_bounds") + end + opts.can_inline == 0 && push!(addflags, "--inline=no") + opts.use_compiled_modules == 0 && push!(addflags, "--compiled-modules=no") + opts.opt_level == 2 || push!(addflags, "-O$(opts.opt_level)") + push!(addflags, "-g$(opts.debug_level)") + if opts.code_coverage != 0 + # Forward the code-coverage flag only if applicable (if the filename is pid-dependent) + coverage_file = (opts.output_code_coverage != C_NULL) ? unsafe_string(opts.output_code_coverage) : "" + if isempty(coverage_file) || occursin("%p", coverage_file) + if opts.code_coverage == 1 + push!(addflags, "--code-coverage=user") + elseif opts.code_coverage == 2 + push!(addflags, "--code-coverage=all") + end + isempty(coverage_file) || push!(addflags, "--code-coverage=$coverage_file") + end + end + if opts.malloc_log != 0 + if opts.malloc_log == 1 + push!(addflags, "--track-allocation=user") + elseif opts.malloc_log == 2 + push!(addflags, "--track-allocation=all") + end + end + return `$julia -C$cpu_target -J$image_file $addflags` +end + +function julia_exename() + if ccall(:jl_is_debugbuild, Cint, ()) == 0 + return @static Sys.iswindows() ? "julia.exe" : "julia" + else + return @static Sys.iswindows() ? "julia-debug.exe" : "julia-debug" + end +end + +""" + securezero!(o) + +`securezero!` fills the memory associated with an object `o` with zeros. +Unlike `fill!(o,0)` and similar code, which might be optimized away by +the compiler for objects about to be discarded, the `securezero!` function +will always be called. +""" +function securezero! end +@noinline securezero!(a::AbstractArray{<:Number}) = fill!(a, 0) +@noinline unsafe_securezero!(p::Ptr{T}, len::Integer=1) where {T} = + ccall(:memset, Ptr{T}, (Ptr{T}, Cint, Csize_t), p, 0, len*sizeof(T)) +unsafe_securezero!(p::Ptr{Cvoid}, len::Integer=1) = Ptr{Cvoid}(unsafe_securezero!(Ptr{UInt8}(p), len)) + +""" + Base.getpass(message::AbstractString) -> Base.SecretBuffer + +Display a message and wait for the user to input a secret, returning an `IO` +object containing the secret. + +Note that on Windows, the secret might be displayed as it is typed; see +`Base.winprompt` for securely retrieving username/password pairs from a +graphical interface. +""" +function getpass end + +if Sys.iswindows() +function getpass(input::TTY, output::IO, prompt::AbstractString) + input === stdin || throw(ArgumentError("getpass only works for stdin")) + print(output, prompt, ": ") + flush(output) + s = SecretBuffer() + plen = 0 + while true + c = UInt8(ccall(:_getch, Cint, ())) + if c == 0xff || c == UInt8('\n') || c == UInt8('\r') + break # EOF or return + elseif c == 0x00 || c == 0xe0 + ccall(:_getch, Cint, ()) # ignore function/arrow keys + elseif c == UInt8('\b') && plen > 0 + plen -= 1 # delete last character on backspace + elseif !iscntrl(Char(c)) && plen < 128 + write(s, c) + end + end + return seekstart(s) +end +else +function getpass(input::TTY, output::IO, prompt::AbstractString) + (input === stdin && output === stdout) || throw(ArgumentError("getpass only works for stdin")) + msg = string(prompt, ": ") + unsafe_SecretBuffer!(ccall(:getpass, Cstring, (Cstring,), msg)) +end +end + +# allow new getpass methods to be defined if stdin has been +# redirected to some custom stream, e.g. in IJulia. +getpass(prompt::AbstractString) = getpass(stdin, stdout, prompt) + +""" + prompt(message; default="") -> Union{String, Nothing} + +Displays the `message` then waits for user input. Input is terminated when a newline (\\n) +is encountered or EOF (^D) character is entered on a blank line. If a `default` is provided +then the user can enter just a newline character to select the `default`. + +See also `Base.getpass` and `Base.winprompt` for secure entry of passwords. +""" +function prompt(input::IO, output::IO, message::AbstractString; default::AbstractString="") + msg = !isempty(default) ? "$message [$default]: " : "$message: " + print(output, msg) + uinput = readline(input, keep=true) + isempty(uinput) && return nothing # Encountered an EOF + uinput = chomp(uinput) + isempty(uinput) ? default : uinput +end + +# allow new prompt methods to be defined if stdin has been +# redirected to some custom stream, e.g. in IJulia. +prompt(message::AbstractString; default::AbstractString="") = prompt(stdin, stdout, message, default=default) + +# Windows authentication prompt +if Sys.iswindows() + struct CREDUI_INFO + cbSize::UInt32 + parent::Ptr{Cvoid} + pszMessageText::Ptr{UInt16} + pszCaptionText::Ptr{UInt16} + banner::Ptr{Cvoid} + end + + const CREDUIWIN_GENERIC = 0x0001 + const CREDUIWIN_IN_CRED_ONLY = 0x0020 + const CREDUIWIN_ENUMERATE_CURRENT_USER = 0x0200 + + const CRED_PACK_GENERIC_CREDENTIALS = 0x0004 + + const ERROR_SUCCESS = 0x0000 + const ERROR_CANCELLED = 0x04c7 + + function winprompt(message, caption, default_username; prompt_username = true) + # Step 1: Create an encrypted username/password bundle that will be used to set + # the default username (in theory could also provide a default password) + credbuf = Vector{UInt8}(undef, 1024) + credbufsize = Ref{UInt32}(sizeof(credbuf)) + succeeded = ccall((:CredPackAuthenticationBufferW, "credui.dll"), stdcall, Bool, + (UInt32, Cwstring, Cwstring, Ptr{UInt8}, Ptr{UInt32}), + CRED_PACK_GENERIC_CREDENTIALS, default_username, "", credbuf, credbufsize) + @assert succeeded + + # Step 2: Create the actual dialog + # 2.1: Set up the window + messageArr = Base.cwstring(message) + captionArr = Base.cwstring(caption) + pfSave = Ref{Bool}(false) + cred = Ref{CREDUI_INFO}(CREDUI_INFO(sizeof(CREDUI_INFO), C_NULL, pointer(messageArr), pointer(captionArr), C_NULL)) + dwflags = CREDUIWIN_GENERIC | CREDUIWIN_ENUMERATE_CURRENT_USER + if !prompt_username + # Disable setting anything other than default_username + dwflags |= CREDUIWIN_IN_CRED_ONLY + end + authPackage = Ref{Culong}(0) + outbuf_data = Ref{Ptr{Cvoid}}(C_NULL) + outbuf_size = Ref{Culong}(0) + + # 2.2: Do the actual request + code = ccall((:CredUIPromptForWindowsCredentialsW, "credui.dll"), stdcall, UInt32, (Ptr{CREDUI_INFO}, UInt32, Ptr{Culong}, + Ptr{Cvoid}, Culong, Ptr{Ptr{Cvoid}}, Ptr{Culong}, Ptr{Bool}, UInt32), cred, 0, authPackage, credbuf, credbufsize[], + outbuf_data, outbuf_size, pfSave, dwflags) + + # 2.3: If that failed for any reason other than the user canceling, error out. + # If the user canceled, just return nothing + code == ERROR_CANCELLED && return nothing + windowserror(:winprompt, code != ERROR_SUCCESS) + + # Step 3: Convert encrypted credentials back to plain text + passbuf = Vector{UInt16}(undef, 1024) + passlen = Ref{UInt32}(length(passbuf)) + usernamebuf = Vector{UInt16}(undef, 1024) + usernamelen = Ref{UInt32}(length(usernamebuf)) + # Need valid buffers for domain, even though we don't care + dummybuf = Vector{UInt16}(undef, 1024) + succeeded = ccall((:CredUnPackAuthenticationBufferW, "credui.dll"), Bool, + (UInt32, Ptr{Cvoid}, UInt32, Ptr{UInt16}, Ptr{UInt32}, Ptr{UInt16}, Ptr{UInt32}, Ptr{UInt16}, Ptr{UInt32}), + 0, outbuf_data[], outbuf_size[], usernamebuf, usernamelen, dummybuf, Ref{UInt32}(1024), passbuf, passlen) + windowserror(:winprompt, !succeeded) + + # Step 4: Free the encrypted buffer + # ccall(:SecureZeroMemory, Ptr{Cvoid}, (Ptr{Cvoid}, Csize_t), outbuf_data[], outbuf_size[]) - not an actual function + unsafe_securezero!(outbuf_data[], outbuf_size[]) + ccall((:CoTaskMemFree, "ole32.dll"), Cvoid, (Ptr{Cvoid},), outbuf_data[]) + + # Done. + passbuf_ = passbuf[1:passlen[]-1] + result = (String(transcode(UInt8, usernamebuf[1:usernamelen[]-1])), + SecretBuffer!(transcode(UInt8, passbuf_))) + securezero!(passbuf_) + securezero!(passbuf) + + return result + end + +end + +unsafe_crc32c(a, n, crc) = ccall(:jl_crc32c, UInt32, (UInt32, Ptr{UInt8}, Csize_t), crc, a, n) + +_crc32c(a::Union{Array{UInt8},FastContiguousSubArray{UInt8,N,<:Array{UInt8}} where N}, crc::UInt32=0x00000000) = + unsafe_crc32c(a, length(a) % Csize_t, crc) + +_crc32c(s::String, crc::UInt32=0x00000000) = unsafe_crc32c(s, sizeof(s) % Csize_t, crc) + +function _crc32c(io::IO, nb::Integer, crc::UInt32=0x00000000) + nb < 0 && throw(ArgumentError("number of bytes to checksum must be ≥ 0, got $nb")) + # use block size 24576=8192*3, since that is the threshold for + # 3-way parallel SIMD code in the underlying jl_crc32c C function. + buf = Vector{UInt8}(undef, min(nb, 24576)) + while !eof(io) && nb > 24576 + n = readbytes!(io, buf) + crc = unsafe_crc32c(buf, n, crc) + nb -= n + end + return unsafe_crc32c(buf, readbytes!(io, buf, min(nb, length(buf))), crc) +end +_crc32c(io::IO, crc::UInt32=0x00000000) = _crc32c(io, typemax(Int64), crc) +_crc32c(io::IOStream, crc::UInt32=0x00000000) = _crc32c(io, filesize(io)-position(io), crc) +_crc32c(uuid::UUID, crc::UInt32=0x00000000) = + ccall(:jl_crc32c, UInt32, (UInt32, Ref{UInt128}, Csize_t), crc, uuid.value, 16) + +""" + @kwdef typedef + +This is a helper macro that automatically defines a keyword-based constructor for the type +declared in the expression `typedef`, which must be a `struct` or `mutable struct` +expression. The default argument is supplied by declaring fields of the form `field::T = +default` or `field = default`. If no default is provided then the keyword argument becomes +a required keyword argument in the resulting type constructor. + +Inner constructors can still be defined, but at least one should accept arguments in the +same form as the default inner constructor (i.e. one positional argument per field) in +order to function correctly with the keyword outer constructor. + +!!! compat "Julia 1.1" + `Base.@kwdef` for parametric structs, and structs with supertypes + requires at least Julia 1.1. + +# Examples +```jldoctest +julia> Base.@kwdef struct Foo + a::Int = 1 # specified default + b::String # required keyword + end +Foo + +julia> Foo(b="hi") +Foo(1, "hi") + +julia> Foo() +ERROR: UndefKeywordError: keyword argument b not assigned +Stacktrace: +[...] +``` +""" +macro kwdef(expr) + expr = macroexpand(__module__, expr) # to expand @static + expr isa Expr && expr.head === :struct || error("Invalid usage of @kwdef") + T = expr.args[2] + if T isa Expr && T.head === :<: + T = T.args[1] + end + + params_ex = Expr(:parameters) + call_args = Any[] + + _kwdef!(expr.args[3], params_ex.args, call_args) + # Only define a constructor if the type has fields, otherwise we'll get a stack + # overflow on construction + if !isempty(params_ex.args) + if T isa Symbol + kwdefs = :(($(esc(T)))($params_ex) = ($(esc(T)))($(call_args...))) + elseif T isa Expr && T.head === :curly + # if T == S{A<:AA,B<:BB}, define two methods + # S(...) = ... + # S{A,B}(...) where {A<:AA,B<:BB} = ... + S = T.args[1] + P = T.args[2:end] + Q = [U isa Expr && U.head === :<: ? U.args[1] : U for U in P] + SQ = :($S{$(Q...)}) + kwdefs = quote + ($(esc(S)))($params_ex) =($(esc(S)))($(call_args...)) + ($(esc(SQ)))($params_ex) where {$(esc.(P)...)} = + ($(esc(SQ)))($(call_args...)) + end + else + error("Invalid usage of @kwdef") + end + else + kwdefs = nothing + end + quote + Base.@__doc__($(esc(expr))) + $kwdefs + end +end + +# @kwdef helper function +# mutates arguments inplace +function _kwdef!(blk, params_args, call_args) + for i in eachindex(blk.args) + ei = blk.args[i] + if ei isa Symbol + # var + push!(params_args, ei) + push!(call_args, ei) + elseif ei isa Expr + if ei.head === :(=) + lhs = ei.args[1] + if lhs isa Symbol + # var = defexpr + var = lhs + elseif lhs isa Expr && lhs.head === :(::) && lhs.args[1] isa Symbol + # var::T = defexpr + var = lhs.args[1] + else + # something else, e.g. inline inner constructor + # F(...) = ... + continue + end + defexpr = ei.args[2] # defexpr + push!(params_args, Expr(:kw, var, esc(defexpr))) + push!(call_args, var) + blk.args[i] = lhs + elseif ei.head === :(::) && ei.args[1] isa Symbol + # var::Typ + var = ei.args[1] + push!(params_args, var) + push!(call_args, var) + elseif ei.head === :block + # can arise with use of @static inside type decl + _kwdef!(ei, params_args, call_args) + end + end + end + blk +end + +# testing + +""" + Base.runtests(tests=["all"]; ncores=ceil(Int, Sys.CPU_THREADS / 2), + exit_on_error=false, [seed]) + +Run the Julia unit tests listed in `tests`, which can be either a string or an array of +strings, using `ncores` processors. If `exit_on_error` is `false`, when one test +fails, all remaining tests in other files will still be run; they are otherwise discarded, +when `exit_on_error == true`. +If a seed is provided via the keyword argument, it is used to seed the +global RNG in the context where the tests are run; otherwise the seed is chosen randomly. +""" +function runtests(tests = ["all"]; ncores = ceil(Int, Sys.CPU_THREADS / 2), + exit_on_error=false, + seed::Union{BitInteger,Nothing}=nothing) + if isa(tests,AbstractString) + tests = split(tests) + end + exit_on_error && push!(tests, "--exit-on-error") + seed !== nothing && push!(tests, "--seed=0x$(string(seed % UInt128, base=16))") # cast to UInt128 to avoid a minus sign + ENV2 = copy(ENV) + ENV2["JULIA_CPU_THREADS"] = "$ncores" + try + run(setenv(`$(julia_cmd()) $(joinpath(Sys.BINDIR::String, + Base.DATAROOTDIR, "julia", "test", "runtests.jl")) $tests`, ENV2)) + catch + buf = PipeBuffer() + Base.require(Base, :InteractiveUtils).versioninfo(buf) + error("A test has failed. Please submit a bug report (https://github.com/JuliaLang/julia/issues)\n" * + "including error messages above and the output of versioninfo():\n$(read(buf, String))") + end +end diff --git a/base/uuid.jl b/base/uuid.jl new file mode 100644 index 0000000..c72bfd9 --- /dev/null +++ b/base/uuid.jl @@ -0,0 +1,72 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" + Represents a Universally Unique Identifier (UUID). + Can be built from one `UInt128` (all byte values), two `UInt64`, or four `UInt32`. + Conversion from a string will check the UUID validity. +""" +struct UUID + value::UInt128 +end +UUID(u::NTuple{2, UInt64}) = UUID((UInt128(u[1]) << 64) | UInt128(u[2])) +UUID(u::NTuple{4, UInt32}) = UUID((UInt128(u[1]) << 96) | (UInt128(u[2]) << 64) | + (UInt128(u[3]) << 32) | UInt128(u[4])) + +function convert(::Type{NTuple{2, UInt64}}, uuid::UUID) + bytes = uuid.value + hi = UInt64((bytes >> 64) & 0xffffffffffffffff) + lo = UInt64(bytes & 0xffffffffffffffff) + return (hi, lo) +end + +function convert(::Type{NTuple{4, UInt32}}, uuid::UUID) + bytes = uuid.value + hh = UInt32((bytes >> 96) & 0xffffffff) + hl = UInt32((bytes >> 64) & 0xffffffff) + lh = UInt32((bytes >> 32) & 0xffffffff) + ll = UInt32(bytes & 0xffffffff) + return (hh, hl, lh, ll) +end + +UInt128(u::UUID) = u.value + +let groupings = [1:8; 10:13; 15:18; 20:23; 25:36] + global UUID + function UUID(s::AbstractString) + s = lowercase(s) + + if !occursin(r"^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$", s) + throw(ArgumentError("Malformed UUID string: $(repr(s))")) + end + + u = UInt128(0) + for i in groupings + u <<= 4 + d = s[i] - '0' + u |= 0xf & (d - 39*(d > 9)) + end + return UUID(u) + end +end + +let groupings = [36:-1:25; 23:-1:20; 18:-1:15; 13:-1:10; 8:-1:1] + global string + function string(u::UUID) + u = u.value + a = Base.StringVector(36) + for i in groupings + a[i] = hex_chars[1 + u & 0xf] + u >>= 4 + end + a[24] = a[19] = a[14] = a[9] = '-' + return String(a) + end +end + +print(io::IO, u::UUID) = print(io, string(u)) +show(io::IO, u::UUID) = print(io, "UUID(\"", u, "\")") + +isless(a::UUID, b::UUID) = isless(a.value, b.value) + +# give UUID scalar behavior in broadcasting +Base.broadcastable(x::UUID) = Ref(x) diff --git a/base/version.jl b/base/version.jl new file mode 100644 index 0000000..16c9ccc --- /dev/null +++ b/base/version.jl @@ -0,0 +1,296 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## semantic version numbers (https://semver.org/) + +const VInt = UInt32 +""" + VersionNumber + +Version number type which follow the specifications of +[semantic versioning](https://semver.org/), composed of major, minor +and patch numeric values, followed by pre-release and build +alpha-numeric annotations. See also [`@v_str`](@ref). + +# Examples +```jldoctest +julia> VersionNumber("1.2.3") +v"1.2.3" + +julia> VersionNumber("2.0.1-rc1") +v"2.0.1-rc1" +``` +""" +struct VersionNumber + major::VInt + minor::VInt + patch::VInt + prerelease::Tuple{Vararg{Union{UInt64,String}}} + build::Tuple{Vararg{Union{UInt64,String}}} + + function VersionNumber(major::VInt, minor::VInt, patch::VInt, + pre::Tuple{Vararg{Union{UInt64,String}}}, + bld::Tuple{Vararg{Union{UInt64,String}}}) + major >= 0 || throw(ArgumentError("invalid negative major version: $major")) + minor >= 0 || throw(ArgumentError("invalid negative minor version: $minor")) + patch >= 0 || throw(ArgumentError("invalid negative patch version: $patch")) + for ident in pre + if ident isa Integer + ident >= 0 || throw(ArgumentError("invalid negative pre-release identifier: $ident")) + else + if !occursin(r"^(?:|[0-9a-z-]*[a-z-][0-9a-z-]*)$"i, ident) || + isempty(ident) && !(length(pre)==1 && isempty(bld)) + throw(ArgumentError("invalid pre-release identifier: $(repr(ident))")) + end + end + end + for ident in bld + if ident isa Integer + ident >= 0 || throw(ArgumentError("invalid negative build identifier: $ident")) + else + if !occursin(r"^(?:|[0-9a-z-]*[a-z-][0-9a-z-]*)$"i, ident) || + isempty(ident) && length(bld)!=1 + throw(ArgumentError("invalid build identifier: $(repr(ident))")) + end + end + end + new(major, minor, patch, pre, bld) + end +end +VersionNumber(major::Integer, minor::Integer = 0, patch::Integer = 0, + pre::Tuple{Vararg{Union{Integer,AbstractString}}} = (), + bld::Tuple{Vararg{Union{Integer,AbstractString}}} = ()) = + VersionNumber(VInt(major), VInt(minor), VInt(patch), + map(x->x isa Integer ? UInt64(x) : String(x), pre), + map(x->x isa Integer ? UInt64(x) : String(x), bld)) + +VersionNumber(v::Tuple) = VersionNumber(v...) + +function print(io::IO, v::VersionNumber) + v == typemax(VersionNumber) && return print(io, "∞") + print(io, v.major) + print(io, '.') + print(io, v.minor) + print(io, '.') + print(io, v.patch) + if !isempty(v.prerelease) + print(io, '-') + join(io, v.prerelease,'.') + end + if !isempty(v.build) + print(io, '+') + join(io, v.build,'.') + end +end +show(io::IO, v::VersionNumber) = print(io, "v\"", v, "\"") + +Broadcast.broadcastable(v::VersionNumber) = Ref(v) + +const VERSION_REGEX = r"^ + v? # prefix (optional) + (\d+) # major (required) + (?:\.(\d+))? # minor (optional) + (?:\.(\d+))? # patch (optional) + (?:(-)| # pre-release (optional) + ([a-z][0-9a-z-]*(?:\.[0-9a-z-]+)*|-(?:[0-9a-z-]+\.)*[0-9a-z-]+)? + (?:(\+)| + (?:\+((?:[0-9a-z-]+\.)*[0-9a-z-]+))? # build (optional) + )) +$"ix + +function split_idents(s::AbstractString) + idents = split(s, '.') + ntuple(length(idents)) do i + ident = idents[i] + occursin(r"^\d+$", ident) ? parse(UInt64, ident) : String(ident) + end +end + +function VersionNumber(v::AbstractString) + v == "∞" && return typemax(VersionNumber) + m = match(VERSION_REGEX, v) + m === nothing && throw(ArgumentError("invalid version string: $v")) + major, minor, patch, minus, prerl, plus, build = m.captures + major = parse(VInt, major) + minor = minor !== nothing ? parse(VInt, minor) : VInt(0) + patch = patch !== nothing ? parse(VInt, patch) : VInt(0) + if prerl !== nothing && !isempty(prerl) && prerl[1] == '-' + prerl = prerl[2:end] # strip leading '-' + end + prerl = prerl !== nothing ? split_idents(prerl) : minus !== nothing ? ("",) : () + build = build !== nothing ? split_idents(build) : plus !== nothing ? ("",) : () + return VersionNumber(major, minor, patch, prerl, build) +end + +""" + @v_str + +String macro used to parse a string to a [`VersionNumber`](@ref). + +# Examples +```jldoctest +julia> v"1.2.3" +v"1.2.3" + +julia> v"2.0.1-rc1" +v"2.0.1-rc1" +``` +""" +macro v_str(v); VersionNumber(v); end + +typemin(::Type{VersionNumber}) = v"0-" + +function typemax(::Type{VersionNumber}) + ∞ = typemax(VInt) + VersionNumber(∞, ∞, ∞, (), ("",)) +end + +ident_cmp(a::Integer, b::Integer) = cmp(a, b) +ident_cmp(a::Integer, b::String ) = isempty(b) ? +1 : -1 +ident_cmp(a::String, b::Integer) = isempty(a) ? -1 : +1 +ident_cmp(a::String, b::String ) = cmp(a, b) + +function ident_cmp( + A::Tuple{Vararg{Union{Integer,String}}}, + B::Tuple{Vararg{Union{Integer,String}}}, +) + for (a, b) in zip(A, B) + c = ident_cmp(a,b) + (c != 0) && return c + end + length(A) < length(B) ? -1 : + length(B) < length(A) ? +1 : 0 +end + +function ==(a::VersionNumber, b::VersionNumber) + (a.major != b.major) && return false + (a.minor != b.minor) && return false + (a.patch != b.patch) && return false + (ident_cmp(a.prerelease, b.prerelease) != 0) && return false + (ident_cmp(a.build, b.build) != 0) && return false + return true +end + +issupbuild(v::VersionNumber) = length(v.build)==1 && isempty(v.build[1]) + +function isless(a::VersionNumber, b::VersionNumber) + (a.major < b.major) && return true + (a.major > b.major) && return false + (a.minor < b.minor) && return true + (a.minor > b.minor) && return false + (a.patch < b.patch) && return true + (a.patch > b.patch) && return false + (!isempty(a.prerelease) && isempty(b.prerelease)) && return true + (isempty(a.prerelease) && !isempty(b.prerelease)) && return false + c = ident_cmp(a.prerelease,b.prerelease) + (c < 0) && return true + (c > 0) && return false + (!issupbuild(a) && issupbuild(b)) && return true + (issupbuild(a) && !issupbuild(b)) && return false + c = ident_cmp(a.build,b.build) + (c < 0) && return true + return false +end + +function hash(v::VersionNumber, h::UInt) + h += 0x8ff4ffdb75f9fede % UInt + h = hash(v.major, h) + h = hash(v.minor, h) + h = hash(v.patch, h) + h = hash(v.prerelease, ~h) + h = hash(v.build, ~h) +end + +lowerbound(v::VersionNumber) = VersionNumber(v.major, v.minor, v.patch, ("",), ()) +upperbound(v::VersionNumber) = VersionNumber(v.major, v.minor, v.patch, (), ("",)) + +thispatch(v::VersionNumber) = VersionNumber(v.major, v.minor, v.patch) +thisminor(v::VersionNumber) = VersionNumber(v.major, v.minor, 0) +thismajor(v::VersionNumber) = VersionNumber(v.major, 0, 0) + +nextpatch(v::VersionNumber) = v < thispatch(v) ? thispatch(v) : VersionNumber(v.major, v.minor, v.patch+1) +nextminor(v::VersionNumber) = v < thisminor(v) ? thisminor(v) : VersionNumber(v.major, v.minor+1, 0) +nextmajor(v::VersionNumber) = v < thismajor(v) ? thismajor(v) : VersionNumber(v.major+1, 0, 0) + +## julia version info + +""" + VERSION + +A `VersionNumber` object describing which version of Julia is in use. For details see +[Version Number Literals](@ref man-version-number-literals). +""" +const VERSION = try + ver = VersionNumber(VERSION_STRING) + if !isempty(ver.prerelease) + if GIT_VERSION_INFO.build_number >= 0 + ver = VersionNumber(ver.major, ver.minor, ver.patch, (ver.prerelease..., GIT_VERSION_INFO.build_number), ver.build) + else + println("WARNING: no build number found for pre-release version") + ver = VersionNumber(ver.major, ver.minor, ver.patch, (ver.prerelease..., "unknown"), ver.build) + end + elseif GIT_VERSION_INFO.build_number > 0 + println("WARNING: ignoring non-zero build number for VERSION") + end + ver +catch e + println("while creating Base.VERSION, ignoring error $e") + VersionNumber(0) +end + +const libllvm_version = VersionNumber(libllvm_version_string) + +function banner(io::IO = stdout) + if GIT_VERSION_INFO.tagged_commit + commit_string = TAGGED_RELEASE_BANNER + elseif isempty(GIT_VERSION_INFO.commit) + commit_string = "" + else + days = Int(floor((ccall(:jl_clock_now, Float64, ()) - GIT_VERSION_INFO.fork_master_timestamp) / (60 * 60 * 24))) + days = max(0, days) + unit = days == 1 ? "day" : "days" + distance = GIT_VERSION_INFO.fork_master_distance + commit = GIT_VERSION_INFO.commit_short + + if distance == 0 + commit_string = "Commit $(commit) ($(days) $(unit) old master)" + else + branch = GIT_VERSION_INFO.branch + commit_string = "$(branch)/$(commit) (fork: $(distance) commits, $(days) $(unit))" + end + end + + commit_date = isempty(Base.GIT_VERSION_INFO.date_string) ? "" : " ($(split(Base.GIT_VERSION_INFO.date_string)[1]))" + + if get(io, :color, false) + c = text_colors + tx = c[:normal] # text + jl = c[:normal] # julia + d1 = c[:bold] * c[:blue] # first dot + d2 = c[:bold] * c[:red] # second dot + d3 = c[:bold] * c[:green] # third dot + d4 = c[:bold] * c[:magenta] # fourth dot + + print(io,""" $(d3)_$(tx) + $(d1)_$(tx) $(jl)_$(tx) $(d2)_$(d3)(_)$(d4)_$(tx) | Documentation: https://docs.julialang.org + $(d1)(_)$(jl) | $(d2)(_)$(tx) $(d4)(_)$(tx) | + $(jl)_ _ _| |_ __ _$(tx) | Type \"?\" for help, \"]?\" for Pkg help. + $(jl)| | | | | | |/ _` |$(tx) | + $(jl)| | |_| | | | (_| |$(tx) | Version $(VERSION)$(commit_date) + $(jl)_/ |\\__'_|_|_|\\__'_|$(tx) | $(commit_string) + $(jl)|__/$(tx) | + + """) + else + print(io,""" + _ + _ _ _(_)_ | Documentation: https://docs.julialang.org + (_) | (_) (_) | + _ _ _| |_ __ _ | Type \"?\" for help, \"]?\" for Pkg help. + | | | | | | |/ _` | | + | | |_| | | | (_| | | Version $(VERSION)$(commit_date) + _/ |\\__'_|_|_|\\__'_| | $(commit_string) + |__/ | + + """) + end +end diff --git a/base/version_git.sh b/base/version_git.sh new file mode 100644 index 0000000..d2ac9cb --- /dev/null +++ b/base/version_git.sh @@ -0,0 +1,94 @@ +#!/bin/sh +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# This file collects git info and create a julia file with the GIT_VERSION_INFO struct + +echo "# This file was autogenerated in base/version_git.sh" +echo "struct GitVersionInfo" +echo " commit::AbstractString" +echo " commit_short::AbstractString" +echo " branch::AbstractString" +echo " build_number::Int" +echo " date_string::AbstractString" +echo " tagged_commit::Bool" +echo " fork_master_distance::Int" +echo " fork_master_timestamp::Float64" +echo "end" +echo "" + +cd $1 + +# If the script didn't ask not to use git info +if [ "$#" = "2" -a "$2" = "NO_GIT" ]; then + # this comment is used in base/Makefile to distinguish boilerplate + echo "# Default output if git is not available." + echo "const GIT_VERSION_INFO = GitVersionInfo(\"\" ,\"\" ,\"\" ,0 ,\"\" ,true ,0 ,0.)" + exit 0 +fi +# Collect temporary variables +origin=$(git config -l 2>/dev/null | grep 'remote\.\w*\.url.*JuliaLang/julia' | sed -n 's/remote\.\([a-zA-Z]*\)\..*/\1\//p') +if [ -z "$origin" ]; then + origin="origin/" +fi +git_time=$(git log -1 --pretty=format:%ct) + +#collect the contents +commit=$(git rev-parse HEAD) +commit_short=$(git rev-parse --short HEAD) +if [ -n "$(git status --porcelain)" ]; then + # append dirty mark '*' if the repository has uncommitted changes + commit_short="$commit_short"* +fi +branch=$(git rev-parse --abbrev-ref HEAD) + +topdir=$(git rev-parse --show-toplevel) +verchanged=$(git blame -L ,1 -sl -- "$topdir/VERSION" | cut -f 1 -d " ") +if [ $verchanged = 0000000000000000000000000000000000000000 ]; then + # uncommitted change to VERSION + build_number=0 +else + build_number=$(git rev-list --count HEAD "^$verchanged") +fi + +date_string=$git_time +case $(uname) in + Darwin | FreeBSD) + date_string="$(/bin/date -jr $git_time -u '+%Y-%m-%d %H:%M %Z')" + ;; + MINGW*) + git_time=$(git log -1 --pretty=format:%ci) + date_string="$(/bin/date --date="$git_time" -u '+%Y-%m-%d %H:%M %Z')" + ;; + *) + date_string="$(/bin/date --date="@$git_time" -u '+%Y-%m-%d %H:%M %Z')" + ;; +esac +if [ $(git describe --tags --exact-match 2> /dev/null) ]; then + tagged_commit="true" +else + tagged_commit="false" +fi +fork_master_distance=$(git rev-list HEAD ^"$(echo $origin)master" | wc -l | sed -e 's/[^[:digit:]]//g') +fork_master_timestamp=$(git show -s $(git merge-base HEAD $(echo $origin)master) --format=format:"%ct") + +# Check for errors and emit default value for missing numbers. +if [ -z "$build_number" ]; then + build_number="-1" +fi +if [ -z "$fork_master_distance" ]; then + fork_master_distance="-1" +fi +if [ -z "$fork_master_timestamp" ]; then + fork_master_timestamp="0" +fi + +echo "const GIT_VERSION_INFO = GitVersionInfo(" +echo " \"$commit\"," +echo " \"$commit_short\"," +echo " \"$branch\"," +echo " $build_number," +echo " \"$date_string\"," +echo " $tagged_commit," +echo " $fork_master_distance," +echo " $fork_master_timestamp." +echo ")" diff --git a/base/views.jl b/base/views.jl new file mode 100644 index 0000000..65c6911 --- /dev/null +++ b/base/views.jl @@ -0,0 +1,215 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" + replace_ref_end!(ex) + +Recursively replace occurrences of the symbol :end in a "ref" expression (i.e. A[...]) `ex` +with the appropriate function calls (`lastindex` or `size`). Replacement uses +the closest enclosing ref, so + + A[B[end]] + +should transform to + + A[B[lastindex(B)]] + +""" +replace_ref_end!(ex) = replace_ref_end_!(ex, nothing)[1] +# replace_ref_end_!(ex,withex) returns (new ex, whether withex was used) +function replace_ref_end_!(ex, withex) + used_withex = false + if isa(ex,Symbol) && ex === :end + withex === nothing && error("Invalid use of end") + return withex, true + elseif isa(ex,Expr) + if ex.head === :ref + ex.args[1], used_withex = replace_ref_end_!(ex.args[1],withex) + S = isa(ex.args[1],Symbol) ? ex.args[1]::Symbol : gensym(:S) # temp var to cache ex.args[1] if needed + used_S = false # whether we actually need S + # new :ref, so redefine withex + nargs = length(ex.args)-1 + if nargs == 0 + return ex, used_withex + elseif nargs == 1 + # replace with lastindex(S) + ex.args[2], used_S = replace_ref_end_!(ex.args[2],:($lastindex($S))) + else + n = 1 + J = lastindex(ex.args) + for j = 2:J + exj, used = replace_ref_end_!(ex.args[j],:($lastindex($S,$n))) + used_S |= used + ex.args[j] = exj + if isa(exj,Expr) && exj.head === :... + # splatted object + exjs = exj.args[1] + n = :($n + length($exjs)) + elseif isa(n, Expr) + # previous expression splatted + n = :($n + 1) + else + # an integer + n += 1 + end + end + end + if used_S && S !== ex.args[1] + S0 = ex.args[1] + ex.args[1] = S + ex = Expr(:let, :($S = $S0), ex) + end + else + # recursive search + for i = eachindex(ex.args) + ex.args[i], used = replace_ref_end_!(ex.args[i],withex) + used_withex |= used + end + end + end + ex, used_withex +end + +""" + @view A[inds...] + +Creates a `SubArray` from an indexing expression. This can only be applied directly to a +reference expression (e.g. `@view A[1,2:end]`), and should *not* be used as the target of +an assignment (e.g. `@view(A[1,2:end]) = ...`). See also [`@views`](@ref) +to switch an entire block of code to use views for slicing. + +# Examples +```jldoctest +julia> A = [1 2; 3 4] +2×2 Array{Int64,2}: + 1 2 + 3 4 + +julia> b = @view A[:, 1] +2-element view(::Array{Int64,2}, :, 1) with eltype Int64: + 1 + 3 + +julia> fill!(b, 0) +2-element view(::Array{Int64,2}, :, 1) with eltype Int64: + 0 + 0 + +julia> A +2×2 Array{Int64,2}: + 0 2 + 0 4 +``` +""" +macro view(ex) + if Meta.isexpr(ex, :ref) + ex = replace_ref_end!(ex) + if Meta.isexpr(ex, :ref) + ex = Expr(:call, view, ex.args...) + else # ex replaced by let ...; foo[...]; end + @assert Meta.isexpr(ex, :let) && Meta.isexpr(ex.args[2], :ref) + ex.args[2] = Expr(:call, view, ex.args[2].args...) + end + Expr(:&&, true, esc(ex)) + else + throw(ArgumentError("Invalid use of @view macro: argument must be a reference expression A[...].")) + end +end + +############################################################################ +# @views macro code: + +# maybeview is like getindex, but returns a view for slicing operations +# (while remaining equivalent to getindex for scalar indices and non-array types) +@propagate_inbounds maybeview(A, args...) = getindex(A, args...) +@propagate_inbounds maybeview(A::AbstractArray, args...) = view(A, args...) +@propagate_inbounds maybeview(A::AbstractArray, args::Number...) = getindex(A, args...) +@propagate_inbounds maybeview(A) = getindex(A) +@propagate_inbounds maybeview(A::AbstractArray) = getindex(A) + +# _views implements the transformation for the @views macro. +# @views calls esc(_views(...)) to work around #20241, +# so any function calls we insert (to maybeview, or to +# lastindex in replace_ref_end!) must be interpolated +# as values rather than as symbols to ensure that they are called +# from Base rather than from the caller's scope. +_views(x) = x +function _views(ex::Expr) + if ex.head in (:(=), :(.=)) + # don't use view for ref on the lhs of an assignment, + # but still use views for the args of the ref: + lhs = ex.args[1] + Expr(ex.head, Meta.isexpr(lhs, :ref) ? + Expr(:ref, _views.(lhs.args)...) : _views(lhs), + _views(ex.args[2])) + elseif ex.head === :ref + Expr(:call, maybeview, _views.(ex.args)...) + else + h = string(ex.head) + # don't use view on the lhs of an op-assignment a[i...] += ... + if last(h) == '=' && Meta.isexpr(ex.args[1], :ref) + lhs = ex.args[1] + + # temp vars to avoid recomputing a and i, + # which will be assigned in a let block: + a = gensym(:a) + i = [gensym(:i) for k = 1:length(lhs.args)-1] + + # for splatted indices like a[i, j...], we need to + # splat the corresponding temp var. + I = similar(i, Any) + for k = 1:length(i) + if Meta.isexpr(lhs.args[k+1], :...) + I[k] = Expr(:..., i[k]) + lhs.args[k+1] = lhs.args[k+1].args[1] # unsplat + else + I[k] = i[k] + end + end + + Expr(:let, + Expr(:block, + :($a = $(_views(lhs.args[1]))), + [:($(i[k]) = $(_views(lhs.args[k+1]))) for k=1:length(i)]...), + Expr(first(h) == '.' ? :(.=) : :(=), :($a[$(I...)]), + Expr(:call, Symbol(h[1:end-1]), + :($maybeview($a, $(I...))), + _views.(ex.args[2:end])...))) + else + Expr(ex.head, _views.(ex.args)...) + end + end +end + +""" + @views expression + +Convert every array-slicing operation in the given expression +(which may be a `begin`/`end` block, loop, function, etc.) +to return a view. Scalar indices, non-array types, and +explicit `getindex` calls (as opposed to `array[...]`) are +unaffected. + +!!! note + The `@views` macro only affects `array[...]` expressions + that appear explicitly in the given `expression`, not array slicing that + occurs in functions called by that code. + +# Examples +```jldoctest +julia> A = zeros(3, 3); + +julia> @views for row in 1:3 + b = A[row, :] + b[:] .= row + end + +julia> A +3×3 Array{Float64,2}: + 1.0 1.0 1.0 + 2.0 2.0 2.0 + 3.0 3.0 3.0 +``` +""" +macro views(x) + esc(_views(replace_ref_end!(x))) +end diff --git a/base/weakkeydict.jl b/base/weakkeydict.jl new file mode 100644 index 0000000..ce8da7e --- /dev/null +++ b/base/weakkeydict.jl @@ -0,0 +1,123 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# weak key dictionaries + +""" + WeakKeyDict([itr]) + +`WeakKeyDict()` constructs a hash table where the keys are weak +references to objects which may be garbage collected even when +referenced in a hash table. + +See [`Dict`](@ref) for further help. Note, unlike [`Dict`](@ref), +`WeakKeyDict` does not convert keys on insertion. +""" +mutable struct WeakKeyDict{K,V} <: AbstractDict{K,V} + ht::Dict{WeakRef,V} + lock::ReentrantLock + finalizer::Function + + # Constructors mirror Dict's + function WeakKeyDict{K,V}() where V where K + t = new(Dict{Any,V}(), ReentrantLock(), identity) + t.finalizer = function (k) + # when a weak key is finalized, remove from dictionary if it is still there + if islocked(t) + finalizer(t.finalizer, k) + return nothing + end + delete!(t, k) + end + return t + end +end +function WeakKeyDict{K,V}(kv) where V where K + h = WeakKeyDict{K,V}() + for (k,v) in kv + h[k] = v + end + return h +end +WeakKeyDict{K,V}(p::Pair) where V where K = setindex!(WeakKeyDict{K,V}(), p.second, p.first) +function WeakKeyDict{K,V}(ps::Pair...) where V where K + h = WeakKeyDict{K,V}() + sizehint!(h, length(ps)) + for p in ps + h[p.first] = p.second + end + return h +end +WeakKeyDict() = WeakKeyDict{Any,Any}() + +WeakKeyDict(kv::Tuple{}) = WeakKeyDict() +copy(d::WeakKeyDict) = WeakKeyDict(d) + +WeakKeyDict(ps::Pair{K,V}...) where {K,V} = WeakKeyDict{K,V}(ps) +WeakKeyDict(ps::Pair{K}...) where {K} = WeakKeyDict{K,Any}(ps) +WeakKeyDict(ps::(Pair{K,V} where K)...) where {V} = WeakKeyDict{Any,V}(ps) +WeakKeyDict(ps::Pair...) = WeakKeyDict{Any,Any}(ps) + +function WeakKeyDict(kv) + try + Base.dict_with_eltype((K, V) -> WeakKeyDict{K, V}, kv, eltype(kv)) + catch + if !isiterable(typeof(kv)) || !all(x->isa(x,Union{Tuple,Pair}),kv) + throw(ArgumentError("WeakKeyDict(kv): kv needs to be an iterator of tuples or pairs")) + else + rethrow() + end + end +end + +empty(d::WeakKeyDict, ::Type{K}, ::Type{V}) where {K, V} = WeakKeyDict{K, V}() + +islocked(wkh::WeakKeyDict) = islocked(wkh.lock) +lock(f, wkh::WeakKeyDict) = lock(f, wkh.lock) +trylock(f, wkh::WeakKeyDict) = trylock(f, wkh.lock) + +function setindex!(wkh::WeakKeyDict{K}, v, key) where K + !isa(key, K) && throw(ArgumentError("$(limitrepr(key)) is not a valid key for type $K")) + finalizer(wkh.finalizer, key) + lock(wkh) do + wkh.ht[WeakRef(key)] = v + end + return wkh +end + +function getkey(wkh::WeakKeyDict{K}, kk, default) where K + return lock(wkh) do + k = getkey(wkh.ht, kk, secret_table_token) + k === secret_table_token && return default + return k.value::K + end +end + +map!(f,iter::ValueIterator{<:WeakKeyDict})= map!(f, values(iter.dict.ht)) +get(wkh::WeakKeyDict{K}, key, default) where {K} = lock(() -> get(wkh.ht, key, default), wkh) +get(default::Callable, wkh::WeakKeyDict{K}, key) where {K} = lock(() -> get(default, wkh.ht, key), wkh) +function get!(wkh::WeakKeyDict{K}, key, default) where {K} + !isa(key, K) && throw(ArgumentError("$(limitrepr(key)) is not a valid key for type $K")) + lock(() -> get!(wkh.ht, WeakRef(key), default), wkh) +end +function get!(default::Callable, wkh::WeakKeyDict{K}, key) where {K} + !isa(key, K) && throw(ArgumentError("$(limitrepr(key)) is not a valid key for type $K")) + lock(() -> get!(default, wkh.ht, WeakRef(key)), wkh) +end +pop!(wkh::WeakKeyDict{K}, key) where {K} = lock(() -> pop!(wkh.ht, key), wkh) +pop!(wkh::WeakKeyDict{K}, key, default) where {K} = lock(() -> pop!(wkh.ht, key, default), wkh) +delete!(wkh::WeakKeyDict, key) = lock(() -> delete!(wkh.ht, key), wkh) +empty!(wkh::WeakKeyDict) = (lock(() -> empty!(wkh.ht), wkh); wkh) +haskey(wkh::WeakKeyDict{K}, key) where {K} = lock(() -> haskey(wkh.ht, key), wkh) +getindex(wkh::WeakKeyDict{K}, key) where {K} = lock(() -> getindex(wkh.ht, key), wkh) +isempty(wkh::WeakKeyDict) = isempty(wkh.ht) +length(t::WeakKeyDict) = length(t.ht) + +function iterate(t::WeakKeyDict{K,V}, state...) where {K, V} + y = lock(() -> iterate(t.ht, state...), t) + y === nothing && return nothing + wkv, newstate = y + kv = Pair{K,V}(wkv[1].value::K, wkv[2]) + return (kv, newstate) +end + +filter!(f, d::WeakKeyDict) = filter_in_one_pass!(f, d) diff --git a/contrib/README.md b/contrib/README.md new file mode 100644 index 0000000..fbc736c --- /dev/null +++ b/contrib/README.md @@ -0,0 +1,35 @@ +Installation +============================= + +| Name | Description | +| ---------------------------- | --------------------------------------------------------- | +|[ add_license_to_files.jl ](https://github.com/JuliaLang/julia/blob/master/contrib/add_license_to_files.jl ) | Add the Julia license to files in the Julia Project | +|[ check-whitespace.sh ](https://github.com/JuliaLang/julia/blob/master/contrib/check-whitespace.sh) | Check for trailing white space | +|[ commit-name.sh ](https://github.com/JuliaLang/julia/blob/master/contrib/commit-name.sh) | Computes a version name for a commit | +|[ filterArgs.sh ](https://github.com/JuliaLang/julia/blob/master/contrib/filterArgs.sh) | Update library search code to use only tokens that start with -L | +|[ fixup-libgfortran.sh ](https://github.com/JuliaLang/julia/blob/master/contrib/fixup-libgfortran.sh) | Include libgfortran and libquadmath for installations | +|[ fixup-libstdc++.sh ](https://github.com/JuliaLang/julia/blob/master/contrib/fixup-libstdc++.sh) | Include libstdc++ for installations | +|[ install.sh ](https://github.com/JuliaLang/julia/blob/master/contrib/install.sh) | Installation script with different permissions | +|[ julia.appdata.xml ](https://github.com/JuliaLang/julia/blob/master/contrib/julia.appdata.xml) | Appdata config file | +|[ julia-config.jl ](https://github.com/JuliaLang/julia/blob/master/contrib/julia-config.jl) | Determines build parameters required by an embedded Julia | +|[ julia.desktop ](https://github.com/JuliaLang/julia/blob/master/contrib/julia.desktop) | GNOME desktop config file | +|[ julia.svg ](https://github.com/JuliaLang/julia/blob/master/contrib/julia.svg) | Julia svg image file | +|[ mac/ ](https://github.com/JuliaLang/julia/blob/master/contrib/mac/) | Mac install files | +|[ relative_path.sh ](https://github.com/JuliaLang/julia/blob/master/contrib/relative_path.sh) | Convert absolute path into relative path script | +|[ repackage_system_suitesparse4.make ](https://github.com/JuliaLang/julia/blob/master/contrib/repackage_system_suitesparse4.make) | Links shared libraries from static-library for suitesparse4 | +|[ stringreplace.c ](https://github.com/JuliaLang/julia/blob/master/contrib/stringreplace.c) | Replace strings to hardcoded paths in binaries during `make install` | +|[ travis_fastfail.sh ](https://github.com/JuliaLang/julia/blob/master/contrib/travis_fastfail.sh ) | Checks for queued build tests in Travis | +|[ windows/ ](https://github.com/JuliaLang/julia/blob/master/contrib/windows/) | Windows install files | + +Editors + Debuggers +============================= + +[JuliaEditorSupport](https://github.com/JuliaEditorSupport) GitHub organization hosts julia extensions for various text editors and IDEs. + +If your favorite IDE, is not listed there, [open an issue in the JuliaEditorSupport/roadmap](https://github.com/JuliaEditorSupport/roadmap/issues) repository. + + +| Name | Description | +| ------------------------------ | ----------------------------------------------------------- | +|[ debug_bootstrap.gdb ](https://github.com/JuliaLang/julia/blob/master/contrib/debug_bootstrap.gdb) | Bootstrap process using the debug build | +|[ valgrind-julia.supp ](https://github.com/JuliaLang/julia/blob/master/contrib/valgrind-julia.supp) | Suppressions for Valgrind debugging tool | diff --git a/contrib/add_license_to_files.jl b/contrib/add_license_to_files.jl new file mode 100644 index 0000000..c5e8890 --- /dev/null +++ b/contrib/add_license_to_files.jl @@ -0,0 +1,198 @@ +#!/usr/bin/env julia +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Adds the julia license line `new_license` to configured file extensions in `rootdirs`. +# +# Option `old_license` to remove an existing license first in case one wants to change the +# license text in the future. +# +# Checks also if somewhere else in the file the license text is found (`copy/past error`) +# and if possible deletes such lines - if other text is on the same line it raises an error. + +### CONFIG HERE + +const print_result = true # prints files which where not processed. + +const rootdirs = [ + "../base", + "../contrib", + "../src", + "../stdlib", + "../test", +] + +# to exculde whole sub directories +const excludedirs = [ + # see: https://github.com/JuliaLang/julia/pull/11073#issuecomment-98090053 + "../base/grisu", + "../src/flisp", +] + +const skipfiles = [ + "../contrib/add_license_to_files.jl", + # files to check - already copyright + # see: https://github.com/JuliaLang/julia/pull/11073#issuecomment-98099389 + "../base/special/trig.jl", + "../base/special/exp.jl", + "../base/special/rem_pio2.jl", + # + "../src/abi_llvm.cpp", + "../src/abi_ppc64le.cpp", + "../src/abi_win32.cpp", + "../src/abi_win64.cpp", + "../src/abi_x86.cpp", + "../src/abi_x86_64.cpp", + "../src/disasm.cpp", + "../src/getopt.c", + "../src/getopt.h", + "../src/support/END.h", + "../src/support/ENTRY.amd64.h", + "../src/support/ENTRY.i387.h", + "../src/support/MurmurHash3.c", + "../src/support/MurmurHash3.h", + "../src/support/asprintf.c", + "../src/support/dirname.c", + "../src/support/strptime.c", + "../src/support/strtod.c", + "../src/support/tzfile.h", + "../src/support/utf8.c", + "../src/crc32c.c", +] + +const ext_prefix = Dict([ + (".jl", "# "), + (".sh", "# "), + (".h", "// "), + (".c", "// "), + (".cpp", "// "), +]) + +const new_license = "This file is a part of Julia. License is MIT: https://julialang.org/license" + +# Old License text if such should be first removed - or empty string +const old_license = "" + +### END CONFIG HERE + + +function check_lines!( + path::AbstractString, lines::Vector, checktxt::AbstractString, + prefix::AbstractString, oldcheck::Bool) + remove = [] + for i in 1:length(lines) + line = lines[i] + if occursin(checktxt, line) + if strip(line) == strip(prefix * checktxt) || strip(line) == strip(checktxt) + push!(remove, i) + else + error(string("`path` contains an additional line with ", + oldcheck ? "old" : "new", + " license.\nlinenum: $(i): $(line) \n`path:` $(path)\n", + "Fix this first or add the file to `skipfiles`.\n\n")) + end + end + end + deleteat!(lines, remove) +end + +license_linenum(line) = startswith(strip(line), "#!") ? 2 : 1 + +# Collects all absolute file paths in rootdir inclusive subdirs +function getfilespaths!(filepaths::Vector, rootdir::AbstractString) + isdir(rootdir) || error(string("`rootdir` must be an directory. ")) + abs_rootdir = abspath(rootdir) + for name in readdir(abs_rootdir) + path = joinpath(abs_rootdir, name) + if isdir(path) + getfilespaths!(filepaths, path) + else + push!(filepaths, joinpath(abs_rootdir, name)) + end + end +end + +function add_license_line!(unprocessed::Vector, src::AbstractString, new_license::AbstractString, + old_license::AbstractString, ext_prefix::Dict, abs_excludedirs::Vector, + skipfiles::Vector) + + for name in readdir(src) + path = normpath(joinpath(src, name)) + if isdir(path) + if path in abs_excludedirs + getfilespaths!(unprocessed, path) + continue + else + add_license_line!(unprocessed, path, new_license, old_license, + ext_prefix, abs_excludedirs, skipfiles) + end + elseif path in skipfiles + push!(unprocessed, path) + continue + else + ext = splitext(path)[2] + if ext in keys(ext_prefix) + prefix = ext_prefix[ext] + f = open(path, "r") + lines = readlines(f, keep=true) + close(f) + isempty(lines) && (push!(unprocessed, path); continue) + isempty(old_license) || check_lines!(path, lines, old_license, prefix, true) + check_lines!(path, lines, new_license, prefix, false) + isempty(lines) && continue # file consisting of just license header + # check shebang file + linenum = license_linenum(lines[1]) + if !isempty(strip(lines[linenum])) + insert!(lines, linenum, string(prefix, new_license, "\n\n")) + else + insert!(lines, linenum, string(prefix, new_license, "\n")) + end + open(path, "w") do f + for line in lines + write(f, line) + end + end + else + push!(unprocessed, path) + end + end + end + return unprocessed +end + +# Returns a new Vector with all absolute path: raises an error if path does not exist +function abspaths(A::Vector) + abs_A = [] + for p in A + abs_p = isabspath(p) ? normpath(p) : normpath(joinpath(@__DIR__, p)) + ispath(abs_p) || error(string("`abs_p` seems not to be an existing path. ", + "Adjust your configuration: <", p, "> : ", abs_p, "\n")) + push!(abs_A, abs_p) + end + return abs_A +end + +function add_license(rootdirs::Vector, new_license::AbstractString, old_license::AbstractString, + ext_prefix::Dict, excludedirs::Vector, skipfiles::Vector, print_result::Bool) + isempty(strip(new_license)) && error("`new_license` may not only contain white space.") + abs_skipfiles = abspaths(skipfiles) + abs_rootdirs = abspaths(rootdirs) + abs_excludedirs = abspaths(excludedirs) + for p in abs_rootdirs + p in abs_excludedirs && error(string("Seems one of the `rootdirs` is also included in ", + "the `excludedirs`. `rootdirs`: ", p)) + unprocessed = [] + add_license_line!(unprocessed, p, new_license, old_license, ext_prefix, + abs_excludedirs, abs_skipfiles) + if print_result + println("\nUnprocessed files in rootdir: <$(p)>\n") + for file in unprocessed + println(" <", basename(file), "> : ", file) + end + end + end +end + + +## --------------- + +add_license(rootdirs, new_license, old_license, ext_prefix, excludedirs, skipfiles, print_result) diff --git a/contrib/check-whitespace.sh b/contrib/check-whitespace.sh new file mode 100755 index 0000000..c380d7b --- /dev/null +++ b/contrib/check-whitespace.sh @@ -0,0 +1,37 @@ +#!/bin/sh +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Check for trailing white space in source files; +# report an error if so + +# Files to check: +set -f # disable glob expansion in this script +file_patterns=' +*.1 +*.c +*.cpp +*.h +*.jl +*.lsp +*.scm +*.inc +*.make +*.mk +*.md +*.rst +*.sh +*.yml +*Makefile +' + +# TODO: Look also for trailing empty lines, and missing '\n' after the last line +if git --no-pager grep --color -n --full-name -e ' $' -- $file_patterns; then + echo "Error: trailing whitespace found in source file(s)" + echo "" + echo "This can often be fixed with:" + echo " git rebase --whitespace=fix HEAD~1" + echo "or" + echo " git rebase --whitespace=fix master" + echo "and then a forced push of the correct branch" + exit 1 +fi diff --git a/contrib/commit-name.sh b/contrib/commit-name.sh new file mode 100755 index 0000000..7a139ee --- /dev/null +++ b/contrib/commit-name.sh @@ -0,0 +1,43 @@ +#!/bin/sh +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Needs to be run from a julia repo clone +# First argument (optional) is a ref to the commit + +gitref=${1:-HEAD} + +ver=$(git show "$gitref:VERSION") +major=$(echo $ver | cut -f 1 -d .) +minor=$(echo $ver | cut -f 2 -d .) + +if [ $major = 0 -a $minor -lt 5 ]; then + # use tag based build number prior to 0.5.0- + last_tag=$(git describe --tags --abbrev=0 "$gitref") + nb=$(git rev-list --count "$gitref" "^$last_tag") + if [ $nb = 0 ]; then + echo $ver + else + echo "$ver+$nb" + fi +else + topdir=$(git rev-parse --show-toplevel) + verchanged=$(git blame -L ,1 -sl $gitref -- "$topdir/VERSION" | cut -f 1 -d " ") + nb=$(git rev-list --count "$gitref" "^$verchanged") + pre=$(echo $ver | cut -s -f 2 -d "-") + if [ $ver = "0.5.0-dev" ]; then + # bump to 0.5.0-dev was one commit after tag during 0.5.0-dev + nb=$(expr $nb + 1) + elif [ $ver = "0.5.0-pre" ]; then + # bump to 0.5.0-pre was 5578 commits after tag + nb=$(expr $nb + 5578) + fi + if [ -n "$pre" ]; then + if [ $major = 0 -a $minor -le 5 ]; then + echo "$ver+$nb" + else + echo "$ver.$nb" + fi + else + echo $ver + fi +fi diff --git a/contrib/debug_bootstrap.gdb b/contrib/debug_bootstrap.gdb new file mode 100644 index 0000000..8432fa4 --- /dev/null +++ b/contrib/debug_bootstrap.gdb @@ -0,0 +1,3 @@ +file ../usr/bin/julia-debug +r --build ../usr/lib/julia/sys0.bc sysimg.jl +bt diff --git a/contrib/delete-all-rpaths.sh b/contrib/delete-all-rpaths.sh new file mode 100755 index 0000000..22e18e0 --- /dev/null +++ b/contrib/delete-all-rpaths.sh @@ -0,0 +1,33 @@ +#!/bin/sh +# This file is a part of Julia. License is MIT: https://julialang.org/license + +[ "$(uname)" = Darwin ] || { echo "Requires Darwin." 2>&1; exit 1; } + +if [ $# -lt 1 ]; then + echo "usage: $(basename $0) lib1 [lib2 ...]" 2>&1 + exit 1 +fi + +LIBS='' + +# filter out symlinks +for lib in "$@" ; do + if [ ! -L "$lib" ]; then + LIBS="$LIBS $lib" + fi +done + +# regex to match and capture the path in a LC_RPATH as output by `otool -l`. +rpath_r="^ +path ([[:print:]]+) \(offset [[:digit:]]+\)$" + +for lib in $LIBS ; do + # Find the LC_RPATH commands, with two lines of trailing + # context, and for each line look for the path to delete it. + otool -l "$lib" | grep LC_RPATH -A2 | + while IFS='' read -r line || [ -n "$line" ]; do + if [[ $line =~ $rpath_r ]]; then + echo $(basename $lib) deleting rpath "${BASH_REMATCH[1]}" \""$lib"\" + install_name_tool -delete_rpath "${BASH_REMATCH[1]}" "$lib" + fi + done +done diff --git a/contrib/download_cmake.sh b/contrib/download_cmake.sh new file mode 100755 index 0000000..d122e1e --- /dev/null +++ b/contrib/download_cmake.sh @@ -0,0 +1,40 @@ +#!/bin/sh +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Script to download newest version of cmake on linux (or mac) +# saves you the trouble of compiling it if you don't have root +set -e # stop on failure +mkdir -p "$(dirname "$0")"/../deps/scratch +cd "$(dirname "$0")"/../deps/scratch + +CMAKE_VERSION_MAJOR=3 +CMAKE_VERSION_MINOR=7 +CMAKE_VERSION_PATCH=1 +CMAKE_VERSION_MAJMIN=$CMAKE_VERSION_MAJOR.$CMAKE_VERSION_MINOR +CMAKE_VERSION=$CMAKE_VERSION_MAJMIN.$CMAKE_VERSION_PATCH + +# listed at https://cmake.org/files/v$CMAKE_VERSION_MAJMIN/cmake-$CMAKE_VERSION-SHA-256.txt +# for the files cmake-$CMAKE_VERSION-Darwin-x86_64.tar.gz +# and cmake-$CMAKE_VERSION-Linux-x86_64.tar.gz +CMAKE_SHA256_DARWIN=1851d1448964893fdc5a8c05863326119f397a3790e0c84c40b83499c7960267 +CMAKE_SHA256_LINUX=7b4b7a1d9f314f45722899c0521c261e4bfab4a6b532609e37fef391da6bade2 + +PLATFORM="$(uname)-$(uname -m)" +FULLNAME=cmake-$CMAKE_VERSION-$PLATFORM +case $PLATFORM in + Darwin-x86_64) + ../tools/jldownload https://cmake.org/files/v$CMAKE_VERSION_MAJMIN/$FULLNAME.tar.gz + echo "$CMAKE_SHA256_DARWIN $FULLNAME.tar.gz" | shasum -a 256 -c - + CMAKE_EXTRACTED_PATH=$FULLNAME/CMake.app/Contents/bin/cmake;; + Linux-x86_64) + ../tools/jldownload https://cmake.org/files/v$CMAKE_VERSION_MAJMIN/$FULLNAME.tar.gz + echo "$CMAKE_SHA256_LINUX $FULLNAME.tar.gz" | sha256sum -c - + CMAKE_EXTRACTED_PATH=$FULLNAME/bin/cmake;; + *) + echo "This script only supports x86_64 Mac and Linux. For other platforms," >&2 + echo "get cmake from your package manager or compile it from source." >&2 + exit 1;; +esac + +tar -xzf $FULLNAME.tar.gz +echo "CMAKE = $PWD/$CMAKE_EXTRACTED_PATH" >> ../../Make.user diff --git a/contrib/filterArgs.sh b/contrib/filterArgs.sh new file mode 100755 index 0000000..823745e --- /dev/null +++ b/contrib/filterArgs.sh @@ -0,0 +1,10 @@ +#!/bin/sh +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Loop over all command line arguments +for i in "$@"; do + # If an argument starts with -L, echo it out sans -L! + case $i in + -L*) printf '"%s"\n' "${i#-L}" ;; + esac +done diff --git a/contrib/fixup-libgfortran.sh b/contrib/fixup-libgfortran.sh new file mode 100755 index 0000000..d1f00bd --- /dev/null +++ b/contrib/fixup-libgfortran.sh @@ -0,0 +1,150 @@ +#!/bin/sh +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Run as: fixup-libgfortran.sh [--verbose] <$private_libdir> +FC=${FC:-gfortran} +PATCHELF=${PATCHELF:-patchelf} + +# If we're invoked with "--verbose", create a `debug` function that prints stuff out +if [ "$1" = "--verbose" ] || [ "$1" = "-v" ]; then +shift 1 +debug() { echo "$*"; } +else +debug() { :; } +fi + +if [ -z "$1" ]; then + echo "Usage: $0 " + exit 1 +fi + +UNAME="$(uname -s)" +if [ "$UNAME" = "Linux" ]; then + SHLIB_EXT="so" +elif [ "$UNAME" = "Darwin" ]; then + SHLIB_EXT="dylib" +else + echo "WARNING: Could not autodetect platform type ('uname -s' = $UNAME); assuming Linux" >&2 + UNAME="Linux" + SHLIB_EXT="so" +fi + +private_libdir=$1 + +find_shlib() +{ + lib_path="$1" + if [ -f "$lib_path" ]; then + if [ "$UNAME" = "Linux" ]; then + ldd "$lib_path" | grep $2 | grep -v "not found" | cut -d' ' -f3 | xargs + else # $UNAME is "Darwin", we only have two options, see above + otool -L "$lib_path" | grep $2 | cut -d' ' -f1 | xargs + fi + fi +} + +find_shlib_dir() +{ + # Usually, on platforms like OSX we get full paths when linking. However, + # if we are inspecting, say, BinaryBuilder-built OpenBLAS libraries, we will + # only get something like `@rpath/libgfortran.5.dylib` when inspecting the + # libraries. We can, as a last resort, ask `$FC` directly what the full + # filepath for this library is, but only if we don't have a direct path to it: + if [ $(dirname "$1") = "@rpath" ]; then + dirname "$($FC -print-file-name="$(basename "$1")" 2>/dev/null)" + else + dirname "$1" 2>/dev/null + fi +} + +# First, discover all the places where libgfortran/libgcc is, as well as their true SONAMES +for lib in lapack blas openblas; do + for private_libname in ${private_libdir}/lib$lib*.$SHLIB_EXT*; do + # Find the paths to the libraries we're interested in. These are almost + # always within the same directory, but we like to be general. + LIBGFORTRAN_PATH=$(find_shlib "$private_libname" libgfortran) + LIBGCC_PATH=$(find_shlib "$private_libname" libgcc_s) + LIBQUADMATH_PATH=$(find_shlib "$private_libname" libquadmath) + + # Take the directories, add them onto LIBGFORTRAN_DIRS, which we use to + # search for these libraries in the future. If there is no directory, try + # asking `$FC` where such a file could be found. + LIBGFORTRAN_DIRS="$LIBGFORTRAN_DIRS $(find_shlib_dir $LIBGFORTRAN_PATH)" + LIBGFORTRAN_DIRS="$LIBGFORTRAN_DIRS $(find_shlib_dir $LIBGCC_PATH)" + LIBGFORTRAN_DIRS="$LIBGFORTRAN_DIRS $(find_shlib_dir $LIBQUADMATH_PATH)" + + # Save the SONAMES + LIBGFORTRAN_SONAMES="$LIBGFORTRAN_SONAMES $(basename "$LIBGFORTRAN_PATH")" + LIBGCC_SONAMES="$LIBGCC_SONAMES $(basename "$LIBGCC_PATH")" + LIBQUADMATH_SONAMES="$LIBQUADMATH_SONAMES $(basename "$LIBQUADMATH_PATH")" + done +done + +# Take in a list of space-separated tokens, return a deduplicated list of the same +uniquify() +{ + echo "$1" | tr " " "\n" | sort | uniq | grep -v '^$' | tr "\n" " " +} + +LIBGFORTRAN_DIRS=$(uniquify "$LIBGFORTRAN_DIRS") +SONAMES="$(uniquify "$LIBGFORTRAN_SONAMES $LIBGCC_SONAMES $LIBQUADMATH_SONAMES")" +debug "Discovered traces of libgfortran within $LIBGFORTRAN_DIRS" +debug "Got SONAMES of $SONAMES" + +# Copy the SONAMEs we identified above into our private_libdir +for soname in $SONAMES; do + for dir in $LIBGFORTRAN_DIRS; do + if [ ! -f "$private_libdir/$soname" ] && [ -f "$dir/$soname" ]; then + cp -v "$dir/$soname" "$private_libdir" + chmod 755 "$private_libdir/$soname" + if [ "$UNAME" = "Darwin" ]; then + install_name_tool -id "@rpath/$soname" "$private_libdir/$soname" + fi + fi + done +done + +# On OSX, we need to change the old link (which is usually a full path) +# to point to `@rpath/${soname}` explicitly, so we use `find_shlib()` +# to dynamically find the full path we want to change. On Linux, we +# don't care about full paths, we just set the rpath to `$ORIGIN`. +change_linkage() +{ + # This is the path of the library we want to edit + lib_path="$1" + + # If it doesn't exist, exit quietly + if [ ! -f "$lib_path" ]; then + debug " $lib_path doesn't exist, skipping" + return + fi + + # This is the soname of the dependency we want to swap out + soname="$2" + + if [ "$UNAME" = "Darwin" ]; then + old_link=$(find_shlib "$lib_path" "$soname") + echo " $old_link" + install_name_tool -change "$old_link" "@rpath/$soname" "$lib_path" + else # $UNAME is "Linux", we only have two options, see above + ${PATCHELF} --set-rpath \$ORIGIN "$lib_path" + fi +} + +# For every library that remotely touches libgfortran stuff (the libraries we +# have copied in ourselves) we must +# update the linkage to point to @rpath (on OSX) or $ORIGIN (on Linux) so +# that direct links to the old libgfortran directories are instead directed +# to the proper location, which is our $private_libdir. +for lib in libopenblas libcholmod liblapack $SONAMES; do + # Grab every incarnation of that library that exists within $private_libdir + # (e.g. "libopenblas.so", and "libopenblas.so.0", etc...) + for lib_path in $private_libdir/$lib*; do + # Iterate over dependency names that need to be changed + for soname in $SONAMES; do + debug "changing linkage of $lib_path to $soname" + change_linkage "$lib_path" "$soname" + done + done +done + diff --git a/contrib/fixup-libstdc++.sh b/contrib/fixup-libstdc++.sh new file mode 100755 index 0000000..ee84094 --- /dev/null +++ b/contrib/fixup-libstdc++.sh @@ -0,0 +1,34 @@ +#!/bin/sh +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Run as: fixup-libstdc++.sh + +if [ -z "$1" ]; then + echo "Usage: $0 " + exit 1 +fi + +libdir="$1" +private_libdir="$2" + +if [ ! -f "$libdir/libjulia.so" ]; then + echo "ERROR: Could not open $libdir/libjulia.so" >&2 + exit 2 +fi + +find_shlib () +{ + if [ -f "$1" ]; then + ldd "$1" | grep $2 | cut -d' ' -f3 | xargs + fi +} + +# Discover libstdc++ location and name +LIBSTD=$(find_shlib "$libdir/libjulia.so" "libstdc++.so") +LIBSTD_NAME=$(basename $LIBSTD) +LIBSTD_DIR=$(dirname $LIBSTD) + +if [ ! -f "$private_libdir/$LIBSTD_NAME" ] && [ -f "$LIBSTD_DIR/$LIBSTD_NAME" ]; then + cp -v "$LIBSTD_DIR/$LIBSTD_NAME" "$private_libdir" + chmod 755 "$private_libdir/$LIBSTD_NAME" +fi diff --git a/contrib/fixup-rpath.sh b/contrib/fixup-rpath.sh new file mode 100755 index 0000000..75e0849 --- /dev/null +++ b/contrib/fixup-rpath.sh @@ -0,0 +1,34 @@ +#!/bin/sh +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Usage: fixup-rpath.sh + +if [ $# -ne 3 ]; then + echo "Incorrect number of arguments: Expected 3, got $#" + echo "Usage: fixup-rpath.sh " + exit 1 +fi + +patchelf="$1" +executable_dir="$2" +build_libdir="$3" + +for lib in $(find ${executable_dir} -type f -perm -111); do + # First get the current RPATH + rpath="$(${patchelf} --print-rpath ${lib})" + + # If it doesn't contain the build's libdir, we don't care about it + if [ -z "$(echo ${rpath} | grep -F ${build_libdir})" ]; then + continue + fi + + # Remove build_libdir from the RPATH, retaining the rest + new_rpath="$(echo ${rpath} | tr : \\n | grep -vF ${build_libdir} | tr \\n :)" + # Drop the trailing : + new_rpath="${new_rpath%?}" + + echo " Setting RPATH for ${lib} to '${new_rpath}'" + + # Now set the new RPATH + ${patchelf} --set-rpath "${new_rpath}" ${lib} +done diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl new file mode 100644 index 0000000..97c4297 --- /dev/null +++ b/contrib/generate_precompile.jl @@ -0,0 +1,180 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +if isempty(ARGS) || ARGS[1] !== "0" +# Prevent this from being put into the Main namespace +@eval Module() begin +if !isdefined(Base, :uv_eventloop) + Base.reinit_stdio() +end +Base.include(@__MODULE__, joinpath(Sys.BINDIR, "..", "share", "julia", "test", "testhelpers", "FakePTYs.jl")) +import .FakePTYs: open_fake_pty + +CTRL_C = '\x03' +UP_ARROW = "\e[A" +DOWN_ARROW = "\e[B" + +precompile_script = """ +2+2 +print("") +@time 1+1 +; pwd +? reinterpret +using Ra\t$CTRL_C +\\alpha\t$CTRL_C +\e[200~paste here ;)\e[201~"$CTRL_C +$UP_ARROW$DOWN_ARROW$CTRL_C +123\b\b\b$CTRL_C +\b\b$CTRL_C +f(x) = x03 +f(1,2) +[][1] +cd("complet_path\t\t$CTRL_C +""" + +julia_exepath() = joinpath(Sys.BINDIR, Base.julia_exename()) + +have_repl = haskey(Base.loaded_modules, + Base.PkgId(Base.UUID("3fa0cd96-eef1-5676-8a61-b3b8758bbffb"), "REPL")) +Pkg = get(Base.loaded_modules, + Base.PkgId(Base.UUID("44cfe95a-1eb2-52ea-b672-e2afdf69b78f"), "Pkg"), + nothing) + +if Pkg !== nothing + precompile_script *= Pkg.precompile_script +end + +function generate_precompile_statements() + start_time = time() + debug_output = devnull # or stdout + + # Precompile a package + mktempdir() do prec_path + push!(DEPOT_PATH, prec_path) + push!(LOAD_PATH, prec_path) + pkgname = "__PackagePrecompilationStatementModule" + mkpath(joinpath(prec_path, pkgname, "src")) + write(joinpath(prec_path, pkgname, "src", "$pkgname.jl"), + """ + module $pkgname + end + """) + @eval using __PackagePrecompilationStatementModule + empty!(LOAD_PATH) + empty!(DEPOT_PATH) + end + + print("Generating precompile statements...") + mktemp() do precompile_file, precompile_file_h + # Run a repl process and replay our script + pty_slave, pty_master = open_fake_pty() + blackhole = Sys.isunix() ? "/dev/null" : "nul" + if have_repl + cmdargs = ```--color=yes + -e 'import REPL; REPL.Terminals.is_precompiling[] = true' + ``` + else + cmdargs = `-e nothing` + end + p = withenv("JULIA_HISTORY" => blackhole, + "JULIA_PROJECT" => nothing, # remove from environment + "JULIA_LOAD_PATH" => Sys.iswindows() ? "@;@stdlib" : "@:@stdlib", + "TERM" => "") do + sysimg = Base.unsafe_string(Base.JLOptions().image_file) + run(```$(julia_exepath()) -O0 --trace-compile=$precompile_file --sysimage $sysimg + --cpu-target=native --startup-file=no --color=yes + -e 'import REPL; REPL.Terminals.is_precompiling[] = true' + -i $cmdargs```, + pty_slave, pty_slave, pty_slave; wait=false) + end + Base.close_stdio(pty_slave) + # Prepare a background process to copy output from process until `pty_slave` is closed + output_copy = Base.BufferStream() + tee = @async try + while !eof(pty_master) + l = readavailable(pty_master) + write(debug_output, l) + Sys.iswindows() && (sleep(0.1); yield(); yield()) # workaround hang - probably a libuv issue? + write(output_copy, l) + end + close(output_copy) + close(pty_master) + catch ex + close(output_copy) + close(pty_master) + if !(ex isa Base.IOError && ex.code == Base.UV_EIO) + rethrow() # ignore EIO on pty_master after pty_slave dies + end + end + # wait for the definitive prompt before start writing to the TTY + readuntil(output_copy, "julia>") + sleep(0.1) + readavailable(output_copy) + # Input our script + if have_repl + for l in split(precompile_script, '\n'; keepempty=false) + sleep(0.1) + # consume any other output + bytesavailable(output_copy) > 0 && readavailable(output_copy) + # push our input + write(debug_output, "\n#### inputting statement: ####\n$(repr(l))\n####\n") + write(pty_master, l, "\n") + readuntil(output_copy, "\n") + # wait for the next prompt-like to appear + # NOTE: this is rather innaccurate because the Pkg REPL mode is a special flower + readuntil(output_copy, "\n") + readuntil(output_copy, "> ") + end + end + write(pty_master, "exit()\n") + wait(tee) + success(p) || Base.pipeline_error(p) + close(pty_master) + write(debug_output, "\n#### FINISHED ####\n") + + # Extract the precompile statements from stderr + statements = Set{String}() + for statement in eachline(precompile_file_h) + # Main should be completely clean + occursin("Main.", statement) && continue + push!(statements, statement) + end + + # Create a staging area where all the loaded packages are available + PrecompileStagingArea = Module() + for (_pkgid, _mod) in Base.loaded_modules + if !(_pkgid.name in ("Main", "Core", "Base")) + eval(PrecompileStagingArea, :(const $(Symbol(_mod)) = $_mod)) + end + end + + # Execute the collected precompile statements + n_succeeded = 0 + include_time = @elapsed for statement in sort(collect(statements)) + # println(statement) + try + Base.include_string(PrecompileStagingArea, statement) + n_succeeded += 1 + catch + # See #28808 + # @error "Failed to precompile $statement" + end + end + if have_repl + # Seems like a reasonable number right now, adjust as needed + # comment out if debugging script + @assert n_succeeded > 3500 + end + + print(" $(length(statements)) generated in ") + tot_time = time() - start_time + Base.time_print(tot_time * 10^9) + print(" (overhead "); Base.time_print((tot_time - include_time) * 10^9); println(")") + end + + return +end + +generate_precompile_statements() + +end # @eval +end diff --git a/contrib/install.sh b/contrib/install.sh new file mode 100755 index 0000000..1ce6f27 --- /dev/null +++ b/contrib/install.sh @@ -0,0 +1,35 @@ +#!/bin/sh +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Usage: very similar to `install` +# install.sh 755 src1 src2 ... dest + +PERMS=$1 +shift + +ARGS="" +while [ $# -gt 1 ]; do + ARGS="$ARGS $1" + shift +done +DEST=$1 + +for SRC in $ARGS; do + cp -a $SRC $DEST + + if [ -d "$DEST" ]; then + DESTFILE="$DEST/$(basename "$SRC")" + else + DESTFILE="$DEST" + fi + + # Do the chmod dance. + # Symlinks to system libraries are ignored because Julia shouldn't modify + # permission of those libraries. If we tried, some platforms would cause + # an error. + if [ ! -L "$DESTFILE" ]; then + chmod $PERMS "$DESTFILE" + fi +done + +exit 0 diff --git a/contrib/julia-config.jl b/contrib/julia-config.jl new file mode 100755 index 0000000..d69e09a --- /dev/null +++ b/contrib/julia-config.jl @@ -0,0 +1,136 @@ +#!/usr/bin/env julia +# This file is a part of Julia. License is MIT: https://julialang.org/license + +import Libdl + +const options = [ + "--cflags", + "--ldflags", + "--ldlibs", + "--allflags", + "--framework" +]; + +threadingOn() = ccall(:jl_threading_enabled, Cint, ()) != 0 + +function shell_escape(str) + str = replace(str, "'" => "'\''") + return "'$str'" +end + +function libDir() + return if ccall(:jl_is_debugbuild, Cint, ()) != 0 + if Base.DARWIN_FRAMEWORK + joinpath(dirname(abspath(Libdl.dlpath(Base.DARWIN_FRAMEWORK_NAME * "_debug"))),"lib") + else + dirname(abspath(Libdl.dlpath("libjulia-debug"))) + end + else + if Base.DARWIN_FRAMEWORK + joinpath(dirname(abspath(Libdl.dlpath(Base.DARWIN_FRAMEWORK_NAME))),"lib") + else + dirname(abspath(Libdl.dlpath("libjulia"))) + end + end +end + +function frameworkDir() + libjulia = ccall(:jl_is_debugbuild, Cint, ()) != 0 ? + Libdl.dlpath(Base.DARWIN_FRAMEWORK_NAME * "_debug") : + Libdl.dlpath(Base.DARWIN_FRAMEWORK_NAME) + normpath(joinpath(dirname(abspath(libjulia)),"..","..","..")) +end + +private_libDir() = abspath(Sys.BINDIR, Base.PRIVATE_LIBDIR) + +function includeDir() + return abspath(Sys.BINDIR, Base.INCLUDEDIR, "julia") +end + +function ldflags(doframework) + doframework && return "-F$(shell_escape(frameworkDir()))" + fl = "-L$(shell_escape(libDir()))" + if Sys.iswindows() + fl = fl * " -Wl,--stack,8388608" + elseif !Sys.isapple() + fl = fl * " -Wl,--export-dynamic" + end + return fl +end + +function ldlibs(doframework) + # Return "Julia" for the framework even if this is a debug build. + # If the user wants the debug framework, DYLD_IMAGE_SUFFIX=_debug + # should be used (refer to man 1 dyld). + doframework && return "-framework $(Base.DARWIN_FRAMEWORK_NAME)" + libname = if ccall(:jl_is_debugbuild, Cint, ()) != 0 + "julia-debug" + else + "julia" + end + if Sys.isunix() + return "-Wl,-rpath,$(shell_escape(libDir())) " * + (Sys.isapple() ? string() : "-Wl,-rpath,$(shell_escape(private_libDir())) ") * + "-l$libname" + else + return "-l$libname -lopenlibm" + end +end + +function cflags(doframework) + flags = IOBuffer() + print(flags, "-std=gnu99") + if doframework + include = shell_escape(frameworkDir()) + print(flags, " -F", include) + else + include = shell_escape(includeDir()) + print(flags, " -I", include) + end + if Sys.isunix() + print(flags, " -fPIC") + end + return String(take!(flags)) +end + +function allflags(doframework) + return "$(cflags(doframework)) $(ldflags(doframework)) $(ldlibs(doframework))" +end + +function check_args(args) + checked = intersect(args, options) + if length(checked) == 0 || length(checked) != length(args) + println(stderr, "Usage: julia-config [", join(options, " | "), "]") + exit(1) + end +end + +function check_framework_flag(args) + doframework = "--framework" in args + if doframework && !Base.DARWIN_FRAMEWORK + println(stderr, "NOTICE: Ignoring --framework because Julia is not packaged as a framework.") + return false + elseif !doframework && Base.DARWIN_FRAMEWORK + println(stderr, "NOTICE: Consider using --framework because Julia is packaged as a framework.") + return false + end + return doframework +end + +function main() + check_args(ARGS) + doframework = check_framework_flag(ARGS) + for args in ARGS + if args == "--ldflags" + println(ldflags(doframework)) + elseif args == "--cflags" + println(cflags(doframework)) + elseif args == "--ldlibs" + println(ldlibs(doframework)) + elseif args == "--allflags" + println(allflags(doframework)) + end + end +end + +main() diff --git a/contrib/julia.appdata.xml b/contrib/julia.appdata.xml new file mode 100644 index 0000000..3d45119 --- /dev/null +++ b/contrib/julia.appdata.xml @@ -0,0 +1,35 @@ + + + + org.julialang.julia + Julia + julia.desktop + CC-BY-SA-3.0 + MIT and LGPL-2.1+ and GPL-2.0+ + High-performance programming language for technical computing + + julia + + +

+ Julia is a high-level, high-performance dynamic programming language for + technical computing, with syntax that is familiar to users of other + technical computing environments. It provides a sophisticated compiler, + distributed parallel execution, numerical accuracy, and an extensive + mathematical function library. +

+

+ The library, largely written in Julia itself, + also integrates mature, best-of-breed C and Fortran libraries for linear + algebra, random number generation, signal processing, and string processing. + In addition, the Julia developer community is contributing a number of + external packages through Julia’s built-in package manager at a rapid pace. +

+
+ + + https://julialang.org/images/julia-gnome.png + + + https://julialang.org/ +
diff --git a/contrib/julia.desktop b/contrib/julia.desktop new file mode 100644 index 0000000..6b41981 --- /dev/null +++ b/contrib/julia.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Name=Julia +Comment=High-level, high-performance dynamic language for technical computing +Exec=julia +Icon=julia +Terminal=true +Type=Application +Categories=Development;ComputerScience;Building;Science;Math;NumericalAnalysis;ParallelComputing;DataVisualization;ConsoleOnly; diff --git a/contrib/julia.svg b/contrib/julia.svg new file mode 100644 index 0000000..ecdfe4c --- /dev/null +++ b/contrib/julia.svg @@ -0,0 +1 @@ +Asset 2 \ No newline at end of file diff --git a/contrib/mac/app/.gitignore b/contrib/mac/app/.gitignore new file mode 100644 index 0000000..eb4e046 --- /dev/null +++ b/contrib/mac/app/.gitignore @@ -0,0 +1,3 @@ +julia/ +dmg/ +*.dmg diff --git a/contrib/mac/app/Makefile b/contrib/mac/app/Makefile new file mode 100644 index 0000000..f3677d4 --- /dev/null +++ b/contrib/mac/app/Makefile @@ -0,0 +1,68 @@ +# Make sure that you are building openblas with OPENBLAS_DYNAMIC_ARCH=1 +# You may have to wipe your openblas build to ensure that it is built +# with support for all architectures, or else performance may suffer. + +JULIAHOME := $(abspath ../../..) +include ../../../Make.inc + +# X.Y.Z or X.Y.Z-hash +JULIA_VERSION_OPT_COMMIT:=$(shell [ $$(git describe --tags --exact-match 2>/dev/null) ] && echo $(JULIA_VERSION) || echo $(JULIA_VERSION)-$(JULIA_COMMIT)) + +# X.Y +JULIA_VERSION_MAJOR_MINOR:=$(shell echo $(JULIA_VERSION) | grep -o '^[0-9]\+.[0-9]\+') +JULIA_VERSION_MAJOR_MINOR_PATCH:=$(shell echo $(JULIA_VERSION) | grep -o '^[0-9]\+.[0-9]\+.[0-9]\+') + +DMG_NAME:=$(JULIA_BINARYDIST_FILENAME).dmg +APP_NAME:=Julia-$(JULIA_VERSION_MAJOR_MINOR).app +VOL_NAME:=Julia-$(JULIA_VERSION_OPT_COMMIT) + +APP_ID:=org.julialang.launcherapp +APP_COPYRIGHT:=© 2016 The Julia Project + + +all: clean $(DMG_NAME) + +$(DMG_NAME): dmg/$(APP_NAME) dmg/.VolumeIcon.icns dmg/Applications + hdiutil create $@ -size 1t -fs HFS+ -ov -volname "$(VOL_NAME)" -imagekey zlib-level=9 -srcfolder dmg + +dmg/.VolumeIcon.icns: julia.icns + -mkdir -p dmg + cp -f julia.icns $@ + +dmg/Applications: + -mkdir -p dmg + ln -fs /Applications $@ + +dmg/$(APP_NAME): startup.applescript julia.icns + -mkdir -p dmg + osacompile -o $@ startup.applescript + rm $@/Contents/Resources/applet.icns + cp julia.icns $@/Contents/Resources/ + plutil -replace CFBundleDevelopmentRegion -string "en" $@/Contents/Info.plist + plutil -insert CFBundleDisplayName -string "Julia" $@/Contents/Info.plist + plutil -replace CFBundleIconFile -string "julia.icns" $@/Contents/Info.plist + plutil -insert CFBundleIdentifier -string "$(APP_ID)" $@/Contents/Info.plist + plutil -replace CFBundleName -string "Julia" $@/Contents/Info.plist + plutil -insert CFBundleShortVersionString -string "$(JULIA_VERSION_MAJOR_MINOR_PATCH)" $@/Contents/Info.plist + plutil -insert CFBundleVersion -string "$(JULIA_VERSION_OPT_COMMIT)" $@/Contents/Info.plist + plutil -insert NSHumanReadableCopyright -string "$(APP_COPYRIGHT)" $@/Contents/Info.plist + -mkdir -p $@/Contents/Resources/julia + make -C $(JULIAHOME) binary-dist + tar zxf $(JULIAHOME)/$(JULIA_BINARYDIST_FILENAME).tar.gz -C $@/Contents/Resources/julia --strip-components 1 + if [ -n "$$MACOS_CODESIGN_IDENTITY" ]; then \ + echo "Codesigning with identity $$MACOS_CODESIGN_IDENTITY"; \ + codesign -s "$$MACOS_CODESIGN_IDENTITY" -v --deep $@; \ + else \ + true; \ + fi + +ROOTFILES := $(shell ls -ld dmg/*.app *.dmg 2> /dev/null | awk '{print $$3}') +clean: +ifneq ($(filter root,$(ROOTFILES)),) + @echo "We have to use sudo here to clean out folders owned by root. You may be asked for your password" + sudo rm -rf dmg *.dmg +else + rm -rf dmg *.dmg +endif + +.PHONY: clean all diff --git a/contrib/mac/app/README.md b/contrib/mac/app/README.md new file mode 100644 index 0000000..5017543 --- /dev/null +++ b/contrib/mac/app/README.md @@ -0,0 +1,16 @@ +Julia OS X packaging +==================== + +This builds the Julia OS X application bundle (.app folder), and stores it in a disk image +(.dmg file). + +The application bundle is actually just a bundled applet which opens Terminal.app and +executes the julia binary (which opens the REPL). All the Julia binary files and their +dependencies are bundled inside this. + +Run `make` to build. + +Other files in this directory + +* `startup.applescript` is the script which is compiled to the applet. +* `julia.icns` is the Julia icon file. diff --git a/contrib/mac/app/julia.icns b/contrib/mac/app/julia.icns new file mode 100644 index 0000000000000000000000000000000000000000..7660e6650937b7764446c36b931db555c8eb3d9b GIT binary patch literal 164450 zcmeFaWl&sC^!GV4xVyUr2=4Cg?gV$Y;4-+o6Fg{wdvFg9L4pQ%cbDb&->t3MeX91& z^KOf(ndzyz-Frx} zQux0&EcE;54{PVu_X6muDkTc2njky?0N`(A#6{FSfhT%ib}7HH@-ojBI4t$!oG5K{ zokLKCiYky=NKqOK=^;WaQKWn=X;q>E*jGJ=>@2-UfeGStyBC>4@H zAhntv;R00A#Qdl}D%4%(+tWKgwwDeLpPJjHqZ{9ELRpDvIXPP|t3Fq|X|maRV}-mnAALYK3q68Jq%}oF6n+FHMcxg8QGMm+L4>u z;HuG6lQUaZ%3nms)PIkAmSt5XkdGh}kr zv%HtDx!+=GfTE+tq|`$pcpBfr*Zu49XV^>f+M@P--MbhOhlYBXfyz#`pfaw@X$hej`q>}3#pZR9fYREuz)jz_NWp$?=u_n4ESKgfI8A;r4tE${~ z*YM2k>S5Mww)sf-;?Y*<_DGm#qy=9Q`1N^O6A)^}Wagxz&m${>)EMs>sCKSjqTkf= z7UsUSaw25t3eKrV&2-K)@yo!#vN` zy$E=sju_VAvk+Aa(_nyC4uWFspY+UPlxKkNK5Vw2ml?#kF|ttsq{Gi^y(xiPsxWPT z$26Pi&J+SBx2cPw*`?CpjlH7srhugqz$T1)F2)~w&ioGs3#{!tSw>IoUI%PjF^*q3aJelCPtLW^-2kPQIeW*g1;Xd%aDb4cT22dIzs!2W_d#nXNFklacz$e4}Hm>4=XB>%;V8PgTRFz>qyZkJ%1{psnWd1K9Ttwry;ZAePF>Hahy_C8byBSKc|F*2yKW?Gl#ci%)+jLIXsp zh_aHtkFs4q>Jp8Brk!1j73{lZT2)>zD#WrsQw37CjBF_N*yL%+`caTVmMTy7O-rPI zgPqc`F~Tb!S2^VOcZ7GXEphVb!(+c`t)TsP%Q3^61~RH5i`j6B_0+pLj|_*H4daF^ zEjFsckx?K?(68ip%JK2J2pgcqN>l~j40t8eGa7KTWFT+I)HFb%DT@R0YSeLhx~t)D z*{go{Uw`rtUk}KxZyZD2Wm4$F-}T<$3pwTHUp0X@L+S~q-a(cc!5ZZhx~=wfZ2wyh z!jB^M?nN&||H;cuxuqMYbkr${EkXkB3RI}8$gjQ7ysO`&_)PNiWb0-JtHhWwpANdx z^V0yUU6#+HbDz1KE%BQzYb-K6_jzUeFKo8ekOdLeo7FTzu^LUK&MnW|cG{=g;#3~* ztIV7pJ^T5e5Ec{13CpHke*?|!2pTRp?P4F8>=+kQxYy&9Jp-KmYlmZyT0^KwVJBTe zGVC)8<|4bl%#l5x*(vm5AnTwuIivnBGb(Y;gsx)_D#a`r{5$_tTShpX`W`H6SSU zCgI*Ed^eDYQY;34RdSphSc9#MikYM5H*TFaPDAqr1TI|CprAn!ejXtp!=yv7Ubs@} z9bQXeMRa0;*St;M?%a3W@w&2OV;)I%Xpnj0%Auq&x7~Fh5=o5!IVI-BviV0`s{kP- z6|r8;UoWOFIA0X$vDO0eRLWvuTVN}PcEcIWrg~ zUn{@dylhQwu&r)nw`r?nPhgXk=0n2URk(OTz4^7Qk04ITp+73-uIG%ZPu!o5p3@W8 z?6X&%6BS9=cP4j2cV0153VV(WgMJ`YlEhQQJrYIj?>ychiWU5^woqpCgo)B)EPh)S zEcrpP72>fH(0(p}zuzhc;4pjqR_dF{iIwxbD}x7c7>*%u9IM*2Ff)SIaWh&1D1ooyl%A=^WHh;QztW+r z%{HA!GGR>UIP_be>|bS5ap2lYz&3rm2^J$=ZD1>13Xx56-Efkk#^ z^U%R2{UwD#QkNz~h*9@)rdf_r62?RO^s(@nRqwpf>9?qqr*LoIwU7&uD`(2ej;T~i zc@#;9PHUbPL5mN&v&^J`&NPEh2sLR zCFMiRiRWicb?4b`q15^2j^nLyL~Jm-kBsP)Mx7AftgpnZnPJz0x-C`e&c!K9R5!aMWchdc#I@MzaEKb(kM<0Eea51pn;-dD z<%h?sO8Ou7*;!g-c)G=^ju-+8O3XYe*b0(Vf$Sw!Wn}>g%?u$BdLe6gu9;-kB`nc> zGup&?&vr6T*<4Y>omlLLN4aHLhoasiRp1|)CjoG2TK2Gv^dkN zS^5@wA7kIPP+<89qTH*9@fV2hoAeu{`M5CKtk9xe_H$byWqF7<^?Lgt6_kQgrazrs8P_ngk}Q( z*dqQ{2@U*DGz0!eLhHMfS^U2wG#l^#C87NgvM~o5{k6VMjxjP}NuVkBkoKdJv~&mY zbKnQdGJA`BkIdSJzf7Q%yt+)M<9eX}^oQVuATeAk*{Z7BWeXv8)oRngO9~rS|o3wp>jktS1u92oe`43I5+G zf}+8&j7MW$ypTV3bN%H*u@0*?Thop41yWhPVeQ@65S_Eh+^EH(;eB-(kr?cJ9lINq zki-HiLTd@IO!+PylgVl;d*aT@zCBO+9gJ-G^oWD8gqe5TG3PCXyxo7t)r!3!+UJ(0 zs!%n&^5Z*`nDL58tB}e2VxTrUcq1Aj2Po;g+KLwPPKEysb3fnMF9U{x>GXuR&RPtp za`k5?CxX3{;1jEJ(yBICM^pb>29Pyr@`#?-_Oy&73H_a z`|B!4?dT~a7nR*14qF4FK|9aH*5hdYjEN1yB+k|b96v=j#`{uUK-U(5)X89>5?v&r z3*;T*yxn|zV@dI3|HS*!=-~3Dz4G^i{{gBIlrrz}Oq#r6dxEtB>PSXN8V4@Y+LZJ4 zlhvKS;s1vG{fAtTymoo^kH(?u7hv1wqKvjFHKm$M4J8Wf*2S zZK!QN<)&IV&C!Y$v^NMkPyk#K7FcOpq{LsEuw4=N@ToV+viC`m*fY;5orIv5G5}Zs zlIkiZ+;Y%!d!#@?y6l_%S*io{3^gNm3mZS;!~e3Qw4qI~b9-+-eXb|E`5B}QsSGLC zK=Ee3lM*o7G}hM@9SxuZZ*KajTT_r6ERV;H^07>E!tMIQu3mpXO+P{zGnisGC!9&W z3UTpo^&M$z8@c)N@@h~4kfkgzuAgkt|8^-fh5J~JtFV+Fk*JClokiW3WWmM>SJ~NY zGQB*R7L|Id&qxO*m+P`|h({*-jxpx9#kc>UK%@+lr&W{q9aW70nLg+V`DD#w@2L4~ zdtbD=dmM~R2SyaW{5+Zu^K^$2YILjXqTY6VqXx{Zfp*hph+N(*X;kBKkWWpm_IdvS z0LKe?8T=ZAHB9wT(w_;a-RK{`j#&*3`RJHzpAy|8y{(c&~IJFFmC%ExB_kO=HXEu2_g z>z@>Ut1h&yuylpHWn$`sobpZZzs}N{#;-w8)nu_;Km$3;+k5`xPZ{(j1&FHaWpum{ z2QCH0v@v9&vh-?oY zcj@ecncMHkBf}ARo{SuSyq+DIPrbU5+o-*(7(pi)uqePwRR5b}wkpy5N%Cu1?e$z3 zGJkd-NM%=JI2tT13@rt(C#6McYStCh^@KtVwQsqL46M z$6QQg!KlOHy>agL(#{q?=cZH+>R_tq0JN&2+z35nT(#vR%2a@2?_V z-0&gONsyQ3grfZ(6IlluQf(odyD%J2;BW45+)1UfgBn+?Bh5^(gV$BYL*FA4#ug>= z3RrWvh|q`mZOG*QY!1{%tNTP4wEP*-)aYbefds2ysE#Ix0ZiX-B{lYK!)-4 z*V2P!6xl|{4roThV?IP6q3yCYiCV2{MlnE7erd+X=s+*=AVQyvB@C@fz-B8brEn;0bor~>4-6ms|_%GA22{OvYyfx=tnSiZ%$=Fg*s3V zveJ_WU5%~T@e%Bu0InjzgTHK>81bx?vWM$a2gR-nf zW$Qx@*Gq32$%7F>K2o{%NDKUgki)b6`u5LdOd{M~a(l9>ODsb{voI`S`~vmyN_gX6 z!&v;cN)Sm(j)`LpoK>WFk0}y{H3RUZvADf9YkS6Wx$obRqy;;6M&yU;$xYq>d3E~; zP1?XBv|#LAoLZvB0n;~j(uW)DFkr$LSOm_leJ$*lZX7yrgX$CxMIF68a_505r2e8v zShgy8H#{j6`y9#*4_q^J-9sKIAE57UUK$$+rvnY%+N>XXKfGHKl^u*_@x?3t4HE#h zUgwh$K^w=F;-v^!V1SFR0H}7KT5(`f|0!x2O%Dzd{*E|)n18NkC#VA~64r(N1n$RIen8-VTvavx4DK+XGVj?cUr-SZxDDQURa z)!o9+x~Qw4lt7BzjERUcX_PbIS_4dX9Ux_(4*Z@vZEPI>TOMR~{+B{ez)+GIKKtM3 z9;W}=(osFuKrq8G13Sd)La6gSgcq4;mfwwb{|x0$O-pwEi~&h1S3F>Ce#x2J3H{!} z3BJI)<{=L|nV(DyfdmP>LGed-iS!g2ZVMP%6+nAj&P0J^07t|er~%;czL14yrU`jA$T zfXpPj;3xFZjclNGplu}!^lv$T^^;&vWLk`CYEahR*JRlD znSr3(>Q{yRmt67Np1K7rY^h3<`K_*3=X<8X1~6t>(IDcQwtx3UFbaB3e)J#0tTP{2 z6^(nh57^Yi5+gM6spVi6>6oUGbEYYK#z}J21jzi+cv=8S!^<->wc-O9hKul* zQ0r2RJE)AZaYDbm56dVm?H+(2?GCq!NeRc>(<;u|?;y(s(LGqr#v*x3Ii0^AaIgDGcZIC+S~YRAN5dHeQt+fQ<1gmhqb5bW=YIc_}%1U;a& z@Z|wR5}sKzPesL?p!pvhyZ3%6!j`NF1P4xpu2_u!t56v-?Jr0smgl15{eHeTkU`Ls zm;d41P2z@v2cE1mGY>H;%cp~NGy(SD&bN>u_!HdfdaNmEWx>`HVrclx#ADz7G$tKm zsHre*PWcs7G4*xRpzok%;Sr%`j5_ZWvptfk=V7>a{jzPMW6~K_F0eowkvxe;ikDb1 zmY}`vs_EB5taX6c4-TayAt$s7gk{mk>(-cl52W07*a>5@Bhtd~d-yp8`6&g&>dxZ@ zJ$RcMNp_fz1T*0w<(8>M&j9QZcv7)~|A~sV++~t=SP5h%1CzTWk^~4^|0UT0CV>p9 z_XH#Au%zrkr$@34C8`6~tf-iM660KQI;GW2+);j?_Vh&=H6Y?d<1hw6z4P-dGWCE%SD3hVc5D}B?FksFQ|xWgI}NL`vlF3hT|(wJ?gswxT6<(^+B zgyOPu?;;Dco;A5!mlYJJltR7oLu|zP*8F#CY>j3A#cR$G*|QER0mAA!LT@AGLhgMO zXu>GO6vwsOdyEdxzk-M1Nj;8ZOikgDMZVDW*-7sJO%>zOcSHQ<%dU>HVb8@FLHe#& z6|i%z6Z~SbmT-1|lJB`|`FqCBojoeM{_>@=N>#N!R5r8u#?(`ZfEmD)`DU)emEffo zyvYSZ(i9{Yo?ZLnIy;yQ*R4skK8yH^MXvGgp_`*7%e&|A^Tl7Vf>ap$SAp5^5rE8B z-jY*&jvf2kM;|bo9)K7uO{sb6`^1RF=1)z5ST2>&L@u^~K1k=$@e-}c^&qMU>tsI; z*F=dj-j9d5+3kj<6|k$kL3zLJ70FCx2WJYQy(be#qD0ya-`)2^nd_lXOf;-BP5?5! z2K^jayEo~7D*Y$pa-nF1iOM>wCuIuBuQ5NLD)*P+yV8WBqhklK<`N zdWrfTn}~xJv(<0JltG`b*I+e>0v`@{o9}(XU|V8^s?u!m-f$eA@q*IKIf+4LaM~q{ z<-I>z+Cil=?cH%b@+y2u@ys*MHz4Do*f&^4gMck-;c7H{$+KXg0QI%6Zj+^=1)faW zpgk*MBz7_C0j-6iQ}w(`5`mVk&9JX)|7w^d6g!WB@6nDQz(N$6!*m+s_;cw>K7M2? zlsz_4yDcvA*iETn%?EP>#x12THm`D}^Pr9IY+-oW8>fZ01|`26z(s8+(6+`=w%rv< z&J7%m{I+IuxAxDxBklsNx7<;O!yLo$;7bvHk06*Fs4$>BfUV+876uv}>KQ2L1BA-^ z<2^S#^4kk8?@_P*v;FvV;7^pGYaW-=OlrPfyjXWG>{v%` zL8ae_pz>hJP+>N>`#o#WSKc7GmMclKRZ}qsV`Zf-#O7Qn@C@C5^1h(doV7OS-AVcD zs5nhvmU?KnD9O#dnK)c{W6kdk^$mYZdnnH~i8Dhr;V0XEqVD%;AXd3pm6IjJ+d%kH z3jSmAq+!`!h~ItdB)cK7dZ0tA?fTJ+>{yo5v@z4$Nv6g`38Fl#Qz+Zq%&9f)n|OWM z=#UNVO~jR)3PXzyOYKieejFKD$XHsU5w=>y2Sv-wt^82_Jw^TRB;)vd@p1I6PwUhp zCj0KGGUS(w!Y@+^bW;_5o=1b+sK(qaVP6p3AZixk+kKDjgv_b9(;V}5&&t<(7ka49x~-0z@rUxWftCsp z4Eu!Xas_!f7t$xc=rn!ToonS8VIVD(v{%%!TpB2@wIdFFX%c>b4G=a~i_vaHZ z5-$=?0!ks*j15KniTG3;o-hHcQT6tjsLDmCqW9nHjH!-$8g(nh$I1SjZw?hZ`mS(d zO>AI+?pE)&Yl%z>q6?m@B@qbiWse{ai|zXf)PkA(3nxVJ*CB4ig5&QQ#YWRvwzYA& z?q0qQ2Kd`O&1VJXkn*3pp{k`;x6|0#q;iumg_neUg1)GUog zyMIaQ4-ekAuw##hn`qtct;#0Z=AnEGitXY8ez)!47C<8*tTh@jgi%U8!~C|7L8F@H zO2bdovQ>b;@x=VNpD#K0&#FU;5eHH(efiG>?1pL4GtLzf27;_Pwsf=^?hti#o7ue_ zI;TPW=LF$Uh%luFEV4ErY!TawkJjm@X66NX^2KXsWq81w*-hSq^S(Y5Iup zY2j0;0c18 zOpYnhb|oy|EArT`=@#eXaNPlZoaCGaB(@IZtyGQ8DGSQsnxX-JRhd*CmIiPZNyw4X z;_S33q|71PmMLBE&8nyASpr$?o`YGV$Z$Zk^9;qH>2@H^>%Q5T|9q~LsR{9Dz66hV z-rb}MqEcXelNV9p9ya^l=C`v$o&noys!4~33TF4Eao^`Yi{sx`5Y2tY*paDMO3ifyl&v-&Bj$FACs=^ckZyRYdyB-FO2ECQ;+KEShHM!Z=zqA0=DioXyn9YZS%F(mzQZOB0Fe!??qxR&~m+%wGcbflDR#l7*H z0q<3G%mEg8OK_dtPQwVBwsQeBZ-x2h;)q|*V>1(^%~x^=X-vR@re@F`E7Xi1yi*iG zuKu&I$#3p~&VcS`YFJlKR0q7=GcBa0A>xFf;v$8xY3f`iXes6joqpHEt7{jgv%8YDW%|J@$vr6Q@a)y&dA|WYmSz-|58pPyU+b{l z?GWw=c|H1qVbe<1vZu6i-+@M|qm+h64Cm-Pz5>TYoXj-Wj@dnGI;f7TvW4Pg@?}UM zptF$slAjicSp1`rlVpwALb#N|Bk0EdZ%CG6n#Mzmd1#o+y@`ijz*=l))8k=ztA4v*-sZjN_dY-Yy1YBlf z^uoqoM~ps|kw(m!X27#we(sgBpyhuP(yLjjh$qv>|15M_JQ34YeCSB=-|J+i!hF!> zujPt%HoLIutwBTXNVKMfN4rZ1Ev8O}c~zHb!iB~?Rl>7ZO!7rh{>5~<>r4SDQ12#f zs2_S2QJvkt-m%=z_EkFb{9hYs`{L<#hFrc3Yw3o>rzjTmIn@jne)ivqf!fOBbD^$x z)S&}wd=I;>O{vFJ@fYCkWnxRX8kax|h6OHTaAm8ArB?*78G48M`cH(E3YO9zVX2R8 z6#(FMFlQ7=g`ZGOj%rbe^kQ4g|D*-2dxW)j7p#d!gN73IH% zwE?b_Wj?n%_lKQc6&C=EEj6nCA?tXo+srk#IG=ykq!^rmODhQL($owAqLy7z$R z*DS+bi*{Sv&x~{vET9Bo;WWO^JYVq5bl88x24gijifMo5dUd10ejpx)?}q1IP27!- z)_4248B%v9+(5$hsyUQ$3d#wG4L6$1z_y2mEp7y8#_`_8B-H)g+@57TzGKP}PEM5% z3f9mhnV+aREIu-nODmn4-_`H9CuA6qg?v)4*{YthvXsgr43d6wPB_W`MnZDu{-bn8 z7f_P4CdSXp^{T#~dd|xOiAJDIi}VnzS{pdy834}lYq?051z3+zg_HUMzYt0VFex<6 zM#G|a1Q2<@O{_eX2G_XV4Z#cde+jP4DHv!1t*ox4&^aTUK!rc0RUsGAW%h=sNFmek zxve%x7|MEID$62ttM88Qzx=u=6>I5#Z`(^FZ3p$EzRqpSqUv4 zz<@YE@l5l2FeF;P&=VEY9f)OF2clu0Uj0*AkIjO~eb%3hjA$d%m&a2{wZ+ z_Jcc_)~As?>)0GNEwJc}xH?AAfSh4K1#z3Arrembhu9kQtH1-KJhhDPhS|7xyLiNV z?sFH|i_)vK_Hd%Mr27diMwFQZN2`@#apJ0i|7AMC+O9=t=*+dJe8&Vh=m)*L6m0WA zp#>(;@$xh&w^NYX(SLgj6>UCubEf>(-*7@TF2`E|upn~TfL70t^o#$qq;cQOA z(7g;B7k4pS+htjgL47>dZg5QtWv}y_0fipFcWgn=#zb!o^#3$k7v->L@1b3QZK)3| zQ8~HM>t-g*(l$Bdur7ID^|}r#Zhh$V!d(amaVFM#lSmN{jB=TGuwp^|3$|P$>=3ky zbX|OAN|=h0ZgM^e<6Z(;t=JmenZg*MQK{q?W)H$v&8*=%|E)E^VtHEHi<1B^9Pq#F zVHFmCPfjov;_VA;v1$O24-Vh25=r@`#<4LjVnCv^9%CnO804Ztq9G#r6xg_JeFCtWGzw8D$M+yit!MCqEF&!dQEXjwoGrt=OjPFP^{!D0SqrdnlwdOKcNm!M*a|!yh!Q z=We{Kbtq8LMx%-jTTgyXb-U_#2>ucwG@H1-&?RVE3Z-p?u-?C4wHGvHOuYFZE$US_ zy`cWJ-_uoi6@FdePTYken_BLala$a9hOan3U7CjE-J$~FG1*W7^lrUyH2B)>#G861s&G1zd6hq1FU`$; z;w>X3*$sK0+v8D%p?cVjAe?8*07b8}0+Lfw{H%1pD1-wI?me77Vf*hX7GKENk%V%y zI42xQbxJ?(J%rE;QN`&)f$_jf=pA7JEoO=?l3>V~v@Rw~P$lu~(|(bT;!Yr74#no` znzIT7cJiq89TjW2#-3Ft+y0l^k;v&z?Ts`k&02s?4jzm<5W34()&j0~p~wv(;@|n%Rw+*Q5zXfvnanbi0?MgsMqsb zTe&}vfAeJ&K8KQD4(zd5^Bg{^Cb>SeuL?MR4AvHD8g{!&xr_QdLkk5$AN8*pe$q?c zQz+RA!Uz<;FAUW_%4N`juWFH>M72sO_;D3u6og72&D#WMmu$Sh#^2(ZT`Eb6M|^6m zbO~B_*Yu)9ne2XYkkP7F5 zG8xXrccD2$yw$%v3Ttng>d??UTSh#;K+e!HgV%>WZw>{{k!LS% z*A=E9&t`&NP$1TCTs14s>0!&@LiKW6E+Oa{Jab+E3K8#zQ?O4kHMm~>(A}{+2WSJQ zpjMs{o`6~TaI2QRkx%F=U|cBAZ_Lf$U#1u6Jc-;;7=e;U3U9?ccT%)~;vG4a;>$J3 z5-+&t4jR~mGoh&9wGd1u%pbzWQ8>(DV?L421^1?{Ll7>T5VY6vv^nF+S0#+RM4(^0iatL|C!em9 zF>n<4M}qidfm5bwHFvKGzV0Guo-vT+G#>acE>DzebQCAvrU zJCv)p1lsLn2J81E4`Jw)dYkmDr`^;#cd7Gwf&nR-kZQWW6JXG}dm~!k(6*K}Uz=t3 z%leWze@fd&A`cm2U@$JA?cu_?@-|XoX+$NHcq5p!J*ey4t&B<1_l|;1U;$LY;9woi zOwa8Z!T@ecxSaI6lXfB_Rvc7ufm# z-0ee%p-kWJ$`t<0;Y|H%5&Y#%Xt<101F%=EtrBlw{{E=SEaBlO>*yI{urJu)$CVRG z_w^aK*Pj6zIU&2=hV5-fNPQ}X{uMnOg$kuXYgH&=JX6K{cqCc-9ju^XqpRN>Nl1iE zZN_iZ9B1&uRJZY-+!j(EYM!*z0p!9WdI^92W?(#;enwW4MbYdwfD8~o-OT{2FQh*6 z4nuclc?GA<+B0#oHPhq9`+;uwM-g$_tQeWZAn7r%@t4jkqdOb8<|t^f00O9gF2a#% z8$-iEe)PB z|GS>D51;w!OxF22IcMLiQJSomoV&Sdx?s0)FQH0=%Sq=UJ+H z$!`A#%B^B0z9)E(4pr{KK!oAjCRhHTtaZ(T->8{7dy18xMaB_-eywVq^2R3ISL)<3v+&^ZbzsOL75DD#tSWhlS z4JSfG2N`%vOoRd#e`RtZ4nzL!-6k`XpH*3IWbZP}KzAvHH~e#l=2Vvx9PIL&d5xNO zA7;k?+jzoDvb?o<1t5$mzz-)T8gGGMVxvH~lr`<)31!8WlR;bXvnjA5rAUU0;6au=(QFN8KC^FGDI z(&t|W!qiZG`U`_Mw>dx|bslZPQ5Fo1??xuU*!B z%|fp7hTN}QP4;hDFTmB2aLmsqm!?1Ob7rt(ln9R2G>5%t0px;aI{zk_1v|n&)6)eu ztxp{LQ}MhsWU*Ntw33+Lh6K2t2-crUu~2h{TSB)dB{J)m=3%tK#J*WRsE+FaQ6V(s z4m;yLH=eZNwyBX-^V$D}55DE)W#u^bIMTPFxEC_?yPxg5Ay`1ave{`sKAFpo3EVsz zU*7-R_fx}(xuV1!!+-)u{eWT6@u4G!I@{e4c4!R27ot@be-!ZSKIY#ETGHj?l702D zu{eRctvbmmT~O=`Hf9U5k#`)WC4jUVJ7c_5LGn<_6gt&3+8@6DwwI*MW->*T#_SJ$ z3fY@f=k?qDTYCFTLnn#a509R#RtTX{bnXfx^!h5V+dVStm!Urk-kLD0-L$efRE|Ox zYV0P@0YOGT^L%v5_Ta%8dThQ^&GgnQD4Rr^6_C>-bhY!Mh$EM`B63HkO5c9TI|w?B ztFf*3w-Qui>~aTQi`w&tNRWUO*-9Ah>-O}zu*88=bd`84_vC8(;kBOo>f*J<0i*8T z>GJ;9k9>BOpOAD~9h`lFjQ{ozY!%V&8E4h-x#K!0Rp(x`g)!^*j;!9L621MM&7w0H zx$mK8_1=V#)?8gLwlz?V>qM2)deemat=Yq_P}l3{A6r8zKXtFwkGO|$QhAZ=Suz)+ zN7%e&C_LH3585Mrf{Ihu#Xn2#!<_MDGKJph73J{%Tx$dmjzG~hxH}cBzcOF=b(%l_0RrT8*xkC4Af7)Ujc8_HWbg9)$cTLfV zczeD-hMWuGjNfX{>CT9M9lbOcejWQMm3>D=1Agmf(aaOuVNzn!!hb-WVjl?aF88>& zZ8oM}E1foT^k^o2KHQ{JzBmgkePUa%!o2xT|B_F;BikXE`P_sNAcuEbIkOwjw7_yU zo=H;%HPud<@r$XyaF-_AI3O?5kHPAB*#P~dR2o}(_K&N*TFm<3n(n3AUc3+d-)>lX z>RgEUW}-rd%4M0tZeqkhll_~wE{v!+dd)3r&u9xEd-~22ng+#ruh4*e4-~tMauplY z_J)%D83PyM-r9s--D~x@^ZMB^n&*1ffMHG1vxgm*AYmIaL4=1=F3Y->^^ll=P>GnK zd&6V1_0?|%hFiMS_kUfkliwm{Zh*%#cJ>h_4KuIQeBfFOITBy+MWog~$n$Kq({If* zz4J=3fpE1_`;6IDRN-Fy=cTtcM6<_E8$iQmZNHgqIvf-tpmVb~fa)*21? zQ<#;NQ;eTe4E7^5fRmHF|H2Fyt_yq)4e@J? zA*32HmHAJnNLLOJtRA?Gs5hB4Mh$=N4$FX|=W(5$9c}9n(6@8OS`Mbk*xPXM zar=FX%#=Pdt2EGYhAHeSsTEg#isiCtio{SIB8nT9dy8w5;jO1@sZ8&zmf3KHrbCs& zoa_t`c>7#)u0Gb3N6>UR$8d`DT3rUq?{wl?s-tTd#ywNr&05xE)>5x0q;D=9JiIxJ z3U4Ax2oV;LrdM*Z@Z@9-^_q(a_ptZv^=R5&0_szAFTSlCHcHCpqt+{!TnlMeOmT0P zlpbaFPC+d6_myVojll%$`a@dV3b8rzK1-uv=gEvpfLhf0OM<>@6WGcbgXlM#<}5%ccj@G=U&pyC}R~45iDh- zDEu5ovI$-GC{?|sr%OQLtN!Co_AYo>u68H>8W&Ex^f+LMG$fRG*YjS zB3>aIWaK}Z6kC?={NFs`t-ubRiN+Z>??VSzmp1H6h>bFPi~hwP#r9+K^*w4UPO|Cp zO-jhcTUF8PpcSPs)qUvP_&!5wnm~B&eR}=x#1NK8k))_Y^kdy zb3Hl9I0`FLIy@|(7yv~#Ec5?Pq09^Uin5k#@QJ>Tkoo_~;V^T5a5x_v&IgC{!Qp&x zI3FC&2Z!^);e2p79~{mHhx5VVd~i4)9L@)a^TFYKa5x_v&IgC{!Qp&xI3FC&2Z!^) z;e2p79~{mHhx5VVd~i4)9L@)a^TFYKa5x_v&IgC{!Qp&xI3FC&2Z!^);e2p79~{mH zhx5VVd~i4)9L@)a^TFYKa5x_v&IgC{Km5-B2ZzJL4gh@R|6ljR0lniop#O6}oNLe7 z$N$%UIKqJcb3dHp95^uMK*!t9A8nQ%KqwV)Obir8stcoECiK5W{@t8FdX$v59o{{uYs^;%Xxz{ZI)MSO|9^ggu^?NpbVX%Q0l-0R6}?3m0GXr&fEKjPyDf5A3fP{ptmKJPYt)DYF2GU~wF# z6gl`nmAe}u(u6uLt%=$gD2TG}XHv0u+5-my2DM_+lMui9zA)}%I8Il5ov%XqH_G*l z)YW`V(#p}^#uDi_t-4L19C&$ftF+(CpkIq5Tf;Rpr7c!hVXR-Jrg8Xhz)NaXlNMTc3)<+Pn9 z+&uX#Ombb{P)SKIhNCACDj*?K$;1hpORwG?+U(acx=kJ2EXg#s$ubhK z0)VJP--P>;DbJ3a$zZ|4iE#VKYo_lT?hHt=^hiywiEPKcv3E>rg*aZd2BWwMt=;1r zngOFSvpJE6Y=y;8d6XcB>HS#YaRZ_;wMB#&oiO$8WlEz0s@gsO52n64EUGVBduABA zyQCW>1f*l=Zjdeo1*B6#7)rXkhZ2yGk`M_&T3{$akOpaxl4kDV_k7=T@87d$?{!wZ zd+m3vb9Nd1t8OraQHLGE4;qHj|C-md-uRSo;LV1nYYYUZ_O6&QJfjYXw1JjIMx^PLv{W zWf9~Mp(L3=e^j#2*q!$m$47SuWuN+UgC75wzgCq%rzs{Me6sMp)jt&PBPNBbc{|a! znEBAiWm$wXFvI+-@W(KOB4kD|VQ1HUOOmFyGc99dVxg00IJ0fjk7p@@0f!BdN$X+6 ze}bcuBiahT4+u82xUTn`?Yz=0a#-~z0);#Ro~Qzw5lH}%Ka~8ZGVa+WtA5H`wCO*v z%k{?XxCPBt!o}eXhtsM<>l7nsUFD*MPs{Y&M#z ztROVT>`k1S7zOL?gS$i@J*S+t-O0N3@ySbR%gb1Wme!$dXI###lz&)G<=S1x@4Oc# zx!zdux$vssLsR4dckmb{$stSbo}<1aD7yS?q%V2HwXu6tB(QxM8`V;(8u1>crcGsF z<^W&eZLwT>8a#i^!!}A59R|b0?gmBK6v=K(5>puq@_To${&Gd~`l1q-*b_sly1c5@ z^F7v)aGZ@jvbdcO2bg|cCePf-e|bMRqR@l7F&U9C>$$l5@`;7@V10%A3H*hnP)!Kd z1_U>PDU(v?>VCH8@y}Zxu%j$G(KbKu7doXV=$e-GT*o_qT&^G$CVRdTkTh>w!g?*q zhUtQgUKxmi7SjW-eqo>F2E3$!;z$nYFXqV!)ge7(!(kTEXMuqz_R64D0l3;5Hv z;sn9o{@dX)d?(g%u8pX--UT5~IqI@-pGQv#e;2xtA1A!*C_Mkv`x_ogy!Jke1P&&e z3Jsf!Af}Rj*DYpVv9Mi(s&K3Bs#|dXj1E2f4Vt%F;Ssl?cbwXRE;QK-l_5(C+w7OVT5;rhFmJ=vf6?yM|02V)QNq=;B8QA zP?ni+Qgs;g{n7%;zGxaRXBCA$uar@+N2fv5vLFjDh-s=3eAyYuzSlT}#o?q}zfi%3 z2clJ91U}}dd31x$^j+$=-NuRXxT|b3rG;WL&N{|dHoGDfBkE*UVSs7D^%bBa(kzjjp!)m2-ERQaoqp2K- z9;A*lkMXSc_#Wq2(BIms=Jn;Couw!YQ35?HfY@pXP*h0RTXwfRw?YF=@POP%>3YeHNJbu7M|A96!Gpvk?)0+#668H8ZCA=n)S=d0rS_Ue=h6}qn1-> zEQo&b{fVIVJoeszok$(nhJ3v&8``6fZxbh47678?$>YDRRT57As98U-t^6v(z1W|Ly^}7AAic%h2o~K^8WG_}BEmyFIx;3Y&``!1b?iHxz zh3as+1GZwVRY@&J)=}SSPo>t7tuLPXjYv@$AFQj zKa&;5Z+`>(?6!%Ta6-vXTya!blUA#R5!abqul?xLe?H4@Y*<0z2{Fc?I)!L7T(TTW zHWT{JJo2b1KGpV+<+p;xMU zL-R$kwT)j6xo3k*{gt}p_mMQF{qca+Thp`9PZBy%6~i%Um+^n+7{f08QR_J*i(!U#ZSGj=JI$$j?aOvXEE!& zWQwKpn+ie&;s3z7PiA{*aW{aiEyc~|dDi(+6vlWA-L8BLb!N=lv{I=6M7P?szmG%3Mr-y~NC-2_}U&JJ~S+-4hecPU{D-oDLs`=%^`webfniiK|AimjyCI`SuSQV3G<_CtCU+sW-t zU8j*aJp-Sh@zHD5Wckr|SleH;Nv+0dCmN5)Z*IdsF%n^e@xTh9)Y(F8{AUH=JE(rF zsQzO8QWqC>U~@b?fe>bf>6{vuXza1M`?-0C@ZE?yfbAzIExZ@ljoB=vUkzLF^1Hmt z2qP}o5ct-sCx>4&x0~yajxGrxWx(E!Nb^em7X%e{+6%^RlOo;)@DF2YpD2PpkH7#y1z;^tM~J6x z-X)0LYM&uIiT@7t(}409SYg^PPoS4)Td}yGj89vHKMkNWB4r_eL0&|RF0Q%Vb2%m4 z=`Tbk<6e8+JFoYUHT~d*oG#0322T?cx8%kO~@?%9g=_GBj<(JagUfqvb^l0pcDF zxW~%e91FZU;yN|S?sXpl>;23KgEoDuUP!>2ZUI4|YKnl?^pZbIE)ok~@<)3p7|yXX~OBKrqh@HBqoqho|U9Yck^$SXNL0?Ge& z6bnJ(cDE;qtcZ`O!tO|j;g|>oYiBK=X^XoXOoFO15lR4GjiB30?ht%SP~_<@Wxbj8 zfFE;22Owu1B|#t;2AFI5S<@<^IO;{W0*w!;{a?tXn&nFoEPE>)z*K3WANw2Oi8$pGH(|Ax06XhQkdGxx zs|WY_P$0YB0?<#EST@5I|IVd)Y3B&%>y?=WJ|YksJKR~l&NZQ?sQEn_GzJEsC@~{5 zcE7-zCAuLfZFOKya~v8JhS-MoG_%bm{jiz-=>~@p11Qc3QDFU+WpFF7iZjh&fI-B` z1YYcCUC+$m$7OXU25Uf?D(ckB9M9v{{r$KvC3uX)snk(wiqt!ypc)KD(%ktuOq0XF zW}itJkx*Y)2~)v%AOlRI zs8D!mbhCNIY6-!Sr#aabQ{o1Y^(Fmoh*xNY51n$VnV>h{#zRE}z+gHGV`YqLEHVbd zM~p~fVCxo_8$Ss+pAR0JZZp1d^;3jjCzB$b6H>wOEKqWZw!wRT5Q$;l1b3=s$7-X- zUsAH)sEJTzm!H5OE%%_cvEJpoxO09GViB-I6D^%fsc({?VoZP=s)QE~`wq3Ig0)B) zod`0IQU79#0=7LU`Kz>#)8T?iSJ`iG5>v!aAR+9~CQ5jHqla_-kY6A)L=rea43d)Z zF64F>20RMDTfb$c0tI+}6CsLBul3H-L=^$Z8=&| zCGrV*Fl#=DKUKG|0 z&Dn3CPUUTdLfak<7WKK0&5-aw3wehfF7Gx?(JS0`lnCIjGZ7q +MrDih+A5&S+h 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 ukEn!eH9TmY?{`1}>H0KSvPze~ +#include +#include +#include diff --git a/contrib/mac/framework/Makefile b/contrib/mac/framework/Makefile new file mode 100644 index 0000000..8e86025 --- /dev/null +++ b/contrib/mac/framework/Makefile @@ -0,0 +1,189 @@ +JULIAHOME := $(abspath ../../..) +include $(JULIAHOME)/Make.inc + +# Build and bundle Julia (release XOR debug) as a Darwin/Apple framework +# usage: make O= framework + +default: framework + +# The codesigning identity on Darwin. +# Used with `codesign -s $(DARWIN_CODESIGN_KEYCHAIN_IDENTITY) $file`. +# The default "-" makes an ad-hoc signature. +DARWIN_CODESIGN_KEYCHAIN_IDENTITY ?= - + +# Set DARWIN_CODESIGN_TIMESTAMP = 1 to add a timestamp when codesigning (useful for notarization). +DARWIN_CODESIGN_TIMESTAMP ?= 0 +ifeq ($(DARWIN_CODESIGN_TIMESTAMP),1) +darwin_codesign_options=--timestamp +endif + +# Set DARWIN_HARDENED_RUNTIME = 1 to enable the hardened runtime on macOS. +DARWIN_HARDENED_RUNTIME ?= 0 +ifeq ($(DARWIN_HARDENED_RUNTIME),1) +darwin_codesign_julia_options=-o runtime --entitlements $(JULIAHOME)/contrib/mac/framework/julia.entitlements +endif + +# framework directory structure targets +framework_destdirs := $(sort $(addprefix $(DESTDIR)$(prefix)/,$(framework_currver) $(framework_headers) $(framework_headers)/julia $(framework_documentation) $(framework_resources) $(framework_frameworks) $(framework_modules) $(framework_helpers) $(framework_currver)/lib)) + +# symlink targets +framework_current_symlinks := $(addprefix $(DESTDIR)$(prefix)/$(framework_directory)/,$(FRAMEWORK_NAME) Headers Documentation Resources Frameworks Modules Helpers) +framework_version_symlink := $(DESTDIR)$(prefix)/$(framework_versions)/Current + +# targets: + +$(framework_destdirs): + mkdir -p $@ +$(framework_current_symlinks): | $(framework_destdirs) + ln -s -f Versions/Current/$(notdir $@) $@ +$(framework_version_symlink): | $(framework_destdirs) + ln -s -f $(FRAMEWORK_VERSION) $@ + +$(DESTDIR)$(prefix)/$(framework_currver)/bin: | $(framework_destdirs) + ln -s -f Helpers $@ +$(DESTDIR)$(prefix)/$(framework_currver)/lib/libjulia.$(SOMAJOR).$(SOMINOR).dylib: | $(framework_destdirs) + ln -s -f ../$(FRAMEWORK_NAME) $@ +$(DESTDIR)$(prefix)/$(framework_currver)/lib/libjulia.$(SOMAJOR).dylib: | $(framework_destdirs) + ln -s -f ../$(FRAMEWORK_NAME) $@ +$(DESTDIR)$(prefix)/$(framework_currver)/lib/libjulia.dylib: | $(framework_destdirs) + ln -s -f ../$(FRAMEWORK_NAME) $@ +ifeq ($(BUNDLE_DEBUG_LIBS),1) +$(DESTDIR)$(prefix)/$(framework_currver)/lib/libjulia-debug.$(SOMAJOR).$(SOMINOR).dylib: | $(framework_destdirs) + ln -s -f ../$(FRAMEWORK_NAME)_debug $@ +$(DESTDIR)$(prefix)/$(framework_currver)/lib/libjulia-debug.$(SOMAJOR).dylib: | $(framework_destdirs) + ln -s -f ../$(FRAMEWORK_NAME)_debug $@ +$(DESTDIR)$(prefix)/$(framework_currver)/lib/libjulia-debug.dylib: | $(framework_destdirs) + ln -s -f ../$(FRAMEWORK_NAME)_debug $@ +endif +$(DESTDIR)$(prefix)/$(framework_currver)/libexec: | $(framework_destdirs) + ln -s -f Helpers $@ +$(DESTDIR)$(prefix)/$(framework_currver)/share: | $(framework_destdirs) + ln -s -f Resources $@ +$(DESTDIR)$(prefix)/$(framework_currver)/include: | $(framework_destdirs) + ln -s -f Headers $@ +$(DESTDIR)$(prefix)/$(framework_currver)/etc: | $(framework_destdirs) + ln -s -f Resources $@ + +hier_symlinks: \ + $(DESTDIR)$(prefix)/$(framework_currver)/bin \ + $(DESTDIR)$(prefix)/$(framework_currver)/lib/libjulia.$(SOMAJOR).$(SOMINOR).dylib \ + $(DESTDIR)$(prefix)/$(framework_currver)/lib/libjulia.$(SOMAJOR).dylib \ + $(DESTDIR)$(prefix)/$(framework_currver)/lib/libjulia.dylib \ + $(DESTDIR)$(prefix)/$(framework_currver)/libexec \ + $(DESTDIR)$(prefix)/$(framework_currver)/share \ + $(DESTDIR)$(prefix)/$(framework_currver)/include \ + $(DESTDIR)$(prefix)/$(framework_currver)/etc +ifeq ($(BUNDLE_DEBUG_LIBS),1) +hier_symlinks: \ + $(DESTDIR)$(prefix)/$(framework_currver)/lib/libjulia-debug.$(SOMAJOR).$(SOMINOR).dylib \ + $(DESTDIR)$(prefix)/$(framework_currver)/lib/libjulia-debug.$(SOMAJOR).dylib \ + $(DESTDIR)$(prefix)/$(framework_currver)/lib/libjulia-debug.dylib +endif + +$(DESTDIR)$(prefix)/$(framework_infoplist): | $(framework_destdirs) + /usr/libexec/PlistBuddy -x -c "Clear dict" $@ + /usr/libexec/PlistBuddy -x -c "Add :CFBundleName string $(FRAMEWORK_NAME)" $@ + /usr/libexec/PlistBuddy -x -c "Add :CFBundleExecutable string $(FRAMEWORK_NAME)" $@ + /usr/libexec/PlistBuddy -x -c "Add :CFBundleIdentifier string $(DARWIN_CODESIGN_ID_BASE).lib" $@ + /usr/libexec/PlistBuddy -x -c "Add :CFBundleVersion string $(JULIA_COMMIT)" $@ + /usr/libexec/PlistBuddy -x -c "Add :CFBundleShortVersionString string $(JULIA_MAJOR_VERSION).$(JULIA_MINOR_VERSION).$(JULIA_PATCH_VERSION)" $@ + /usr/libexec/PlistBuddy -x -c "Add :CFBundleSignature string ???" $@ + /usr/libexec/PlistBuddy -x -c "Add :CFBundlePackageType string FMWK" $@ + /usr/libexec/PlistBuddy -x -c "Add :CFBundleInfoDictionaryVersion string 6.0" $@ + /usr/libexec/PlistBuddy -x -c "Add :NSHumanReadableCopyright string \"Copyright © 2009-2019 Julia project contributors (https://github.com/JuliaLang/julia/contributors). See LICENSE.md.\"" $@ + +toplevelinstall: +ifneq ($(DARWIN_FRAMEWORK),1) + $(error Darwin framework is not enabled. Please set DARWIN_FRAMEWORK=1) +endif + $(MAKE) -C $(BUILDROOT) install + +# frameworknoinstall assumes `make install` was already completed. +frameworknoinstall: $(DESTDIR)$(prefix)/$(framework_infoplist) | $(framework_current_symlinks) $(framework_version_symlink) $(framework_destdirs) hier_symlinks + + $(INSTALL_NAME_CHANGE_CMD) @rpath/libjulia.dylib @rpath/$(FRAMEWORK_NAME) $(DESTDIR)$(bindir)/julia + $(JULIAHOME)/contrib/delete-all-rpaths.sh $(DESTDIR)$(bindir)/julia + install_name_tool -add_rpath @executable_path/$(libdir_rel) $(DESTDIR)$(bindir)/julia +ifeq ($(BUNDLE_DEBUG_LIBS),1) + $(INSTALL_NAME_CHANGE_CMD) @rpath/libjulia-debug.dylib @rpath/$(FRAMEWORK_NAME)_debug $(DESTDIR)$(bindir)/julia-debug + $(JULIAHOME)/contrib/delete-all-rpaths.sh $(DESTDIR)$(bindir)/julia-debug + install_name_tool -add_rpath @executable_path/$(libdir_rel) $(DESTDIR)$(bindir)/julia-debug +endif + + # fix libjulia paths + $(INSTALL_NAME_CMD)$(framework_dylib) $(DESTDIR)$(prefix)/$(framework_dylib) + $(JULIAHOME)/contrib/delete-all-rpaths.sh $(DESTDIR)$(prefix)/$(framework_dylib) + install_name_tool -add_rpath @loader_path/Frameworks $(DESTDIR)$(prefix)/$(framework_dylib) +ifeq ($(BUNDLE_DEBUG_LIBS),1) + # Install name should be the non-debug variant. + # Julia_debug variant is selected with DYLD_IMAGE_SUFFIX (man 1 dyld). + # julia-debug explicitly links to Julia_debug so no need to manipulate DYLD_IMAGE_SUFFIX for it. + $(INSTALL_NAME_CMD)$(framework_dylib) $(DESTDIR)$(prefix)/$(framework_dylib)_debug + $(JULIAHOME)/contrib/delete-all-rpaths.sh $(DESTDIR)$(prefix)/$(framework_dylib)_debug + install_name_tool -add_rpath @loader_path/Frameworks $(DESTDIR)$(prefix)/$(framework_dylib)_debug +endif + + $(INSTALL_NAME_CHANGE_CMD) @rpath/libjulia.dylib @rpath/$(FRAMEWORK_NAME) $(DESTDIR)$(prefix)/$(framework_frameworks)/sys.dylib + $(JULIAHOME)/contrib/delete-all-rpaths.sh $(DESTDIR)$(prefix)/$(framework_frameworks)/sys.dylib + install_name_tool -add_rpath @loader_path/.. $(DESTDIR)$(prefix)/$(framework_frameworks)/sys.dylib +ifeq ($(BUNDLE_DEBUG_LIBS),1) + $(INSTALL_NAME_CHANGE_CMD) @rpath/libjulia-debug.dylib @rpath/$(FRAMEWORK_NAME)_debug $(DESTDIR)$(prefix)/$(framework_frameworks)/sys-debug.dylib + $(JULIAHOME)/contrib/delete-all-rpaths.sh $(DESTDIR)$(prefix)/$(framework_frameworks)/sys-debug.dylib + install_name_tool -add_rpath @loader_path/.. $(DESTDIR)$(prefix)/$(framework_frameworks)/sys-debug.dylib +endif + + # fix private lib paths + $(JULIAHOME)/contrib/delete-all-rpaths.sh $(DESTDIR)$(prefix)/$(framework_frameworks)/* + + $(JULIAHOME)/contrib/fixup-libgfortran.sh $(DESTDIR)$(prefix)/$(framework_frameworks) + + # Add framework header + sed -e 's/ $(DESTDIR)$(prefix)/$(framework_headers)/$(FRAMEWORK_NAME).h + + # cleanup unnecessary install outputs + rm $(DESTDIR)$(datarootdir)/julia/startup.jl + -rm $(DESTDIR)$(datarootdir)/julia/build_sysimg.jl + rm -rf $(DESTDIR)$(datarootdir)/icons $(DESTDIR)$(datarootdir)/applications $(DESTDIR)$(datarootdir)/appdata + find $(DESTDIR)$(prefix)/$(framework_directory) \( -name '.DS_Store' -o -name '.gitignore' -o -name Makefile -o -name .travis.yml -o -name .codecov.yml \) -delete + + # Include Julia's license info + $(INSTALL_F) $(JULIAHOME)/LICENSE.md $(DESTDIR)$(prefix)/$(framework_resources) + + # Add the module map file. + sed -e 's/Julia/$(FRAMEWORK_NAME)/' $(JULIAHOME)/contrib/mac/framework/module.modulemap > $(DESTDIR)$(prefix)/$(framework_modules)/module.modulemap + + # Make sure EUID:EGID owns the framework and permissions are set. + chmod -R u+w $(DESTDIR)$(prefix)/$(framework_directory) + chown -R $$(id -un):$$(id -gn) $(DESTDIR)$(prefix)/$(framework_directory) + + # ad-hoc codesigning + #NB: must be the last lines of the recipe, else signature may be invalidated. + + # Codesign should look at the embedded Info.plist to get the signing identifier. + # See JLDFLAGS in Make.inc for Darwin platform and Info.plist target in ui/Makefile. + codesign -s "$(DARWIN_CODESIGN_KEYCHAIN_IDENTITY)" -v $(darwin_codesign_options) $(darwin_codesign_julia_options) $(DESTDIR)$(prefix)/$(framework_helpers)/julia +ifeq ($(BUNDLE_DEBUG_LIBS),1) + codesign -s "$(DARWIN_CODESIGN_KEYCHAIN_IDENTITY)" -v $(darwin_codesign_options) $(darwin_codesign_julia_options) $(DESTDIR)$(prefix)/$(framework_helpers)/julia-debug +endif + + # Append the library name to the base codesigning id. + for file in $(DESTDIR)$(prefix)/$(framework_frameworks)/*.dylib* ; do \ + if [ -f "$$file" -a ! -L "$$file" -a -w "$$file" -a -x "$$file" ]; then \ + idsuffix=$$(basename $${file%%.dylib*}) ; \ + codesign -s "$(DARWIN_CODESIGN_KEYCHAIN_IDENTITY)" -v $(darwin_codesign_options) -i $(darwin_codesign_id_julia_deps).$${idsuffix} -f $$file ; \ + fi \ + done + + touch -c $(DESTDIR)$(prefix)/$(framework_directory) + + # Sign the (current version) framework bundle. +ifeq ($(BUNDLE_DEBUG_LIBS),1) + # Don't forget to sign Frameworks/Julia_debug + codesign -s "$(DARWIN_CODESIGN_KEYCHAIN_IDENTITY)" -v $(darwin_codesign_options) -i $(DARWIN_CODESIGN_ID_BASE).lib -f \ + $(DESTDIR)$(prefix)/$(framework_dylib)_debug +endif + codesign -s "$(DARWIN_CODESIGN_KEYCHAIN_IDENTITY)" -v $(darwin_codesign_options) $(DESTDIR)$(prefix)/$(framework_currver) + +framework: toplevelinstall + +.PHONY: toplevelinstall framework frameworknoinstall hier_symlinks diff --git a/contrib/mac/framework/julia.entitlements b/contrib/mac/framework/julia.entitlements new file mode 100644 index 0000000..8cc185a --- /dev/null +++ b/contrib/mac/framework/julia.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.cs.disable-library-validation + + + diff --git a/contrib/mac/framework/module.modulemap b/contrib/mac/framework/module.modulemap new file mode 100644 index 0000000..04f8155 --- /dev/null +++ b/contrib/mac/framework/module.modulemap @@ -0,0 +1,5 @@ +framework module Julia [extern_c] [system] { + umbrella header "Julia.h" + export * + module * { export * } +} diff --git a/contrib/mac/frameworkapp/ExecSandbox/ExecSandbox.entitlements b/contrib/mac/frameworkapp/ExecSandbox/ExecSandbox.entitlements new file mode 100644 index 0000000..a22bc37 --- /dev/null +++ b/contrib/mac/frameworkapp/ExecSandbox/ExecSandbox.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.temporary-exception.files.absolute-path.read-only + + / + + + diff --git a/contrib/mac/frameworkapp/ExecSandbox/ExecSandbox.h b/contrib/mac/frameworkapp/ExecSandbox/ExecSandbox.h new file mode 100644 index 0000000..391a3b7 --- /dev/null +++ b/contrib/mac/frameworkapp/ExecSandbox/ExecSandbox.h @@ -0,0 +1,7 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + +@import Foundation; +#import "ExecSandboxProtocol.h" + +@interface ExecSandbox : NSObject +@end diff --git a/contrib/mac/frameworkapp/ExecSandbox/ExecSandbox.m b/contrib/mac/frameworkapp/ExecSandbox/ExecSandbox.m new file mode 100644 index 0000000..4f4b2c5 --- /dev/null +++ b/contrib/mac/frameworkapp/ExecSandbox/ExecSandbox.m @@ -0,0 +1,149 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + +#import "ExecSandbox.h" + +@class JuliaTask; + +@interface ExecSandbox () { + NSMutableArray *_Nonnull _tasks; +} +- (void)taskTerminated:(JuliaTask *_Nonnull)jt; +@end + +@interface JuliaTask : NSObject { + ExecSandbox *__weak _delegate; + dispatch_block_t _Nullable _onCleanup; + NSTask *_Nonnull _task; +} +@end + +@implementation JuliaTask + +- (instancetype)initWithTask:(NSTask *_Nonnull)task + delegate:(ExecSandbox *)d + cleanup:(dispatch_block_t)onCleanup { + self = [super init]; + if (self == nil) { + return nil; + } + _delegate = d; + _onCleanup = onCleanup; + _task = task; + return self; +} + +- (void)launch:(void (^_Nullable)(int status))onTermination { + dispatch_block_t onCleanup = _onCleanup; + JuliaTask __weak *weakSelf = self; + _task.terminationHandler = ^(NSTask *_Nonnull t) { + if (onTermination != nil) { + onTermination(t.terminationStatus); + } + if (onCleanup != nil) { + onCleanup(); + } + JuliaTask *strongSelf = weakSelf; + if (strongSelf) { + [strongSelf->_delegate taskTerminated:strongSelf]; + } + }; + @try { + [_task launch]; + } @catch (NSException *exception) { + NSLog(@"NSTask launch exception: %@", exception); + } +} + +- (void)terminate { + @try { + [_task terminate]; + } @catch (NSException *exception) { + NSLog(@"NSTask terminate exception: %@", exception); + } +} + +@end + +@implementation ExecSandbox + +- (instancetype)init { + self = [super init]; + if (self == nil) { + return nil; + } + _tasks = [[NSMutableArray alloc] init]; + return self; +} + +- (void)eval:(NSString *)p + withJulia:(NSData *)executableBookmark + arguments:(NSArray *)baseArgs + task:(void (^)(id task, NSFileHandle *stdIn, + NSFileHandle *stdOut, NSFileHandle *stdErr))reply { + + NSURL *executableURL = + [NSURL URLByResolvingBookmarkData:executableBookmark + options:NSURLBookmarkResolutionWithoutUI + relativeToURL:nil + bookmarkDataIsStale:nil + error:nil]; + if (executableURL == nil) { + reply(nil, nil, nil, nil); + return; + } + + for (NSString *arg in baseArgs) { + if ([arg isEqual:@"--"]) { + reply(nil, nil, nil, nil); + return; + } + } + + NSURL *temporaryDirectoryURL = [NSURL fileURLWithPath:NSTemporaryDirectory() + isDirectory:YES]; + NSString *temporaryFilename = + [[NSProcessInfo processInfo] globallyUniqueString]; + NSURL *temporaryFileURL = + [temporaryDirectoryURL URLByAppendingPathComponent:temporaryFilename + isDirectory:NO]; + + [[p dataUsingEncoding:NSUTF8StringEncoding] writeToURL:temporaryFileURL + atomically:NO]; + + NSMutableArray *args = [[NSMutableArray alloc] init]; + [args addObjectsFromArray:baseArgs]; + [args addObjectsFromArray:@[ @"--", temporaryFileURL.path ]]; + + NSPipe *stdIn = [NSPipe pipe], *stdOut = [NSPipe pipe], + *stdErr = [NSPipe pipe]; + + NSTask *t = [[NSTask alloc] init]; + if (@available(macOS 10.13, *)) { + t.executableURL = executableURL; + } else { + t.launchPath = executableURL.path; + } + t.arguments = args; + t.standardInput = stdIn; + t.standardOutput = stdOut; + t.standardError = stdErr; + + JuliaTask *jt = + [[JuliaTask alloc] initWithTask:t + delegate:self + cleanup:^() { + [[NSFileManager defaultManager] + removeItemAtURL:temporaryDirectoryURL + error:nil]; + }]; + [_tasks addObject:jt]; + + reply(jt, stdIn.fileHandleForWriting, stdOut.fileHandleForReading, + stdErr.fileHandleForReading); +} + +- (void)taskTerminated:(JuliaTask *_Nonnull)jt { + [_tasks removeObject:jt]; +} + +@end diff --git a/contrib/mac/frameworkapp/ExecSandbox/ExecSandboxProtocol.h b/contrib/mac/frameworkapp/ExecSandbox/ExecSandboxProtocol.h new file mode 100644 index 0000000..5dd16e7 --- /dev/null +++ b/contrib/mac/frameworkapp/ExecSandbox/ExecSandboxProtocol.h @@ -0,0 +1,31 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + +@import Foundation; + +@protocol TaskProtocol +/// Launch the task and upon termination receive the exit status. +- (void)launch:(void (^_Nullable)(int status))onTermination; +/// Terminate (SIGTERM) the task. +- (void)terminate; +@end + +@protocol ExecSandboxProtocol +/** + Evaluate a Julia program with a Julia executable. + + @param juliaProgram Julia source code to be evaluated. + @param executableBookmark NSURL file bookmark for the julia executable to run. + @param args Arguments to pass to julia. + @param reply Async result with task and standard in, out, and error. An error + occured if task is nil. + */ +- (void)eval:(NSString *_Nonnull)juliaProgram + withJulia:(NSData *_Nonnull)executableBookmark + arguments:(NSArray *_Nullable)args + task:(void (^_Nonnull)(id _Nullable task, + NSFileHandle *_Nullable stdIn, + NSFileHandle *_Nullable stdOut, + NSFileHandle *_Nullable stdErr))reply; +@end + +NSXPCInterface *_Nonnull CreateExecSandboxXPCInterface(void); diff --git a/contrib/mac/frameworkapp/ExecSandbox/ExecSandboxProtocol.m b/contrib/mac/frameworkapp/ExecSandbox/ExecSandboxProtocol.m new file mode 100644 index 0000000..a7e13f6 --- /dev/null +++ b/contrib/mac/frameworkapp/ExecSandbox/ExecSandboxProtocol.m @@ -0,0 +1,14 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + +#import "ExecSandboxProtocol.h" + +NSXPCInterface *CreateExecSandboxXPCInterface(void) { + NSXPCInterface *i = + [NSXPCInterface interfaceWithProtocol:@protocol(ExecSandboxProtocol)]; + /// Reply sends a task proxy: + [i setInterface:[NSXPCInterface interfaceWithProtocol:@protocol(TaskProtocol)] + forSelector:@selector(eval:withJulia:arguments:task:) + argumentIndex:0 + ofReply:true]; + return i; +} diff --git a/contrib/mac/frameworkapp/ExecSandbox/Info.plist b/contrib/mac/frameworkapp/ExecSandbox/Info.plist new file mode 100644 index 0000000..4339a0b --- /dev/null +++ b/contrib/mac/frameworkapp/ExecSandbox/Info.plist @@ -0,0 +1,31 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + ExecSandbox + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + XPC! + CFBundleShortVersionString + $(APP_SHORT_VERSION_STRING) + CFBundleVersion + $(APP_VERSION) + XPCService + + RunLoopType + NSRunLoop + ServiceType + Application + + + diff --git a/contrib/mac/frameworkapp/ExecSandbox/main.m b/contrib/mac/frameworkapp/ExecSandbox/main.m new file mode 100644 index 0000000..3ca1ff1 --- /dev/null +++ b/contrib/mac/frameworkapp/ExecSandbox/main.m @@ -0,0 +1,28 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + +@import Foundation; +#import "ExecSandbox.h" + +@interface ServiceDelegate : NSObject +@end + +@implementation ServiceDelegate + +- (BOOL)listener:(NSXPCListener *)listener + shouldAcceptNewConnection:(NSXPCConnection *)newConnection { + newConnection.exportedInterface = CreateExecSandboxXPCInterface(); + ExecSandbox *exportedObject = [[ExecSandbox alloc] init]; + newConnection.exportedObject = exportedObject; + [newConnection resume]; + return YES; +} + +@end + +int main(int argc, const char *argv[]) { + ServiceDelegate *delegate = [[ServiceDelegate alloc] init]; + NSXPCListener *listener = [NSXPCListener serviceListener]; + listener.delegate = delegate; + [listener resume]; + return 0; +} diff --git a/contrib/mac/frameworkapp/Julia.dist b/contrib/mac/frameworkapp/Julia.dist new file mode 100644 index 0000000..3052688 --- /dev/null +++ b/contrib/mac/frameworkapp/Julia.dist @@ -0,0 +1,26 @@ + + + FRAMEWORK_NAME + + + + + + + + + + + FRAMEWORK_NAME-framework.pkg + launcher.pkg + + + + + + + + + + + diff --git a/contrib/mac/frameworkapp/JuliaLauncher.xcodeproj/project.pbxproj b/contrib/mac/frameworkapp/JuliaLauncher.xcodeproj/project.pbxproj new file mode 100644 index 0000000..31dcbbd --- /dev/null +++ b/contrib/mac/frameworkapp/JuliaLauncher.xcodeproj/project.pbxproj @@ -0,0 +1,487 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + DC69B3E6226152320004C981 /* ExecSandbox.m in Sources */ = {isa = PBXBuildFile; fileRef = DC69B3E5226152320004C981 /* ExecSandbox.m */; }; + DC69B3E8226152320004C981 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DC69B3E7226152320004C981 /* main.m */; }; + DC69B3EC226152320004C981 /* ExecSandbox.xpc in Embed XPC Services */ = {isa = PBXBuildFile; fileRef = DC69B3E1226152320004C981 /* ExecSandbox.xpc */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + DC69B3F322628ABC0004C981 /* ExecSandboxProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = DC69B3F222628ABC0004C981 /* ExecSandboxProtocol.m */; }; + DC69B3F422628ABC0004C981 /* ExecSandboxProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = DC69B3F222628ABC0004C981 /* ExecSandboxProtocol.m */; }; + DCECD36721B6461B0099A8C3 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DCECD36621B6461B0099A8C3 /* AppDelegate.m */; }; + DCECD36921B6461C0099A8C3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DCECD36821B6461C0099A8C3 /* Assets.xcassets */; }; + DCECD36C21B6461C0099A8C3 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = DCECD36A21B6461C0099A8C3 /* MainMenu.xib */; }; + DCECD36F21B6461C0099A8C3 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DCECD36E21B6461C0099A8C3 /* main.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + DC69B3EA226152320004C981 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DCECD35A21B6461B0099A8C3 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DC69B3E0226152320004C981; + remoteInfo = ExecSandbox; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + DC69B3F0226152320004C981 /* Embed XPC Services */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "$(CONTENTS_FOLDER_PATH)/XPCServices"; + dstSubfolderSpec = 16; + files = ( + DC69B3EC226152320004C981 /* ExecSandbox.xpc in Embed XPC Services */, + ); + name = "Embed XPC Services"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + DC69B3E1226152320004C981 /* ExecSandbox.xpc */ = {isa = PBXFileReference; explicitFileType = "wrapper.xpc-service"; includeInIndex = 0; path = ExecSandbox.xpc; sourceTree = BUILT_PRODUCTS_DIR; }; + DC69B3E3226152320004C981 /* ExecSandboxProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ExecSandboxProtocol.h; sourceTree = ""; }; + DC69B3E4226152320004C981 /* ExecSandbox.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ExecSandbox.h; sourceTree = ""; }; + DC69B3E5226152320004C981 /* ExecSandbox.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ExecSandbox.m; sourceTree = ""; }; + DC69B3E7226152320004C981 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + DC69B3E9226152320004C981 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + DC69B3F1226152410004C981 /* ExecSandbox.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ExecSandbox.entitlements; sourceTree = ""; }; + DC69B3F222628ABC0004C981 /* ExecSandboxProtocol.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ExecSandboxProtocol.m; sourceTree = ""; }; + DCECD36221B6461B0099A8C3 /* Julia.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Julia.app; sourceTree = BUILT_PRODUCTS_DIR; }; + DCECD36521B6461B0099A8C3 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + DCECD36621B6461B0099A8C3 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + DCECD36821B6461C0099A8C3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + DCECD36B21B6461C0099A8C3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + DCECD36D21B6461C0099A8C3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + DCECD36E21B6461C0099A8C3 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + DCECD37021B6461C0099A8C3 /* JuliaLauncher.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = JuliaLauncher.entitlements; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + DC69B3DE226152320004C981 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCECD35F21B6461B0099A8C3 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + DC69B3E2226152320004C981 /* ExecSandbox */ = { + isa = PBXGroup; + children = ( + DC69B3E3226152320004C981 /* ExecSandboxProtocol.h */, + DC69B3F222628ABC0004C981 /* ExecSandboxProtocol.m */, + DC69B3E4226152320004C981 /* ExecSandbox.h */, + DC69B3E5226152320004C981 /* ExecSandbox.m */, + DC69B3E7226152320004C981 /* main.m */, + DC69B3E9226152320004C981 /* Info.plist */, + DC69B3F1226152410004C981 /* ExecSandbox.entitlements */, + ); + path = ExecSandbox; + sourceTree = ""; + }; + DCECD35921B6461B0099A8C3 = { + isa = PBXGroup; + children = ( + DCECD36421B6461B0099A8C3 /* JuliaLauncher */, + DC69B3E2226152320004C981 /* ExecSandbox */, + DCECD36321B6461B0099A8C3 /* Products */, + ); + sourceTree = ""; + }; + DCECD36321B6461B0099A8C3 /* Products */ = { + isa = PBXGroup; + children = ( + DCECD36221B6461B0099A8C3 /* Julia.app */, + DC69B3E1226152320004C981 /* ExecSandbox.xpc */, + ); + name = Products; + sourceTree = ""; + }; + DCECD36421B6461B0099A8C3 /* JuliaLauncher */ = { + isa = PBXGroup; + children = ( + DCECD36E21B6461C0099A8C3 /* main.m */, + DCECD36521B6461B0099A8C3 /* AppDelegate.h */, + DCECD36621B6461B0099A8C3 /* AppDelegate.m */, + DCECD36821B6461C0099A8C3 /* Assets.xcassets */, + DCECD36A21B6461C0099A8C3 /* MainMenu.xib */, + DCECD36D21B6461C0099A8C3 /* Info.plist */, + DCECD37021B6461C0099A8C3 /* JuliaLauncher.entitlements */, + ); + path = JuliaLauncher; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + DC69B3E0226152320004C981 /* ExecSandbox */ = { + isa = PBXNativeTarget; + buildConfigurationList = DC69B3EF226152320004C981 /* Build configuration list for PBXNativeTarget "ExecSandbox" */; + buildPhases = ( + DC69B3DD226152320004C981 /* Sources */, + DC69B3DE226152320004C981 /* Frameworks */, + DC69B3DF226152320004C981 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ExecSandbox; + productName = ExecSandbox; + productReference = DC69B3E1226152320004C981 /* ExecSandbox.xpc */; + productType = "com.apple.product-type.xpc-service"; + }; + DCECD36121B6461B0099A8C3 /* JuliaLauncher */ = { + isa = PBXNativeTarget; + buildConfigurationList = DCECD37321B6461C0099A8C3 /* Build configuration list for PBXNativeTarget "JuliaLauncher" */; + buildPhases = ( + DCECD35E21B6461B0099A8C3 /* Sources */, + DCECD35F21B6461B0099A8C3 /* Frameworks */, + DCECD36021B6461B0099A8C3 /* Resources */, + DC69B3F0226152320004C981 /* Embed XPC Services */, + ); + buildRules = ( + ); + dependencies = ( + DC69B3EB226152320004C981 /* PBXTargetDependency */, + ); + name = JuliaLauncher; + productName = JuliaLauncher; + productReference = DCECD36221B6461B0099A8C3 /* Julia.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + DCECD35A21B6461B0099A8C3 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1010; + ORGANIZATIONNAME = JuliaLang; + TargetAttributes = { + DC69B3E0226152320004C981 = { + CreatedOnToolsVersion = 10.2; + SystemCapabilities = { + com.apple.HardenedRuntime = { + enabled = 1; + }; + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + DCECD36121B6461B0099A8C3 = { + CreatedOnToolsVersion = 10.1; + SystemCapabilities = { + com.apple.HardenedRuntime = { + enabled = 1; + }; + com.apple.Sandbox = { + enabled = 0; + }; + }; + }; + }; + }; + buildConfigurationList = DCECD35D21B6461B0099A8C3 /* Build configuration list for PBXProject "JuliaLauncher" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = DCECD35921B6461B0099A8C3; + productRefGroup = DCECD36321B6461B0099A8C3 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + DCECD36121B6461B0099A8C3 /* JuliaLauncher */, + DC69B3E0226152320004C981 /* ExecSandbox */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + DC69B3DF226152320004C981 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCECD36021B6461B0099A8C3 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DCECD36921B6461C0099A8C3 /* Assets.xcassets in Resources */, + DCECD36C21B6461C0099A8C3 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + DC69B3DD226152320004C981 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DC69B3E8226152320004C981 /* main.m in Sources */, + DC69B3E6226152320004C981 /* ExecSandbox.m in Sources */, + DC69B3F422628ABC0004C981 /* ExecSandboxProtocol.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DCECD35E21B6461B0099A8C3 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DCECD36F21B6461C0099A8C3 /* main.m in Sources */, + DCECD36721B6461B0099A8C3 /* AppDelegate.m in Sources */, + DC69B3F322628ABC0004C981 /* ExecSandboxProtocol.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + DC69B3EB226152320004C981 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DC69B3E0226152320004C981 /* ExecSandbox */; + targetProxy = DC69B3EA226152320004C981 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + DCECD36A21B6461C0099A8C3 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + DCECD36B21B6461C0099A8C3 /* Base */, + ); + name = MainMenu.xib; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + DC69B3ED226152320004C981 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = ExecSandbox/ExecSandbox.entitlements; + CODE_SIGN_STYLE = Automatic; + ENABLE_HARDENED_RUNTIME = YES; + INFOPLIST_FILE = ExecSandbox/Info.plist; + PRODUCT_BUNDLE_IDENTIFIER = org.julialang.ExecSandbox; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + DC69B3EE226152320004C981 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = ExecSandbox/ExecSandbox.entitlements; + CODE_SIGN_STYLE = Automatic; + ENABLE_HARDENED_RUNTIME = YES; + INFOPLIST_FILE = ExecSandbox/Info.plist; + PRODUCT_BUNDLE_IDENTIFIER = org.julialang.ExecSandbox; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Release; + }; + DCECD37121B6461C0099A8C3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.8; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + DCECD37221B6461C0099A8C3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.8; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = macosx; + }; + name = Release; + }; + DCECD37421B6461C0099A8C3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = JuliaLauncher/JuliaLauncher.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + ENABLE_HARDENED_RUNTIME = YES; + INFOPLIST_FILE = JuliaLauncher/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.julialang.JuliaLauncher; + PRODUCT_NAME = Julia; + }; + name = Debug; + }; + DCECD37521B6461C0099A8C3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = JuliaLauncher/JuliaLauncher.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + ENABLE_HARDENED_RUNTIME = YES; + INFOPLIST_FILE = JuliaLauncher/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = org.julialang.JuliaLauncher; + PRODUCT_NAME = Julia; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + DC69B3EF226152320004C981 /* Build configuration list for PBXNativeTarget "ExecSandbox" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DC69B3ED226152320004C981 /* Debug */, + DC69B3EE226152320004C981 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DCECD35D21B6461B0099A8C3 /* Build configuration list for PBXProject "JuliaLauncher" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DCECD37121B6461C0099A8C3 /* Debug */, + DCECD37221B6461C0099A8C3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DCECD37321B6461C0099A8C3 /* Build configuration list for PBXNativeTarget "JuliaLauncher" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DCECD37421B6461C0099A8C3 /* Debug */, + DCECD37521B6461C0099A8C3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = DCECD35A21B6461B0099A8C3 /* Project object */; +} diff --git a/contrib/mac/frameworkapp/JuliaLauncher.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/contrib/mac/frameworkapp/JuliaLauncher.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..05896b2 --- /dev/null +++ b/contrib/mac/frameworkapp/JuliaLauncher.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/contrib/mac/frameworkapp/JuliaLauncher.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/contrib/mac/frameworkapp/JuliaLauncher.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/contrib/mac/frameworkapp/JuliaLauncher.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/contrib/mac/frameworkapp/JuliaLauncher.xcodeproj/xcshareddata/xcschemes/JuliaLauncher.xcscheme b/contrib/mac/frameworkapp/JuliaLauncher.xcodeproj/xcshareddata/xcschemes/JuliaLauncher.xcscheme new file mode 100644 index 0000000..dbb4b36 --- /dev/null +++ b/contrib/mac/frameworkapp/JuliaLauncher.xcodeproj/xcshareddata/xcschemes/JuliaLauncher.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contrib/mac/frameworkapp/JuliaLauncher/AppDelegate.h b/contrib/mac/frameworkapp/JuliaLauncher/AppDelegate.h new file mode 100644 index 0000000..4c08646 --- /dev/null +++ b/contrib/mac/frameworkapp/JuliaLauncher/AppDelegate.h @@ -0,0 +1,6 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + +@import AppKit; + +@interface AppDelegate : NSObject +@end diff --git a/contrib/mac/frameworkapp/JuliaLauncher/AppDelegate.m b/contrib/mac/frameworkapp/JuliaLauncher/AppDelegate.m new file mode 100644 index 0000000..db2f13b --- /dev/null +++ b/contrib/mac/frameworkapp/JuliaLauncher/AppDelegate.m @@ -0,0 +1,536 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + +@import AppKit; +#import "AppDelegate.h" +#import "ExecSandboxProtocol.h" + +/// Terminal's bundle ID. +NSString static const *const terminalBundleID = @"com.apple.Terminal"; + +static bool launchTerminalApp(void); +static void execJuliaInTerminal(NSURL *_Nonnull julia); + +/// Controller for an XPC connection to the ExecSandbox service. +/// +/// The ExecSandbox service allows Julia code to be run within a restricted App +/// Sandbox environment. +@interface ExecSandboxController : NSObject { + NSXPCConnection *_Nonnull _execSandboxCnx; +} +- (id _Nonnull)remoteObjectProxyWithErrorHandler: + (void (^_Nonnull)(NSError *_Nullable error))handler; ++ (ExecSandboxController *_Nonnull)sharedController; +@end + +@implementation ExecSandboxController + +- (instancetype)init { + self = [super init]; + if (self == nil) + return nil; + _execSandboxCnx = [[NSXPCConnection alloc] + initWithServiceName:@"org.julialang.ExecSandbox"]; + _execSandboxCnx.remoteObjectInterface = CreateExecSandboxXPCInterface(); + [_execSandboxCnx resume]; + return self; +} + +- (id)remoteObjectProxyWithErrorHandler: + (void (^_Nonnull)(NSError *_Nullable error))handler { + return [_execSandboxCnx remoteObjectProxyWithErrorHandler:handler]; +} + ++ (ExecSandboxController *)sharedController { + static ExecSandboxController *s = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + s = [[ExecSandboxController alloc] init]; + }); + return s; +} + +@end + +/// Location of an installed variant of Julia (frameowrk or nix hier). +@interface JuliaVariant : NSObject +@property(readonly, nullable) NSBundle *bundle; +@property(readonly, nonnull) NSURL *juliaexe; +@property(readonly, nullable) NSString *version; +@property(readonly) BOOL updatingVersion; +- (instancetype)initWithJulia:(NSURL *_Nonnull)exe + bundle:(NSBundle *_Nullable)b; +/// (major,minor,patch) components parsed from version. +@property(readonly, nullable) NSArray *versionComponents; +@end + +@implementation JuliaVariant + +- (instancetype)initWithJulia:(NSURL *)exe bundle:(NSBundle *)b { + self = [super init]; + if (!self) { + return nil; + } + NSAssert(exe != nil, @"juliaexe cannot be nil."); + _juliaexe = exe; + _bundle = b; + _version = nil; + if (_bundle == nil) { + // Try to locate the framework bundle. + NSURL *frameworkURL = + [[exe URLByDeletingLastPathComponent] URLByDeletingLastPathComponent]; + NSBundle *bundle = [NSBundle bundleWithURL:frameworkURL]; + if (bundle && + [[bundle bundleIdentifier] isEqual:@"org.julialang.julia.lib"]) { + _bundle = bundle; + } + } + if (_bundle) { + // Extract version from framework bundle. + _version = _bundle.infoDictionary[(NSString *)kCFBundleVersionKey]; + } else { + // Exec the julia and have it tell us its version. + + NSData *juliaexeBookmark = [_juliaexe bookmarkDataWithOptions:0 + includingResourceValuesForKeys:nil + relativeToURL:nil + error:nil]; + + _updatingVersion = YES; + + id remote = [[ExecSandboxController sharedController] + remoteObjectProxyWithErrorHandler:^(NSError *error) { + [self willChangeValueForKey:@"updatingVersion"]; + self->_updatingVersion = NO; + [self didChangeValueForKey:@"updatingVersion"]; + }]; + + [remote eval:@"print(\"$(Base.VERSION.major).$(Base.VERSION.minor).$(Base." + @"VERSION.patch)\")" + withJulia:juliaexeBookmark + arguments:nil + task:^(id task, NSFileHandle *stdIn, + NSFileHandle *stdOut, NSFileHandle *stdErr) { + [task launch:^(int status) { + NSString *vout = + [[NSString alloc] initWithData:[stdOut readDataToEndOfFile] + encoding:NSUTF8StringEncoding]; + if (status == 0 && vout) { + [self willChangeValueForKey:@"version"]; + [self willChangeValueForKey:@"updatingVersion"]; + self->_version = vout; + self->_updatingVersion = NO; + [self didChangeValueForKey:@"updatingVersion"]; + [self didChangeValueForKey:@"version"]; + + } else { + [self willChangeValueForKey:@"updatingVersion"]; + self->_updatingVersion = NO; + [self didChangeValueForKey:@"updatingVersion"]; + } + }]; + }]; + NSLog(@"Getting version by execing %@", exe); + } + return self; +} + +- (NSArray *)versionComponents { + NSNumber *compsi[3]; + NSArray *c = [self.version componentsSeparatedByString:@"."]; + if (c.count < 3) { + return nil; + } + NSInteger i = 0; + for (NSString *vn in c) { + compsi[i++] = @(vn.integerValue); + } + if ([compsi[0] isEqual:@(0)] && [compsi[1] isEqual:@(0)] && + [compsi[2] isEqual:@(0)]) { + return nil; + } + return [NSArray arrayWithObjects:compsi count:3]; +} + +@end + +@interface AppDelegate () { + NSMetadataQuery *_Nullable _mdq; +} +@property NSMutableDictionary *_Nonnull juliaVariants; +@property JuliaVariant *_Nullable latestKnownTaggedJulia; +@end + +@implementation AppDelegate + +- (instancetype)init { + self = [super init]; + if (!self) { + return nil; + } + _juliaVariants = [[NSMutableDictionary alloc] init]; + return self; +} + +- (void)dealloc { + [self stopFindJuliaWithSpotlight]; +} + +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { + [self findJuliaWithSpotlight]; +} + +- (BOOL)applicationShouldHandleReopen:(NSApplication *)sender + hasVisibleWindows:(BOOL)flag { + if (!_mdq.gathering) { + NSURL *juliaexe = self.latestKnownTaggedJulia.juliaexe; + if (juliaexe) { + execJuliaInTerminal(juliaexe); + } + } + return NO; +} + +- (void)addJuliaVariant:(JuliaVariant *)jv { + if ([self.juliaVariants objectForKey:jv.juliaexe]) { + // Don't overwrite. + return; + } + [self.juliaVariants setObject:jv forKey:jv.juliaexe]; + + // Track the latest known tagged variant. + if (self.latestKnownTaggedJulia == nil) { + self.latestKnownTaggedJulia = jv; + } else { + JuliaVariant *latestJv = self.latestKnownTaggedJulia; + NSArray *latestV = latestJv.versionComponents; + NSArray *vc = jv.versionComponents; + if (vc != nil) { + // Compare version tuple. + if ([vc[0] isGreaterThan:latestV[0]] || + ([vc[0] isEqualToNumber:latestV[0]] && + [vc[1] isGreaterThan:latestV[1]]) || + ([vc[0] isEqualToNumber:latestV[0]] && + [vc[1] isEqualToNumber:latestV[1]] && + [vc[2] isGreaterThan:latestV[2]])) { + latestJv = jv; + latestV = vc; + } + } + if (latestJv != self.latestKnownTaggedJulia) { + self.latestKnownTaggedJulia = latestJv; + } + } +} + +- (void)findJuliaQueryDidUpdate:(NSNotification *)sender { + if (sender.object != _mdq) { + return; + } + + // Disable updates while enumerating results. + [_mdq disableUpdates]; + + for (NSUInteger i = 0; i < _mdq.resultCount; ++i) { + NSMetadataItem *item = [_mdq resultAtIndex:i]; + // Grab the path attribute from the item. + NSString *itemPath = [item valueForAttribute:NSMetadataItemPathKey]; + NSString *contentType = + [item valueForAttribute:(NSString *)kMDItemContentType]; + + if ([contentType isEqual:@"public.unix-executable"]) { + // TODO: Verify the executable is actually a Julia. + NSURL *juliaexe = + [[[NSURL alloc] initFileURLWithPath:itemPath + isDirectory:NO] URLByStandardizingPath]; + NSLog(@"Found Julia %@", juliaexe); + JuliaVariant *jv = [[JuliaVariant alloc] initWithJulia:juliaexe + bundle:nil]; + [self addJuliaVariant:jv]; + } else if ([contentType isEqual:@"com.apple.framework"]) { + NSURL *frameworkPath = [[NSURL alloc] initFileURLWithPath:itemPath + isDirectory:YES]; + NSLog(@"Found Julia framework %@", frameworkPath); + + // Iterate over versions within the framework. + + NSFileManager *fm = NSFileManager.defaultManager; + NSURL *frameworkVersions = + [frameworkPath URLByAppendingPathComponent:@"Versions" + isDirectory:YES]; + NSArray *versions = + [fm contentsOfDirectoryAtURL:frameworkVersions + includingPropertiesForKeys:@[ NSURLIsDirectoryKey ] + options:0 + error:nil]; + + for (NSURL *frameworkVersion in versions) { + NSNumber *isDir; + if (![frameworkVersion getResourceValue:&isDir + forKey:NSURLIsDirectoryKey + error:nil] || + !isDir.boolValue) { + // Version is a symink (probably Current) so skip it. + continue; + } + + NSBundle *bundle = [NSBundle bundleWithURL:frameworkVersion]; + + // Form the path to julia in the framework's Helpers directory. + NSURL *juliaexe = [[[[bundle.executableURL URLByStandardizingPath] + URLByDeletingLastPathComponent] + URLByAppendingPathComponent:@"Helpers" + isDirectory:YES] + URLByAppendingPathComponent:@"julia" + isDirectory:NO]; + + if (juliaexe == nil) { + continue; + } + + NSLog(@"Found Julia %@ (%@) at %@", + bundle.infoDictionary[@"CFBundleShortVersionString"], + bundle.infoDictionary[(NSString *)kCFBundleVersionKey], juliaexe); + + JuliaVariant *jv = [[JuliaVariant alloc] initWithJulia:juliaexe + bundle:bundle]; + [self addJuliaVariant:jv]; + } + } else { + NSLog(@"Ignoring Julia at %@ with content type %@", itemPath, + contentType); + } + } + + if (sender.name == NSMetadataQueryDidFinishGatheringNotification) { + // Initial search is complete after app launch so exec julia. + NSURL *juliaexe = self.latestKnownTaggedJulia.juliaexe; + if (juliaexe) { + execJuliaInTerminal(juliaexe); + } + } + + // Safe to enable updates now. + [_mdq enableUpdates]; +} + +/// Start a Spotlight query for Julia frameworks. +- (void)findJuliaWithSpotlight { + if (_mdq != nil) { + // Query exists so return. + return; + } + + _mdq = [[NSMetadataQuery alloc] init]; + + // Search for the framework bundle identifier. + NSPredicate *searchPredicate = [NSPredicate + predicateWithFormat: + @"(kMDItemCFBundleIdentifier == 'org.julialang.julia.lib' && " + @"kMDItemContentType == 'com.apple.framework') || (kMDItemFSName == " + @"'julia' && kMDItemContentType == 'public.unix-executable')"]; + _mdq.predicate = searchPredicate; + + // Observe the query's notifications. + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(findJuliaQueryDidUpdate:) + name:NSMetadataQueryDidUpdateNotification + object:_mdq]; + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(findJuliaQueryDidUpdate:) + name:NSMetadataQueryDidFinishGatheringNotification + object:_mdq]; + + if (![_mdq startQuery]) { + NSAlert *a = [[NSAlert alloc] init]; + a.alertStyle = NSAlertStyleCritical; + a.messageText = NSLocalizedString(@"Cannot find the Julia framework.", ); + a.informativeText = + NSLocalizedString(@"The Julia framework cannot be found. The Spotlight " + @"query for the Julia framework failed to start.", ); + dispatch_async(dispatch_get_main_queue(), ^{ + [a runModal]; + }); + NSLog(@"Failed starting Julia spotlight search."); + [self stopFindJuliaWithSpotlight]; + } +} + +- (void)stopFindJuliaWithSpotlight { + if (_mdq == nil) { + return; + } + + [_mdq stopQuery]; + + [[NSNotificationCenter defaultCenter] + removeObserver:self + name:NSMetadataQueryDidUpdateNotification + object:_mdq]; + [[NSNotificationCenter defaultCenter] + removeObserver:self + name:NSMetadataQueryDidFinishGatheringNotification + object:_mdq]; + + _mdq = nil; +} + +@end + +void execJuliaInTerminal(NSURL *_Nonnull julia) { + OSStatus s; + NSAlert *a = [[NSAlert alloc] init]; + a.alertStyle = NSAlertStyleCritical; + a.messageText = NSLocalizedString(@"Cannot run the Julia REPL.", ); + + // Create the AE descriptor for the bundle ID of Terminal. + NSData *terminalBundleIDData = + [terminalBundleID dataUsingEncoding:NSUTF8StringEncoding]; + NSAppleEventDescriptor *aedTarget = [NSAppleEventDescriptor + descriptorWithDescriptorType:typeApplicationBundleID + data:terminalBundleIDData]; + + // Command: activate + NSAppleEventDescriptor *aevActivate = + [NSAppleEventDescriptor appleEventWithEventClass:kAEMiscStandards + eventID:kAEActivate + targetDescriptor:aedTarget + returnID:kAutoGenerateReturnID + transactionID:kAnyTransactionID]; + + // Command: do shell script + NSAppleEventDescriptor *aevShellCmd = + [NSAppleEventDescriptor appleEventWithEventClass:kAECoreSuite + eventID:kAEDoScript + targetDescriptor:aedTarget + returnID:kAutoGenerateReturnID + transactionID:kAnyTransactionID]; + + // Set shell script as direct parameter. + char const *juliaFSRepCstr; + if (@available(macOS 10.9, *)) { + juliaFSRepCstr = julia.fileSystemRepresentation; + } else { + juliaFSRepCstr = julia.path.fileSystemRepresentation; + } + // Add ';' at start of command to mitigate a race condition. + // Between the time the shell opens and the "exec ..." command is inserted, + // it's possible for stray key events to insert leading to the command being + // "asdf;exec ..." (for example). The semicolon mitigates the stray key + // presses. + NSString *execJuliaCmd = + [NSString stringWithFormat:@";exec '%s'", juliaFSRepCstr]; + NSAppleEventDescriptor *shellScript = + [NSAppleEventDescriptor descriptorWithString:execJuliaCmd]; + [aevShellCmd setParamDescriptor:shellScript forKeyword:keyDirectObject]; + + if (@available(macOS 10.14, *)) { + bool retry = false; + + do { + s = AEDeterminePermissionToAutomateTarget( + aedTarget.aeDesc, aevShellCmd.eventClass, aevShellCmd.eventID, TRUE); + retry = s == procNotFound && !retry && launchTerminalApp(); + } while (retry); + + if (s == errAEEventNotPermitted) { + a.informativeText = NSLocalizedString( + @"The system prevented running the Julia REPL in the Terminal.", ); + } else if (s == errAETargetAddressNotPermitted) { + a.informativeText = NSLocalizedString( + @"The system prevented sending AppleEvents to Terminal.", ); + } else if (s == procNotFound) { + a.informativeText = NSLocalizedString( + @"Terminal is not running and could not be launched.", ); + } + + if (s != noErr) { + [[NSRunLoop mainRunLoop] performBlock:^{ + [a runModal]; + }]; + NSLog(@"AEDeterminePermissionToAutomateTarget failed: %@", + [[NSError errorWithDomain:NSOSStatusErrorDomain code:s + userInfo:nil] localizedDescription]); + return; + } + } + + s = AESendMessage(aevShellCmd.aeDesc, NULL, kAENoReply | kAECanInteract, + kAEDefaultTimeout); + + if (s != noErr) { + a.informativeText = NSLocalizedString(@"\"do script\" command failed.", ); + dispatch_async(dispatch_get_main_queue(), ^{ + [a runModal]; + }); + NSLog(@"\"do script\" failed: %@", + [[NSError errorWithDomain:NSOSStatusErrorDomain code:s + userInfo:nil] localizedDescription]); + return; + } + + s = AESendMessage(aevActivate.aeDesc, NULL, kAENoReply | kAECanInteract, + kAEDefaultTimeout); + + if (s != noErr) { + a.informativeText = NSLocalizedString(@"\"activate\" command failed.", ); + dispatch_async(dispatch_get_main_queue(), ^{ + [a runModal]; + }); + NSLog(@"\"activate\" failed: %@", + [[NSError errorWithDomain:NSOSStatusErrorDomain code:s + userInfo:nil] localizedDescription]); + return; + } +} + +bool launchTerminalApp(void) { + // Launch Terminal.app. Use newer or older API depending on OS version. + + // Send hint to Terminal to not open a new window when opened. + NSAppleEventDescriptor *aedLaunchedAsServiceItem = [NSAppleEventDescriptor + descriptorWithEnumCode:keyAELaunchedAsServiceItem]; + + // Common launch URL spec for either old or new API. + LSLaunchURLSpec lspec = {.launchFlags = kLSLaunchDontSwitch, + .passThruParams = aedLaunchedAsServiceItem.aeDesc}; + + if (@available(macOS 10.10, *)) { + NSArray *terminalURLs = + (__bridge NSArray *)LSCopyApplicationURLsForBundleIdentifier( + (__bridge CFStringRef)terminalBundleID, NULL); + for (NSURL *terminalURL in terminalURLs) { + lspec.appURL = (__bridge CFURLRef)terminalURL; + OSStatus s = LSOpenFromURLSpec(&lspec, NULL); + if (s == noErr) { + return true; + } else { + NSLog(@"LSOpenFromURLSpec failed for %@\n%@", terminalURL, + [[NSError errorWithDomain:NSOSStatusErrorDomain + code:s + userInfo:nil] localizedDescription]); + } + } + } else { + CFURLRef terminalURL = NULL; + LSFindApplicationForInfo(kLSUnknownCreator, + (__bridge CFStringRef)terminalBundleID, NULL, NULL, + &terminalURL); + if (terminalURL != NULL) { + lspec.appURL = terminalURL; + OSStatus s = LSOpenFromURLSpec(&lspec, NULL); + CFRelease(terminalURL); + if (s == noErr) { + return true; + } else { + NSLog(@"LSOpenFromURLSpec failed for %@\n%@", terminalURL, + [[NSError errorWithDomain:NSOSStatusErrorDomain + code:s + userInfo:nil] localizedDescription]); + } + } + } + + return false; +} diff --git a/contrib/mac/frameworkapp/JuliaLauncher/Assets.xcassets/AppIcon.appiconset/128.png b/contrib/mac/frameworkapp/JuliaLauncher/Assets.xcassets/AppIcon.appiconset/128.png new file mode 100644 index 0000000000000000000000000000000000000000..a3ea0c6f9876766cde21e26f53ea7b82a623f049 GIT binary patch literal 7291 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

N8W z95`LpV_M^F!7f#!gRxY2VpXa}0(?_~svp=mkQTZXtSf*ODfJ@`l;i=Kk3kr*{%7lc z4=mmhF6}lYEiJ3;FMSJnzLd;&tL3o!um_#S;{Oq_Vwk=v6K}`|qGLPbd-lz3U_VcB z!{bJFnGUX7d)o@-KW=x<_6G~hLYHQoSeFu{r8!|=R6fCyoH z<2(P!;;{=o1*E}UI!W@qMgW8p|L)fuppFC5q?Sp^rq^mwS*|sa2a~?|l0MMP5FGDn z#Y+&P+#M#U6w4T3cr4G+&jEqd&-mfL1z6)U!T~1oHU~yxY9cq9FfWqglGPdqi7U# z>!BT1 zYWBkcp9|3N-&p{LV8i`;WkycaB+M}%9TBddZyXM)#|9O_C62Ob?4eZhne;AeO?K?| zd*644IQlYe2lKx^qnr=VPUlG+PvwZ#IbUe+h_ah5R%q>vj5@U&>GbL7kKR!o)c=17 zbM<>}%?-0LW*FsEYPJVN+{fQ2SC_ux5jeuo(9nK2*Vk|ZWdDbk|2`;f?ze8;*tA0m z2Il)TO!~31n%(6HxP#5j&EZDMwCL#Qikq6$Y$YW_zb`6>>-1=ru|^q! oq3-{?CmHPx%c}YY;R5%fJQ%g@1Q562}433qS(y59~1<_!{q%kgx3$-iDf(;uJ7bY%=NyDFT zW7wFmFvOjUZW<)6tx63sF)WJ@5?YW)LZBGhI+FcFZX=ZP4?|Z#o*ID_@Hue#c$xjq1pU+R#YBkLx zXFo25zIr48iU=2&?DNqsX9! z8iYyq1hlrHW9RiOT9s7@S%&&>4CzOYDM=aMKKw#uX~&-e^>QE5Pj3Y<|8qE&Wo;f5 z-lMfvhTy6w_4>j`w6->Ju&tr;bq9hUNuaT-V~xm6)7&H$hkI~XtyYU}tLb;VOHCw> zuA^hxAZ%Cot5~fL7{gd5a0`XPtdvR_oNmy#vx7tEy)lXl!~N*L6zPx)cS%G+R9FecSKUt>RTMuv`@IY7LisAi-K7wrsMtzTsTggG!77POOnh4E6A6(T zUQKuv(g$8lj5XC5ANmJ0!2~fz(SSg~h87U0CBTBP%ZIzmEX(fB%#P;_JHvh#EvAPI z_ulhyf4{l+%)Ms;p0cR^1HKw#a`7?Zqlv}y*zV)Q;v>X#M{15rfWg^HdU|?V91cfw zWo6}={QP{IR;$e;6prI|yFMcZ z1QDarsF+E}Mn2?AF%gxJrettfX z4#CZChy!zX#9O1EiC^I}INvXBvYvFVB5p|lx9(#kK--uilu*f{#ek36+uLtcRaF_1 zjl)Yn!?$<8fCuZ7u*q(~hJOv_+z;UH>TghBDuyykc|wTQYSr0nwq|4(86O`XLt{c- zq!7R*d#9tL<4a7o6!1CE416>GIq-ZK7}dcGsZ=Vd&kG#Py6?mDC(l8i!IDfuT~Sfd zh$N;aCMF)n8xxj=lBTV#?S|cMH^)24{huBUfJzkQVM-g}L-4~we~O+I)z#Hznmai$ z;|VZ*eSO#K>gt|LI|C}${i~TwlAc!MFTX3J56w*&=%C;#9aNSyH8nYnMmk^8iy%Dy zj1k1lJ~TIB;Or$bfn>0Lg0bV_Q5x680 znB9QqBV2m+U3l?SeYPopFbE5s=sr;{6C<7NStHS?Q|sVbeLK{Zo`cbqUtlM&B~4hE zQv$C!-h|rHI_2}sHdhRblFcXIW*Yfoi4Wx2T?nnNLS)keYLgiZjtbBf6u?X6FH29n zFZBgZ0G}rSTPr@`LOkT0FhY^t3dTHrf@=&*!j=gnIM87C_et35zk>u^(p!>_F_|Fu zY(1QGUIRv_Bb6cqI5n^cEBDBb4ppy|Df1{n*pUhFbfx0Bkc__Z zQCR=_7N{6J;W~Y#7rNWk82;Vky zl4Mv4?J<_+hsS9Ku#D>e`-e&P0^pDqEnf*$O;orX=TEbv5;&xC@Z}uakJI>xpGyB9 X!E})Vb(~ux00000NkvXXu0mjf5tO*P literal 0 HcmV?d00001 diff --git a/contrib/mac/frameworkapp/JuliaLauncher/Assets.xcassets/AppIcon.appiconset/256.png b/contrib/mac/frameworkapp/JuliaLauncher/Assets.xcassets/AppIcon.appiconset/256.png new file mode 100644 index 0000000000000000000000000000000000000000..c13e5c36cf6a23796bfda010362ce86b7be96e29 GIT binary patch literal 16262 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 literal 0 HcmV?d00001 diff --git a/contrib/mac/frameworkapp/JuliaLauncher/Assets.xcassets/AppIcon.appiconset/256@2x.png b/contrib/mac/frameworkapp/JuliaLauncher/Assets.xcassets/AppIcon.appiconset/256@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..4781fbbf401d9af50918efe866b14d967a762f55 GIT binary patch literal 40480 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 literal 0 HcmV?d00001 diff --git a/contrib/mac/frameworkapp/JuliaLauncher/Assets.xcassets/AppIcon.appiconset/32@2x.png b/contrib/mac/frameworkapp/JuliaLauncher/Assets.xcassets/AppIcon.appiconset/32@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..8fe905555b25c9b0979b6a90da7f834f92db2fc6 GIT binary patch literal 3368 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�OXgFghauJuFox&qyf#!5+T} zFA~RUVByV*?C3l3DDCQ5{3ti(YW*qS^^OZ*Zl8spt@UYe;L91iHAnC4FM^8wQ=wV+ zz~~n2WH(}flq>Lix$-k-GrzegKPdglToG2?!+?Lmb>w9ERPo{<&cKgX%qZcmEk{Fl#Nu5_c$TVdJn@uTmZF z(W?{SV>w6N2K-hSoHXcna9qN(06{R$!gDyd13VsNl0pM_=kvzcI(LzS7chLW5FdDy zi=^y^)Z6Xc^#W~?zd$+j%743U^|7`{ELu;G|N4T>hy#cjK;X4=YaZ(NCelK-p7#=; zu8+Q9D0pZAnJX8@mx@9DhQLyAdQ_Z#2g}weW{HOUYh@dOKx60e^|yY&Nk%rXKPyQBnDoW z+i-NaN4>idt7yg<#289_l|GPD5=Ij^yWx7KR^`Iq8-JId--?MdclogCy0?B25ZM$5 zGLqvwGugK14kijAr{yt8&~GA`ru3Qk=u3lYN>yNJAj**b?Nar(#^P1LWwdih8B9~V z-ErmEuCtobm|yy`sH{Ae`A}2acBQK|dy!9P)TPP(VptHGVyI z<-NFn*;>Wx8i?diQNvXqET-zz2d|eZ5D9fc8g-ri#>jiM4XLi{2I8O__To}y-#^B^ zhEr?xO0N0iuuNMR*f(>G-gs;X@mk>{gA?@A5iEPZ!u-%(W5e;aBNKvq{Ncw2(BA{B(ne3Tjr8 zAva7<+ZfQT$ga%n_ZGMZ&M{03&2uPo2ne(BCt0`@`xA4r?En73n z@C{}7ND0yvVT@2%<@*NP^42?thPC=x;7Tbc;Vss`RW&G;*9UKMsk;dSK~8;X;`B^@ zW1Ul{!*{J!fR87*ph-u^Q4N!dP+b0wai~6;mbdYGJ@r)H#qtDwnM(7X8s@xnV1ebA zPcnksd?|#u_v4DF%Z|jWBiUQ%E_+V!#_@#PQ zE|x&fDbVx{Az#6K{+p+} zEok|OIE+FYlPnL}Y8g&!F z=&^|tRU1P5%yIXld2ms`=s&xlM@-MZyL=aCS2>ij%N5`#xb@KC8-A#_tp-3!Y+|pcj!9MD0@sW`=k|l97SI4do3C)^-P=*e zT$2LDO=ZkA;-V)we_|l+(FafVTwA`*%t4ylrz=`fs$%xR`Hia28JB~z!Hlqf5-D2h zv3}EqvNxfRm+xZJabN@#5;KEUThG9H0;=;l5qtkzld}0XsqE8_GlC#yfg#hmf;STE zK3HBeQZ+VyCb!Y8bh2h97Ps0p;p=eiAm#i&@Xs{K*gXSF@p5|lrpY|>Z8bFA@-?}M z)yG{5pr+Ucw=qqSC3MzrO}NlGkk(=E?icp^-?;#KY@TR9Vg%{$8NGrq#@rM>itgsmlW z`}^Ywm32P9E?Il8=YE**oAIt{nE1M{Y8!+?;qjg*2|DdUOWbygQ1?M;Y4+jl|+!5WbCXN#A6@ zB2Y^8sTTE+7)^lTK3+~?+L`+KJ2CtSBsm)&pQARVZy|!y2-3orRs&y3Qon1mG(*zB z;54PD;OQb$L@*Ipk+e@NGg7^W-6X6VT@>u6l}AyotpRJ0(3X{uy_qJs=JG5NXd=on zw`TToZfG?Lu$oqK*O{uNWc9m`5eRVL5oCEaEQ+I}vymN;k`Gl%^|3ZY=RsA=?KI@a zn@Ak2bQrH4xrcU3j2UZIJak)oAUC+ko=pNLrIUF*B2s8^WdX}DCkcN02>rl4NfIvYiolICYo-@2m(oCY zd)}lkb1gSjGI=qD;c@UL~T&Fq7NgF+wOj!4WaKTq@>B^qdkUgu%fP zMVh5LujoyFbbGR7^pTWnYQaeGbVOE@Q^OSZql=ASx;d>QiA|1q38CQ+j&V-Th{BmL?r4H^j+i=v&(((1Fu z(doeYL|uvDB6PU1hn^H^u4ygJ;V{|Qukn;bcvICYR#amnM^6GavUcBOAOV@@QBlgF zLUB=r_pFKW5nzTs3{FnB@>Wvy>t#S=nTIs2$Z|AG_t{c1%<1HUWgJXmb0zz%?lEfmjd|@o2;N(gM6FeY`>`w>y@pH6xnF z#z*qi7QYs(JuWEr%j(Nc$z!04$W zXbP_Q{dpQ*4f-T%L0gFJ2~5x;S`DuxGMt7PG92^Pb8Pa=#?O$MOP!xA9?pS{^&w42 ztu2%VO2t0QQa)@&0fa4wCo|1%{xlPzU-Dn}<$QO8MS+!PItXus;*Dn|f_)^#8--Hd z13Obk-Dzkmub<-ej3ox-qeK)1mx^W5nAEMx2fv2?aAXJ}jpQp-C^!P6CxiIE{2X;{_=cE;u7QDp zlFNh7r@!(^Wy9e#CN%IjQ;Pd~mNXiMBmnp+gQ9wu#oSX=g;9Wyp_LL!oxw(GT1C;l zLQ#u9>GAN4zfrSmAtK5@D9g!Q{1?m%%FFftaRSd|@1cy2ZNPc+qSG|Y5Y8APMoT1X z$Vp&~6x)<)V3rHo%0&ft=s3vw95hjCVT2u?WXTj9%*KGP$lZG9M#ZbcsuTik+ ziVb+KosXX8rU8^f>CN`o1n2W?#(EDJUGD)k7BfBPUjsINo`&CQF56P882J_I2kj{~ zlJIr846k>hjNZ6u=Q}J+%N1p71-ZV_fK!_vdU_Vy@Pey7* zff@G6VETs8Kc1u)+&0SZZ8ktl#T?rsCUID9#_LUk0SyJ=knn(v>4I=k1u&PqO|~za zC_~uCy+5xuAy}+wbCabszx6c_1fkPN;eDgmA>lSNVHN*O8~%F(tCg?swn*fsJ(sB9N0&Z09WwbpQO4szS-DP9P-3V1szK+ zO+HnBe4*&NXOV@11ALW>8|(^YL;KjFT5^D7p?u91;wTq{i%(xll$NE2C&riFY=7J~ zz)h@PR3`yH)O1gx3GzPdME8u#iaVu5KmRnexfbxRI6S$-k90nc_}CAP5F+ zyTEbmssc9z);zb#<|>00y@7>^dpSv_w?x$w+D|@Xao;*p3oes|rYS`R?dr|kxlsfw zZH&FF4s@s-vfLO(fW?N<#e-%^B*mOW5mA{~NGS^Nx(RabXWZ8V%5t@I_s)x|F}<<> zPD-0kE%mzhOZ?gY_#qmrRY9C_85lBE0a#10><;bZ6wEZK2VF#dnRy_{Fv<}tPGWQ* zEl#iUtUZF&HG``#1|nJ{t%?3ft_GX7>VV9Ns*wU#mc-Kil!0BiH_+WPx&*w~=USQt zWfUJjxW(`wUBy4z<;~L4(!HBQR^lfdnnHW$O&bf08_fc9lzu-)g^~%y3MXdf&s-|1 zdOdd5Urh}xB?$~a>oiq>4BP-2UJ3wYm_a+k2Zl#I-xVD$)lBI84>_fIGNfR40_DCH zrGUlo_zv{S;nCB-PlAVj<%Hg|ASTQxcgxeb^1}yhP4d7Y>Cf~D1ur5X03={3&kKw# z|Gf8ZF51N}E67d{2{>N|Sy^E-`m4h0H_t8rPh22^wu346S=(R{kIvGB?* zQH#&1Tf_rGKYL`Q$n(lj?L0SZAULvmk3G~pD$h_WkToxb-+xC7IK%lpli99W4}RmT zCK1&+CSjuA!N>OLqlS24|HRieJxCpdo;8R&VsNYoGfw`|H`>qUVMy>uK|!GQwId%) zN)h`Do=O00hKx>b|#XaXOQNUKBTDep?ELygsn^3 z2FPpR)=6n`W1#?_@dsV9c(PseSXh9>IxCynC3o^1np1kw4^Rk<9@}vNMzQP9)k@d#? zT?@pYr&hZs4wojxR%r97z?p0j4!?EvnuZP^8`IMQ?1sVV>2VW$Ka{^bcvl^e7M}A6 zGVgM@39?iRvhf7jUZ;3J8^%}wki;QE_8Qwc^=oY+_Mj3Xp8r_6-$yu{S9~TQrI@)i z?y>>F90!s+{Oe$jaG;cwvHBRtNLJq*F`$7Me9+Bgs0UpWzW+WUN7nQI>@?z~`0wJ_ zW91~!uF3(vON1(NYWeyMBp*_dllaSO%$EtO;A)d9CvHps=B@>lkkAD%nZ}rDKbTAt z0l6OBL@%8!Uys#PR(9JaSs0(Xy21d?)<>65Ie5)K|8k=4-Yqk2Wx$NU%hbS0Dimns zTJ&V|bAz=eO%O#3rJY&y*|G^b7B;7Of!lFek6h50@3*CRVIZgz_`MVvXQT%0EE;uZ z`|Hiq_-GO)3pktZGXa9Ax~DJLp)RL>KBiIm*V#IhXi6Io5#Lz>o1ax!?9RG+X4SnFQcC1`6BGQ0Mk! z*)$dk65x%NwyAtUd1EHeBPf4G_nQqAd-=-67{P=oAQacq#A)4v4VT*#+W!i{fsIFM zPJTJo?j=uZw!Qf$jwQoa@BKY5d?lJ#O2JJ5lKP+s;M!(-Hm%4-~08pZFWpeAA%aAYC zf?km(7ef$`2Nk^RShmMj;Lh4F4=}}wd3pX0E4a*ZWF-k6?N;n1nONy)g^>Ylae$6$ zKDT!?vKQELx;!wiM+w}qH$9I14tV{9&t^{TQ%l?FSpfj=0n+5H)?F7$o=UlbtdoH$ zt8sM;3l}vqRU_kr59>hKsi%MP1XUaX6GMVqLD%hU)O$*zLs)zo0Swj~2uOKl13UZS zg=_5U>#d=1oI?M`!VTew{?>>Oy8{w;TmJ%QE>#B?`GuNr$D@Bi%^Q#Im4$sT-svp23B#NR9?Dg8sZOQ;p4W%l z8lEB_+FH`J0XmInF~xs!uDjoQN}{x1#h4v(4ym|HwbyUGNOSxlgC(U@s3imLqQt3o znWL#b&1Zw6HTZrST0mbdV3nvAnU}>p(!)TvfSUF=HxyYYV3ok}B+PR2_4Qxvs+CIRLS|!x!+jS2LuoE-n1C{u#kocwc`(}ki zu}t722H=vbmC3Meg0cWYZb`)@w@#qoZ;1-`e4#e4D+eS=xrhtyn!PO>2|ED5V2cza4_w{E@bi`ed zNrMLV1>J&Kkvo?tq;ScRG8PAnKYw=(G5A)$-@Q8yP3;DIaTh3kXG?yjz|lZ>?TPc! z*Kgm=l_-IH(U`Enm)4Ff9K?9MOh67Ae3hJwP#fQA4=Zvg>wHv-1^gpQGwIm_urh=A|K@VfTA&>RSG2=psFfdDKT)tKORDmuDA_tRS72@%LK1h79X*~_op z=lfN*&O<1DC>)|ppKLXAar9hiHlNxVULbXNY$$$9P5S6>41Z>_RwE>@8Oq4r&%wVF zj0cNf4Qx6L_(FOBi{k>!tySb+_UBP44B8xQ++(18f1!b-*Ruv~TVD3j3dtHj1(xv4FA*@R>fXo-rpV2jblM z9#HEzmYCyn{v{DG5MUsPUG^uN9_QT;&rSm2KMpQ$h4iaJRaaO|+PDM%vCRv@qeAgl z8df>?n01@%1!XCq01rk_5-~awxTUE-AExvt{$q16mH?evCyAV)D46SO0=VdPa7p>2(l~+7fVGT1G7Qy)MSgw-UZ+&_hHG0f1rA2nc zX`^1DqxWTq46i}vvK}vpDM_~Pqz`gljpGQn10uqvMZi-L^EtqmNx><@YRn8R#0#A# zeKO|?&T zVt?P1^<+<@BqHpGrgwd~c-?q_(93A*ISq*k{wcwBp*V9_FIYb`G4M^Wn7xV(RvHPjx7d@)8Y{OZWC3c+k5M&7InPbuZB9J)q;8#zFxuto2ZV&M8K)4Ql10?(# zWfOTz#lFb0eqsYQr-T}=uPH}U2fU6(PFP$#-Ksqwgz{hr67-FVc@p7mBf*?opJ*|c zeuKhaAUIcmMSE!Vc9-t%o4ewMeRoJ9#hr}=gKHjp3a4QPT0MTtN}nddzoV9SSxl;J zkSevY{iCaLj9Skx;}ee-q{s4ex3o6sE-jv{_=t`rGP=*iQOjxOnSt~5smG8$!wR8T zx(iGMg8Qm2Qjq|ikK~VVp-tyEl_C-^JW(`Qf&|>Fg>gm8dpd?cYlRs1oQ(|7v<1a> zdfB+5%d$RUus=Ox{KT8e!QilYH`Ag#&xKqBng&Nznj5KcVcTW2GTgYxj&>u72f{i|3@tN6o@sG1;3h@wwZVGD+! zEvw(R*ziJM;bNm7aJChG>))nlwi4}lx!0$6ysigoB@SFd}9Ise+fR_i$bU#hAbIHbb^1w=!6tXFr zFob%T^O#TA=!Tn{+4qf3E(O!M@r4)2s0?5Ad)_ zBCPB1zOD`6*6{~Ka!2N#5?mKh!y&K*-+@$*%!Ko#7eb*Ftfwt(3@9RfDIQSyRnqd$39p zpoq;8XUm-Cp4)95eMxZU^O8K#kdfL1d<@?}50iO7g*+z-Y>MZTgM)57=0cfR<`b0>luL_2}ApaIrwk{#_= zXo=Ub>j?V@!})y|qf=>LV=O5w%mR}?Xa3o2PeTZ%?Oxp%MLMsidwLXU9$YIqc1dA~ zQ;4ZDx0|zp^RBbi+j`xnH70%@2gD;a@SdWBRY1R;9~_{eIPvD@xqCnGFscD9$w)x;D~n5=~vR@#b9Vg{Hp=dUVjSn6S93M&^TMczOEfA2hCTxoeEb zMXd})Ic!2-^WnRuvcToc#02lg6UgyWR8hN3`@XRKG$AhFc%iNJDKzJ zANn&qB^1_tSACP{q>*w}1}Q3Nex3y3#U;_VIzgFYr7f~x{4K|KjuhZzG>YZ9qpgM0 zqv+ZjmBoNfDVB6h%Zv(*-wCHo!v4(y=mD>tJjrgr5PW=uX2f{al=EitB8{t{WYNDH zOS`2e6sIAG`K?6xQ?Ta(oD4ep^LQ9!bb+IHf}@`u0h>Mj+ugRw>rZ1H|5Pvt)CDzh zxo)kcRl3{SseGqhy`?A?vwI9vaIf9w(2pJunn$+vTCTsWZvq|>@Nd-!_)flo5v`$D zDs(6k*saN&Q-|+ne_EUirOko(Lwj;;)-U$;{y^I;?_Ax5#lrsbNl1KWFa*t`stMxR zl2Mh}lF%bj_b94tAiyoqg2XwZoO0T-6tGh)-kEQ>Eo!+s@lwLNed-`P0So#`mrp*> zSJ&y?up9eoPNEu-e;ybNx9PgY4ocAd5zCLMW$2MUARUGi0+EwqsZ6D!IxcmP*1-gw zOXI8Jt*WW%YfBW_G{1~KHYIrx%x~UBby|1_?Rp%pK6wKZrc%Zdt|9Hj4sQQykQS7; z`gYB)+Z23voeq~4=_w9Gatw$h_h}N?f%P-7qYJq3-av!~pf4U9hkiesN?l+M|2a&i zXES<-1NrqT=2?{OnFB?a0Tqvrzau+@M6-bp3kza{jQ`l*&KF;J+@Pa??J9{?akD-^ zZ>Hnv0D&-iHi-9(lWCXAYY;0t6DaUeCni=SXKv+A-tWx@Sv{pi9^|ptSforKOcxs7 zuh6%OrAnCoy?Ez5w<8!@8vz z2vNV`|Lvjz#h^dmw>UOAPO|H#;6$>|4Z|wT0?E&agVSf;ZY`%=ynGVHf}upof}%SF zdQW#fNZDNksh_gjt`N@e(ay0f@Om9Kb4{XqoA3KS+vjLFkd5E~@+=by2C?G}!`vzI z<=7tq3b4d2GN!-TsR)N&@_8J#1CsQN01OMw+h;mhTAn=4f`48i#f`2$#9<^j0bPCU z0dyMs*T}57n0Ao;w^QyQFxq$_yhE04;U4NDV8~daHjiE=H_GXDDw5zzTXJSl-46D) zKr1LBl0|K?a`iQsQPyg$2orz8mis```a>u7CSV*>q{-^?i#g*yarqw_z<4+MEzb>c z49*CoAIrq}(M6vxUu!2qk>KTU%HaGFp)oFyv(z~Za}h$0JD}L(OYL)D<=vC@bOKf$ z_V7ny{dwp;Y=8$mrsge{CbVr&r>W_`&NSp4yRK9yx zN;k3L#o>?Mz}$=rHs~ELUTAd|E*zFQ;rBh&m)1j)60m7b(}K{zLDL6$YgtNASUwST zOsf zcnhjp2<^}6jItpc9N&tDWJ-Z+i(-A;n(CUHB;jM6(rcx&36a#RSb`h!%dIJoJC#aH z+r7VkZEPovt65%%AsD|pP8?_gh?OkRxveQkB1=q!byiP~F1@%$`QGv3U9RIAlcLMG zZ{_SB+uoc;kJ6%adtb#-eOko}(*cNOKz>>+{kfW6v*-%)Ox=nfeij2FB2EFrnt< z!TsIQDJ#881`8#xY#EmfjYAdLg&CBwxHyzD{n=5){rKTbdIMgNb@*`g?VdRA|pNMNKAMr{8c8=LhJn?YLxNFg;eOe+W;MV@1O0CRlz#a|5eTZ@CS$B`Q*e%B-!)y0Pc%!TrENR6~Kd4M?Clll*e}BJmL>LG<`-Vm&X3 zW%Oac2=DbqmL7ATk;+qxz`J}A@zdC_W{SIBfB=O?}1z3k5(by z{?m<4E?)XuV*~zli?%<8jGiWFy#x%m-_f{IGt!XoWuV{|4q-I6PB`W7*EOI`DfvWRgJ zel5L&{607x75C&ea_(G`e~^xSN9t1_zj9^~INJtQMq!RX#@BuFut2k4M^4|yiGDR~ zC6p}9K^Yg6)9d!F=BD}bUVm74LecxGhe{(i{_ZIV`_~0%n9QBG zs2h}Nt&!?sHL06AwchOxCol=fj{0xy$N!hLh2YW%zr%=-}M-%{FUKlpFS58NukZm*8QvGCmuEz1d}HX%whP*DxNP z@dhU%d45Kz%f`9l79}NR|2GkN45kk82QU#7fO@cel*G9wusd#bXs}e)^($*&K%`}P ztI!@|#}+h22x-BsB7-xHB2EftVkW5IA^Ywl{MP{qu(rKS?)vWiWpeqfFRmDf& zv6vb&Z41=ScvkN*a3N#Aod8lD;HSADyEKqp`!WIh2^P!hR1n(W#xtHwA>0Tpi<1sA z*YRfmaPTbB}a@7o8;@4b3&(kHx;P#+xxVVFrh#Py(Px zRi6kCFVxTFJKi|qBeJ?WO~Ug7A6=&k6_!k%UCdjoV_yb8Q3FwYpET^ zg_4}WfTjk0&kR*!7k0LI3HY6_B(A)zG#~t zekoAzc7b>nR6Gq1n3fLS|@aXu&)2>vhKY zV`|+&_-RctbMCMmxNhG!qAFUfOcZ|=iu(YuicApf$QLJm^tp$&s*cO$qW@y1ZeR7U z+5BS;15EZc4VFGSI3d1y$)o(vrntMyh^161G48Hhj^-5#QK7OS^Pq7E8nHpBw-(Mm z%`tWJf6}(&yOs>hewpfSk-Of>4mRNeUYG#Q^T)cqmSxjch5PFYMMhNjI8Gggv8Uu^bwsk1s{Bpg(vkq zOh$<0S?}-Pl}ki(Z}AcYtQVP^Z?3IApD%Uju-({PXix*5Xx>a23BKS*yjd`XNrI@<91X~j0IxZiFdFK)5xsDfB4*-Ww)^+7-s54~Q!!+QahRJt zh{cWg>$NKpNgDxymgx0ycSWsj%MEIVwNS@?b0|3s7DxiAF74OrXNos+e1ZFCP&CvY zd-_4L%|P5Syh4Rd(;|6KmUl%zxHs>$POS+RfB~7Lp;ozfxJisLIC6wv-82Zd=WqM|>AG7xdR;|RWVGK1VVe9F4o`r>g9goaZVy@p8vmVmH^ouA z5h9{3EY*07ihDHq`S}^Et8S)Y&mzB4nZoO4t;u0Mr$#U(mXxz{k_wOu2n%{dGb!Kg zcZG0Ue2)%fSNn5^4R#}gZ|->TR`{89fNB9PRZN^lZ%>(+tc0J0=8ocZ&Qov+M@KO$JM=>_zOvgf>p^p4Z;bHs~+k17~928Td6X5YMJ|5Q{;kFn)=ad8)r8#urtdg7c0+yHn~g!?q`-pBBSoA zr=mdYn#`*NS^uYIK@=u&9xv3SAvwVlZlG?wozWj1^{QQ=T=DjCJ?ct)?Wd33R;H#@ z4b8WZK^KMaRK3Hfj3DULbM!s@%5}Xt`_%T_)`9dxq0t`C6c^qkS3A)3455df4}i-H z1%Iylglirt??3v)gzs(ee83GoY>oz$ZS<*$?K2##Cmv~N7LHhT89wIa-so*b37LA5CmtEXtMm=-@1V?w95&V7X7Cc+*^9iBEpxdEIm5CpqAx%lhSd{NG&IUvA! zfq}o<&&yoQXkpb>Wt@P042MZYGQtPyg^N%Uzfdo(3mWhHe)jLbG@^@kqsWm??Y~F% z!Sh?)9fS3&qc)8v_@f1)B{J+pum!vX?78E24Zau;_lBTbyc&E0P?`=fRR$Nq_poHd zgUG_~6XQnO?S{)o2SYB`csYzUwD> z5{RGsk*7pnK&r6U;gLbLi~Qd5#yay+lF7R%f*L6Yhy0c18}e-haL+@t)KhxD;gTck z!!rE>3`apL7(S`yowDcAzqCJnoLKVs6}m7v*<~|D;s&itKmk3L>OnW$a<1scmw6pn zI&*N%*$J2ecqOaHO!DpjBG!2PRWuvt!YdbYCD5$%8w*vvV^3> zu+zTuEa5T2dwXiPvMOXpZ}en-{cBZrXts9qOCS>`@9`mxz(lscQyzn+w_6+iFWWfB zGnTr)1Omfz(^X`ha-}Crt)MDVH~gf~g{G7{sD5?o6U~bD?)*#6YldHUJ1LCnHSSWJ z*URbZQA2b!-YoPT@9R2u@Zn~%VhKqytC`5RIjNfkM<5sQN&hI>)s-6J4<_F3fdLg@ zeM`2Ekb&MA+^n3yY1rLHf*t2#A*+4Kkme89=QqTX_!+N33JV5$KUmPzeY#uwv418G zCi+F95a)>Y(35ZAo9M`LWa)Ha3WG#I#z|ut2mv}O2yi80+j;DjRMn@W!DyaImWJlE-swA50e&si! z4$`{d`Jh01i+rW~<3u<0%RMUjiG)2}SP2>K7A%Igavav&_?IeTy{<5Wx+}30#6d_4#{lQogr%8M%9$ z*Q@561eu{vQc1$;%swx(j%Y*i6YTp{za?TXb?!CpOo_RZD`7!4@~+S{dh}WNz@;ym z``+(O=4Ty>^*Hb9=|oIs@i+u;zKg&d3qKar>zO3=orRT(p@{I&U%snf&nAHpjq^my z5!piQ2Jk@xcWXV|!)0Bu`_iZF?%=&ZF&(PEX!f~@LX8yl@QNQz^=^lM8^5|$HJGvS zneauyx!45q_03XI;`5y0G2N~<%I`Wirn~gtJt~`B{52c#;oy27NT$%74*W;))XN~! zB``p|q}AFQNSV{D$nwB`ao;k-1(MxK7R12Ki6hFJS7idXHkYnZz#GPoVA0Bw=0sUP zd4nw79N7ZjUx_Q)Lf^tSM6}8Ok|23Hr+t-|{`^;shQBLXfp@>G`rDs19(t40K4hiD zT=L<@*R7$P1h*5j5M=9#YS}1YR6d-&7Kv1~K}q~$RR0_hX)f0*pGjn`n@MWjztzqE zP-@7tS~^nn`Pstr6B(30sx*FkT)>!kC9DQ*=p*vf0QvX#=dy>wd5=`}3qWa_fIY9Y zYSxt>oKJM8oyryZ)uC)bQgL1U;Ds6au?sH#HYY$m90>~Cb!1r`#ZQfVa9bn8^Nk3W zFH^ifKf-w8mt%0J5q?zrJB9a!IJfwZ=6)!4FF#r*s9oa7E!ETL3#vg=sDvd_=dIc! z?1Qn7B>o@1-U2Ghu-zJ-8HVmga%cn*1f*l=?o?7jBm|KbltDVAOOfu7kW#5pKvL-t zq`Ra$=6?n~-+9mZ*Y~Z3EM4ny=DF*-uh@HEIG^UtrQh@nBop%HMSkN;dgW%vnZ1+l}xaJgKx$TK(`|?|fY3X#%Ap8bAhE&>-B}+S;|F7S&3Cxt-xpVAVh~ z(xe2EhQ!4}Zt9_E-_Hzssv;|`Iv4R%d0n+y(@J~ipq+8u?yLSvPF(66$nrNXl5E83 z?x2Skarsuwqu|&T8^tVRLyhocinHUrBH!TP!KIOU!L{guskP|i?GYp1>E_gm(3@f! z0?~%Miwp9n$YwhXmo(=I%x|%)dG82%7D2j5nDNJ-RYXioH4qN_Kb|!l7KIGoRYCO) zc82sLlqvJzCr^xxUbWC2vyzaQVo%VJcxd0kJe`TO*nBW(9~SYbT94&D&T#%|@S=Vh z%VE;Xl|?dB4q+je+~HIgYCM$iLLzN>7xS#izo0N%2>NZFA2Vo()!#Sv7GH0o=^u4g z7uBo<0}K$~nN^wgPaPo9*VcAxr(X=88GxPL98orCOzhFXpUhUStX02jwTl-gYj@qo zg*$urnPk@7&w|}$D@|_mdoKG~+@Ev%Od0aPv?$HZ=#OuRyd)*H7O9QgU@Z4Ijb+GC z|Ha9fcq)>U-`P<-Lb$;>=f@AuK>%?=L1B9L-(f{l_)5#l9BgeB#t160WbX1Hqk;-kCSUcdq7w};%z5!#{bB#@rHqJA-d>LM;$l_efZtS{%x zp61GxC(uDMM`emX0YjGI`h0)?2K#*{>T^$_%{v|$ZQV6r(iWYqWYmb0o?8nwHPPwm znpDfa7xr6uY1aMJ3+^X&+l~+QTeY|@Id4azhG?DZ1iumevs^1$RzNFBk2xgxK?k|w zipvAA1fboAYc!=4so1M_F6y^5L{RCwZZ5d<9S0rS1-{yX4kOI&N{s92v>%Wit!|#vs zH+dnlTZQxd0SabjXPFth9SYJ+Wu4!H50g*+bSgf4J3%x@sZ92f4{Y&vZhq6_{Z4N) zr2%Cz$4s8(a;4E?`Q^be* zbR`koyZl=sLf@HP8~~&vaU=NGhhM8dOCI~LThaME*gC+<=0scl|NOBM1Wc58iPjTA zdPi^Ia6U1>T?(f$xtowjmGF1u|M|NQz=&#bj^h3p2(#{*Gr)kqA$a^!ik{{MQTv|{ z0)OuVU{5#C{%#2A!oq+Py|{lr#-I9m9GvWCSKRo~e~q+BED+rPj91IjCj`}fH2zd? z^p3YI5-2&}H~|0sJ5UilF8BPQRRavf$fOPOVQp8XAmiUl{y#?*hyFmVQt}n2@r`vm zqaCE=(rO;D&Dq9hv%8Uv^Xp@Fv09&q8RKX5F#nSFMW2Hp)6$hIB|Aqjr zA?44W00Ty&{Dyn-K2*Tozz3Iw}Abu&*$J5+s;Ld`1j+lq=MCV< z0+@i`$y!9|A7nz}u(1%gD1dJVLmU3b8Ur6@B?~OGgfqBm?$b|0+jSV7kLt>~WYS*` z>%S)$NKf1YuIvm0kvr9&*nQuV8-onGx@dPPZvVyX|F7YY1Htl?s;4};N6s9J=pcwB zNtYl*?k`dE@5=t4-zEs4cC1xO_b@xkooX-w3se&7I>-K2(AMBTzy0suxp@D*!-v`e zUs?i?peXRV|F`i&gT9bo0+GMA%lG6> zwamaU23riih+E$Y1^(BQz&}-h=wmxSu`L0RUt3ndK-_$83WGBaKpy$eeFlPP0p-bt z#Enp|B`5|mTDOA=aSQ$o|9?M41(XFFg22evi{i%d)tFWl?SFws|GAa!C?Sz}pc0oS z)qiNosCht-tps)x{vUft%mowC=^SJL@y!o%wtB0AtI*E+pP#3u2us$`Wyv&i;V}6H%>2j7ReQK+Ikg4R$7Zf}>|> zBQ{!Rs5d>N))8)1OiBSH!P3aVmE&c`Gj2Uui{f`?Kn+{G&l(GUeJn+LU|gU;TeHjT zjh(#HUH%Sbr9v5&iWy>kYybCpgT-le5&GOzc?M?w)^2Lwb9?S5YlO3>TX2(Go?p7{ z8Pc2#owx)bej6q)m6UR5*PR*|X_}X{8~QhSda6AHe+lCFI}`veq|moSOn*x^>FQd` zZP{l$O~9!Y@z}F0B{_nn2USpLgx%*B0P~@G=3hz9Odqz}d>h|DresS=K*Le{0>>s zB6e7@9}47&&;EH83DS&FxX&J~Ty(qvAdYTmfF*R$^T07wFCvD5#;DjurKU>eGqNL?!BHF#r(X3#KcIH4x<&++GOR_O;s=S{g8hoa3&D+HG*v6pDtTd&tW7bp`35V3UlXIn0@$#8|%2pJwn4R_aLXFSQ{m&Q4zCXMt$g<*C_}J+*jIn=%OXgJJeN8af z+Oa;g$f;0z-7hm@J{f17*kQqO@{)}cm0-j@TNTeO>{=N4CjGX@H(YCp*W}cuKgeOF zK%s3S1;o?Rd3?lE^P8ccp{PZ^E%dxct)-Jwa!;G*(xG5h#}1!jp;gtHz-kuLO&EvE zc>cXkIoAOG7GMCuy;{z>1)Rd%(pZNZFed1Gg>3Ch>8H-lNuHmzZPoC*xJ~cH25?C5 z5`+_tuWTM7(KXwZA3u!=*I{#9kaD)zohgZet({E0bKm;eXU`&T(axmrq|fBZG+Sov zBww9JZAAqE`ktn+?rY2>o(pP~&Y}vXi-#)*x@WkX!LoA-r4zMpxwR!#_wHHMO849p z4wG?r8Lv~b#5t0#7d;L;UUh7?l*=@k&C>0L$C~~^{n^8BtL%P!V7E^#5KG9hWy{Z@ z#XZN9Ws*(pm}0;cidZ&%N7)9di~&+Coa8k0^!eiRQ>tVWx5H`?`M_2F{t3v+-QBQd zkd`XuducE^Kj4u8k`wb4c|w=%VpP*1U%#`+G84+^>dZCFjwCCFh5N#ryfZc;u`{I#_5QvK)?( zI%qvqpl&`~y0`JtSB3kdY2VhxYm1yM{18j7YaNq=Me9W&I`D+CoJYbN$TwI!=kt0b{n?AcFDr?dfCx_v=NTP9Uu2fDcdS{I5T<${YW z`VZkozao9Lgh0EGK3w|;QBujwT**ZM9xf+Ycx=qTTpRK5oO~;*(Yfy#pzN5k*C?NJ zJ4A*nW|TZK;H`>$7e^dLC*{X`uzSqX3q56+wqIu_mFgt=^S)%P;xj?$>=gf&jiS`7 zo(sucT#u;wT@U1l?)GvL^#8eNxA4qdUv0kB5+GQ$(TnytzC}V=2FWFNi;OU`5 z`j~dS6MX1!_0w*op7B*>Y^mai^V2tymKZX zzjj6%PDnU3$Ap3q1$nJtOyp**guu*~(Jo5eBq?|;cM z1n~D-_K|i^*MAg;pAuThg97%&D9Ro22G8A7fj=p_c2Zi^-J0YjMjRy6t&L6lABvg+ zh9~{>S4H9IX?H1Uy3&elZq{*JIiA#vu!%kLA_`!yIrR@Uy6<~eXwVuiHV+S4@1$NQUVFb^3-c=44I$81JQr-G7j0mnOq}>lH8my=N1aG0*o!f+devM11m&NF)jsp8r@FFLYSa(XR67g<#2m| ze2@SqDHJ5(LyV_9nt&u)DENERw!6C0V?60Lrj(;&MiQ-H1)xk7_n2mD?7b-GYx`Ma z__A6{?85s*pOPhYx-~zxa4LD3#NTFHg0{7@QiZ$}+ zZzZTW74ev(f#z0&X2fm^PS9hjWFAl+TTtIUSZ$KKl?bj2JenA;@wdS+!7Ml2*M>Dz^dlzkHrQ-&&;9?Y+44zUtNmc z7yjpF=;%QPTP(zn&2vsshXEY|ySD=3I&?WDr*_IdFdwhQ&e208ZQ3|-+Jl*4%$INb z2y*T_l^oSo=1sbLFN9rbh@ zxK=WxVuaxBy;~HUQ78Ip5zTXxNXGe>*7aE@Cv9|1Dba$95#3cLr2KHV z8Ag6qsPTu~`te9guta}bqd*X$i!3q*N>&ZMI{KQrUXry{u%;kc7Uhx^)zeY!di{KU zLfe@Ez=KFZaI4L269X`Yt5|?EryU^R6yg0gZTUoGBvJXa z0vX$oOBBRTryWVj(qtH{O_FvC(rY|0B+?w>*gtbm5dQ?Yo`iu*|MjuVizLR2uK@ib zwV7K*_tN)!mejfYUc=WXsV(@vgkZTwlUAGC8NO{;rN!<^?ngt!kj!HBIiNGu$X)M9 ze*>WfuF7uUR-&g0YPvH!_55`A(Zl*Xu3jyUD%gR!Bq~ZQk;t zP^_F&*85?jNqPr(iPeGpir;IDIX=on=-;)JH?4daMb4%n_WUPFX-ZK?UdXc<B!47`}jb zj^n+ucer+p#AoTW)gv-N&}vK)D|z^m&(0kta8p74**RVD(b#xGQLZ&u8xDvj1nQAb z9E1_QSZeGim$(=Xg%@bw=^%>h-5%m#C4wk2liZ5s=7CH&WDA!|)`k2$OHT~H>CM3n zxoF>62gFOe85=BZ#nsLZx}Ho3@%fSej@Hsz*?ljShvOof&4mt>M)o=`p3Hf-{&2DM zIXa10RQLC+dp41xiRFzCnYVp-zEm*w#ucLov0P4{|f@3UY;cp#9YKJD$GQk*xn)}v<-m8K~? zw600b`cwFGy4mCWXw#vWz04|(dtWsB3{4%80P-}-F+hHfW;Ei~MY+B0A{7)*3rM&O z0T+(8^7VFE&GDIEhwiJU{#o|NC_qw}2KrmJn$71-z|DUy%^Jt5D3E?3D9B@%oT;D% z`#RRBX|tKa2t2{GlwGVg&+cc4}Sg>S%{2<7B1z2>dAdp1L5-;a9 z9@Cp6?b2(ob$swgHM^ZQ<+V!*t-_Vjx+rGG%yk&>mW|GwrbJeY3LoUeyRga?h>&>ulD4*@1SA{s;G)gRkVbY5`O8jN?&Zz04+2G~IdI1$7;02MSal~m9uD#9rqyP9iF(R5(JGP%a7P7`^TE&-l-PUdVbCtH0V-iXIO z^h?!m)|Z;${L-Ea9B|tmJBk(tP|)Q&zpuTz=X8EbY}xm4-_n9Ed$L}Q3x&TFwb8+z z?W|dXuV?OOmtq+S#OQ}X#8L|V($5eRT!8HkL8lUdgkwlj$DOBQgBo|tR{*~Bz1|5~ zq9{_p5PB>*S>|LiUBlF$r%nVhJaJ(5hQ%ev(2`r@@dkl@2saHaV z-dMdM7ZGH*4D90s%Cg$qAMR{-MCg~4-m~p($`@+wl;$q6zPiztzQP zfMsi2Irrn&Uc^j&Ob)32J;?{GNeW-seq}5PJ5>b86@OP`ME^5C^izl|jDf{m*`Ytf zwJ@gg62E`}yYK*wHRPfX?DC0bJnigUk3}7srR+169`gv6=v?D>(()$9fKX3MNXo#D zrXi?XkGj;tZ2Yj-E|@ME82mR(&0=&NUOEvTH?;og3avX3=2wX``x)u{oOS31=&YhB1zO|F)+ zC9aX(dq!o>g~Of^kPB_)q>f_jae-TrdW;Ws)| z$`^ZQ*J@MLAQK4}d*JG;mpXe1(v#_DPic#HxofVaMbCMbb>SAt^ z4A=WnscW49C;V?BZGfR$p*6w0V*I`W>5#5V_8)s3w@f&03PiAUn65#4x2e(miUU@X zjOfA2mQ%K?d6}$U(Q|TfT|0T5t&@JExRn#=2n5j7+>JT@5_nj`C5S;vV1KP#CK;Z3=+sVt1dB&8#r8|In#v|Q5j zezuPYV(D{07p{$u2L13W&2_nVu7@#QVGw%TzbZWKZSL87wRIn8Px4+45x`d+gxt7g z3dy|Ov#1tdxgDuo)L#`srbN+Dk-XMBQKsR(NY1qGq&GH@+Oo3_mDl*`tX z@0k~6@A_k|DLRUC&Cm$zEtTxPKa?YP=d9A@E@$dvZ4Yr`W0GeOFzPS`6f0u`@>`F%`vtWa0GNMqm#KR4 z&?c^zLg-3STP{vr*BkLc5NvceER_C)=*~XRldH~?%bly*6RDlt(`PHwkCYw0F86jV zA3bahS_9+Iu+5cy(Dlbia+tZep1pi1c9(y0+^LF^0t3Kms&FJ64v3&~w@$R#i&BJZ z$ZKgWAm+&rAlvjTYqxQ1$>(^%{s!5WqDYOf8TmHrvOcD8?QURdrO>xyAF)sKcjp9j z1>&Z_BYf+HhfNMt?^_Kj=R#UL47KNeiw`A9J~>lCpB&1L!e@^@H)2DYLVAoNdBMra zrKgL2mieo*&#?D^<{WUVNH|a%k-6y0tGz%m=*4SnpH#vu2X1z%7ALZ`s5Atl4~Qlr zt*yBbMSz~eIZEhW=WrwhFn^-615<642!D&m!~H#yj$2O5Jua`A zN~Z0VpXf(whui?m;}HuNxy`(rNeC0mtdC??fOF)@LUstK>*7R5*!=i|I&B*{^Ur$s zQ!U_wDDm|6k2~H*f@t=d4gqNN2XpnPGoBJlNo>gM?cX3b>;8QnM+)Z(HHn+z0HhXXX zW_juiM@-_Sf#}(kcfa3lW+PC3kgSYO$*C8|CM`}I{}1v|1lUvnR@g>yMJ+q^sCPXUf04`5i}>Q-yj z%InB5nYiV_-?Kv+(vJXAHkc%iTm^4A>kUZt5rInBhG2j0{j5`p>RAw!q)H-?wb5^# z`b0{$`q_&2V?+IL2&HtRH^UVMI;z3zeA_l{voLG0VuCa*;?0SgeZgHnL=p1oh;|vx zY=q(ZL#@6$#OG&ce0;x#f#}r!GA<9KkuttBy}YNAz4O&_@cN{cFYmIZrqxb8=aOzI z%o>Iiykd3}2ptof+g*$uobjGk+#4hXLBOWub9uejJ2kPTJ*~SAO7|C{-MLWpj9wlU zUC`6;zIRmfLV`&TRK0dfYS^n3VQ{`Z)Z54(9{@HN7!r~7eN5w6Vi|UT<(;75t~Z$^3cv&u*XnBgjCnbKgAGd zAOLyMJPCRkM^qn}+A(LN)3*T?Z@sP@*$cTCaoIgv$)W76wtax+uJpYDT^IwfsJO}; ziw~k_`?4?Qd0i=D?#H6}7zhTOT~fQ&v!3BnVvW>B4YA?hbX|QWlQ~&y7&v!0z}Jo6 zrX1u(Ul~ScVr;oC>b@q0D=u_}-{1@;Dc|WEg9v)CYFk?^Q{G61k%;=w%IJ*;T!l@V zrp{J!q5OX{0Z?l|f#sBrF65x^ab5fqZ;|HRF_FQgoYYYv1vs(?N+kIz>D86poBbWP z+ntxb{-V6!dlvD2{ZP-&l`f{IOiQWqLnRIWDE_{q1Q=XM7T#_q-Gg+qs93 z9FUTdn%m@1{4Xs4F9z4cURYq8^;qlbHB)K9wCsGrgDuO@&gYc4EU+poNXwUpc(wJV z+wEak4*lldc&td~8x8|YU~1-8E;dS$3-wL)7B)2xN}aolw*q}*cd+VM(4{?s(sb>& z>Naua*$DzNedyN^JT4&-5If?FH)xZ2b?L)AkxLjwCVJTsP{>#fP=$V5SIS6SE;ggk z9}>bo_n{(pZpdri%w|L9-6zqIRwMro^w0I=5CqEJan%AVGmT2h!WKb&u04Z+IL;R}0+ z35X)Coo?>k2@B}S49Gk?`GzoKTY5qTeNI+fOb##yln@ov7wdrm{;$d0)_r%yrsQ%x zacR;Q~E3YeT{NLA=JgB z;J4eWBV+HyghFzu0M-Q>%?@t^xRpF%0@>ew`p0%?4v$*2|Ps8f_-(-qpkg>y_DP8_c)w z6%lVhUrYPE5LdB`1u`#JeeGqRJ#!^o^WX=Gu3g35J>jo1YnLtFnjOt#y5jGhl1U1< zh!uPB!~&Apv%U=)X8C@8{sic-p<2zXoH=4M6ZqPeJ1}CN|7vSER?kPMG{0IRine-n zHpmXe%?faF@8KGZV0T&mF;qP5<+IEMf?s&Eay#1l^w7%~sIaYqz&g8VtEiR%*+Kx6 ze0t@&zDs?rN}17oRUq6 z=z~6#%1$u1wxPS2@6}(W06-~Uhok(tp(gV_mH_H5)7i{H?TNNBl`+3$^BVfgO~5*b zy8QhZ=OJABn}^T4gv7hD|u=_Vn|!mpa$mLddnK0$iUE zQsnGV?(rl4eteHPR;GLddW-jlT;X@P18rGUTR-inZb%4~_vnt&umo@k4i`U0v!<(x zp%XYd(=0|SrvgM`0T-41L-DKhV&JA?8{N{dn!BCi8Gt}hmw3+NYrx`E;SWgvdO~L+ zzyjcN63Nqi)B544g2+$OOx zOa|XO!TN-t%M|rge;N}3>(+qVMC-cw7ikmvmv_~oW3`V===Z42>XXbY`X)t}DB&W! z3dq?B_{=+6!-W>h%l9#to5pDMDXV)plBg;9+6$4e5#^f1>iu_!8x}7DP>$N~mlp2{ zzK4+aV9}C6T`n$&-)FYfDq$Y=2M_|w`CsPzGaZt}fqj3g5U`Y4Nk^&Wh##Fja8dnf6mLwSEXM zSfx}QfO4-+2!>`fL9*qKR{$h8K!3BBgDAJ|dD31kG8uGVYHC^=RqHh?13*dO%TH}% ztQtjgY+jTRUF=Mta!&MX`D(DInug>BBuK&)7Y>m!(prhE!<|muHC< zqohS+;l-0#%MFWfv$l&~#sUt0u%@3gJk@>6xiXOuG$bpsJ2Y{a6(!YpfA?ZmcRb*g z{`UK#FOmL7o_{&ZE1WtjZahk$d()*jbelWWKP7i^Y}u?|%`@jL<6i#R3@B`gA+2MQ zJg7Y4s<_$tfY5L1CzY(6ho~qctaPK0(-;zCc-~Rtn*(lFuo^<3k`G{#A(`X zY^y~L<~d$$?J;#;m52h*_LAwuU@s+0X5Mal4tT?sFZXUohPW-2+hKb?+>MDLYQBpE zWh-}up8Xv#NyOC`3Z`2*FLwf%*xMczxc9gPG)0@-;Y4%$syM)v6JPSA7^_fUEr+Ui z0{Y)&1dbGP4$O+l1)Nq!56c96re;KG$~cmcK%>c@eKzm$%Jd-g!aI%oK4>q_VC+Q7 z&;OQZ6!XLA)MwRATV54Ms*R0}T-%{|W%-}mgaW?nK>;Xl3eBuFSPCKb)Kt4$0Ar#$ z;T{l}9paqQZq)eu(4TFj&Qs_R)9$Zoz!)_Lf~@mf5Nv)ILJfoMek1Ga2I5_8y z4jdS76(RgkKEW3L6V>NA-OB2gl4J7#Spiqi8~`yE)MPk=<_M+p&Xo8#=rwMMO0!sc z=BFdz=#pRP^G|U_@ssR3DoHm4>f?ubkneD~$jA#1{NadIniB?+? zk8s_pAUvLXi~+JF|57MLUco9pP8q4Dn(f{Dc=DuCA|HqTu;0>>Gk_HX@LdBSO-V)m z`?ft$Ok3A1a~V{`6?P~oph8?v`J>jM?_|--4{beMXoE#1i88wj=Q!+}w&5&Zr zNejo`E!+h~E+5gamG43|?ktaug7NttIrM`t5JUmw)UjGl6v>hB^2*eD)LK|lcp>DI z4+IRo;u*D_Yk~o3$C+##Jkdu@lD&h*DY~0^#J#g!K<^Uh7vht_-jnh~O$mFnvtl1Bc#!# zB|`3Ec<^1vmw*_WnCyUpOv?96P&{R%rKNVuFb_P6VDBoYW0H_ynfC~*S(l(U;Ouu%%>OB<5tGwiDTds`^CMqh!co-0zmq@}0 z62F`SXJd5cDn9L(RNUrkffeA#FezJ9Z!@7kq6W8$8vb@Ri{6%nzHl3LCLb9l_mfdb z$(RJP)6$gwPNcTGMQn|!l~Msf_dO#H>^X;>1gM4kMKKfY5e0s42l-wgfuH(tRJ-^k7PJ~oGuSs}T+l5%cMOEP47mK-z+2{qCWN z8|j-DWCOB)prRWxW+?a7uoT#w0i>h=w9Wu;*F%`gT)E!Ce70d5``qk8grV>$&yZdocHkP6@4(vrj8W|xS}>Y z9v!`Y;!VAMG0mW-@#$Kfq6>l8#LZ8EXe6nFvEH2k5=;SJQq&+Cz)D9A^PuuDBMA&n zxlAZ&%y3%)dv2zUKouI`FIsVI&vFrj@!W5Fv?AJ~t_=dD`NR-lkUA*3k}PWMNz3L5 z#rBqlS|=FQGk@+n`1Z94OotZIL;)Mf=fxD=Z4JoPi7HFIN`B08$A8TGx+Q8dy|Ezp z*;Oi69FPm5k%Q^n^-3!ptH*qK#YeYP!7N|8V2_K71hnM<6+jGP5c!1)t-jKmCi1oE zk0C@-dudy#4cBrb0TCF$*$EU1M5ZZmca^SJysbEf2h~_}2?tw+L~vIVeA$F+m?^MR&Au$s z4nJ!0dv&DFviGB#_4xYnNdOxfcTS^*m=^Bsj7<%bu<#2JH~Ns&8t=vzxJ>L!PjOmV zD$OAHtyzWtIq)b3gt0QLl(x12w3#_A%fKsz^+Cr>19Gz+D$_ouY?$qWU<)s=KmMT{ zoSIU{CZgvWy_{=mBN4sa5*#`BgY`g9`qAr&y)un!B`h>B&j8ZITU@C$MeEW3v_VIb zOt%cSBLE3I@fu1QKkn8on%S7&35UK$OPm=&DC@xcSbE$P*if^*f$!y>x;lU)c}?}7 z!bFo*_bbL7eo(ko=7QWriF?jKq2|PneysMr-rVWw+MS=XJ^JT#8{MWc4Z>tN0)QEB zBg6Sf4WHDZA@hSW`S0?zjkU>olbDeH+fP z?Ozk}(3}3pWxF>nZrZ>vlU1AP{!v^k#K@sj6e#R7y|Bv&BSq-#?>Q`Fb1;j+m=VpC z@k{K^7~X|nZrs51HInU~UaMY7r%dWP+<>!zqthM&j%wlt;O53s)j0!gs z;{o((zg(hj47D8s6ozeek`AYWe-@S zq%Q+Lb|NP@px22)=uODaLh^(H3=UyN=r^@y5nZkaM8C`a9SAkLG`vV>EIuSvK2}1@ z834CVvCD|0PWr5r#)a^c(2n@ykn=NUD>?h&YtYhaQvDW?ej2G=Z&?s|qzSZ{pVC~r zI+>BD??@+CJ~|#BpL&V|w7G+42Sa-#8RzfkKgIW1+f%OQwZdJ^qbQoGJBBg*F8sV< zw74B`yAZqz1uK*6V!xy)AF9VR+Z)$pk@n<<*Rf(fX_Eye)x%Soj!29%{t-y!(Qq@3 zhM9)gS7p`%DAw_IUcG&00^!6FQy_|s2)*|;$aY7^5;$oz#SDIaOHFu<{1EKR#V9?l|6twnHrUqn1Rpjl4)VT9r!f|hH(IbZ6k&~o3hrw z?oO~O$LKgYIn`U!^Fabv8Uy$qbKO^DBs~PW-RcL|EKmUZMvE00Du3UrE1o*Ds3oIf zmTbadSaQp!zH(A~`^WT0jp-ise=Dd2N4yDNzWicY1$}9i4caW>PiZ~*@W`o? z$H<@ivJUL8Uf5Y$?5$XS+3v~fLX%G5F3{JSlQAHM%2uV9jUL9Ym%Yepxy1s5rY6^s zR8P%|&$BI_&%=Nx0Y3cc<&3NZq}=y;8S8huB487adU#4nyz#94_zjdzI^v0cZ{8^P zDA>#bhd@X~|6(ZB&NA?xjV&YE$swWP1q*ZSf6UczJUc)={&?y|0?-QrvdQ963_^7@ zcsM4J2uHKuCt7rM+kdK({I#gR2Uir9ZHi^Yic8agglL<{;bs?NqTT@T#*qr|w%#62 zt>msfS&l&>=b=G(7s_xl=*3<>63yRuuK~Y*htYQr6R(cNCoyrQvH5e9SLNtRt(x1g z=aA?Y2|hfR%O@1AlOu0#!Owi zB?Us*NvKc}#4;z*k`d=|cW-<#5G0Rnj@%6-`Soy8vwC#xEB3WO598o(HRu@XWhzCd zGH9p(5$x1Y2qnCZAn(-(AchVR&&l=#NT+y>(J z(emIUBBvgoo7v^zgea#p!K-IxY^MaWs`AH1S4N0w(KCg(1#z|_h;~RaoXYfnPi8F! zN@&Jyx>#@#LqxuV5p*Zq9Q6>O2=?ajjOh_0L-w@OLp)#cLtgZZdU#4U$OZ%R8NF#i z?{~bFlcyX@DZUcz`LRf zg!hhliGQqA&n#(D@?+)vx6YU_ot4|HN0|pP*3oy6>t?g;Xieo?Y~~F8*&h}oAAx8j z3+ScwL`GV1@rUOa_nZ`k81x+*LD%e@rvlH8#9 zQ{kp!OccJ+S@&k!VvOfw>;!ZWV=&@)PZ@|bbUVK?t`dJyU=_+quXZVFRD?$;@cnYw zquw(){6fV-PW`J|Y9ZVKvN07vbh)Zp*?WH)%#CKA!DFD8gzvsqAG|T75Pt^i%p53B zf?|DXt9_X)?7i{gw_Z<(J0MK>DGY=SIZQ2j4c;#nqXOvxxZHss9-fWgRP44b9)U!? zUU~45{ri^NkikE9#rGG;9*oZpzz+VJj~rZ|-rjC~-FR_L7TK^QtNajYk@J40z^7%$ z`jS1S(&RvfvzpJF8)6OoBFTgWuT1}0Kg)z=u5e@V`tlfJ&;b(Y$7F7*xcY>R%~ zf{o+uM;pI(Qhg|q%*7q{Z}Yb1FjCRNYNTj{W2@BBk1^?*i zh+zs(SxGZ{`)!de(6 zu@N6~j4CapmbhNUq`+q@GDce6!?u7JhV$QSR^uc^WPmQ~Ca+ez@B{e9O58FfNha!_ zw{CS%A4al+CTYzcAOIf^*t`*Nojtk9y%lg|K`JNZ`eBR=;iheQ`a0X#Itk(t4vYpA z3Q|Rx30}^R3l;cVC8aHinzM1y9}}I4Xw`DYP19oFu_!Webp>K{ zUM={`Hms9?C~c~C3@|kbj=^cT6j(ImBDZ#R+lV^)i;uiePU8B8M@QnwL;KfYtJAZA z(Ou&Y5AXZ)(EoUgjHkAYdpi2z(?3%LC@(d!kia^b=iA*>CoLZsa1WV`6pFFYtG0JxrGOeBm`8HzF zh1#`fJ&3?TGM(mluMbix^MPwm8Wk>tz~tg@E3{UP;`l)XG6!EOPl>~ub56FWMEuq1 z8Sa5cuV#(SHSH|Qf6?Dl5JI0{9&px2f^4TBj*+$YgoKIX2e~!Bakm~O3bRGseT{RS zDxp#f=%WLV1$^dLIoIn2S;vcR`>pC&TzEU#mq%4=J&yD66c4J8;SB|*u0)E3$d##u z%X1{SH^Ra_sQ{NvVJ?Q1ubIpD)UmFvVIPL}YtU-TGgfhu3yB(gq71$d8nr1OP`v*M z1;<>^&iv+e+CMf$XIx-bl#mT6TsP3ZAPa;64l9*05gA zYM7HlyUk!8lHKtUm3s-_dome;o5hKA;$TencHDWP=07J%O$D)-pyNmhI`RxHerl|Y zTj2J+M03p=5KnyT_{8)9GQ8~z^^eIqr`u!^wytl;^r7M@e5pr1S28z5Za@{9lO(PY z9R4P>{S9TazC;~b#|JNeVDbq_TqO}pFB?J~q8JVnQr<3gE% zrWyP?Lt&>!Efo$9pZAZ&{Y1FBm9lL@j*n$N?+E9j?Bzoifz@PZ$)@FyFs{fC#=!}< z<%Mhn3C&!aI=R3A!6}3iyjf zSqdHT8wva|k#l$4S0E>7edF2Z%<3T+l|gW^p3S zJ|s)g>o2{ZQgkxKST9U;bKR}u6i+33wM3-i6{JVl^p{J+{V+0Z2@p0;^EYytCPDCQ zxjoPX1OtAz=GzzQO)FqQL_Rref?k8+*fCtzt=}|X7;2bgAHHj>FZa8edcI|`y#4OK zrXm<93T;e(xxfC^mUPgJPpLP>s~d)BKqX+J&*vCh_6tz0i1#pDd*a>ehbG+Qz!$if zSmp?Zq321Jns>PDqw`yI2x^?_Svd@GKj-E#`vD08$nRCc;MiI1t&$`SYYAkgw}^?U z6}752+&8FD+N2Q&90~|v0X=DfY(mp3%_~RUKaYfIl~fh)=lIEr38pb`;|Ue7Ei4!x zes}qXS1|rN*=|oK{Dp{93}rXWw1O>A7A-y;f_Ra&z^bn~h79H1MOGZx3p$S$z4DUVx>~^2iFrk5%bMdS66j7-&%R#!r?|HCtI>aBK|a z*G-PoC2*5aK}JHcZh^*}7&VNNMtKG5oXP*7*14qU&C_N6nj=rh#&dShTZyB~^{@TW z-G%G`+b0rKU%~a=Qhm`u8dmB&UiSDK8SvtscGEp=H4Spk)gd=qhl+E~Fohe~;yyx- zZV?%LGherrmtmtKj{htopeC?!Rr=H$yCc^PU(G=?USrJ+o#F`1mj?kUNl~@<3ij^y zhZO%GO;_O%)z?IKmz9uCQBoS|k}hfKF6ov=LVA&q2BoAC6i_;(5kwFH=>`cYmF|Z9 z?&9zJ2j0GSckaxYGc#x2vswECZIzv>xc@&E7yZMJ%;@ste?_mm@bN!igSdrNd{UC*GhwXp$S2AZjt=or6_UqBDf?pE%PSw5 zuI1e)vu0uP~p_)-H5H(q_IbGBJM4hvObfztL=_-)*wvEjSq3ifR z*MbKZTb+q#44kc>mbUKhC0~!zIC&K$EOBIE^q)6ne=K!m^i=yrx{=Nk<;bq4)Tjo> zCXq}=bW7-c@W%>bHOqZ!_0vUy&YTSZ3sj{7?QK`vRLG(K|ACg(j4#!$6gZIgS$qQL zk{HN?cXJNPti%IgZ2=hFz$%vSqxWz6@$i|KIB^r%#?xjK+@6(13kWyXV;z5`igj$R zmOTB~YJTWAWRZgl;|>b=R^mbvJOeH6!0MK;6EJlYMNjLG2ES7?2(sJ{{f*4(* z{3Iz1xwbz#KbjmFAO9(*99#=7(1E71m%t_1HU4VJU3O0=1UF3|7ec-2Jmco&j)Nk{ zN@d%Z4|=2!Br%%E@@ze#LEUOfz4Q+vgFrN6?h+Xs&A*=?{VCV+(|)qlPzLPqV3-&-(3jyw z?LH<>9MQ##+VS!mS1nZ?cHBO#2IOow{-<8|kK48Xw`mJlPF|g-l7PLDfwe+3(CB3A zUd3J1l!Gk+;~e=kD=(oS)a0=$zN ze5_1)+&?4xo4JqSIEGC^1x|C%RN$lgl9&z2h^H-^PeZR0w|LFxqP^I`ffs-SH{rp7 z&}Z)J%N>O{sdmuO9kNxlye^wM>XyO4Q$)9cw*5d=xgcNx$s!i~T`^%5H4pXeo`vru zFQq2$Z%G>lA~*-8-9ZWuP!>M)Ipoc9gVQvxC6zLBmCHAgd#{y1^8N;%Qf~V^1peoZ~tqy`YLK@5b;ZyA>GgeS%)CVI(k9YAuq8NF{>hWG^S0zGyaPsUtFAIV>`7# zXCd$Z4hk2uSosauJ9r&-MEN(3`N$jt(gQ%9c`B^#~ru7S0^qp8Lu5!U`r_s@S@*>ZPVtV^XR5MY_vLjPh(?N zw8pftXU|#tJ`INmoIRrbJLdD28F7`?`nb|Nzb78P)vi+e)^v9S6SEz{4KsMGJEwn% z+TlyR_l-h{p1?(%CHVjOL3;#13I@gRyCUc-E527L#&!g13PX;58UE+=oE}NFzC7|W z*T3sEV~}Q<-Ans--5TB5#25$Y{%a$BhS^`2IVd_|f3og*?bKB+MlCVI+1*bK`4_45 z0dS73hsi#m4*5Lr1^>IvE`)_TAPd}j+8wx=F?44=VMlao#!pfU*j@ti{pmrwI%h%R~5>>rXWvFj^ zgElu~I#hc3jDX{&uu#bc9gm!@__?qtk`vJD+;Nrb#WT^9D34n4O$gKexY3j)cJPBQ zSCbzmkQxj}O=+y92N)_?l@81#J~F7S}Gk*N6p zHKb1r*w`Gt4v@Eiu*{G|TmXx?=ahvB@~uTUIt8Tm!uX52(;o57nl@h+U1rX~ab z7w!TPS?FytwKJuYBVilSrq2{o_8ubA`TuMguF-x*T{9p}ICf!BOF*6Tuhd@fv-?qW2w(2+N-HxaPnL-<26tqD;sOptSV}wNk5P`wla|N&0|Vch znpYv?7Z2tdF+^jBwk2e2V|ZIY5$ zYTk27Xs7BQHA;h6FYb4gpfr|9-{8r2EH{R$dA9o%KVcgO&w)z+WKU;8RM`4CIgryv z^kF8mRyZ%Ap>a|K{*4g*gL~H-!agUv_yaiBv-{jD5@fgzeOdd@U>8T0k9_>}VsGck z^L*ll#KKIQK9ona1)O>CX|gMDg@^kumWdHrI6D$=&N}_-d0`J8u!b2a{)Z2gb8wft@n(H1bvX7Sq64m zxUF9+Z#Epg(?l_*B)cQ3XOFD)lS_NT*pw&g)L0Z{G5r7HeDW5th$Tn6IRuuCL1Mzq zxhJL5zXhs!9n{G832yRfutK)C^#>^SGI=shG**9p{%P)KnxZ*5WFO6RlGFxJL!K+& zUtMPPM6@zkMm~M|)gkmdco{!fzdpgHRIAFmH9ve*japQHhbq~?{ z=H`!uX{Yfde^=YO3?@vVMUY4VwNY;WDI&#IgGvH^JbWaMipXGfYW@7Ofr8&&*PS?9 zCH+a>r?DP!;QzlDGrX2VKxKV|h8yF&oAdldNds5PS<6hGNlTqlD1s6k;2c(QJF`C z6Mv~@oDlCZ6@Gn9$=G{wbw955M=-WsyolD1}ibCynO9cB9z?LE@ou(l#)n7IcpjZ3Y%g6eq z1SS3_@1{_gJWxynvNG%1nfrP(npU-o<^j_E5JVdzSr%<1NH+kc?Oh;B7?gRMH~PxU zdSRxNqAK1RI0yF7Iu`1G#Ekq|64yX!ALO|p;w818eR5Hy*+I3rTjfx1zl$3*=!Oti z#q9hvIhgbIL?{#IOT6Vr*KQ(r(Rc{&joHc5zP;qQyYlOuu6sbd7yZO5Pog`Z8iQv4 zP7Yk>?jZ|!q)%o{g+Nw5)Z zPcFrkiiECHtu=1&T_Jo-)%tQZ<7JZS%cP_!CQtf_GK20%y~JSU>^@xUue{G&B*2Fb zC);zVqZt>pUJtDil~2$Fpj#*oaX#@)8xKvYYE95!HLd7zvHHM1%fAaqgK~NXq?HMa zzOR$L=qBQTvv5HSKQ({}Awy;5uB*;p~aI#$PTP9lj|$IX9Xd@TUb@qj7HDN=ZKfZ#FH0*5_3(HJ!=Y0D zaC<5Ma0qoCw;2!aheS;+^4Jgh`nW5J5phiT`QVr+WoW5K^V3r&8yvA98;^EAUu^TJ zL{JWme~1|CQcr@LgS6^t*mw@{k=Mybev7?(zgWvmOaf&JC4`KP!SFc>D}Nklz(mlYP1}HN4gWuY&o%5VEH%e#D~Ov71ZmasruX@ zTnHZB$XJFn{O#`oPI&c;VFF<<>fQl*Mn|2=%p_a4%QNeVX_b22I#Ws0pomK)Icn|p zGKi;@y)$3X)tK>fyl9DehEd4tHtQgG>F<0R??p}0Qd<2_M*6tgN_Bj^CnO6s@j&|( z(G6+xkb(PY8b&hgA`ny|A%w&L->$bhcT!E4hG0=edKK$CKV+Yc#72|)RTzE3JaPnk zzVb?;xTK`SjmV(bE_j)oHST zNb{GH6YisV+lp7QDl-EEYL--Bs~cW;t0^w+su94V_=~8Nw zk&EiZGZKFBpvFS=v_NMGBiU}A(Ht_RvuM->A&3F6HdBqh6CFB9tdSBNJfuv@83o;) zY`ab|z~EIpxtm<@oCF*?`7JSGRlz6`wxjhq|D9I9u6R~#wG4*tde-tP}6|?x} z*~EL!T((;ckA>041NHuop{1l+gU4gXw;dl61nSUbxFPN4qP$q%!3Yu1JSp=O7m$X7 zTEa-7>Ok8!G^=jS^vTSQ*Pgy3bwgSP_irQzQ<)Pn9ZglfL}j>-?!OR~Z8ut#7ABL% zfxJ{GS5>elA@PiVS|#Es0$-()U3{2rH1Yk@MYi#y#GfJbLCH3i|K5WkRrs z8Tw>}*#Ff=fc$VL{md1ckVAF0tWQ|!IuRs3Z>x~Qi;5?N91bGnYXpFh^OISz-C+Y? zMUq4(6w=)C0cI5oh~Gb{oOjr6Lk305XCvjG1ZwD+*dI;h{o@YgY0Lc}EPa`!OhwwO zSbFEIz*T+KUdaD{n$!i1oYTu8sjF+iP;A1>;5UzADPJS~qb6wNBeJg$4aFv-g{E?L z3^1>3mm8>8vNiOiaX$|bkfNRq;M-_ zP8Cv2)Tz^26j!dlQD!qMxnho+kIuP&5fx1SB(ni0Tf!~| zapJKd06l!>m?2?HWvf`|tlaXmOP-3lm+Z75Jl*5H^Yr&O;j}k3DzJCNNhLn~6FHM= zuXsNh+IMM1;-&$YVU7(I#am1eG^r41QL}pF&c&hH(MpoKma=k;Dd}w5hvDkwUx{1Y zp*KlHf|OX;5mN8XQPt@BJMv*}9LT?xi#po8`TAZ*n9jI{1$N^z($`*;6J9tYf%#cp zOz&XCz;4+aHbZ_@s7DHU=qz7HP9n<``ra!f<~y2dbq z1g1zIG^u|?2tnin<+O}KhZDZ$?~+8qhV_jb<;}4fmGQOR#YDXafL26Yy7ABU?HoPW zo~=*F2|9OZeNe}_Tn1r_pKO6xqRnLT8{^}kAXh^Kutg1FY;=T{YJk)xTtl=@E5|f2 zlj%PXLdHlyReNoAU|(I_P(ZW+79i%Yt=SexczQ7ud}E=x3;dk?H^kIi*<@hG^wcHN^u!fxN^$uxN0{tw`-WY$H7t0pH9E-{{1#WX?*S8OeNU{f9Wha z!$h2c!XqGPMGlFE8wulby%;{e(zq?uH~Zr9`efkfMF>WFB38FL%Eu9qZ(djU@=e=9 z@Q6J-Hsyu$cM;E)e}9+0X>rAby@K|e3%kgR`?d_|b{iB~%*iuXsaccLN)?GmgPxk7 zY@8V6a!;%Evwq)geKz-OP@DwM(OC8G_$On-_e5xjlhJrK|EIIg?5KvTb`$tm10yEH z;JlFrEa9}tseVVhXP;%c6nb-#ddvixIH^f%@l{O?!hPLMI2-)vQ7BB37>$bXbRBwX z@0*9(%F5!XgHA2pH$AJK?|uC7jf4yy3)q|VpLO8N#&^`K@RLcI5%^ExT!hX|} z<9X`2UOy>>TM>M+F;V29BQ*rbuvc<^6^R46aum)?zK{a&eJhwQ$^ALRTVC}|ZXVq2 z8KY`Rr4AnP+2u_zpq?3-9jX-zjxX0N6?@K0kJCyYhB;$F^(e zsFuk!nD<%p|G+ASP@^^Fg?1q=Bu{S5?zJTZfq{@?x@8HXn>EOTi!ur)v^1O?);l{z za5i|+l>|96q>V`uZgMRSy={z>rOh$%x~n^Imxz6ZhGLL_`rp4f7hsLn=w@#k3|)Mf&ht+ zQCOTvNT)r~L~q=y z0?uiSyHP&=PT&7)9*SV3OEs%t%i2;}^BGx`*PRIq9HI@W0c{T)bmtU6iaZu2ZkrGd zR4f$~s0xy=!0t=^fIqaJDbRNt=L-sBC?i^vmmark3B*S^zBRC4)K;6A&;NiwNk&AN zf0M0wY)Se*U`#SV0%eP>`Bet7(&kD$D}elIQOKVWb-%IPlKai>Z0Tg4uD!CQD$>A4t^8RYZiXM?*QfrY3 z_SV#Sb>}o4S4WfB^XIPNp50s*ZPoTy5dy6!kaud(?e;o&-8rwJ=ttpb9Uu9+X65>0xg0uXsLk11+r~7Fd-b zyh(+*<###xy0dosMPi~>`^|32e%ytw-guGif(#F)+fe00ea*79oBoS-gB=TKm3ouVYwe7D<=Ibq&V69 z{Bn#?jMq}NX zm8A=`OIEnaM?yx3H0F>c>Js4rpFX`O#k8YIZLd9BUU4^t5yV)EN@2kPumf{}m>;&0 z+kqE@O?Vq(ij1h>8^uf;c>ZUf@i?zK$N#>KZv@4{AaY zx%CYr1f*-5>QYB)AN5;OHzj%P;_^PeeUVY<=sGs`sczsNt31eN%>`kXFO*xEKTs=s zQz$?5HB_)L0VYxYRRmuv3UEgnOIR9e70pAEoON%{SGj^e=+p;KEFWmj8QS%Q$TolS zs{4P>0UfO6Dz`~lsded-4&5s^BPv)o*Be@848KK-2mhL1v|xl)$GK?99{Vwf_u9#C z7#T^~6PS@*TtU*MCFBeyCf#`)(FgysBuX273fT)rzrddWn1byp_d)PvD=~Y=fu^xg zJMBVyBa7<}fBi*iR5=N@%&f7x$44b7&x~t-yu|7ublDzf10ljdC69~@dG{FXvUpwv zgp8FlGLo=X6xq(LfV7EkJqd+A64`;PfMBb?gyO-ZLzigsq2v8#PHII5#M{TyqYfBV#r)vTDAszs9;)hh}%y(%N-kmK1( zIx;ryjI2MWh4v*kSkSSIk#`{vY(X)j>z#8zd68U-s^9o_!!q9b_Vz@HWeIPv4E=ac z`Bn+l)(CZjvjEU^yI~7J)bnGEYg}oKzn80x@vMWcA6qJLV{L5D@(K9138wbu*Slnh ztlknd&z~P=Qr}Oc{><_$O}ad;Zxi_Vqp6=yK1IXyKg&$#+oM`yYK^9%qVk+H@d1(C zWctmC$pf2s7T2Hu@4xvD&v&xd{RxaSTi+VXt?3Eo4-q?FO~a3jJLv5+H}o?LX$Z87 z*kPhWX=P7D`RM#=Cs@5S6NGc;%JDSlJ4-UrjR)*ZjUCw=gZUe_t=q1xM|!vIj^9s| z*e6v-U~}g4wn?%&(-FOWQ$_JgRUVxOWvGv(1mtg;32XHnCZUb8+aDu5G6F1bo}_0E*B$kB7H&1(t|$8tCP_XG>q7RFCw^gHaz({D;@y1;QUZ_49%bPSWS??rQEb8J`$ATXpfF=ZcVMn{bBD0beiPFmnAWBJW7p+xP!$zy5KNjrDH z*RV*snjFL*_J{Yud>fhXoVY;H`DpHg@vZD5c<9*k;Vut?S9m5En1=W_d^%tZr`jQ1 z7Z}!QAAMenMaHgTW$n-3)sm0zpd!xsS3S{S2mOaghoU$D4#t$rEwkH(exFSb2HASv zKCqQpg+QbrO0v>A6BaIlAH()X0Kt>T|Fb{qld8^nfoYfELO@cE|F$sj2M;KGx^%VX z?S1gO0(h}ZA$N&R0pSBS8sc3S zq0{Q(z7Ci0;W-aJp0E&KRuv0bLhJJ~} zUxtrafiFn-N8UF6>iw@vCr$sGHc7WZ&5li`cczal7N2YQ;YH@Fqa7{N%L95WNggv;KQzBr+N`4go< zNo0#Dsc}kN^E$oOeB_iq$rlLYO@sbz723vQGjgcVt#5G@wW3NP>T%oHQo!Kg4+48~ zmU*hE^toDkfsmC6V2p48exl3{*z?Kaf7tIYCwdeOVh*S01&PXC#pb$v*{+ziF8LcQ$0X zrEXY=kQW4)M{+D=O5%0?EjFG@;RCm??H>JJ{8F?1!rB65xX(RC@|{TlbwvEEE1LAM z1DorQZk1|!eg2CK3Agm*JK)=HdQ5IB(oRgO?`arGlSE5WKoU3A7spPb%n^UnTq$2s z7{g|Q)p%e8mAoOJ@i-zkH@mcpt2ym$+!YuoHtv-^1766`z(=~88)dX&!qN7E(E*Rn z0#zo${I`aySv3-*$^8Bju%O*=RVHNvId;gkd7P8gN1}>KaBZ zxJwrn;+kf`S6(Py8(h!AQjc$YI($e^R@c+-gy{-5abjPUmKrZ+Rer8|c8}(w;Rn4{ zFbrEl%oy*5IOKV-%Z)jI7YqSyMabA&#KrE9ojFDQzkuBwS+7p5f>EAcC)(}fx2n!?Z?e5gUlW#^=z=L)Px3+@EqnI_zED=9W8i&mG zuwt`K)wR{B*wlYch8X;pq5Iw^z>Q&)56p1a6cAfmg)?qx$-iMd?xe8QnB>U?S6T&vMlX33k4S?~UfB$o)DlNRB)> z&c7)?bNj(89zH1CVGVI#w*T>dhbFM-&zEYrZ5B_+w}Ilc{SWwC|A8`Yu%%Q{XW^Qw z<-C)yw$593g3+LeTk-jYE98507r2z}dWSZC#w3#E`ZjY-UWbdJ`0J>@bJT{0A=t`~ zD&@I`eyG>9Edc=uNQ*t-uU(NTQ*^`JzYqKQyJNS3Do7>0)b(jT zGl3g@NX~JP@MaV<|H$De^Y;bs!Aj0ABH_}O*1J^XqQiVAK8n2WN8IX*K+EITeJ0} zG{{p05J6Zee8v@6xh0%6RsEopRdJg)-{}WmBHSBdN+q!wS#fNW%`xXbAYQ;@b{k-> zeR-(Z)4^%k)?N_J4!TF*30v(?`8zZ>U2Hc;d0~ZiHeu2sDWs|` z0qE1qB7MetRa9JfG3N0VS5PCkZ7svP+<0Sr|MZIgEM`*a3mu)kzF5Afr{SHOKuZ3Y zHsG`}_L3$`Ed5*1pZ&MGyR%>5pzO0en8Bj(-4vo0nMsckh6>_eu)@weiSk1{X*CH& zyVH>aG#J%bOMkXLZN14w35Aaf$z6OBjkn%I>=l=`Cr4)3rQ)` zQM-35q|!={oR^@~H#k>b5jZDeSGEi#?edG;DHdD7=r0{V4-$qn@lr_0%7lhKZ$yWo zgJ#1-V(3HiW6ELE%IwPLx8u@{GPTY0oDMh4kQoLq!6go@rku%aS?;HRd7y_?*=LP$$PS+FI(>Bh z$p6$fb3fCiDaybHe7S!m-V1AO)R%iMkyN=7`S$1PvVOOAZCoXL>f$nRS$lLc77W&u z1((*R(e-|9VMldsLEsSt=4t&Qp%|;f8Mo|E=_ojmp9*;`B2qEQ2*j0%#gcgIE7S?m zQiQzQ2%a%4_I6^-4m#U((slO9@(MS0Z5>y1K?QlKY90_BoWy(`hJ}+qZ3oNZt?dEt z(tb@(N?$DOEFoaJFCm<&;mF1~V(j}Leces~YuT2RNHl%8ax_xw8sMmid+A9ZlEj%I zZ)G>u_tuAI!bF#O<31WlP9$J0R*J=GRtxV(&T=JF0-&yIMO$r#`rL$KSzdU~6jZYb zGudF3di&jjpiJ_J1W(l)eyK=8mic%^y>sGoa8J zQAk6BdKjLABa%5_^XbPd_kdRe@uLRmQt3spoGsotB(Dnm@I7CGDNocnAi(^+;fV#u zE{*Gj;ML(F**Kn5rwDB0Hb@R|CxjE7P;og^-P|Z3$mVUc8OB1x=Y#AWAg&8KiYc`|(ePK#5scrZDuaCdpC}Zu`A(%})vnT`WBe$s%-opa^ z9alq!l-WzN)4}C~p|f1kaZND;1#^=xv~#q*znE+wJ$S&wPFbY>=y{%2TxVFZKJs4q zZxnOR5X$cM^n+i6c%|Ia=jh+11-Xf2U|tvvDJN2!txjj>q{7ED%oM8YlpJv-;yA=`uv!MMeifLxeL~CFje^ zUF_UD+H2hjOvvw`n%D-j$y+(c{jPl$oqhxa8XYKuqg67uUu;|VEz%r)PXnPM5Q>nJ zQ%&SNZK)$cOrGH#cGPT=YY0JLob>I%3QaS$Y4dT~PyfWSsk`Can!qv;W!IMb&s9jjUQih2E|LY?2lew-K$?Ls@4>j7)qWZrID^GN}n<2@3 z9iP7bt==iSTc|f@5XqQC%#5-$&X6~er!+32?CK&Q%)akUuR*yVb0OHBz&-xkdS^*; zSnRjR@d)EX5RRBQURP$jzoz-srUg2;X$|g}XSdT7JS8(lTjSHFfWe;Wl#7))|;FFRJS(0kY+mlBfuw65Z`Bo$m-s`tn|4v`r*aF-NHZF8kVPd4F1nBDnfWa%fI5X# zGz3ZYS8R$6_M#BBT(yCGZzi53)`8v5`WI9;?zf&U#mIj>qtX0z0$2bSF4!)laxx%2 z<#<6(&iTs-cbQA`*29@P=UHyXW6`n0&&nUJTMphY8r;5_TPSBp^thr?jGJ42dlz}U zv$6+<#+!&R(Z%UtJ?|md=v=oV<-Djib)I2O($+?GC z@R+5Z2Kb;&K|5Q9d9DXnk18w;oH9@PU4yhMqRV;v1SYig8Xr4ekKr zSEl4H4z;8}Ir@ksTdkE$+16xyt5ozsu>&0Ovy~*n$6H=P+_pHX7ZG)AQne+SVDr@R zJ@`9RO^p-~h-zpUwRS1{4xB5qovGSQJcczcbHTq9ju_Rs@Y z+05?ePU#J9N1+9vMt9LJj+tH(ZEZ0IJL67V%>-J44%jyg1W%nA=ZW~!!#0pul5OcP zi23zhlIHU6VKQLxtCTVY5l=N;+3yQdKrdHK_s{qKgek+}S&Foc+ikj?Jw#QKLbo0S zdakzRD6ft~yKw|bZV!{{Kao~QDBN}wL9-J4CFt3!X>ElvgT0k>f-6%^yE4;ODpBtt zuAsNgMp6}xtG%`XNV6WU7U*lG7TnE+V2Dd*Y9=El9CpS`@R!U~Qek>6{W0*1j2|WT z-hnaZP$@MyS@62`y%O20HNnt5@+IgBoH$OZeF;9bCCQuT>=oRTrd~8t_&7#coOkau zJva9XmuLg;;7^{<>aDA>(*$I)M_2Vg7mh>~%#<b^IXsSz6M|Hwmrb&zaLan%qJ7DW>-Q%h(FkG z*;fjxBO=D6##H!fS+Xr;4gPbpN{!1`Xe%6CcV^K#KE{6DeT5Oe)@`KwULK)KwD?wta8L`oNS+;)@e5 zs!8~c>GS*jZ@$#QE{ZZn*R)S&T{Rny%sse==cx(t^T_rfo06Z%p_n*y1TK#Bc<1R# zH7=J}y4h}zz;K^5Ef@ToN*UukS*f#+UA1I924LX7-)XnfF@+Qm6R;41t8;(;@do@>+ZJep!?lDi08pV~ojhMq~_jEqF%g=>;s(G}$#P`rR>ERwzS$TOp z8JB5RS#(*ve>k?=+2P-@&nw~Yeqyg3h1MfSJr#41(w_b2?bMV@zEUP1SGrNROwoz0g%4UuaIGiWh>EaV5(g+!znOI-2~O z;aIM84|cdd{l3(4-92;}8o8w|#Qmnex`R^&kp%*a3?_pm&-!e0nqt5+psKVs?;(0- z{*&U-ZOH>w#9SzIzjHoE#TId}G0~EY5*H+%7?FoKD~!9A%5#H1j~U#q3-Wzoh|N5F zE7wPFNRs-e>0bS7s{r>FuFyjVO_v67gftCH9To~?@@o1LX@~U`YIiYj%o_xRE^bLn zTseHk7os1}`iwaDaViRF^?!{PeRTX95I$m1_$|+GI94PsbctB!ckX->Q?m(@>~B0H z17*^$s^r`gL`20;S&C-db!+*kNVut0Bg>$~B&Clu+KJS#)o@{jSe1RjMMQgT4n|ag zyR+Cm8_(u@{|p9hKdZI)icbF!KjyY1AP+r$1M;w4_?d$@10}&O4S2Vt-ib0}hxry? zPf$F`*cu%wfw+sQRx zh{U--E=a3E0<^gjQzAo+wRu>C2EVax;%Jau3SnWcsL+xT5+cL*2j@kz9y~?!9B<2J z`&{#_M@_f(S6hPEKIYu>FTjTJxar9usSD5xw)qQ5%B52`X{19CmH z;qcIbhWmb<#vM)J*nht-mSnF`!;DNw$;iT z`Um4OdcrQIqAtez;qauTMnc#WRQ?4K6*Gnx zpYbwOE+NoNqR1%o;GRe%-I54%uoZna;Bi>F3wsLbUreddFJeD;`1S&;_6pb8Un-uP zx^y?GJ4CuG>tQf?LJih#4ZFgH-Pb2f*+;{|p63{LHsCEqnR4tZTJ|4{D0A4`B&vvQ1@Hh#d=6S@0foXv!tC;W~!`=Jw7=(nlBeqty{IaC-O^z@~N;C7%F34 zO~A)t2)rMVl2S*BABK+Fb{Au!bbhj#GJECQi5zUKpI0LtEfjTcAWw<|nq5~)`b*Az zC&Uf%aF(Qsp~@O%Cal3%6ubKVqlJ_~{J zc29{(%SmXQX=Dq?Sb*##IfPOmo1Pj6*)7Q;K{M>w1BFUnOb;^QLj8_5_4L~QJXxP> z0U~kQuCH7ClXKj5O=OSRf((Uf>DYl~EiYZ6&Rv&jDbM`FAn{Pa;Lo}T(1aXAv zMzV(mxGNx#N7vC%38^H^!;psn2@3`f?2zrRfny6PMj2nN)>cK*0&JkdUOm5a#Qp!Q zmsAv1?m7u5BGr?hiE%Tep}^>S;tHJ1t3@t?+`GM1(uSX3wE%Lc(?3dt)%_3|aln;V zF5s%gKxLR!k8ucxbzm;W&f#bAn~}%(V^;1j)VtDjVLb8};tM{dcRFEqptaJ438<#< z2kD_Xn|Vp`!P<%2KaSpNq*O64-j{T~etwR?Jpb)gsW8ASv4=I&;#)IZ`kCQD0IDM? zTNQX2yTOX-VRzY5KLJ!UiYb>rL-@RLKC|;1C z9#SE>W&<<8Gru|u4LfEY3N(Pn>&{=&2)a@R?q=G>v}Xl@M9yc}NQkK+ zS?2JeW+LDQ!%@vmNyFF6bnC%Ps@EfF4kLEuJ(rz=m{TvL=1w2n6D=@>qpRG7%2HNa z6Y!Elop=HXM!vzDx-yX0oc;bz>mRKC$}E`vv0n{FUSD~R>*{MN!utgj1*7*BaM$yu zZlNyj7luqqI)7-ulYnowC4+*g9L9A+b`l5aPP$J1Sw4`(Rfs}3Gb9#Lmo1O!Vg*xL zH7h;)&4J`~*uV2xHmPD&=)EE>5TrdK{^+oI==T?^T^;G<=RU4;wMxm=0w%NAn)M>LvsK!sk@Q(&&(3Hl z76>My2k&$iu^XSv);Sj#*4PfW_#S|Zy5(->g)30W+|Jg=GBKM;snAPI8@USJPZ!e# z1NK1>8JdDrLhjzm*zeldq?NFShm^u!%Of1TSm_bm5I6I74fQu5)lg`A_2V`iD4IZk zcAXE^Ix(`ow(Pnvi#-{+!_H8|hCEpamYnk(Rh8H2nqcruIo;&>80q0d@crYTMypfu zRc^QoVfSZ&AMU=znwwu+-IwL>Nd~out-VuG+{|wJyMdGif+{POz09f%!6w<4aVSzV z2$P}UYR8fKa4mS`_jO2Nz!}?_Fw_qU#U_|)z9ta&pQRwdb>Lc9|E8~1`M@0dmllRO zl_vFMvtih`GK~&RQ#kT3Hq+2Wn|I-I3UXpXPvq~rq~g|AlX!9EH9hysS;Jnpsc`rr zbg@H8kbIvl|1xQ5^i7#*o7?be9wPUWLI0WQ{`i>2RpW2X3pQm|&N}oJu%!4cFQM>+ zvF5K9sFf_O1|4iqk-u@=EIipQIX>Q9k*Um3j{{9oD24P)?d#N==vf(D|_u6&ND3*6jD~VTMsX%<_?- zGP%x(#Y!|HOQbc^O*uf1B}A=?>g+ns5A;9LdNf_}X70LZsXzFY9ju6rEM)!LZGceW zGoR|h>8?K1r~LXnCfI!o)#S*n4%QtorXUR%Y&4cr4I2*jFeZ>rE%=&ZNmjr11 z{@iZ@lXr3NR>Bo%NNFCzXhRSTiw2d6Wd*7uPW9*-4*p_cb=*Y0fovU{r(9X zfK?p&kSD6^cc}5xS1Kxs7Q^;*^dgWFcbQJnLtYU?MhRYD(Jj(6Bke}q7f#7kPp8H~ zMGhU>O|*E`lY4r0`)$iR@9bY8srnejwobRM%Z0orcq#*kFtL4ty)V=;Pmn4`haZyc zzpU2qNm5+}Dza;euO@4Q6%GCNSw4}s&G-N?JRwA2ATHK^ZbtgDLS6#R>WMt{hVFxM zNbdDvQH^fZmAk7As_i0htTiY*7OQ=`cTeNZ^AD)Yook5$a>xAEK+BT0Kv~N^Ni`c& zE-K^>QfZm9E&u!LMNgEV;g`pf{igisKv4q729NEh*$bsU`TE1ghV!|<;k|vK5@O6l zpO?iYy6l508`qbdYvPmrMZRg&J7K@l@1aThWV&4~aEM&IISo_wihf*8dU~R9JXTs{+l_ zyRA0%q@NS8kcBAPG2?DZ(o+A_Qg^uwc03M7#@^#FNugOEqiU!Bz9Gf1Wvw0N#nyXL6Y45nuZubl2Q?|jY-+KVxJ zbCdD(E){19bUUJNKMKfS*71ovZOU$HAHX4}Z== z4Q1@39Yv1!k7Wz!gvmVXl0%Kwngx(uAHmg&?l#{Q!C z#eyl6g_!*P79=Nx7`%?tucMdxIs4J(I=-rG8gCzN^-)T+lABUvxxGV{%e3<6IUVSW zmcK5)tbgXPv{}kia=;QI(6j{ksc+&%8lEyGY{<}TOxebvd>1bi@&$s3X^(_(-D;2z zcv_>oLNScOXETl!UbV_%ZGT;bkg&(H^kX$afwy0}S;c#LztV5%IfPebYu7o#yXTZ? z?_VeE8V3hmvIxSg+LqOmCwBI@AR$7)T2|2GQh#I2ssdW zl|Ff8)XhHcBH!HfdgGz6Eg(w(k0>QZq=bGE6ICX|_hGH}jQ=EcR*tqmDqRL#yh&nv zk@EP$+cNFj^+xaWzg}e2j1U(e-sePYR1o;U$p2t5hhBAA8CABS$^>aaTPhy219v5n zXM=y|{dWYK2*}5}yX=xQ<&v8-rS!rF<_x&cEifzDh9_SNbK>8EL5)5=CGF3ZG^2jz zQRx3wv~~*v`DnO6VU;`3%S&fK6i&;DuznFMiX|(TI25vW6-sv{7`W?^ah<*Mx_ifm znI!fe90${r=5LXmi=o`h6MJet>I!Z#Yb>B%ZOSob-CuYH zuYUA+&S2PzSSPQ&+&i9i9qkCm?r*qW;PktSILpx-EklL-jUNdCKS}d@jP}w?CX?$K z4O{&LZiOqiP-qn&(K=sU`*b}F$xtT4cv$j=H(jiQ_5~c~Lf?+>^6L0ldgCe_pHKBF z*zA2&?oyl_N-5zGv$F4?IDqhsv1m;fN%KJNluYue);ji>@^}uPV7q0L*hF|tfLFy>U z!_Y%0F?T;Lv;|Ab#e09B$ccWO5xA^wziC7Bx*H{Q`CSd+19?+r{%F4pIq!oPMH|9; z^4l#qneMIq$kAQnTeiPn5?v4}HeJ>qGx>CWkF`X>sOTN8!f!nS=`!W#*FZ8tJwfui zVffA2-K)^0BtHUyk8vK8pwGYkC=Fb)+INm^3KiqMDQtg#J{x?$oFHjhkzv(4mEE|v zbiy>3(L5i-_NnV`_bmcZc;%zcX1LoXD3!SJLQQ`!)Z1}&VbnIrkS%%WfgjaQ# z@u{rPKqty#1B7odL-7JQ-yo1I7a7RH$4NHwTU(S7GznlSTY_4Q8lvFRr~XKDM|1Cl zzUHTy*u(6rs>Z_{t^uE|{WM19)FJ)0b6viJJ$lW*(tMWcjC=ggFCigfM2q#;ecQ(B#-?+E#gqFP5Z#0iv2@>+^G$e%+|qD3t95EH!~f9cbKbk$cdZ zKLcnc8QsVTKt(o%j@!su*I@mI%NoI(ME5hMldm&P>weJ@$+ng2g3kV_n2u=Oh!^27 z-*R_-AQnTXF*4`)OUU(e{?WCBqL?xRAXS-_qJ& zOxG(d#Rx(2q@eHHV%5e(Q=XgpXzmi~yvYQ=OT@*g^CbLHCpM>lH1;_R)rx z)e<9AT)acvPl_F@xRVna|Hi=wpN_8X^X8q|V5SbE0ruM`qTi+@8tg$YhewlY#eU$|Mg52PyV=G7;vo^7w~nFRodewnkICvu zF`7M*^mSq2!E7fwH!210AY7~urAXZ@T*Yq@WzlNVe~zXjsH;&)H0wkbFX2ax3-vzO z5%=3PMk+n)9%3{zOgyx`tBO>!?-O;{i>Xm$u;kCOle^V0b64}xStW2{Q7)UEzOSzQEIwDtB$>rgvu(!u}el)WP~JpWN+t0Q94#ek&%(TNeHK9W|h5# z>=`=NIp6E(+4K4O{0HA3-ra7xb-U?Y=ei!(W8NR*8q~|)zt)I|eD!2gZD!kl{+0Ht zYdL`yXWkorR`!gT?4f2p&$W{-agFWNr1-{Zwt(hz`lkw9N{}SA$M6PsZX9*+Ktz|s zkLP_adPVv_uQ3#V}S-h9!PCa8RUs zr($^-`Z@D`KauOv$nfV!1y|oonEKFn)GRRPOIt=(KrKADz!)Hq8cFj1@ZkCYF~Mo& zQn;~O4!Stn$o`gULpsaTc(S=pO&*m`7Z)8@G6`Dz9~&VTHbPZv59;Q^g+&E^AT`bD zK;xTB@ideIpDVoU{AzYA^mErU?HHvMiHSMO7 zmS;Efpc{DG`A@mJ27%)@qAq786y<$GwPMlBwH)Xey1 zcK=)kE#e+;^mdWcTk^}auupW8WML&wx`sZ*wJP-~+D&FdDr|n8fvsveY0F>tU&Bj$ z3B6IJ@k2qRTl_K1stJb_dCjGF4t9~g)D7h*&$z~UicfJ&aidA~-2sK^+b?rm2s{+V z%wOv6NnL-=i)!AE^MEK>b3VPy^quK+eUwU0=f%5aQ!mC2{l7@$IT}tHp3zyLaYH+O?E{t563GjG8iLathc4%lpa8}@Y?Oo!F zGt#SRjS5yG4OxRcQvm=v=jFoD_fPovh!yz<*|;S_s_azEqVpF+W;S@_&~0S92c&Yx zSd;xPABS0kS8FWYXw0@AeGd;gxZx-!RhSNnEIFMpdh3+K*z6&OZ`9xdpj zkMB0(#tnaT-q?NTw*11;!9i$cqA{(wb}`?oZPW{GMqvGdT(q}jTnIE&c4r>RfAHh% zQcq{+y=M@wlBRMJo^5D$H7a6yv!;?=hH3URFy6+uF(pF>~P7UCFZ&!bvLAg$CJuSc*W1QaIRe| zTVG^Yh+WdoseNGJ#b*4$x`D8_v;f+^q})>-RI&{_^p)Ac>}wE;&~wK{h2qF{e3S5{ zk1jd*fr5DwkkooD=eAHyZhFACd&vDMp}eninW;29Zz6n-wF`>u7)G&OO%g}=w5tR_ zPK*z}n=e1Rc=LJB;TP#(N!b|JiLY7tSKJpu)5evpM2Q#5Y^X>gs7Nsa5!aV?uNewr0R&6~6WxD~PgsZBfnTQwVzEiMdb_@3~R`Z zo;eysrdU_1a)g}k$v$i|EAh2s@5I3+$DRxoaV`QaYnE`v z!SRi$lP2qiv5F3GRWNP73jLMuD;vMTC+i5Km$j~8KkGD>$@tKSVucZZlAV+#P>V!9wrm?t3&F0N$}y))YHctb=qwBuGbVB ztnf_*2nS?0JzTDwK9`Q{J)^~)w^RY60;9v`;PrQD;=!-&{F;XI-yXynbKu3;{kG_z zp13X6bmJJzDpUST;yicbAqTQ73 zm@DG5S!E#Rl)OIKXK0=sA=>aFqsNLEMYHhmH~iiJk-*)&!2sVRjly_up)4Q`zaLTF z_d~BhFYF9e^>kl>;rFcT&$selYtV&zcyxaV<4Erh<9y@OSZ>DpeaZquvpvJ|0e1E7 zOGxZKV%>x_a*@+c-cQ37%ozyW6ck@DevKEMv7shq$>_Qfr4hgDL|3##XeQ@Zl^Fbw z>4#@SXZwPD$c{g<`ZZxW^;4dUk%+ldX7ZV?K27S$3+}h^uY!j(l6*d%N{*@&CskR3 z$_!%q`e(6g?*+DA{;23k=K!vb4JZkmc}7d4hMKg>SeWOi`dZ|C6hLWZ5kVs^8hlV0{V#XTKLrMnAbk67+pE4Wp3iFc}5B|Gd$`M1P_ zp%^u>N_8Hb>6Lc~~0q(QaEYCCkgkGTP)mWVgCqdpmaLe*3)vSx{4?G0Z^mt~^ z`TU7f>dk~k-@xD*TM89rean+&+>hVXR_@xB{S=(>ejjyk~9w2>!wvoR^bUr)F2E9(Z{_#}wvDuH} zmf2}mGOmRbcYemuac~fW{k>slR6Ioj4y^B=dU1Nl8?KLYVAO}Th=iY~k7j2gV`3Cv z9k+c}^RxX2wl%ZoGZ~f&wrUHZS5{^wQ_+Fa)0sjr3XfHWEMLU01yVnjIezuKLA{0x z@z+dMT|ws+gKeQpW^*2WSNQdG)nmn-^vpX~ydI0oHPU)}#=)u9e}S-dCQ@eARd0`> zqgV5jcgeG4B+x=PpwbXi#Ta2eSdN3RqzvjMZ}VU}8hGu|#b)5ujLauSt(cNq(oy8g zyS8Or=8vB1>ZL@Y8BWbvGo4rcS_X26=ZTxle6qNGDa!MHD7yC{SBg@F(bg1;> zn2@@Pluk4@&8AhdUMan@DQ7-D#}hsn_i(3e3~HUeciXygb7cNd1si03Y?sNvvO-F{ zFXkY*%7H5<%_R9Y+f$2=>q#joAUg7hVG_FaF7HsJV=F89*^C00KDd=o^;p06R2u!T zfqfzIyPpxhv+;%p)>ict&}7y1x!-eb#oiM!%UD)*;g_#cSC6y3a5H{9$a^enoszBZ z!#>s4LvAAOgBAGl#;v8FD?E51uC7MepSR`t>`&dPpsJLjXP6iO+lUD z=XT?Ia+DnX8PDXtZiR!zw$m*A@qT-$opGCoiNEGEquKzxVIswPs3&9!j>Q&Jp#I|4_b8EAr&8chs zT^rxtRoVQecBAH#*XFIhnTgvq9mWNIsn74#az`F$R`vw@MGPOGF=WemHPQ9ziXQn1 zjzY1`y+f&bCGVo5rhd0^70EC(FNy*}q~j6pR`IrY{aBO1$iA(+=?#{m8w{G!{Z-22 z0UI)rlwpVZ%O5J!7Mlp^w!MI=m}umiw!Sp(UO3g=iBS%|hpWjZw$LA1%bTRlb$KBP z+1R?9_l(MJ3Fmp~$mES@7wEf8D8!yKIA{Dt2q-xyNKQ zFiK~d8ppv>3QCE&u8A;Tuay;i>g?^^Z?umSFQv(Y#G+&)@MmM z{1jqsr$a$7q4Z~a6;`}#Rufm2s+24aIdb$YCcGMH&ZWuioW*!j@$oOE!1fRrPsHW5 zlmV*FU+X)zm_X5?qF*~{3?;WRMJhb|eX3{P&j&}=51$IFH6_|~&6*r*C`GaE?D;L% zH!QmC^W%*|h(_#MDjWno9nO$alf5Vj{I&t>q2xn(!CH8Ed3hEXSyJvw^t-Q$@J;^Vx2`zfulx{#hiknYmZVT!W{rcTEPJ+r^_$hM#il2ArgvsHn>FVjP z_!9S-N@|g4d#91>@zf$Sh6D!UC7%!o41s4ZU0C*V855Pa;kdk$zvjKP&r8wHaRcY= z)Kk+-K36b4DW2Dt`?S|le|ykjI$yWQnf%yql{rC~GDFE9!2`u+)Xk|3bWc^%r5YTa z@k*9j*y#<#S4s7ww8}dna|SZ-SmD@+M~6?{m)&n~zcaIGTpV=TRk4h^!dPEzzMaz4 zAc2<{mE4SQsk@sZt(*O2*Uh*+!Ob`#A+{JBEF*Uk@HQH?4wL+j_M}=Kd2o~VAWnGS zo4wyK!tu&`sl6tH3I&>iUiIhas=i05CF%E(2X%ZexQq?1Gr=&Lz`WQWfXr4)rQP?@ zW&FvGd)0(y?XLLb%rn~hRNYXz_#A#q@BB!PT^pp$E(0EPn~MTKb^=(AKsdbcLx7PC zl(xiNG%R>9U601ugPiH>M78KW|LL!a7QgN7FGn%O1-O`$lQ+0Wycv6wx3<;)HN#&j zvgJr< z#H$crfj}?vQTNpL0h_HT`84xkjuFxAPAf9O<~u8a_I@gP0mk|I-*N+n%YN?+QP!Ig zE0OWp>M0)A3qHOR$*yTy+$8R%5n~#OhilGWM2e2yJ64IwUxA4nC7DQ*7kl+ouX4!b zdUAi3OwP1EpWRz3`JIT}*5Qe+_Ww=vL^uxfUAaBN=e_UMI#i<+xro^9N!86Ch*sZT zauAPJm*)6VMjkAdH=0G+xU9C{YDEOX2t5H)VTY3Kg&W3tU0|WPWXlfWgKJEFVKI-> z$yBZRP--qOZ8sm@#s|+(LO0&)dB8Ck@tn+Wso7g1R{KuCW{!SfY5=I3?5r>pD4Ehb zPdO;;0a|P7lw^)oDsg%HC(Nvl3hwUzwp*5G_WO+vd<&&idg-`~iOV;T$iJ>GK@*r( zs8gn@nNyZ#+=V=7Mjk$E<5c%uRD=M#hT@VWb3C7ar{3>+F_BYra~rX5_ntFZt?f>8 z0dRdb{O(&3-#Paw`3z53>X+Ox10AqDb161bw9!AzCUfPI(}LI0jBK)5T|eifyB0>u z6%S9oHS~8T+BQ5U*z1YEv*>vT6KFcsts1O5+ZL;3P}4j^%% z6dCUXFnq2yZ$q+U>lw*sNtY#?hn&@RdOK?{Nh$1_)2N7Ll}gCIk*NOZG0ma1^;OcR zkCuSIQprFY#Tj;KkLa&oHKeT5dNsUjTCZ`y+f0D!-DI}$zXM;UN8d&U9x&CSa&GMd zyPK6i-k|x10Dq{U52mZed1Enus!&-CSVT^58pN~om+3d*T;}p1Y88^FN7klSa4tGhZpRKvVK@o{SF#Z$gsO_Lfu#z})U8@EMoJSZ%fy(NFjQr*)y(31< zz1VI3y2gD@9n_7zs!f4|U0`@H@T=t&- z^ci&#xx_`M0@FBlN<|>VY=iH@9rvgWLmpmUCFb)+T%hmtcVhhZe@sJpz7!08T^Op! zVUxZWwY9IwARczOr>)5K1`hU<_x(#JGec>e;duYNq2tT0Y6*w_IF$(ID^YO#SDfNM z*F`e4%tjUWu^ubK20P0`(@mCd@0Y9TzFh6XGW%wz1NO_6W?z1$hB@9r#5Ac8+9Yhd zQE~*l6$iyS#TUQ7%W!>wgJyxVB+tFD@Vt~7;SpFx$87A(hj!=c6 z%!;-cu=fe)s>>jS3^?gy*Zw;Y!YFeBN$jVbQ)38w_M+g<4X(wO0=wO>Y(AD7YKIyR*5wa~S8Y$x}b8x*Sj zw+cP9Zl}Tlm3(GcrJP!&?(M->l{@8JbPQmL&!6q?3S32e9e8i(fKN%V?tFFSlH=fo z*7o*aH?Q9Qk74iX;EH#bhr5cQKpGP&9n|aI@^RBi!6Xx6L_dX{NCe$kBQz~K9pbm9C8S=qhJb7; zpvw)Lh`?>0_3Zm{=^H}9k3f;UKtKHjpLGjq=z(P!$6w_Tei8HVA?!2=hBQ*7`&LpX z3QngzNPZcB_#6wQy3FJ4pkWCt%^vdpLXs`%)V3v%6%>3;+SBxxx@rW1JqTDiciB zc(8e6s3EX!YiS@#3lePf-&JMkouV3Q`hx_hef%E^f0FOo2!H5%)BWO1*t1jsIW9$J?0;?pYw6FISnn3p= zJ1yD4^yb}NLC%#25Y|(!lQ_&*TGywv)PFS+X8vSp55Wm1f1>N4F<ZMz(lwHm+V}Eg01FW|7``KjN+1)pLn_0^~sj# zvgmxLK<5-@%=$S(Sp$$Fyr*IcL1iR>JC6M&(YamCDXEhFC7N4_@5)PJJO0<*e=Q== z7rwr}mjdvvFQq;6M>-m^PnYzcbu~c)PKkgpq&By|oVg66&&_@G#%{}7Z?NfDsc1rP zufEh!E6GXz!?kqxF{7iVrzIUtlm8q=PKKv{nVKWwsK6fZNA@F;{lZ z=RfiHa?q%Y7)X%M>6|YsNu9+jzzDdSqG=%nK`z&PbfC^?@kWWBVM^_1XO*S?qT~QD zU?->e9_4?Zz6tZfS`Qd|b26k zIIi9!9PB!mKd1Psh)}pkXfu-`i3Br;8V#?N`vb0g0E^?c;7xlDkSq_(quNEZstfiK zMaS3n_UBULK1!W3YqA3BC9x9!nJWVPjb`#0k_`tB)Z*LimsSP{{t~8#K2~Jc5PTUx zqdoEIzjFUbqiJBG8T^BLQL|5^V>>4wCi)sy`NBW>@`p;;5L~-ao*# z%SE&NJ!iNBv@WuiNZ6k}5sVKR^k)$?{B?Z5XN+V>qSs-S8Yc;95C{jEpFQC;^0ads z_S8kiGg`0C=H=!6FS)K69f=a~_uhl4EzUHwc1;S%;~!@L(@}~7ru#@_fN()DJH|iF zuu0Bs!#IiWIogQ;38`(>;`rA^n1b)DO^Ak2oJe>??S$tyY*i&&F8Cm>0VF9DcM+&= zd?FfU0TZqo+(*)kGB!>=q<+;5LrOaSFV6v7VDPO|^47w4HIkdV^TJ6E#hO0E(Z$D& zMpJ-AWG!NtFehP}+ZCQLaiOx&x#lyPNtaU~k|4|bmnV+34Ou5%zyAhXy?oB8 z+q54)F@H|#B^_##4|7RPTwYLs6R<)S8gTwMe8vowd0MsO5D&@0`4YjhZ`^U|-*df& zFDK=?&vo8G61_V5bb#tqJiIYwp(F?>b5<}R_Ff5GX!Hq;tl7@v$5$sOhMV`E zGcz$Vwmds6_F1*I9t_fXd1jrDUG5(NN6AtT_6D7hT}PAV-`yQh7WoeEkkaLCwhk)i zMxz9Q-im{Lx(k=38dR>Th)k^ctT_YhZ}T4R|4w|$BvdP?_}|stCo8Y0m<25= z33F+|r-Qn3eT zN;RXJS7GycIs+4Bv$M0U$u13or%#{$miN3v_4o)@}RxL(ewZU1w^_FXDV_l?;Z6$pmNU~wExte9knO)-{=R@R9- ze|O!Uy>I@?X}FTa*J@GfEK5B$*GK%SXu`#YKntRoN|rw5bse0K;xtKmjXn{5bO7IO>xQkmLlpy5-$NFFXdNwd6C-WBE{yGT7P6gasMko7Gb3q} z|D{$b!g1uS=Huh1dC~%RUMB7(+TP!v4Ngo{B+;rHae*9V%a%t5*l{_c>rJiixRd4n z!z!C4xxIPbYL%9-zs^ReDXv(>v5+SUFw@U62TmlBTb@@x94!NFg1F8pi@-nfTO1o7 z%!J5@2W3zFCO+SB90OzO&o3-2e9O}l5LWz`o7%LGxCsn{)&kmc%`4M4X~PRE?{Hx- zjR$zRagZp8vU=(Yk83`P#Z25Fb3CsgK=ib@ZL~(J>}{ZNf65%Y@zbg@tdc&0HWi#SJ%fu!jL8)Mdj_SGGqG3YqJ!=~T+ZLf82PxPGFoP( z<~#L>Wt5ql^RLmKG{zNQTsHn>H9b*my`1#8snNmi5)WMvw6TjdESO~w21CTrwG4EL ziSO8N^{$WTB!ZAb%%(H0BU!2)O!yVbGb+nq=KXh980vdAHtm{HV|*v%@acn9kK6B^ z4$j>0rx|!;+OH{jq?yzp4@sKIl6k&(yYaQ99(k=gi>WK-*EQUbP1?%;3STU2BA8h3 z-cAHGR4+caRDoR$j&Gu{w!X^_Lnv$E)MkL-ogTT&Nn5Q@P;y)KX4%Df39vgls zh;usqAF)(mk|WV(H+&oqMyj5gRdnM;AI>mg&N2d?q|&CU3q?_4IJbZCBZ=qA-uFgH z8l-D>xvx|WJgpb__hbY(E!*WuQ<*`D(B>tII@N_YmILR3stvyj<$xXm4cwDT7P%tH zxjKE#(rFP%QZ6ETZ!}^gordwIUH^vm7Q36XBp>>AV^BTIv%{oaa;)7S3E>gIaiL=> zY}yfUo>DF9p~S-N@&R|f536H!)|)e}u`w~+BnOm=Y5uqSwCX=*;H1Ljd{5>kXfV<% zuK9@^s%qz$@=kGdg|HUltp@m8Kv+f3lFm=Jp@*a8qKif)CHSdY#U*|V+}nB}eQdG6 zXes;DA7}`_puCD$x`R-p=A9I zr1|qZh&SXjp7P#r`gQJZ{lxm>p3df4T^7&P(c7SwpWkk{_bKpekIW1g#}QBis!&tT zgJ$TZseod#;{L(;Kil&o1KacKqVazWw&!0t8G-a-K?%?pvIo9=`SQ|(AIfE?VaCVtSa}9Ul)$X zfr9dsHOKwcw`;B^T8n9xkspegy!_6p-2w+wAc^9K=S?EKbfLH*;~i`4&Yi{F+ZAtQ zH$F35a+|p(`lTh}AN@JYwZ7Rd+76nKg~5}1JGE6^mFTNW#QK4UGingbAmb!0dQjG7 zSptTk8e|Eu!OGju)D?XeIa^%6V_b8v**@FM8SG*5?638sQ-$tpy2Slm$2ehsRvgGdg9ATXz+VVJ{&KE(xd!}K0E-CwNM@=;XJUu z^*i0Lg?ZO3&6|e*6lNZbrOKQ;aeBnx8Y`fQB6T<4!40CqXbPFUX-4- zSB&6ojDtB2&GuX022JmGo)lVnm=(Cc;|8Rs70mKgFP!E5$GX9JEGF`H#_T7`pyC9n z1<4hev4#=510fiQIb`L2QJ;b#_@3eH{IVn^al**dRYvK;6ka^w1l5->sytjcyL11H*&&H$& zM;(R$Ce#k%(X31u2Aw~iPM+F!W@syb1}`yF1hN;tmdB&DWYpF%<) zsfr(AaJsW+ETUlV(_@Nz?tJphnF650+}D#gYuv2KDfKTOrLiC|Ys#6qxpvTABSLZl zNI-IY-$1RR8U9@p6OuK++Tt`=(*jm3`g2MVd@;*>E{s7UXvzRMkIbD`P!NRwHjX>^ zqaHuk<3P96EQqJ$0Cp!GZDp48XxWgceN0f}G@S0*c_=wlqwrTp4-eflw3(4)@CzB)Mmmgm2{%M7H0lP0#wcPx*k42LQ0*ET_%ehagz% z?G4!vYqF*33f9}(SrO8f-3S$TS@q4p_(;wU{yC_|B#>WkBmunGpq_}2|DtN1knE)+Ev%f4)?E7iRjjK8V0$Fw^kwTDL)(wMEiwgDb4WmP$(PosKSZoS40^`}8V-5iLIc)|tXxyy@66gT z7qP)&xuRbH&p7(UTuP=Y$OKY(TBX@*^yd~>vPh+_%9US<1@Xr1*3QlZ;Iv(vZHup> z`bXAcm`b4p!rnk*o5X-AFK7!oanHulBRKh+OL~(m&3-4qIUq1ghIJ2mj<@yccY(<8 zL0P$6U#TvkXlnr%x`9FC?SM=oc6N3ceFY#>BsqC_vXpiqC)H1#^rpyAy}x(5XHoaV zY6l~MD_UEOayb7TS89AoduHvs4Mf&3*(96XYAG^g43VI0fmJIJdOy*DuUV-9?bEgIXSGq z#J6L46~4cIc0L+hpZb)oNJhvayL;_uo0xL4NRF%LfGhhzI(4AL+}!Kuw*iiRgD891 zj0d?}j)f#p_M-r<&kgUG2bBIO^{e9B#QX;-s;H3XcYLNEZFVc2xZW6aIO-Dr>W{X; z)4#at|5kP#ckJ$T5B$ZlAt6zmkiKDs_Plu5bHt64q3A&GLGSRuM0gLr zdhNo#NX42sk|^Ne3oz^$X5bWDOM|`zoR&>F*b*dVH;B5veWQKGrDlwNJxBiVy5IKV ztf-8Q>x@iw-{s_4HF{y?|H;AOd(ILnoCiR!3{JJ`ulbha_QLO6-RwgS2f2ErLM^g5o+y za?abnf7Aka+X)P^_Nz2>wHIOA1SzNhP$-FPW?nA?kGK@Umx}}N)13H0qKFyt8Cqf| zt+%v^m}+q@<2;b;n;awo?so+k1?Wos?liOU1w>lWF9O(F1U|~MBRzd|U_z>zkhT0S32|wPCz`K|l8z(*ExN1zQ zb+#L>v}<)*2hs%gLPbVO;znKFd7+UcejPx5Awxw?^%uuU!yW@tRCkgi*e+P0M+1M4eJe7N;!L)r2m3&yvdEZ)jrmA@yk9{%onv|< zSFFTAj-An`G?T*+bkvI_EMIzjFE2BJPq;os-7woEia)mFODXUF^bpvsE9_8G z@=-nhobTD|!2Kq13^mD8#|i4adQ5DKSC~j~sVCQyAQ)O{=KhY~BmECsdJuCPcf$99 z$(v%fea(umZ!$m%esC-VwIXhyGxPF?%g_(kbeX}~*F{RLnrVGOo?#ZSR`uXMmtI3c zu+p>((jlZU;s@y%y`+@fc1}1u2tw$nU*&C&Wj@GMO;W|RuEO4d+RoYHy490)G9<4V zP+tzZA5R3+TBAXXYqmT<!UCzr8WeAiL=IJrSj;wa* zT`wE|o&icY25WdN=DT0VCM7NAoqu_R=LAsC{QTJlikv{d%ZVIy{Y2vi+B({A+oUi#dd87b z%_nsy!#IPYNiH%8_pkGMl$Fw8zzwYNooNfj47YC5C4>AFPW~96`s53gO5o|&57L@y znwiN6=E+ynK@z^x40^b?{Ty5ePAC(&-(1m09{VN#9mlx=0w|?u>&cYl3huT`>0b|T zDnR-w45*UeVAL}bMW+R#$3PSmO+7gAB?#&5u!T@rAr;D$Sv9x4xM-CHLd(~=vC^aq zeMjHY))q_2aHa*nG0iU~CZ^w9g%a{JwR)?1&F`dQl~pe(`=_i6(ofeGE1Zr>LX#D{ zrD4mR=(Z25jv(CMTAMIejp7gnpalKC1_^aLsRJ^W=0Cr_=^f_c;w1iVbhADE+g0@*W#NowH? zkluEcG<~Yl9`lnP@Z+3vrunyukoop`q|8+~6$W7Q?SEJv3#T2Vz0A|qw9F^(MpwW->pN%34aG*RHCN6OM8Y@n<)RFCVr&|0RY71>r_X&3i(Fc!%}8dlt}XMY9soGa-H#FKXL`s84x z-ShXLrBEv~xFab#vfeOvA7A|V>WIABC%Hf9#zQ(F@CezbBrA}O7)*h9siDVCTX-Wv z`G$C*AiVdGK2{831t1Js*A}yZ(MRD5aG){6`BJy>8UT2==mKYoe}lp1_ryiVe6 zJRG100sy}B2wF*#a=DZHW>sj|a!J7`*Rp!IG5bS$&{GtN{S<}i$|HJiuASN@nd%2; zT;Fhjgl?vIXD{NmI`H1XXfWwx2!IFPwC~GjDzD8$0G2hG1ZkKAP>Nwf%0T{_>q;CQ z9laUi_>D?{Gpb`x{RmA)WCC8W{G4r1I%XL9aBQXcqOnyj&{!?8W2d`;hRzYQSfBi$ z3mm5CK22a+qEEp9qb635`?cF`i5gd)?QPs*&CsO0plKD`M1kB{l0R}>(%DFn3&lN2 zpjQ!bJU0yzPYjfHqG_{dD19F2>c)C3_O>n@FI^x5sR=e$Ge)2<@ywuf!2@})bAw2V z93DNgJniC=cg{F7_-PXX&pbO%67P8uMk?J!?f(ccoPcXF$L3kAgE-PIRJO`_%vhxH zz)gVE;UJmI1-5Zg4@bTNvfD37Zw_h|8kO;w(wS8K7!W_>Q271G?LjEtL>Dw7epG-q ziz5P#+-J0fmxA1XVPNzz&F(#ISxe0cWkS+q+C)e$;J%ON{2+w6iR3{}`fj4g7$ zsWe$i$p`lO3v%>F3F62DLW1LmKd!*Zk@&KK%#j(>F0N|3HE?YJ;R_sqSW~xFet}im z%Ovh?pg9MQ-vX*GD;GYO9B>BVUjscE>YzsJ@I_3bu!opRo{2v~3FyKlfe)Us@~l*z zq;p*`-#s?CXzIk$6IGw88tFzb3ulpZt#}A*%J?5j&-+6E3nAAX0 zw8K>npDJEkRsCFQm!)yo>gv9nt8l~&06GSX<7Y}8NX#h+49+ecO|nXQo$4Tu|Wok46rX9J516j?P{Y74FSyNnmCOUJ+8OSByZ%fHPUh@`LywiHj+dM2<8;Y z#HwmB);&351q&hq9eMobN4Z0p&ECrG zAYhQ$v4?+mPD&CrX>-+)Lb*5|3oKC%xs!`B$1Wl*wXsME=^k#26j&qvi|EG&O-VHB zJtcTbNlE~@zavZRFOqzIrzWeuHr`v}jln^%tuhbXj{mjXAh9D1aPixz6dPFQ zuR&AQMj4h}_&>_+hdv<4<*Q|E%zz>{AEU&~Y`#;0rW8Y(Wam;-UEtCI-#~S^+C>yV z_pTpTkzCx-XJI=I1Yjvr@XHll_dq%TR0^1-hPizICEQ<1PCSpL`G*>C(mg6@iHH4j zkJIE^{iU4{|9k1hsx@yw2$(v5{7-{{_KE#roTh5+>dFLvCQv>2fDoS{rM6!dp?yW*OEWny5yB+ zfCyEa1emI9Pm8Giy325-pe(DqVS&V|p&g?HUjEzfe##@=d{he%Zq|}g$r3;cqUplP zp#HMyIgdRNrujX}ycv0*hT~g?3iXw>_rJ9Ksy{ImJW* zYy>>1j3_WFA>=d}cZq{f=1&3-$PKyM-z_>mNCYM~(@t$*sdI2LkU2tJ(k>dvB#F)- zw}%!GxpY+48^_&!bAZW{d~nIvdIsB)59*Ta`U_u*yNn-vE~u|A9YVy$#`2V#JBq(2 zMOSw-*GkMA3CfOxr5}7wQXYXcpuTJM=9HiPj~6s@hTf<$)rMiu`%!H=4BvHZxL<3tfsrh}w$ zxtRd3X1&sX3J#NH!AIAAU8;rFzkXx6#KaWa3SKdwaXkZSf@ek(Gn~LEy=Sol@(E-FN!mRjUT+C^h2!+c~H0s#l3_ z*bH(YJehYHKb-Qm{(a>1!|1L(6N-dboP9_-EZs$nm`8i#d3r3*TPe%&QmhOpUi%_L zwj8K4hXU9+ql&Syao(C!%2;;vt5?g)>gsJzem5ixSq`ynGw<&13XJa?-)}6ov9XbP zfM2&PEA46+A1TjJZID=xklJyo87t;&wkzNmN#wul&c-Pkc0^%1D-flB9;CGL20OyQ znM!5I)V-))6}6wL5>+57DC0|uSf>54vDm<%p{VsImcNcJ$r`x!H{YIcI66g!A(y82 z3JX$l1Qjt|veS^KT24-hu^^+We27I#@-%)KXf|)`8F2i2!xu?_g+_{q7b!9$m1 zdPk~mL_(L&mPTUKfzDE=8f1j_cUM0S+;~i=-rEf)Kk`gKh(K}yMYMP+NWKZzq#~&? zstoS<6B==!f81u?M)!bTc1)e9t4_ohdfck|nopFd(6u6mgFf{2WBjIIhq%@A)>whOYJ~>g4M)DZ{a5K;c z){~`2;st?5!FFC?JYj&hJ*kcut0c3%koQAI)5d_Z{}#^qOLfy;Dj;Z3;$w+b&_lGR zvR1UwwX*xivjhTa1eo?AQ + + + + + + + + + + + + + + + +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contrib/mac/frameworkapp/JuliaLauncher/Info.plist b/contrib/mac/frameworkapp/JuliaLauncher/Info.plist new file mode 100644 index 0000000..5e16115 --- /dev/null +++ b/contrib/mac/frameworkapp/JuliaLauncher/Info.plist @@ -0,0 +1,63 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(APP_SHORT_VERSION_STRING) + CFBundleVersion + $(APP_VERSION) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + Copyright © 2009-2019 Julia project contributors (https://github.com/JuliaLang/julia/contributors). See LICENSE.md. + NSAppleEventsUsageDescription + Apple events are sent to the Terminal to execute the Julia REPL. + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + NSSupportsAutomaticTermination + + NSSupportsSuddenTermination + + UTExportedTypeDeclarations + + + UTTypeConformsTo + + public.script + public.utf8-plain-text + + UTTypeDescription + Julia source code + UTTypeIconFile + AppIcon.icns + UTTypeIdentifier + org.julialang.julia.text + UTTypeReferenceURL + + UTTypeTagSpecification + + public.filename-extension + + jl + + + + + + diff --git a/contrib/mac/frameworkapp/JuliaLauncher/JuliaLauncher.entitlements b/contrib/mac/frameworkapp/JuliaLauncher/JuliaLauncher.entitlements new file mode 100644 index 0000000..e48ecf9 --- /dev/null +++ b/contrib/mac/frameworkapp/JuliaLauncher/JuliaLauncher.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.automation.apple-events + + com.apple.security.temporary-exception.apple-events + com.apple.Terminal + + diff --git a/contrib/mac/frameworkapp/JuliaLauncher/julia.idraw b/contrib/mac/frameworkapp/JuliaLauncher/julia.idraw new file mode 100644 index 0000000000000000000000000000000000000000..d3ffc06d61343e11f87807becbac054da52229ff GIT binary patch literal 72288 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

P_x>iFDDTZ7gutMFje#>4;!KK6A2yh#-p1=7_!} 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

t^(=`C# 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 ab-U(##(2}_^^a}<|8IilFJfHa=YIg5@z<9C literal 0 HcmV?d00001 diff --git a/contrib/mac/frameworkapp/JuliaLauncher/main.m b/contrib/mac/frameworkapp/JuliaLauncher/main.m new file mode 100644 index 0000000..04aedef --- /dev/null +++ b/contrib/mac/frameworkapp/JuliaLauncher/main.m @@ -0,0 +1,7 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + +@import AppKit; + +int main(int argc, const char *argv[]) { + return NSApplicationMain(argc, argv); +} diff --git a/contrib/mac/frameworkapp/Makefile b/contrib/mac/frameworkapp/Makefile new file mode 100644 index 0000000..93392cd --- /dev/null +++ b/contrib/mac/frameworkapp/Makefile @@ -0,0 +1,124 @@ +JULIAHOME := $(abspath ../../..) +include $(JULIAHOME)/Make.inc + +default: productarchive + +# The following variables default to development environment values. +# For a build intended for distribution, they must be overridden. +# Set APPLE_DISTRIBUTE_DEVID=1 to use automatic (It Just Works) distribution +# signing. +ifneq ($(APPLE_DISTRIBUTE_DEVID),1) +# Identity to use for signing products. +DARWIN_CODESIGN_KEYCHAIN_IDENTITY ?= Mac Developer +# Identifier of Apple Developer team. +APPLE_DEVELOPMENT_TEAM ?= unidentified +# method value in -exportOptionsPlist flag to xcodebuild. +XCEXPORT_METHOD ?= development +# Identity for the product archive (the .pkg installer). +DARWIN_CODESIGN_PRODUCT_ARCHIVE_KEYCHAIN_IDENTITY ?= - +else +DARWIN_CODESIGN_KEYCHAIN_IDENTITY ?= Developer ID Application +APPLE_DEVELOPMENT_TEAM ?= unidentified +XCEXPORT_METHOD ?= developer-id +DARWIN_CODESIGN_PRODUCT_ARCHIVE_KEYCHAIN_IDENTITY ?= Developer ID Installer +endif + +# X.Y.Z +JULIA_VERSION_MAJOR_MINOR_PATCH := $(JULIA_MAJOR_VERSION).$(JULIA_MINOR_VERSION).$(JULIA_PATCH_VERSION) + +FRAMEWORK_DESTDIR := $(BUILDROOT)/julia-$(JULIA_COMMIT)-framework +JULIA_FRAMEWORK_PATH := $(FRAMEWORK_DESTDIR)/$(framework_directory) +JULIA_FRAMEWORK_LIB := $(FRAMEWORK_DESTDIR)/$(framework_dylib) + +# Xcode configurations: +XCCONFIGURATION ?= Debug +XCARCHIVE_SUFFIX := -$(JULIA_COMMIT)-$(XCCONFIGURATION).xcarchive +XCARCHIVE := $(BUILDROOT)/Xcode/Archives/JuliaLauncher$(XCARCHIVE_SUFFIX) +XCDERIVEDDATA := $(BUILDROOT)/Xcode/DerivedData/JuliaLauncher$(XCARCHIVE_SUFFIX) +XCEXPORT := $(BUILDROOT)/Xcode/Exports/JuliaLauncher$(XCARCHIVE_SUFFIX) + +PRODUCTARCHIVE := $(BUILDROOT)/$(FRAMEWORK_NAME)-$(JULIA_VERSION).pkg + +$(JULIA_FRAMEWORK_LIB): + $(MAKE) -C $(JULIAHOME) DARWIN_FRAMEWORK=1 prefix=$(FRAMEWORK_DESTDIR) darwinframework + +$(XCARCHIVE)/Info.plist: $(JULIA_FRAMEWORK_LIB) + xcodebuild \ + -project JuliaLauncher.xcodeproj \ + -scheme JuliaLauncher \ + -configuration $(XCCONFIGURATION) \ + -archivePath $(XCARCHIVE) \ + -derivedDataPath $(XCDERIVEDDATA) \ + archive \ + MACOSX_DEPLOYMENT_TARGET=$(MACOSX_DEPLOYMENT_TARGET) \ + CODE_SIGN_STYLE=Manual \ + DEVELOPMENT_TEAM="$(APPLE_DEVELOPMENT_TEAM)" \ + CODE_SIGN_IDENTITY="$(DARWIN_CODESIGN_KEYCHAIN_IDENTITY)" \ + JULIA_FRAMEWORK_PATH="$(JULIA_FRAMEWORK_PATH)" \ + APP_SHORT_VERSION_STRING="$(JULIA_VERSION_MAJOR_MINOR_PATCH)" \ + APP_VERSION="$(JULIA_COMMIT)" + +$(BUILDROOT)/xcodebuild-export.plist: + /usr/libexec/PlistBuddy -x -c "Clear dict" $@ + /usr/libexec/PlistBuddy -x -c "Add :method string $(XCEXPORT_METHOD)" $@ + /usr/libexec/PlistBuddy -x -c "Add :teamID string $(APPLE_DEVELOPMENT_TEAM)" $@ + + +$(XCEXPORT)/Julia.app/Contents/MacOS/Julia: $(XCARCHIVE)/Info.plist $(BUILDROOT)/xcodebuild-export.plist + xcodebuild -exportArchive -archivePath $(XCARCHIVE) -exportPath $(XCEXPORT) -exportOptionsPlist $(BUILDROOT)/xcodebuild-export.plist + +appexport: $(XCEXPORT)/Julia.app/Contents/MacOS/Julia + +$(BUILDROOT)/framework-component.plist: $(JULIAHOME)/contrib/mac/frameworkapp/framework-component.plist + sed -e 's/FRAMEWORK_VERSION/$(FRAMEWORK_VERSION)/g' -e 's/FRAMEWORK_NAME/$(FRAMEWORK_NAME)/g' $< > $@ + +# This target makes a component package for the framework. It has some +# important properties. Together, the properties allow one "Julia.framework" +# to exist at a location with multiple versions of Julia within. +# +# 1. The component's identifer is versioned to match the bundled framework. +# This allows multiple versions of the component to be installed with the +# Julia.framework/Versions directory. +# 2. The component-plist identifies the Versions/x.y directory as an upgradable +# bundle instead of the parent directory Julia.framework. We want the +# installer to treat each version as an atomic unit/bundle and not the overall +# framework. +$(BUILDROOT)/$(FRAMEWORK_NAME)-framework.pkg: $(JULIA_FRAMEWORK_LIB) $(BUILDROOT)/framework-component.plist + pkgbuild \ + --install-location /Library/Frameworks \ + --version $(JULIA_VERSION_MAJOR_MINOR_PATCH) \ + --root $(FRAMEWORK_DESTDIR) \ + --component-plist $(BUILDROOT)/framework-component.plist \ + --identifier org.julialang.julia.lib.v$(FRAMEWORK_VERSION) \ + $@ + +$(BUILDROOT)/launcher.pkg: $(XCEXPORT)/Julia.app/Contents/MacOS/Julia + pkgbuild \ + --install-location /Applications \ + --version $(JULIA_VERSION_MAJOR_MINOR_PATCH) \ + --component $(XCEXPORT)/Julia.app \ + $@ + +$(BUILDROOT)/$(FRAMEWORK_NAME).dist: $(JULIAHOME)/contrib/mac/frameworkapp/Julia.dist + sed -e 's/MINVERSION/$(MACOSX_VERSION_MIN)/g' -e 's/FRAMEWORK_NAME/$(FRAMEWORK_NAME)/g' $< > $@ + +$(PRODUCTARCHIVE): $(BUILDROOT)/launcher.pkg $(BUILDROOT)/$(FRAMEWORK_NAME)-framework.pkg $(BUILDROOT)/$(FRAMEWORK_NAME).dist $(JULIAHOME)/contrib/mac/frameworkapp/installresources + productbuild \ + --package-path $(BUILDROOT) \ + --resources $(JULIAHOME)/contrib/mac/frameworkapp/installresources \ + --distribution $(BUILDROOT)/$(FRAMEWORK_NAME).dist \ + $@ + +productarchive: $(PRODUCTARCHIVE) + +signedproductarchive: $(PRODUCTARCHIVE) + productsign --sign "$(DARWIN_CODESIGN_PRODUCT_ARCHIVE_KEYCHAIN_IDENTITY)" $< $<.signed + mv $<.signed $< + +clean: + -rm -rf $(XCARCHIVE) $(XCDERIVEDDATA) $(XCEXPORT) + -rm -rf $(FRAMEWORK_DESTDIR) + -rm -f $(PRODUCTARCHIVE) + +.PHONY: appexport clean productarchive signedproductarchive +.INTERMEDIATE: $(BUILDROOT)/xcodebuild-export.plist $(BUILDROOT)/framework-component.plist $(BUILDROOT)/$(FRAMEWORK_NAME)-framework.pkg $(BUILDROOT)/launcher.pkg $(BUILDROOT)/$(FRAMEWORK_NAME).dist diff --git a/contrib/mac/frameworkapp/README.md b/contrib/mac/frameworkapp/README.md new file mode 100644 index 0000000..43cee0e --- /dev/null +++ b/contrib/mac/frameworkapp/README.md @@ -0,0 +1,26 @@ +New Julia Launcher App +====================== + +This builds the Julia framework and a launcher app and packages them in a +product archive for the macOS Installer. + +Run `make APPLE_DEVELOPMENT_TEAM=xxxxxxxxxx` to build the product archive. The +resulting archive may be installed to the home directory with +`installer -pkg~/Documents/pkgs/Julia-1.1.0.pkg -target CurrentUserHomeDirectory`. +To just build the app, build the `appexport` make target. Read the comments at +the top of the `Makefile` to set appropriate code signing parameters. + +The framework is installed in `/Library/Frameworks` and the app in +`/Applications`. Installation may be system-wide (i.e., relative to `/`) or +local to the user's home directory (i.e., `$Home/Appliations/Julia.app`). + +The `julia` binary is embedded in the framework at +`Julia.framework/Helpers/julia`. + +Multiple versions of Julia may be installed at once. Each version is placed in +the `Julia.framework/Versions` directory. By default, the version is +identified by the Major.Minor version number but may be customized by setting +the `FRAMEWORK_VERSION` make variable. The resulting product archive will not +overwrite other versions but will upgrade a version if it exists. Thus, the +`1.1` framework version that is actually the 3rd patch (1.1.3) will overwrite +any existing `1.1` framework version. diff --git a/contrib/mac/frameworkapp/framework-component.plist b/contrib/mac/frameworkapp/framework-component.plist new file mode 100644 index 0000000..43f71a1 --- /dev/null +++ b/contrib/mac/frameworkapp/framework-component.plist @@ -0,0 +1,18 @@ + + + + + + BundleIsVersionChecked + + BundleHasStrictIdentifier + + BundleIsRelocatable + + BundleOverwriteAction + upgrade + RootRelativeBundlePath + FRAMEWORK_NAME.framework/Versions/FRAMEWORK_VERSION + + + diff --git a/contrib/mac/frameworkapp/installresources/conclusion.rtf b/contrib/mac/frameworkapp/installresources/conclusion.rtf new file mode 100644 index 0000000..8d794ae --- /dev/null +++ b/contrib/mac/frameworkapp/installresources/conclusion.rtf @@ -0,0 +1,80 @@ +{\rtf1\ansi\ansicpg1252\cocoartf1671\cocoasubrtf200 +{\fonttbl\f0\fswiss\fcharset0 Helvetica-Bold;\f1\fswiss\fcharset0 Helvetica;\f2\fnil\fcharset0 Menlo-Regular; +} +{\colortbl;\red255\green255\blue255;\red249\green249\blue249;} +{\*\expandedcolortbl;;\cssrgb\c98039\c98039\c98039;} +{\info +{\author Stephen Larew}}\margl1440\margr1440\vieww14400\viewh9600\viewkind0 +\deftab720 +\pard\pardeftab720\sb120\sa200\partightenfactor0 + +\f0\b\fs40 \cf0 \expnd0\expndtw0\kerning0 +Conclusion\ +\pard\pardeftab720\sb80\sa120\partightenfactor0 + +\f1\b0\fs28 \cf0 Julia is now installed. Running Julia.app will open a REPL for Julia in Terminal.\ +\pard\pardeftab720\sb120\sa200\partightenfactor0 + +\f0\b\fs40 \cf0 Command Line +\f1\b0\fs28 \ +\pard\pardeftab720\sb80\sa120\partightenfactor0 +\cf0 The +\f2 \cb2 julia +\f1 \cb1 executable is located at +\f2 \cb2 Julia.framework/Helpers/julia +\f1 \cb1 inside the Julia framework. In a shell, run\ +\pard\pardeftab720\partightenfactor0 + +\f2 \cf0 \cb2 mdfind "kMDItemCFBundleIdentifier == 'org.julialang.julia.lib' && kMDItemContentType == 'com.apple.framework'"\ +\pard\pardeftab720\sb80\sa120\partightenfactor0 + +\f1 \cf0 \cb1 to find the Julia frameworks on your system. Alternatively, run\ +\pard\pardeftab720\partightenfactor0 + +\f2 \cf0 \cb2 mdfind "kMDItemFSName == 'julia' && kMDItemContentType == 'public.unix-executable'"\ +\pard\pardeftab720\sb80\sa120\partightenfactor0 + +\f1 \cf0 \cb1 to find julia executables on your system. (The commands assume the files are indexed and searchable by Spotlight.)\ +\pard\pardeftab720\sb120\sa200\partightenfactor0 + +\f0\b\fs40 \cf0 Uninstall\ +\pard\pardeftab720\sb80\sa120\partightenfactor0 + +\f1\b0\fs28 \cf0 To uninstall, simply remove +\f2 \cb2 [/Applications/]Julia.app +\f1 \cb1 and +\f2 \cb2 [/Library/Frameworks/]Julia.framework +\f1 \cb1 (e.g. move it to the Trash in Finder or use the +\f2 \cb2 rm +\f1 \cb1 program from a shell).\ +\pard\pardeftab720\sb120\sa200\partightenfactor0 + +\f0\b\fs40 \cf0 Path Modification\ +\pard\pardeftab720\sb80\sa120\partightenfactor0 + +\f1\b0\fs28 \cf0 In order for the shell to find and run the +\f2 \cb2 julia +\f1 \cb1 command, the +\f2 \cb2 PATH +\f1 \cb1 environment variable must contain the path to the +\f2 \cb2 julia +\f1 \cb1 binary. Either add +\f2 \cb2 INSTALL_LOCATION/Julia.framework/Helpers +\f1 \cb1 to +\f2 \cb2 PATH +\f1 \cb1 , for example in Bash shell\ +\pard\pardeftab720\partightenfactor0 + +\f2 \cf0 \cb2 export PATH=INSTALL_LOCATION/Julia.framework/Helpers:$PATH\ +\pard\pardeftab720\sb80\sa120\partightenfactor0 + +\f1 \cf0 \cb1 or link a file in +\f2 \cb2 PATH +\f1 \cb1 to +\f2 \cb2 INSTALL_LOCATION/Julia.framework/Helpers/julia +\f1 \cb1 , for example by creating a symlink with +\f2 \cb2 ln +\f1 \cb1 \ +\pard\pardeftab720\partightenfactor0 + +\f2 \cf0 \cb2 ln -s INSTALL_LOCATION/Julia.framework/Helpers/julia DIR_IN_PATH/julia} \ No newline at end of file diff --git a/contrib/mac/frameworkapp/installresources/logo_hires.png b/contrib/mac/frameworkapp/installresources/logo_hires.png new file mode 100644 index 0000000000000000000000000000000000000000..d37c7ccdfd3f0598f8b6cfb36aeea4416b2dc7f8 GIT binary patch literal 26833 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 .jhbuildrc-custom +setup_sdk(target=_target, sdk_version=_target, architectures=[_default_arch]) +os.environ["DYLD_LIBRARY_PATH"] = "" +build_policy = "updated-deps" +modules = [ "meta-gtk-osx-bootstrap", + "freetype", "fontconfig", + "meta-gtk-osx-core", + "meta-gtk-osx-themes", + "gtk-quartz-engine" ] +EOF + +jhbuild bootstrap --skip=libiconv --ignore-system +jhbuild build + +cd ~/gtk/source +curl -O http://ftp.gnome.org/pub/gnome/sources/gtk-mac-bundler/0.6/gtk-mac-bundler-0.6.1.tar.bz2 +tar jxvf gtk-mac-bundler-0.6.1.tar.bz2 +cd gtk-mac-bundler-0.6.1 +make install +cd ~/gtk + + diff --git a/contrib/normalize_triplet.py b/contrib/normalize_triplet.py new file mode 100755 index 0000000..7bc5554 --- /dev/null +++ b/contrib/normalize_triplet.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python + +import re, sys + +# This script designed to mimick `src/PlatformNames.jl` in `BinaryProvider.jl`, which has +# a method `platform_key_abi()` to parse uname-like output into something standarized. + +if len(sys.argv) < 2: + print("Usage: %s [] []") + sys.exit(1) + +arch_mapping = { + 'x86_64': '(x86_|amd)64', + 'i686': "i\\d86", + 'aarch64': "aarch64", + 'arm': "arm(v7l)?", + 'powerpc64le': "p(ower)?pc64le", +} +platform_mapping = { + 'darwin': "-apple-darwin[\\d\\.]*", + 'freebsd': "-(.*-)?freebsd[\\d\\.]*", + 'windows': "-w64-mingw32", + 'linux': "-(.*-)?linux", +} +libc_mapping = { + 'blank_libc': "", + 'gnu': "-gnu", + 'musl': "-musl", +} +call_abi_mapping = { + 'blank_call_abi': "", + 'eabihf': "eabihf", +} +gcc_version_mapping = { + 'blank_gcc': "", + 'gcc4': "-gcc4", + 'gcc7': "-gcc7", + 'gcc8': "-gcc8", + 'gcc9': "-gcc8", +} +cxx_abi_mapping = { + 'blank_cxx_abi': "", + 'cxx03': "-cxx03", + 'cxx11': "-cxx11", +} + +# Helper function to collapse dictionary of mappings down into a regex of +# named capture groups joined by "|" operators +c = lambda mapping: "("+"|".join(["(?P<%s>%s)"%(k,v) for (k, v) in mapping.items()]) + ")" +mondo_regex = re.compile( + "^"+ + c(arch_mapping)+ + c(platform_mapping)+ + c(libc_mapping)+ + c(call_abi_mapping)+ + c(gcc_version_mapping)+ + c(cxx_abi_mapping)+ + "$" +) + +# Apply our mondo regex to our input: +m = mondo_regex.match(sys.argv[1]) +if m is None: + print("ERROR: Unmatchable platform string '%s'!"%(sys.argv[1])) + sys.exit(1) + +# Helper function to find the single named field within the giant regex +# that is not `nothing` for each mapping we give it. +def get_field(m, mapping): + g = m.groupdict() + for k in mapping: + if g[k] is not None: + return k + +arch = get_field(m, arch_mapping) +platform = get_field(m, platform_mapping) +libc = get_field(m, libc_mapping) +call_abi = get_field(m, call_abi_mapping) +gcc_version = get_field(m, gcc_version_mapping) +cxx_abi = get_field(m, cxx_abi_mapping) + +# The default libc on Linux is glibc +if platform == "linux" and libc == "blank_libc": + libc = "gnu" + +def r(x): + x = x.replace("blank_call_abi", "") + x = x.replace("blank_gcc", "") + x = x.replace("blank_cxx_abi", "") + x = x.replace("blank_libc", "") + return x + +def p(x): + # These contain characters that can't be easily represented as + # capture group names, unfortunately: + os_remapping = { + 'darwin': 'apple-darwin14', + 'windows': 'w64-mingw32', + 'freebsd': 'unknown-freebsd11.1', + } + x = r(x) + if x: + for k in os_remapping: + x = x.replace(k, os_remapping[k]) + return '-' + x + return x + +# If the user passes in a GCC version (like 8.2.0) use that to force a +# "-gcc8" tag at the end of the triplet, but only if it has otherwise +# not been specified +if gcc_version == "blank_gcc": + if len(sys.argv) >= 3: + gcc_version = { + "4": "gcc4", + "5": "gcc4", + "6": "gcc4", + "7": "gcc7", + "8": "gcc8", + "9": "gcc8", + }[list(filter(lambda x: re.match("\d+\.\d+(\.\d+)?", x), sys.argv[2].split()))[-1][0]] + +if cxx_abi == "blank_cxx_abi": + if len(sys.argv) == 4: + cxx_abi = { + "0": "cxx03", + "1": "cxx11", + "": "", + }[sys.argv[3]] + +print(arch+p(platform)+p(libc)+r(call_abi)+p(gcc_version)+p(cxx_abi)) + +# Testing suite: +# triplets="i686-w64-mingw32 x86_64-pc-linux-musl arm-linux-musleabihf x86_64-linux-gnu arm-linux-gnueabihf x86_64-apple-darwin14 x86_64-unknown-freebsd11.1" +# for t in $triplets; do +# if [[ $(./normalize_triplet.py "$t") != "$t" ]]; then +# echo "ERROR: Failed test on $t" +# fi +# done diff --git a/contrib/prepare_release.sh b/contrib/prepare_release.sh new file mode 100755 index 0000000..7d4e55e --- /dev/null +++ b/contrib/prepare_release.sh @@ -0,0 +1,106 @@ +#!/bin/sh +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# script to prepare binaries and source tarballs for a Julia release +# aka "bucket dance" julianightlies -> julialang +set -e # stop on failure +cd "$(dirname "$0")"/.. # run in top-level directory + +shashort=$(git rev-parse --short=10 HEAD) +tag=$(git tag --points-at $shashort) +if [ -z "$tag" ]; then + echo "error: this script must be run with a tagged commit checked out" >&2 + exit 1 +fi +version=$(cat VERSION) +majmin=$(cut -d. -f1-2 VERSION) +# remove -rc# if present +majminpatch=$(cut -d- -f1 VERSION) +if [ "$tag" != "v$version" ]; then + echo "error: tagged commit does not match content of VERSION file" >&2 + exit 1 +fi + +# create full-source-dist and light-source-dist tarballs from a separate +# clone to ensure the directory name in them is julia-$version +git clone https://github.com/JuliaLang/julia -b $tag julia-$version +cd julia-$version +make full-source-dist +make light-source-dist +mv julia-${version}_$shashort-full.tar.gz ../julia-$version-full.tar.gz +mv julia-${version}_$shashort.tar.gz ../julia-$version.tar.gz +cd .. +rm -rf julia-$version + +# download and rename binaries, with -latest copies +julianightlies="https://julialangnightlies-s3.julialang.org/bin" +curl -L -o julia-$version-linux-x86_64.tar.gz \ + $julianightlies/linux/x64/$majmin/julia-$majminpatch-$shashort-linux64.tar.gz +cp julia-$version-linux-x86_64.tar.gz julia-$majmin-latest-linux-x86_64.tar.gz +curl -L -o julia-$version-linux-i686.tar.gz \ + $julianightlies/linux/x86/$majmin/julia-$majminpatch-$shashort-linux32.tar.gz +cp julia-$version-linux-i686.tar.gz julia-$majmin-latest-linux-i686.tar.gz +curl -L -o julia-$version-linux-arm.tar.gz \ + $julianightlies/linux/arm/$majmin/julia-$majminpatch-$shashort-linuxarm.tar.gz +cp julia-$version-linux-arm.tar.gz julia-$majmin-latest-linux-arm.tar.gz +curl -L -o julia-$version-linux-ppc64le.tar.gz \ + $julianightlies/linux/ppc64le/$majmin/julia-$majminpatch-$shashort-linuxppc64le.tar.gz +cp julia-$version-linux-ppc64le.tar.gz julia-$majmin-latest-linux-ppc64le.tar.gz +curl -L -o "julia-$version-mac64.dmg" \ + $julianightlies/mac/x64/$majmin/julia-$majminpatch-$shashort-mac64.dmg +cp "julia-$version-mac64.dmg" "julia-$majmin-latest-mac64.dmg" +curl -L -o julia-$version-win64.exe \ + $julianightlies/winnt/x64/$majmin/julia-$majminpatch-$shashort-win64.exe +cp julia-$version-win64.exe julia-$majmin-latest-win64.exe +curl -L -o julia-$version-win32.exe \ + $julianightlies/winnt/x86/$majmin/julia-$majminpatch-$shashort-win32.exe +cp julia-$version-win32.exe julia-$majmin-latest-win32.exe + +if [ -e codesign.sh ]; then + # code signing needs to run on windows, script is not checked in since it + # hard-codes a few things. TODO: see if signtool.exe can run in wine + ./codesign.sh +fi + +shasum -a 256 julia-$version* | grep -v -e sha256 -e md5 -e asc > julia-$version.sha256 +md5sum julia-$version* | grep -v -e sha256 -e md5 -e asc > julia-$version.md5 + +gpg -u julia --armor --detach-sig julia-$version-full.tar.gz +gpg -u julia --armor --detach-sig julia-$version.tar.gz +gpg -u julia --armor --detach-sig julia-$version-linux-x86_64.tar.gz +gpg -u julia --armor --detach-sig julia-$version-linux-i686.tar.gz +gpg -u julia --armor --detach-sig julia-$version-linux-arm.tar.gz +gpg -u julia --armor --detach-sig julia-$version-linux-ppc64le.tar.gz + +aws configure +aws s3 cp --acl public-read julia-$version.sha256 s3://julialang/bin/checksums/ +aws s3 cp --acl public-read julia-$version.md5 s3://julialang/bin/checksums/ +for plat in x86_64 i686 arm ppc64le; do + platshort=$(echo $plat | sed -e 's/x86_64/x64/' -e 's/i686/x86/') + aws s3 cp --acl public-read julia-$version-linux-$plat.tar.gz \ + s3://julialang/bin/linux/$platshort/$majmin/ + aws s3 cp --acl public-read julia-$version-linux-$plat.tar.gz.asc \ + s3://julialang/bin/linux/$platshort/$majmin/ + aws s3 cp --acl public-read julia-$majmin-latest-linux-$plat.tar.gz \ + s3://julialang/bin/linux/$platshort/$majmin/ + curl -X PURGE -L "https://julialang-s3.julialang.org/bin/linux/$platshort/$majmin/julia-$majmin-latest-linux-$plat.tar.gz" +done +aws s3 cp --acl public-read "julia-$version-mac64 .dmg" \ + s3://julialang/bin/mac/x64/$majmin/ +aws s3 cp --acl public-read "julia-$majmin-latest-mac64.dmg" \ + s3://julialang/bin/mac/x64/$majmin/ +curl -X PURGE -L "https://julialang-s3.julialang.org/bin/mac/x64/$majmin/julia-$majmin-latest-mac64.dmg" +aws s3 cp --acl public-read "julia-$version-win64.exe" \ + s3://julialang/bin/winnt/x64/$majmin/ +aws s3 cp --acl public-read "julia-$majmin-latest-win64.exe" \ + s3://julialang/bin/winnt/x64/$majmin/ +curl -X PURGE -L "https://julialang-s3.julialang.org/bin/winnt/x64/$majmin/julia-$majmin-latest-win64.exe" +aws s3 cp --acl public-read "julia-$version-win32.exe" \ + s3://julialang/bin/winnt/x86/$majmin/ +aws s3 cp --acl public-read "julia-$majmin-latest-win32.exe" \ + s3://julialang/bin/winnt/x86/$majmin/ +curl -X PURGE -L "https://julialang-s3.julialang.org/bin/winnt/x86/$majmin/julia-$majmin-latest-win32.exe" + + +echo "All files prepared. Attach julia-$version.tar.gz" +echo "and julia-$version-full.tar.gz to github releases." diff --git a/contrib/refresh_bb_tarballs.sh b/contrib/refresh_bb_tarballs.sh new file mode 100755 index 0000000..d79c0da --- /dev/null +++ b/contrib/refresh_bb_tarballs.sh @@ -0,0 +1,48 @@ +#!/bin/sh +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Invoke this with no arguments to refresh all tarballs, or with a project name to refresh only that project. +# +# Example: +# ./refresh_bb_tarballs.sh gmp + +# Get this list via: +# using BinaryBuilder +# print("TRIPLETS=\"$(join(triplet.(BinaryBuilder.supported_platforms()), " "))\"") +TRIPLETS="i686-linux-gnu x86_64-linux-gnu aarch64-linux-gnu arm-linux-gnueabihf powerpc64le-linux-gnu i686-linux-musl x86_64-linux-musl aarch64-linux-musl arm-linux-musleabihf x86_64-apple-darwin14 x86_64-unknown-freebsd11.1 i686-w64-mingw32 x86_64-w64-mingw32" + +# These are the projects currently using BinaryBuilder; both GCC-expanded and non-GCC-expanded: +BB_PROJECTS="gmp mbedtls libssh2 mpfr curl libgit2 pcre libuv unwind osxunwind dsfmt objconv p7zip zlib" +BB_GCC_EXPANDED_PROJECTS="llvm openblas suitesparse openlibm" + +# If we've been given a project name, filter down to that one: +if [ -n "${1}" ]; then + case "${BB_PROJECTS}" in + *${1}*) BB_PROJECTS="${1}" ;; + *) BB_PROJECTS="" ;; + esac + case "${BB_GCC_EXPANDED_PROJECTS}" in + *${1}*) BB_GCC_EXPANDED_PROJECTS="${1}" ;; + *) BB_GCC_EXPANDED_PROJECTS="" ;; + esac +fi + +# Get "contrib/" directory path +CONTRIB_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) + +# For each triplet and each project, download the BB tarball and save its hash: +for triplet in ${TRIPLETS}; do + for proj in ${BB_PROJECTS}; do + PROJ="$(echo ${proj} | tr [a-z] [A-Z])" + make -C "${CONTRIB_DIR}/../deps" USE_BINARYBUILDER_${PROJ}=1 ${PROJ}_BB_TRIPLET=${triplet} distclean-${proj} + make -C "${CONTRIB_DIR}/../deps" USE_BINARYBUILDER_${PROJ}=1 ${PROJ}_BB_TRIPLET=${triplet} install-${proj} + done + + for proj in ${BB_GCC_EXPANDED_PROJECTS}; do + PROJ="$(echo ${proj} | tr [a-z] [A-Z])" + for gcc in gcc4 gcc7 gcc8; do + make -C "${CONTRIB_DIR}/../deps" USE_BINARYBUILDER_${PROJ}=1 ${PROJ}_BB_TRIPLET=${triplet}-${gcc} BB_TRIPLET_CXXABI=${triplet} distclean-${proj} + make -C "${CONTRIB_DIR}/../deps" USE_BINARYBUILDER_${PROJ}=1 ${PROJ}_BB_TRIPLET=${triplet}-${gcc} BB_TRIPLET_CXXABI=${triplet} install-${proj} + done + done +done diff --git a/contrib/relative_path.sh b/contrib/relative_path.sh new file mode 100755 index 0000000..ebb3095 --- /dev/null +++ b/contrib/relative_path.sh @@ -0,0 +1,31 @@ +#!/bin/sh +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# both $1 and $2 are absolute paths beginning with / +# returns relative path to $2/$target from $1/$source + +relpath () { + [ $# -ge 1 ] && [ $# -le 2 ] || return 1 + current="${2:+"$1"}" + target="${2:-"$1"}" + [ "$target" != . ] || target=/ + target="/${target##/}" + [ "$current" != . ] || current=/ + current="${current:="/"}" + current="/${current##/}" + appendix="${target##/}" + relative='' + while appendix="${target#"$current"/}" + [ "$current" != '/' ] && [ "$appendix" = "$target" ]; do + if [ "$current" = "$appendix" ]; then + relative="${relative:-.}" + echo "${relative#/}" + return 0 + fi + current="${current%/*}" + relative="$relative${relative:+/}.." + done + relative="$relative${relative:+${appendix:+/}}${appendix#/}" + echo "$relative" +} +relpath "$@" diff --git a/contrib/stringreplace.c b/contrib/stringreplace.c new file mode 100644 index 0000000..8067132 --- /dev/null +++ b/contrib/stringreplace.c @@ -0,0 +1,34 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + +#include +#include +#include + +int main( int argc, char ** argv ) { + if( argc < 5 ) { + printf("Usage:\n"); + printf(" %s \n", argv[0] ); + return -1; + } + + unsigned long offset = strtoul(argv[1], NULL, 16); + char * replacement = argv[2]; + unsigned long maxlen = strtoul(argv[3], NULL, 10); + + FILE * f = fopen( argv[4], "r+" ); + if( !f ) { + printf( "ERROR: Could not open %s for writing!\n", argv[4] ); + return -1; + } + + if( strlen(replacement) > maxlen ) { + printf( "ERROR: Replacement string length (%lu) is greater than maxlen! (%lu)\n", strlen(replacement), maxlen ); + return -1; + } + + fseek( f, offset, SEEK_SET ); + fwrite( replacement, strlen(replacement)+1, 1, f ); + + fclose( f ); + return 0; +} diff --git a/contrib/travis_fastfail.sh b/contrib/travis_fastfail.sh new file mode 100755 index 0000000..410cbe2 --- /dev/null +++ b/contrib/travis_fastfail.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# This file is a part of Julia. License is MIT: https://julialang.org/license + +curlhdr="Accept: application/vnd.travis-ci.2+json" +endpoint="https://api.travis-ci.org/repos/$TRAVIS_REPO_SLUG" + +# Fail fast for superseded builds to PR's +if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then + newestbuildforthisPR=$(curl -H "$curlhdr" $endpoint/builds?event_type=pull_request | \ + jq ".builds | map(select(.pull_request_number == $TRAVIS_PULL_REQUEST))[0].number") + if [ $newestbuildforthisPR != null -a $newestbuildforthisPR != \"$TRAVIS_BUILD_NUMBER\" ]; then + echo "There are newer queued builds for this pull request, failing early." + exit 1 + fi +else + # And for non-latest push builds in branches other than master or release* + case $TRAVIS_BRANCH in + master | release*) + ;; + *) + if [ \"$TRAVIS_BUILD_NUMBER\" != $(curl -H "$curlhdr" \ + $endpoint/branches/$TRAVIS_BRANCH | jq ".branch.number") ]; then + echo "There are newer queued builds for this branch, failing early." + exit 1 + fi + ;; + esac +fi diff --git a/contrib/valgrind-julia.supp b/contrib/valgrind-julia.supp new file mode 100644 index 0000000..86f843f --- /dev/null +++ b/contrib/valgrind-julia.supp @@ -0,0 +1,10 @@ +# https://github.com/JuliaLang/julia/issues/4533 +{ + msync unwind + Memcheck:Param + msync(start) + ... + obj:*/libpthread*.so + ... + fun:rec_backtrace_ctx +} diff --git a/deps/.gitignore b/deps/.gitignore new file mode 100644 index 0000000..cda55dd --- /dev/null +++ b/deps/.gitignore @@ -0,0 +1,3 @@ +/srccache +/build +/scratch diff --git a/deps/Makefile b/deps/Makefile new file mode 100644 index 0000000..500d612 --- /dev/null +++ b/deps/Makefile @@ -0,0 +1,204 @@ +## high-level setup ## +default: install +SRCDIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) +SRCCACHE := $(abspath $(SRCDIR)/srccache) +JULIAHOME := $(abspath $(SRCDIR)/..) +ifeq ($(abspath .),$(abspath $(SRCDIR))) +BUILDDIR := scratch +else +BUILDDIR := . +endif +include $(SRCDIR)/Versions.make +include $(JULIAHOME)/Make.inc +include $(SRCDIR)/tools/common.mk +include $(SRCDIR)/tools/git-external.mk +include $(SRCDIR)/tools/bb-install.mk + +BUILDDIR := $(BUILDDIR)$(MAYBE_HOST) + +# Special comments: +# +# all targets in here should follow the same structure, +# and provide a get-a, extract-a, configure-a, compile-a, check-a, fastcheck-a, and install-a +# additionally all targets should be listed in the getall target for easier off-line compilation +# if you are adding a new target, it can help to copy an similar, existing target +# +# autoconf configure-driven scripts: llvm pcre unwind gmp mpfr patchelf libuv curl +# custom Makefile rules: openlibm dsfmt suitesparse-wrapper suitesparse lapack openblas utf8proc objconv osxunwind libwhich +# CMake libs: libgit2 libssh2 mbedtls +# +# downloaded from git: llvm-svn, libuv, libopenlibm, utf8proc, libgit2, libssh2 +# +# there are rules in this file with the . replaced by a % +# this is some magic Makefile trick that tells make +# that all targets with a % in them on that line will +# be rebuilt in a single invocation +# +# to debug 'define' rules, replace eval at the usage site with info or error +# + +## Overall configuration of which rules exist and should be run by default ## + +# prevent installing libs into usr/lib64 on opensuse +unexport CONFIG_SITE + +DEP_LIBS := + +ifeq ($(USE_GPL_LIBS), 1) +DEP_LIBS += suitesparse-wrapper +endif + +ifeq ($(USE_SYSTEM_LIBUV), 0) +DEP_LIBS += libuv +endif + +ifeq ($(DISABLE_LIBUNWIND), 0) +ifeq ($(USE_SYSTEM_LIBUNWIND), 0) +ifeq ($(OS), Linux) +DEP_LIBS += unwind +else ifeq ($(OS), FreeBSD) +DEP_LIBS += unwind +else ifeq ($(OS), Darwin) +DEP_LIBS += osxunwind +endif +endif +endif + +ifneq (,$(findstring $(OS),Linux FreeBSD)) +ifeq ($(USE_SYSTEM_PATCHELF), 0) +DEP_LIBS += patchelf +PATCHELF:=$(build_depsbindir)/patchelf +else +PATCHELF:=patchelf +endif +endif +PATCHELF_BIN := $(CUSTOM_LD_LIBRARY_PATH) $(PATCHELF) + +## USE_SYSTEM_LIBS options + +ifeq ($(USE_SYSTEM_OPENLIBM), 0) +ifeq ($(USE_SYSTEM_LIBM), 0) +DEP_LIBS += openlibm +endif +endif + +ifeq ($(USE_SYSTEM_DSFMT), 0) +DEP_LIBS += dsfmt +endif + +ifeq ($(USE_SYSTEM_LLVM), 0) +DEP_LIBS += llvm +endif + +ifeq ($(USE_SYSTEM_PCRE), 0) +DEP_LIBS += pcre +endif + +ifeq ($(USE_SYSTEM_BLAS), 0) +DEP_LIBS += openblas +ifeq ($(USE_BLAS64), 1) +ifeq ($(OS), Darwin) +DEP_LIBS += objconv +endif +endif +endif + +ifeq ($(USE_SYSTEM_GMP), 0) +DEP_LIBS += gmp +endif + +ifeq ($(USE_SYSTEM_LIBGIT2), 0) +ifeq ($(USE_SYSTEM_MBEDTLS), 0) +DEP_LIBS += mbedtls +endif + +ifeq ($(USE_SYSTEM_LIBSSH2), 0) +DEP_LIBS += libssh2 +endif + +ifneq ($(OS), WINNT) +ifeq ($(USE_SYSTEM_CURL), 0) +DEP_LIBS += curl +endif +endif + +DEP_LIBS += libgit2 +endif # USE_SYSTEM_LIBGIT2 + +ifeq ($(USE_SYSTEM_MPFR), 0) +DEP_LIBS += mpfr +endif + +ifeq ($(USE_GPL_LIBS), 1) +ifeq ($(USE_SYSTEM_SUITESPARSE), 0) +DEP_LIBS += suitesparse +endif +endif + +ifeq ($(USE_SYSTEM_UTF8PROC), 0) +DEP_LIBS += utf8proc +endif + +ifeq ($(USE_SYSTEM_ZLIB), 0) +DEP_LIBS += zlib +endif + +ifeq ($(USE_SYSTEM_P7ZIP), 0) +DEP_LIBS += p7zip +endif + + +# Only compile standalone LAPACK if we are not using OpenBLAS. +# OpenBLAS otherwise compiles LAPACK as part of its build. +# This is useful where one wants to use the vendor BLAS, but +# build LAPACK as the vendor LAPACK may be too old (eg. Apple vecLib) +ifeq ($(USE_SYSTEM_BLAS), 1) +ifeq ($(USE_SYSTEM_LAPACK), 0) +DEP_LIBS += lapack +endif +endif + +ifneq ($(OS), WINNT) +DEP_LIBS += libwhich +endif + +DEP_LIBS_STAGED := $(filter-out suitesparse suitesparse-wrapper osxunwind,$(DEP_LIBS)) # unlist targets that have not been converted to use the staged-install + + +## Common build target prefixes + +default: | $(build_prefix) +get: $(addprefix get-, $(DEP_LIBS)) +extract: $(addprefix extract-, $(DEP_LIBS)) +configure: $(addprefix configure-, $(DEP_LIBS)) +compile: $(addprefix compile-, $(DEP_LIBS)) +check: $(addprefix check-, $(DEP_LIBS)) +fastcheck: $(addprefix fastcheck-, $(DEP_LIBS)) +stage: $(addprefix stage-, $(DEP_LIBS_STAGED)) +install: $(addprefix install-, $(DEP_LIBS)) +uninstall: $(addprefix uninstall-, $(DEP_LIBS_STAGED)) +cleanall: $(addprefix clean-, $(DEP_LIBS)) +distcleanall: $(addprefix distclean-, $(DEP_LIBS)) + rm -rf $(build_prefix) +getall: get-llvm get-libuv get-pcre get-openlibm get-dsfmt get-openblas get-lapack get-suitesparse get-unwind get-osxunwind get-gmp get-mpfr get-patchelf get-utf8proc get-objconv get-mbedtls get-libssh2 get-curl get-libgit2 get-libwhich + +include $(SRCDIR)/llvm.mk +include $(SRCDIR)/libuv.mk +include $(SRCDIR)/pcre.mk +include $(SRCDIR)/openlibm.mk +include $(SRCDIR)/dsfmt.mk +include $(SRCDIR)/objconv.mk +include $(SRCDIR)/blas.mk +include $(SRCDIR)/utf8proc.mk +include $(SRCDIR)/suitesparse.mk +include $(SRCDIR)/unwind.mk +include $(SRCDIR)/gmp.mk +include $(SRCDIR)/mpfr.mk +include $(SRCDIR)/patchelf.mk +include $(SRCDIR)/mbedtls.mk +include $(SRCDIR)/libssh2.mk +include $(SRCDIR)/curl.mk +include $(SRCDIR)/libgit2.mk +include $(SRCDIR)/libwhich.mk +include $(SRCDIR)/zlib.mk +include $(SRCDIR)/p7zip.mk diff --git a/deps/NATIVE.cmake b/deps/NATIVE.cmake new file mode 100644 index 0000000..026ee4d --- /dev/null +++ b/deps/NATIVE.cmake @@ -0,0 +1,4 @@ +# native toolchain file to fix llvm cross-compilation finickiness +# ref http://lists.llvm.org/pipermail/llvm-dev/2016-February/095366.html +set(CMAKE_C_COMPILER cc) +set(CMAKE_CXX_COMPILER c++) diff --git a/deps/SuiteSparse_wrapper.c b/deps/SuiteSparse_wrapper.c new file mode 100644 index 0000000..165df7d --- /dev/null +++ b/deps/SuiteSparse_wrapper.c @@ -0,0 +1,41 @@ +#include +#include + +extern size_t jl_cholmod_common_size(void) { + return sizeof(cholmod_common); +} + +extern size_t jl_cholmod_sizeof_long(void) { + return sizeof(SuiteSparse_long); +} + +extern int jl_cholmod_version(int *ver) { + if (ver != (int*) NULL) { + ver[0] = CHOLMOD_MAIN_VERSION; + ver[1] = CHOLMOD_SUB_VERSION; + ver[2] = CHOLMOD_SUBSUB_VERSION; + } + return CHOLMOD_VERSION; +} + +extern void jl_cholmod_common_offsets(size_t *vv) { + vv[0] = offsetof(cholmod_common, dbound); + vv[1] = offsetof(cholmod_common, maxrank); + vv[2] = offsetof(cholmod_common, supernodal_switch); + vv[3] = offsetof(cholmod_common, supernodal); + vv[4] = offsetof(cholmod_common, final_asis); + vv[5] = offsetof(cholmod_common, final_super); + vv[6] = offsetof(cholmod_common, final_ll); + vv[7] = offsetof(cholmod_common, final_pack); + vv[8] = offsetof(cholmod_common, final_monotonic); + vv[9] = offsetof(cholmod_common, final_resymbol); + vv[10] = offsetof(cholmod_common, prefer_zomplex); + vv[11] = offsetof(cholmod_common, prefer_upper); + vv[12] = offsetof(cholmod_common, print); + vv[13] = offsetof(cholmod_common, precise); + vv[14] = offsetof(cholmod_common, nmethods); + vv[15] = offsetof(cholmod_common, selected); + vv[16] = offsetof(cholmod_common, postorder); + vv[17] = offsetof(cholmod_common, itype); + vv[18] = offsetof(cholmod_common, dtype); +} diff --git a/deps/Versions.make b/deps/Versions.make new file mode 100644 index 0000000..6cdf381 --- /dev/null +++ b/deps/Versions.make @@ -0,0 +1,43 @@ +LLVM_VER = 6.0.1 +LLVM_BB_REL = 7+nowasm +PCRE_VER = 10.31 +PCRE_BB_REL = 0 +DSFMT_VER = 2.2.3 +DSFMT_BB_REL = 0 +OPENBLAS_VER = 0.3.5 +OPENBLAS_BB_REL = 2 +LAPACK_VER = 3.5.0 +SUITESPARSE_VER = 5.4.0 +SUITESPARSE_BB_REL = 2 +OPENLIBM_VER = 0.6.0 +OPENLIBM_BB_REL = 0 +UNWIND_VER = 1.3.1 +UNWIND_BB_REL = 4 +OSXUNWIND_VER = 0.0.5 +OSXUNWIND_BB_REL = 0 +GMP_VER = 6.1.2 +GMP_BB_REL = 3 +MPFR_VER = 4.0.2 +MPFR_BB_REL = 0 +PATCHELF_VER = 0.9 +MBEDTLS_VER = 2.16.0 +MBEDTLS_BB_REL = v0.17.0 +LIBSSH2_VER = 1.9.0 +LIBSSH2_BB_REL = 0 +CURL_VER = 7.61.0 +CURL_BB_REL = 1 +LIBGIT2_VER = 0.28.2 +LIBGIT2_BB_REL = 0 +LIBUV_VER = 1.29.1 +LIBUV_BB_REL = 0 +OBJCONV_VER = 2.49.0 +OBJCONV_BB_REL = 0 +ZLIB_VER = 1.2.11 +ZLIB_BB_REL = 6 +P7ZIP_VER = 16.2.0 +P7ZIP_BB_REL = 1 + +# Specify the version of the Mozilla CA Certificate Store to obtain. +# The versions of cacert.pem are identified by the date (YYYY-MM-DD) of their changes. +# See https://curl.haxx.se/docs/caextract.html for more details. +MOZILLA_CACERT_VERSION := 2019-10-16 diff --git a/deps/blas.mk b/deps/blas.mk new file mode 100644 index 0000000..cd60263 --- /dev/null +++ b/deps/blas.mk @@ -0,0 +1,220 @@ +## OpenBLAS ## +# LAPACK is built into OpenBLAS by default +OPENBLAS_GIT_URL := git://github.com/xianyi/OpenBLAS.git +OPENBLAS_TAR_URL = https://api.github.com/repos/xianyi/OpenBLAS/tarball/$1 +$(eval $(call git-external,openblas,OPENBLAS,,,$(BUILDDIR))) + +OPENBLAS_BUILD_OPTS := CC="$(CC)" FC="$(FC)" LD="$(LD)" RANLIB="$(RANLIB)" TARGET=$(OPENBLAS_TARGET_ARCH) BINARY=$(BINARY) + +# Thread support +ifeq ($(OPENBLAS_USE_THREAD), 1) +OPENBLAS_BUILD_OPTS += USE_THREAD=1 +OPENBLAS_BUILD_OPTS += GEMM_MULTITHREADING_THRESHOLD=50 +# Maximum number of threads for parallelism +ifneq ($(ARCH),x86_64) +# Assume we can't address much memory to spawn many threads +# It is also unlikely that 32-bit architectures have too many cores +OPENBLAS_BUILD_OPTS += NUM_THREADS=8 +else ifeq ($(OS),WINNT) +# Windows seems unable to handle very many +OPENBLAS_BUILD_OPTS += NUM_THREADS=16 +else ifeq ($(OS),Darwin) +# This should suffice for the largest macs +OPENBLAS_BUILD_OPTS += NUM_THREADS=16 +else +# On linux, try to provision for the largest possible machine currently +OPENBLAS_BUILD_OPTS += NUM_THREADS=16 +endif +else +OPENBLAS_BUILD_OPTS += USE_THREAD=0 +endif + +# don't touch scheduler affinity since we manage this ourselves +OPENBLAS_BUILD_OPTS += NO_AFFINITY=1 + +# Build for all architectures - required for distribution +ifeq ($(OPENBLAS_DYNAMIC_ARCH), 1) +OPENBLAS_BUILD_OPTS += DYNAMIC_ARCH=1 +endif + +# 64-bit BLAS interface +ifeq ($(USE_BLAS64), 1) +OPENBLAS_BUILD_OPTS += INTERFACE64=1 SYMBOLSUFFIX="$(OPENBLAS_SYMBOLSUFFIX)" LIBPREFIX="libopenblas$(OPENBLAS_LIBNAMESUFFIX)" +ifeq ($(OS), Darwin) +OPENBLAS_BUILD_OPTS += OBJCONV=$(abspath $(BUILDDIR)/objconv/objconv) +$(BUILDDIR)/$(OPENBLAS_SRC_DIR)/build-compiled: | $(BUILDDIR)/objconv/build-compiled +endif +endif + +OPENBLAS_FFLAGS := $(JFFLAGS) +OPENBLAS_CFLAGS := -O2 + +# Decide whether to build for 32-bit or 64-bit arch +ifneq ($(BUILD_OS),$(OS)) +OPENBLAS_BUILD_OPTS += OSNAME=$(OS) CROSS=1 HOSTCC=$(HOSTCC) CROSS_SUFFIX=$(CROSS_COMPILE) +endif +ifeq ($(OS),WINNT) +ifneq ($(ARCH),x86_64) +ifneq ($(USECLANG),1) +OPENBLAS_CFLAGS += -mincoming-stack-boundary=2 +endif +OPENBLAS_FFLAGS += -mincoming-stack-boundary=2 +endif +endif + +# Work around invalid register errors on 64-bit Windows +# See discussion in https://github.com/xianyi/OpenBLAS/issues/1708 +# TODO: Remove this once we use a version of OpenBLAS where this is set automatically +ifeq ($(OS),WINNT) +ifeq ($(ARCH),x86_64) +OPENBLAS_CFLAGS += -fno-asynchronous-unwind-tables +endif +endif + +OPENBLAS_BUILD_OPTS += CFLAGS="$(CFLAGS) $(OPENBLAS_CFLAGS)" +OPENBLAS_BUILD_OPTS += FFLAGS="$(FFLAGS) $(OPENBLAS_FFLAGS)" +OPENBLAS_BUILD_OPTS += LDFLAGS="$(LDFLAGS) $(RPATH_ESCAPED_ORIGIN)" + +# Debug OpenBLAS +ifeq ($(OPENBLAS_DEBUG), 1) +OPENBLAS_BUILD_OPTS += DEBUG=1 +endif + +# Allow disabling AVX for older binutils +ifeq ($(OPENBLAS_NO_AVX), 1) +OPENBLAS_BUILD_OPTS += NO_AVX=1 NO_AVX2=1 NO_AVX512=1 +else ifeq ($(OPENBLAS_NO_AVX2), 1) +OPENBLAS_BUILD_OPTS += NO_AVX2=1 NO_AVX512=1 +else ifeq ($(OPENBLAS_NO_AVX512), 1) +OPENBLAS_BUILD_OPTS += NO_AVX512=1 +endif + +# Do not overwrite the "-j" flag +OPENBLAS_BUILD_OPTS += MAKE_NB_JOBS=0 + +ifneq ($(USE_BINARYBUILDER_OPENBLAS), 1) + +$(BUILDDIR)/$(OPENBLAS_SRC_DIR)/openblas-skylakexdgemm.patch-applied: $(BUILDDIR)/$(OPENBLAS_SRC_DIR)/source-extracted + cd $(BUILDDIR)/$(OPENBLAS_SRC_DIR) && \ + patch -p1 -f < $(SRCDIR)/patches/openblas-skylakexdgemm.patch + echo 1 > $@ + +$(BUILDDIR)/$(OPENBLAS_SRC_DIR)/build-configured: $(BUILDDIR)/$(OPENBLAS_SRC_DIR)/openblas-skylakexdgemm.patch-applied + echo 1 > $@ + +$(BUILDDIR)/$(OPENBLAS_SRC_DIR)/build-compiled: $(BUILDDIR)/$(OPENBLAS_SRC_DIR)/build-configured + echo $(MAKE) -C $(dir $<) $(OPENBLAS_BUILD_OPTS) # echo first, so we only print the error message below in a failure case + @$(MAKE) -C $(dir $<) $(OPENBLAS_BUILD_OPTS) || (echo $(WARNCOLOR)"*** Clean the OpenBLAS build with 'make -C deps clean-openblas'. Rebuild with 'make OPENBLAS_USE_THREAD=0' if OpenBLAS had trouble linking libpthread.so, and with 'make OPENBLAS_TARGET_ARCH=NEHALEM' if there were errors building SandyBridge support. Both these options can also be used simultaneously. ***"$(ENDCOLOR) && false) + echo 1 > $@ + +define OPENBLAS_INSTALL + $(call SHLIBFILE_INSTALL,$1,$2,$3) +ifeq ($$(OS), Linux) + ln -sf libopenblas$$(OPENBLAS_LIBNAMESUFFIX).$$(SHLIB_EXT) $2/$$(build_libdir)/libopenblas$$(OPENBLAS_LIBNAMESUFFIX).$$(SHLIB_EXT).0 +endif +endef +$(eval $(call staged-install, \ + openblas,$(OPENBLAS_SRC_DIR), \ + OPENBLAS_INSTALL,$(BUILDDIR)/$(OPENBLAS_SRC_DIR)/$(LIBBLASNAME).$(SHLIB_EXT),, \ + $$(INSTALL_NAME_CMD)libopenblas$$(OPENBLAS_LIBNAMESUFFIX).$$(SHLIB_EXT) $$(build_shlibdir)/libopenblas$$(OPENBLAS_LIBNAMESUFFIX).$$(SHLIB_EXT))) + +clean-openblas: + -rm $(BUILDDIR)/$(OPENBLAS_SRC_DIR)/build-compiled + -$(MAKE) -C $(BUILDDIR)/$(OPENBLAS_SRC_DIR) clean + + +get-openblas: $(OPENBLAS_SRC_FILE) +extract-openblas: $(BUILDDIR)/$(OPENBLAS_SRC_DIR)/source-extracted +configure-openblas: $(BUILDDIR)/$(OPENBLAS_SRC_DIR)/build-configured +compile-openblas: $(BUILDDIR)/$(OPENBLAS_SRC_DIR)/build-compiled +fastcheck-openblas: check-openblas +check-openblas: compile-openblas + + +## Mac gfortran BLAS wrapper ## +ifeq ($(OS),Darwin) +$(BUILDDIR)/libgfortblas.$(SHLIB_EXT): $(SRCDIR)/gfortblas.c $(SRCDIR)/gfortblas.alias + $(CC) -Wall -O3 $(CPPFLAGS) $(CFLAGS) $(fPIC) -shared $< -o $@ -pipe \ + -Wl,-reexport_framework,Accelerate -Wl,-alias_list,$(SRCDIR)/gfortblas.alias + +$(build_shlibdir)/libgfortblas.$(SHLIB_EXT): $(BUILDDIR)/libgfortblas.$(SHLIB_EXT) + cp -f $< $@ + $(INSTALL_NAME_CMD)libgfortblas.$(SHLIB_EXT) $@ +endif + + +## LAPACK ## + +LAPACK_MFLAGS := NOOPT="$(FFLAGS) $(JFFLAGS) $(USE_BLAS_FFLAGS) -O0" \ + OPTS="$(FFLAGS) $(JFFLAGS) $(USE_BLAS_FFLAGS)" FORTRAN="$(FC)" \ + LOADER="$(FC)" BLASLIB="$(RPATH_ESCAPED_ORIGIN) $(LIBBLAS)" + +$(SRCCACHE)/lapack-$(LAPACK_VER).tgz: | $(SRCCACHE) + $(JLDOWNLOAD) $@ http://www.netlib.org/lapack/$(notdir $@) + +$(BUILDDIR)/lapack-$(LAPACK_VER)/source-extracted: $(SRCCACHE)/lapack-$(LAPACK_VER).tgz + $(JLCHECKSUM) $< + mkdir -p $(BUILDDIR) + cd $(BUILDDIR) && $(TAR) -zxf $< + cp $(dir $@)INSTALL/make.inc.gfortran $(dir $@)make.inc + echo 1 > $@ + +ifeq ($(USE_SYSTEM_BLAS), 0) +$(BUILDDIR)/lapack-$(LAPACK_VER)/build-compiled0: | $(build_prefix)/manifest/openblas +else ifeq ($(OS),Darwin) +$(BUILDDIR)/lapack-$(LAPACK_VER)/build-compiled0: | $(build_shlibdir)/libgfortblas.$(SHLIB_EXT) +endif +$(BUILDDIR)/lapack-$(LAPACK_VER)/build-compiled0: $(BUILDDIR)/lapack-$(LAPACK_VER)/source-extracted + $(MAKE) -C $(dir $@) lapacklib $(LAPACK_MFLAGS) + echo 1 > $@ + +$(BUILDDIR)/lapack-$(LAPACK_VER)/build-checked: $(BUILDDIR)/lapack-$(LAPACK_VER)/build-compiled0 +ifeq ($(BUILD_OS),$(OS)) + $(MAKE) -C $(dir $@) lapack_testing $(LAPACK_MFLAGS) -k +endif + echo 1 > $@ + +$(BUILDDIR)/lapack-$(LAPACK_VER)/build-compiled: $(BUILDDIR)/lapack-$(LAPACK_VER)/build-compiled0 | $(build_prefix)/manifest + $(FC) -shared $(FFLAGS) $(JFFLAGS) $(dir $<)/SRC/*.o \ + $(dir $<)/INSTALL/dlamch.o $(dir $<)/INSTALL/dsecnd_INT_ETIME.o \ + $(dir $<)/INSTALL/ilaver.o $(dir $<)/INSTALL/slamch.o $(LIBBLAS) \ + -o $(dir $<)/liblapack.$(SHLIB_EXT) + echo 1 > $@ + +$(eval $(call staged-install, \ + lapack,lapack-$(LAPACK_VER), \ + SHLIBFILE_INSTALL,$(BUILDDIR)/lapack-$(LAPACK_VER)/liblapack.$(SHLIB_EXT),, \ + $$(INSTALL_NAME_CMD)liblapack.$$(SHLIB_EXT) $$(build_shlibdir)/liblapack.$$(SHLIB_EXT))) + +clean-lapack: + -rm $(BUILDDIR)/lapack-$(LAPACK_VER)/build-compiled0 $(BUILDDIR)/lapack-$(LAPACK_VER)/build-compiled + -$(MAKE) -C $(BUILDDIR)/lapack-$(LAPACK_VER) clean + +distclean-lapack: + -rm -rf $(SRCCACHE)/lapack-$(LAPACK_VER).tgz $(BUILDDIR)/lapack-$(LAPACK_VER) + + +get-lapack: $(SRCCACHE)/lapack-$(LAPACK_VER).tgz +extract-lapack: $(BUILDDIR)/lapack-$(LAPACK_VER)/source-extracted +configure-lapack: extract-lapack +compile-lapack: $(BUILDDIR)/lapack-$(LAPACK_VER)/build-compiled +fastcheck-lapack: check-lapack +check-lapack: $(BUILDDIR)/lapack-$(LAPACK_VER)/build-checked + +else # USE_BINARYBUILDER_OPENBLAS + + +OPENBLAS_BB_URL_BASE := https://github.com/JuliaPackaging/Yggdrasil/releases/download/OpenBLAS-v$(OPENBLAS_VER)-$(OPENBLAS_BB_REL) +OPENBLAS_BB_NAME := OpenBLAS.v$(OPENBLAS_VER) + +$(eval $(call bb-install,openblas,OPENBLAS,true)) +get-lapack: get-openblas +extract-lapack: extract-openblas +configure-lapack: configure-openblas +compile-lapack: compile-openblas +fastcheck-lapack: fastcheck-openblas +check-lapack: check-openblas +clean-lapack: clean-openblas +distclean-lapack: distclean-openblas +install-lapack: install-openblas +endif diff --git a/deps/checksums/7z1900-x64.exe/md5 b/deps/checksums/7z1900-x64.exe/md5 new file mode 100644 index 0000000..588587a --- /dev/null +++ b/deps/checksums/7z1900-x64.exe/md5 @@ -0,0 +1 @@ +d7b20f933be6cdae41efbe75548eba5f diff --git a/deps/checksums/7z1900-x64.exe/sha512 b/deps/checksums/7z1900-x64.exe/sha512 new file mode 100644 index 0000000..c9555b5 --- /dev/null +++ b/deps/checksums/7z1900-x64.exe/sha512 @@ -0,0 +1 @@ +af8f38679e16c996ffac152cac49369cf4b609abbd2cad07f49a114a82c6b5e564be29630c0fd2418110cf1a3d0ef3c9cc12f9164a69a575c91d9b98ce0df1a9 diff --git a/deps/checksums/7z1900.exe/md5 b/deps/checksums/7z1900.exe/md5 new file mode 100755 index 0000000..3ce2f97 --- /dev/null +++ b/deps/checksums/7z1900.exe/md5 @@ -0,0 +1 @@ +fabe184f6721e640474e1497c69ffc98 diff --git a/deps/checksums/7z1900.exe/sha512 b/deps/checksums/7z1900.exe/sha512 new file mode 100755 index 0000000..aff2a9e --- /dev/null +++ b/deps/checksums/7z1900.exe/sha512 @@ -0,0 +1 @@ +2924fd60f5dd636f643b68d402b65c2bfab5536122aa688ebba5ae142c7d04ce8b1c8e078f54db8adadce9d5c6fa74c0794604ecc16a4c5489f9ca70a6d9e1c4 diff --git a/deps/checksums/GMP.v6.1.2-3.aarch64-linux-gnu.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-3.aarch64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..6b5879d --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-3.aarch64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +ed06ab3d41f85e837cd605bc3fad476b diff --git a/deps/checksums/GMP.v6.1.2-3.aarch64-linux-gnu.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-3.aarch64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..898c88c --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-3.aarch64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +8b04b36670d77e4a4f281c7587b3d878449ec8b7a07cd86f41a4e594f8428b4587659be0ecef59ff065bc9c6ba607bf03158a082b4ce2cb4c0c9e74411295106 diff --git a/deps/checksums/GMP.v6.1.2-3.aarch64-linux-musl.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-3.aarch64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..ba5ab55 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-3.aarch64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +5041b7635c6f1fc50b66f67075cda4c3 diff --git a/deps/checksums/GMP.v6.1.2-3.aarch64-linux-musl.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-3.aarch64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..9e53a92 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-3.aarch64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +f90edfec0792379c35129ca5b38d987455522da64ef2dd6ff624c8c271f04868f3f816b8c23f07b705af025119708005990e93bc3018952aa5c38b4365e01a5a diff --git a/deps/checksums/GMP.v6.1.2-3.arm-linux-gnueabihf.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-3.arm-linux-gnueabihf.tar.gz/md5 new file mode 100644 index 0000000..7faaf51 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-3.arm-linux-gnueabihf.tar.gz/md5 @@ -0,0 +1 @@ +9da15965b743ecdd869ac374ed7dee72 diff --git a/deps/checksums/GMP.v6.1.2-3.arm-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-3.arm-linux-gnueabihf.tar.gz/sha512 new file mode 100644 index 0000000..0d47de4 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-3.arm-linux-gnueabihf.tar.gz/sha512 @@ -0,0 +1 @@ +1a2915d7e5e0a624f2053c16df3daba567f45e1fca0e19ab81535856e36b83978ead98cd72e6212fb6bc293b409f5674cb8ea8b3369a35b825a2ac3aac57494a diff --git a/deps/checksums/GMP.v6.1.2-3.arm-linux-musleabihf.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-3.arm-linux-musleabihf.tar.gz/md5 new file mode 100644 index 0000000..47a194c --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-3.arm-linux-musleabihf.tar.gz/md5 @@ -0,0 +1 @@ +e073ec5a73eaad5a70660fd75a41781d diff --git a/deps/checksums/GMP.v6.1.2-3.arm-linux-musleabihf.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-3.arm-linux-musleabihf.tar.gz/sha512 new file mode 100644 index 0000000..e44afda --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-3.arm-linux-musleabihf.tar.gz/sha512 @@ -0,0 +1 @@ +2d0e5b9bf07ffc048306d76f6bb27f1897ec4f6480597fa27ab647f742285f3e5ae5fbc495d9abde63f2b3439dbb41ccf7b0ca395106ee7adb3233d3e43d44b8 diff --git a/deps/checksums/GMP.v6.1.2-3.i686-linux-gnu.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-3.i686-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..582265c --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-3.i686-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +d8908bead56aa3db1dec41c18782e532 diff --git a/deps/checksums/GMP.v6.1.2-3.i686-linux-gnu.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-3.i686-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..a209c0b --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-3.i686-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +89da4c61528e51e5bc7293b824cd57fd10fad93757227a5f5a52a1e347f353b91bdcdbcc8591cc0d01c0952d37c03339a107856505cf2a17d25f35328453388c diff --git a/deps/checksums/GMP.v6.1.2-3.i686-linux-musl.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-3.i686-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..80e6847 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-3.i686-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +31564e45dd5fdafe816d29e09402ba79 diff --git a/deps/checksums/GMP.v6.1.2-3.i686-linux-musl.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-3.i686-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..a1d7b72 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-3.i686-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +a43b13ba05812a9dd9225aca5db39615baebad3ef1510458d3f441519c3253774ed46497f4d65730a24e021440bae19342e4f7e56ed0e956c10e77cf61016040 diff --git a/deps/checksums/GMP.v6.1.2-3.i686-w64-mingw32.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-3.i686-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..0484b74 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-3.i686-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +cc730eddb20303e8131dc4b551a48500 diff --git a/deps/checksums/GMP.v6.1.2-3.i686-w64-mingw32.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-3.i686-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..2ecec3a --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-3.i686-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +e080478a9c40572eac4baec78c0295c5f4b3ca683a3a66fb4dd24a76fc9be92d2de7f0e9409b6e281babc6a1ba3f543d3f0ab7e6b12f67957d43931c74076bf1 diff --git a/deps/checksums/GMP.v6.1.2-3.powerpc64le-linux-gnu.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-3.powerpc64le-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..41877c9 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-3.powerpc64le-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +aeb0300673be0b235737eee73ac8ab4a diff --git a/deps/checksums/GMP.v6.1.2-3.powerpc64le-linux-gnu.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-3.powerpc64le-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..5be46a4 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-3.powerpc64le-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +85b1504811515525a4d03f5068760138c0c11f66b47b140eac5009f2a18fd413ea8bbd1a9a02253a7ba14bede26317f46f9daf689d36915a6eb7a0a2bb029d44 diff --git a/deps/checksums/GMP.v6.1.2-3.x86_64-apple-darwin14.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-3.x86_64-apple-darwin14.tar.gz/md5 new file mode 100644 index 0000000..39253b3 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-3.x86_64-apple-darwin14.tar.gz/md5 @@ -0,0 +1 @@ +d574efeb8dae18a80ebd1ce3e6d4ecc3 diff --git a/deps/checksums/GMP.v6.1.2-3.x86_64-apple-darwin14.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-3.x86_64-apple-darwin14.tar.gz/sha512 new file mode 100644 index 0000000..f53fd31 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-3.x86_64-apple-darwin14.tar.gz/sha512 @@ -0,0 +1 @@ +80e7c6eff9cba0dc871f320d0c570523d6fd53a9bcb3022fa6b75b4ef509872c9d4414fb4d37f9e4138cf2266621aac9bbd4da0a7deeb1ed014be8c37cbc2adb diff --git a/deps/checksums/GMP.v6.1.2-3.x86_64-linux-gnu.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-3.x86_64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..9b5c1b6 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-3.x86_64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +8b6da6984c1113f21ebbaf8cd5820418 diff --git a/deps/checksums/GMP.v6.1.2-3.x86_64-linux-gnu.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-3.x86_64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..82b3eaa --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-3.x86_64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +bcdcbd20b950cc8ca94c2df66b85f3633ca997cfeaf042dfc6093607a2cd16f109b9263c1a83cd1c3e259a8c278cf7ef1cf47805ad762f4e49939cc5f132d75f diff --git a/deps/checksums/GMP.v6.1.2-3.x86_64-linux-musl.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-3.x86_64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..d8ef414 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-3.x86_64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +34894a2ce504f62a02bf05190d5e67c5 diff --git a/deps/checksums/GMP.v6.1.2-3.x86_64-linux-musl.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-3.x86_64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..f036f6c --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-3.x86_64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +df4e5467ecbe8d4b340c8c63a691d5de8edaa8b6cf047bc702a5ae32e1868199bb16661d54a97c205488d6fb0238e0c75984723f23e114b11b2bece022fbb0fe diff --git a/deps/checksums/GMP.v6.1.2-3.x86_64-unknown-freebsd11.1.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-3.x86_64-unknown-freebsd11.1.tar.gz/md5 new file mode 100644 index 0000000..65e265a --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-3.x86_64-unknown-freebsd11.1.tar.gz/md5 @@ -0,0 +1 @@ +c553733282390fde94c66b38e1a78a15 diff --git a/deps/checksums/GMP.v6.1.2-3.x86_64-unknown-freebsd11.1.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-3.x86_64-unknown-freebsd11.1.tar.gz/sha512 new file mode 100644 index 0000000..8b67ab2 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-3.x86_64-unknown-freebsd11.1.tar.gz/sha512 @@ -0,0 +1 @@ +45647d628299e0cf22bac66afd457d83fdcc8eabef13b3e5c3f2b1b0ae193c4c86cd56921556a0ecacbf6bb9256c24daccf894dbd16929ef6f4387c615f67019 diff --git a/deps/checksums/GMP.v6.1.2-3.x86_64-w64-mingw32.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-3.x86_64-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..71df177 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-3.x86_64-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +44ded1fc674258c516768e3036f2ba6b diff --git a/deps/checksums/GMP.v6.1.2-3.x86_64-w64-mingw32.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-3.x86_64-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..f785a3d --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-3.x86_64-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +f87d6c4cd0c9e1011d516d88b7f72f38ef29e3342c2c44dc638c24f17d975d5f6ddfac363f1114d2cb476999de7b7675f33998e7ae82e875d9e4c1a1accf23f9 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc4.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc4.tar.gz/md5 new file mode 100644 index 0000000..3071419 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +32c5f933b5e8758e35c87f5054afa26c diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc4.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..61c9395 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +93c65ed59636696b98860c8227e8750636a5b40f0061bc1eb9d0811b4fb813c823fe631fc03ea74446306b9bf4643c0534ee6cafd6bc8aacf44a02a4ca11a572 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc7.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc7.tar.gz/md5 new file mode 100644 index 0000000..ea531d9 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +9658cbfc2e72d0176d358f51c11367e7 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc7.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..5452c55 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +ba3bf88bfba4da816c9a5a68f16e3af0b9d7cb5999d605a9a63d06a241c8c342beb46d96db1130eebb00ae46aed958fd9541868fd7f9cb1c51d0b3305509fcc0 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc8.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc8.tar.gz/md5 new file mode 100644 index 0000000..41d4287 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +0b0920e2a2e279e92e739e3fffd18d6e diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc8.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..35414ac --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +9ca4f3967c83ed3b5a51ec97a44169218e4822eb40c8b1a7d8060258709f72290f4d76dc9c26e6ae41a9bb6eddf76b3908dcc01b839a7b9b7e62ccbb6446e948 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc4.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc4.tar.gz/md5 new file mode 100644 index 0000000..a778556 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +c3ad727fb901f0f663175b307de5b1b8 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc4.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..cd6396a --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +63f351c3c58f8858db69dd964f909f6ea4891f836c20ff18d7344f787e57f5764febce79237aaa6b25a02964eb470c81d2881878aab25c753e79e210c8f7c8af diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc7.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc7.tar.gz/md5 new file mode 100644 index 0000000..4425c95 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +f7f0818187f0d0b3291a7d27e83a1d53 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc7.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..abcca98 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +cae860ca5aeda88742a8916caa4e74d382d3b631f8b5f7b1ebbc62540a7cb2d9890a8a6c8249ab13e3ec3edb4b928ec622b5044066995ec5b91afb851922671c diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc8.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc8.tar.gz/md5 new file mode 100644 index 0000000..d73f1da --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +8d832d7c4a1ffe5057ac9257ae417d2c diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc8.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..2be0593 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +0ea0b64f64de19a24c3184d790772535c2985355d0eb7113d58c3f4bffa66c9c9edff33640c169cc05c3edca6d0abe025e0cf0552a81c0d1e612c3047fb96804 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc4.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc4.tar.gz/md5 new file mode 100644 index 0000000..b86f4e9 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +de0f9114398066ee9e4dc7ad2dc5239a diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc4.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..260af10 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +5811214cd654a08e44a7a103bd5a21e574d45336b579d862485c248464792ca96800ee47490a01387df8aa2af8affa8dac9b8a7a9a5a35815eede112df162325 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc7.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc7.tar.gz/md5 new file mode 100644 index 0000000..33902f3 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +9d076399fbc7e0d400e6b4723223de70 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc7.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..8e85095 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +a4903d8af622fa5f08d0f790dbbc4d1cf7e46b05d44407b1bc757f86b8ddf1f45f728b406fc82b10b8e5c92cd029d182d99b6ab1344d7de645ef343247c66af1 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc8.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc8.tar.gz/md5 new file mode 100644 index 0000000..27711b0 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +9df643893cf28239e4f2ec1a302178ef diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc8.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..04224b0 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +8b22d1ae21746d8ce2f73a75b05cae169c68eed8b2a64cebbfe286e377b31887d2412c68d001dc6e0b92c65a4500779e3a7b9a21d7ef0dbc88c43c6037319eb5 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc4.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc4.tar.gz/md5 new file mode 100644 index 0000000..a3d675f --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +e2962033d78f4562aed94f1d7e0ec396 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc4.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..d365ce9 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +2bd15d7c3d86534ee34097c5a5d31e4f4497979664b8e85f51dbd79e2a48eb25aa1925fa67325581f4717711ee82d91a5d8f6805f0aa3eac1198817814b9d675 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc7.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc7.tar.gz/md5 new file mode 100644 index 0000000..ea690d1 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +e6d9664644ce47046334290735b3f508 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc7.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..4df6682 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +4df060f23066788d93a8122ca6ea6abf7fa76cdde0c05d1cd50188298504150bf48fd115e2a78e332ee12ebd240e2e53397daf21d82235db9eafaf09b5df8347 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc8.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc8.tar.gz/md5 new file mode 100644 index 0000000..5c4a726 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +2db2bccfa589095c8d1f4254d9cfa253 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc8.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..bcd79d6 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +0be45ceb79877f6037d9b0c8ba7439ffe2873b681a949c57573ba9a73c4b224a05c8fcdac6cbde9df7c8ddab324f3f44b9e91c1d120ce427baf4f35ef152bef3 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc4.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc4.tar.gz/md5 new file mode 100644 index 0000000..496db2a --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +8cf440fc1f74d790954a513e81e1334f diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc4.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..b8b9393 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +5ecaf6d3343d3afe6b43686b835c75e35b925c1766459af47ed18bc140e615203a59e6f1df63c4803f0cf7603e406b0e72b824cc8b77a765f894bc40a8d1a8b8 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc7.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc7.tar.gz/md5 new file mode 100644 index 0000000..1d91195 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +71c9a128b6a41dde8672da7540ceb5a1 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc7.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..b6973fe --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +11d3719960c8ba4e68aa02a0c6364bb997a2fa734451c19c4b02027c81240c87355f71c2df776c25eac6d62967c9f06a20af7e502bd3a7def6698a0a0d01de16 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc8.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc8.tar.gz/md5 new file mode 100644 index 0000000..85282ad --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +c2ce6cffa40a4dadf59e99d04ad495b6 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc8.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..f2a628d --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +854c5269e252aa1f076b75a0b8afb369a0ada62ee91365b30d3d108e01008785ebeb511575e65c03080e489c6e3db12db88c6e1729462bf2c6e95e3d757450e2 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc4.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc4.tar.gz/md5 new file mode 100644 index 0000000..fd1cc7d --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +acc66103bc030b0691da5425de12d0d4 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc4.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..2933aed --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +e7fc442db075a588995def014174f8f631b7a06fec3cc21b2f2f4f42a4416fbd9f840d054bc89ab04b20e4e2614ed33fc7c75f7189a10c46b40b06ebb8b6589d diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc7.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc7.tar.gz/md5 new file mode 100644 index 0000000..0e966e6 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +7dc70a32fd21745441387b7f7b7b71cc diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc7.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..1b168cb --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +a858cdb39074e02d4b2d02fc168df8605181bb100ca5877ad8607d2a49603b591e010a6dd8ce5bb97828bde7f948e9e85f537a00ac01a709914663d69566baec diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc8.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc8.tar.gz/md5 new file mode 100644 index 0000000..9bad624 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +f2dcde5881cb4d47c4c954210013c4db diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc8.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..5dc9ee7 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +a900666f3827cdd67953d4caaf2084d5beb680d2cd1a73d8f2640144fc88107d2a7ff8357c7907d57e541be8a1ca3b751c281597840c790837238ec292635203 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc4.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc4.tar.gz/md5 new file mode 100644 index 0000000..726d5e0 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +88ca9ab7847a5cc7136a356a7c91a7cc diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc4.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..d980354 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +6139c8df8232f749beb718b480d3093ef5b7640526a559c0affefdd2c09223d3df859a0a21c4a7d40fc7797acb7e2a6784aea78b742584d09faff16d01943f7b diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc7.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc7.tar.gz/md5 new file mode 100644 index 0000000..6896320 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +8a88e8676ebe377f9245d751fbcb3295 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc7.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..bb4885f --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +404238618223d952de2366601b55eeae18f0cfd4d02f5eabe70ee504791fb5dd2131001826cbd1b2f642d914f91ded47379be8439818d20b8234885c64d4a908 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc8.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc8.tar.gz/md5 new file mode 100644 index 0000000..4b4bcb2 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +6e48d53154e7697e7f01c1bb47e8e54e diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc8.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..56c5fd1 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +17adb6066b6962a76d06b0ae492a9d7330db51b26b9fe1925ebbd3874981a8a8848794fff88bf924fb405cf71ddb48e1efe6c4bf30293c28d342e4c160053702 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc4.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc4.tar.gz/md5 new file mode 100644 index 0000000..76df83b --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +b320e6bcb0184851cc5c8f776424b73f diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc4.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..163ebf3 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +fd3f5a20f29d64173f02b35e0d9edc6a4df6a81a5e7c2355dd0f925869b8fb7258ab43ec0911c4e91d10a3c59432d9c709cbfa845f29c393a59daabf1c7172ae diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc7.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc7.tar.gz/md5 new file mode 100644 index 0000000..9ae1af6 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +d0e5df7f099640b46be8ff5a66e4b6e1 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc7.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..5e1332f --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +e3fd81ca6a5c9b5fc23700c59ef610bf649213bda34940d7e625f90cfbf09dd6f086fb4614d02c022cf9c4eb912e7487219badc2e30eef058b60d8ecdb2219ed diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc8.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc8.tar.gz/md5 new file mode 100644 index 0000000..a8eb898 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +c07804931969bb9e569468416b60db6c diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc8.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..2416453 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +9ccd6bc350e406e782d52d821c1b9ded8adf67c332d90072657b3c0a8d3c42b65f48d891efe855a2b5067467fff542a86cf564675cd73defed828a07814365eb diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc4.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc4.tar.gz/md5 new file mode 100644 index 0000000..087c9fe --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +adf71aff58ce0f5ad224cca2d9c849d1 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc4.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..bb5bbd8 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +52cea3cfd56099777f35c2d04e4313c084f348598502d1c9c045a941bb8ec2767de81dd88abc181883c7a2fb0ec916e651a042a9666b89712162ef3b33ac6c9f diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc7.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc7.tar.gz/md5 new file mode 100644 index 0000000..6447b83 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +8f7ea4ba9465fdfe5a1fb182db51f1c6 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc7.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..51936f3 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +a1e0fdf3a92dae0a2c0bcc81d278003453521bc7b636e0e62565152a600996b1e24720669ee0e9d8fb65e19f8c0c53865168dd5904368e2ac0e1fcbca4eb8903 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc8.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc8.tar.gz/md5 new file mode 100644 index 0000000..1ec02db --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +354fe85d08d3d142404dbbdc172ce655 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc8.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..37941f3 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +b4844ef9c3c0c8be95c2fe07de9a4c2afbcf8e100699d38cf8f6caa38ad6c804812f2063faf1c669d680355c3d6e16b115618b8984313c2aee2cd37f4a6796de diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc4.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc4.tar.gz/md5 new file mode 100644 index 0000000..a13acb3 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +09e636495833682b7b636a379ece3835 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc4.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..6eade83 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +0deeb5229bb55b010212feb80c79d39804a776eec988fc877636df9541ac1135c7114d0814d19a02ced312c42bd84683a8886c50e680e4a6c71582521dca42bd diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc7.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc7.tar.gz/md5 new file mode 100644 index 0000000..f280864 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +40041e58babe9172b34716829867763a diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc7.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..a24f38a --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +e6acd7f2baab220768df9b21374c0548d20eef6e93ffba613e286d3cd0f31464a3fd2d14f65089460113f9603d3852a40a18331bddfee855b24986ab6720c29f diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc8.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc8.tar.gz/md5 new file mode 100644 index 0000000..099c993 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +bd952c914461b396924e48a67765445e diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc8.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..7d85316 --- /dev/null +++ b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +2c51c8ffd9d1c936fdbab2395cf29bc83f416473f3444ee5613a9f1b261c8693c7102bc2c2b53d2c2b5e473ffd94bd0db2d7cfc2e20d32c11129d1c3884b6214 diff --git a/deps/checksums/LibCURL.v7.61.0-1.aarch64-linux-gnu.tar.gz/md5 b/deps/checksums/LibCURL.v7.61.0-1.aarch64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..afa7701 --- /dev/null +++ b/deps/checksums/LibCURL.v7.61.0-1.aarch64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +a679e1c2bcd5b2bcb6e0f7b6398644a3 diff --git a/deps/checksums/LibCURL.v7.61.0-1.aarch64-linux-gnu.tar.gz/sha512 b/deps/checksums/LibCURL.v7.61.0-1.aarch64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..589f8de --- /dev/null +++ b/deps/checksums/LibCURL.v7.61.0-1.aarch64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +4fa6f0e27d99fb9149ae6d133066226660ec53fb8cef2b21e66a98cccd9f5f7a8ca236ac0bc191aa994a47fb9d185f86001b73d3266bd996d1f3f4d620e1e46d diff --git a/deps/checksums/LibCURL.v7.61.0-1.aarch64-linux-musl.tar.gz/md5 b/deps/checksums/LibCURL.v7.61.0-1.aarch64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..b65b03f --- /dev/null +++ b/deps/checksums/LibCURL.v7.61.0-1.aarch64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +e2df01b8890ba6a793dbf62531f3ae32 diff --git a/deps/checksums/LibCURL.v7.61.0-1.aarch64-linux-musl.tar.gz/sha512 b/deps/checksums/LibCURL.v7.61.0-1.aarch64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..3a0ad3f --- /dev/null +++ b/deps/checksums/LibCURL.v7.61.0-1.aarch64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +4105964b14fb0ce99b1f573e51fe3973554bf4cf064b5371bd9e3cb3595b4462ee18f4009c29930706cef882428a3386323dc8f661417ab7da1a830702aecc61 diff --git a/deps/checksums/LibCURL.v7.61.0-1.arm-linux-gnueabihf.tar.gz/md5 b/deps/checksums/LibCURL.v7.61.0-1.arm-linux-gnueabihf.tar.gz/md5 new file mode 100644 index 0000000..178c950 --- /dev/null +++ b/deps/checksums/LibCURL.v7.61.0-1.arm-linux-gnueabihf.tar.gz/md5 @@ -0,0 +1 @@ +d1e6c73aa6290037945bd2aea209b0d1 diff --git a/deps/checksums/LibCURL.v7.61.0-1.arm-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/LibCURL.v7.61.0-1.arm-linux-gnueabihf.tar.gz/sha512 new file mode 100644 index 0000000..019398d --- /dev/null +++ b/deps/checksums/LibCURL.v7.61.0-1.arm-linux-gnueabihf.tar.gz/sha512 @@ -0,0 +1 @@ +edefce343de35f111aaa293e28d9482a99ada565d4c0a262063302b6eb1086bd412c31798e3fd9c4b4811aeb0a49979a29afe2a6a853484c4bd18a929d72b382 diff --git a/deps/checksums/LibCURL.v7.61.0-1.arm-linux-musleabihf.tar.gz/md5 b/deps/checksums/LibCURL.v7.61.0-1.arm-linux-musleabihf.tar.gz/md5 new file mode 100644 index 0000000..fa8aed2 --- /dev/null +++ b/deps/checksums/LibCURL.v7.61.0-1.arm-linux-musleabihf.tar.gz/md5 @@ -0,0 +1 @@ +92f8d286ca7c0fdadf48f8f14fa5464d diff --git a/deps/checksums/LibCURL.v7.61.0-1.arm-linux-musleabihf.tar.gz/sha512 b/deps/checksums/LibCURL.v7.61.0-1.arm-linux-musleabihf.tar.gz/sha512 new file mode 100644 index 0000000..3a8a731 --- /dev/null +++ b/deps/checksums/LibCURL.v7.61.0-1.arm-linux-musleabihf.tar.gz/sha512 @@ -0,0 +1 @@ +0cd2610eb19bde172640fe485c63f9f8eec804988c314ae59d835d207a5b54dd20d7ff9af38ab383531671a03502239f561ff013affdd311aa6b1b04a138282a diff --git a/deps/checksums/LibCURL.v7.61.0-1.i686-linux-gnu.tar.gz/md5 b/deps/checksums/LibCURL.v7.61.0-1.i686-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..a7eee7c --- /dev/null +++ b/deps/checksums/LibCURL.v7.61.0-1.i686-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +e1d1655a61aafa128babc105624483ad diff --git a/deps/checksums/LibCURL.v7.61.0-1.i686-linux-gnu.tar.gz/sha512 b/deps/checksums/LibCURL.v7.61.0-1.i686-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..d0fb1b6 --- /dev/null +++ b/deps/checksums/LibCURL.v7.61.0-1.i686-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +a7c719fbc38bccc1e9a548c48839f3296fb17389924bee27d89aeb61bc462875237df534bc60bbe7571e8cc0494771f0c5ff0f421e24fe7d360be0fe74bccde7 diff --git a/deps/checksums/LibCURL.v7.61.0-1.i686-linux-musl.tar.gz/md5 b/deps/checksums/LibCURL.v7.61.0-1.i686-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..dc6c891 --- /dev/null +++ b/deps/checksums/LibCURL.v7.61.0-1.i686-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +d0f4b89ae107d3e9e3cf429a467c8b5a diff --git a/deps/checksums/LibCURL.v7.61.0-1.i686-linux-musl.tar.gz/sha512 b/deps/checksums/LibCURL.v7.61.0-1.i686-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..bbf253d --- /dev/null +++ b/deps/checksums/LibCURL.v7.61.0-1.i686-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +80031156ebf65ddcaa2bf48db893e0b6f35cd34d27ed91ed3c74e432a41e4f52a640a0d4ea7ffc92f1ce647b3b67056fe927c49fc60bb07e0c3fd4ea2cfc2b21 diff --git a/deps/checksums/LibCURL.v7.61.0-1.i686-w64-mingw32.tar.gz/md5 b/deps/checksums/LibCURL.v7.61.0-1.i686-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..a57c92c --- /dev/null +++ b/deps/checksums/LibCURL.v7.61.0-1.i686-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +feeff120fa186c3bc59db3e91173e6c6 diff --git a/deps/checksums/LibCURL.v7.61.0-1.i686-w64-mingw32.tar.gz/sha512 b/deps/checksums/LibCURL.v7.61.0-1.i686-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..9e07792 --- /dev/null +++ b/deps/checksums/LibCURL.v7.61.0-1.i686-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +6260f42f1480ee47163fa5dc54697cea88511fc81c5abc4086ae32380e645a1f0ac53c70f2d958ee5aed9945442595aaf9f3a980ec3625b2ef57a4495a650be2 diff --git a/deps/checksums/LibCURL.v7.61.0-1.powerpc64le-linux-gnu.tar.gz/md5 b/deps/checksums/LibCURL.v7.61.0-1.powerpc64le-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..c1bd3de --- /dev/null +++ b/deps/checksums/LibCURL.v7.61.0-1.powerpc64le-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +522e59c003b3618f36b7621f42c03f22 diff --git a/deps/checksums/LibCURL.v7.61.0-1.powerpc64le-linux-gnu.tar.gz/sha512 b/deps/checksums/LibCURL.v7.61.0-1.powerpc64le-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..ce3b653 --- /dev/null +++ b/deps/checksums/LibCURL.v7.61.0-1.powerpc64le-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +85955ea8a45eb2dd5efc110f939e80de1c56e1f5c3f89c695f306e03f9e5a1b206a1ee1634e41002c54894ca8b925e387f1a0f7692b72fa1a68fb09e65caf58d diff --git a/deps/checksums/LibCURL.v7.61.0-1.x86_64-apple-darwin14.tar.gz/md5 b/deps/checksums/LibCURL.v7.61.0-1.x86_64-apple-darwin14.tar.gz/md5 new file mode 100644 index 0000000..466cc7b --- /dev/null +++ b/deps/checksums/LibCURL.v7.61.0-1.x86_64-apple-darwin14.tar.gz/md5 @@ -0,0 +1 @@ +f64f5357c95c71774748d25b589ffc3f diff --git a/deps/checksums/LibCURL.v7.61.0-1.x86_64-apple-darwin14.tar.gz/sha512 b/deps/checksums/LibCURL.v7.61.0-1.x86_64-apple-darwin14.tar.gz/sha512 new file mode 100644 index 0000000..577d7e5 --- /dev/null +++ b/deps/checksums/LibCURL.v7.61.0-1.x86_64-apple-darwin14.tar.gz/sha512 @@ -0,0 +1 @@ +928663098cc31d29415bf169ce6e0599190b5bc6f4f5cb0afb9dd5e27a1b755b5895b133692386d72af20587deacabf8992b773e623ef30a2b7c9a01f40661a6 diff --git a/deps/checksums/LibCURL.v7.61.0-1.x86_64-linux-gnu.tar.gz/md5 b/deps/checksums/LibCURL.v7.61.0-1.x86_64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..7e0016e --- /dev/null +++ b/deps/checksums/LibCURL.v7.61.0-1.x86_64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +29f7832336bdad46574fe7a7b79ba053 diff --git a/deps/checksums/LibCURL.v7.61.0-1.x86_64-linux-gnu.tar.gz/sha512 b/deps/checksums/LibCURL.v7.61.0-1.x86_64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..39c7d5a --- /dev/null +++ b/deps/checksums/LibCURL.v7.61.0-1.x86_64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +4e58147b7db1c274158fd04442873293fe353c4eacee79963f09cd3f955f39260e70b96abf93f5a4bc2198397c6c6e5c3e9e64555ac28c86d5a09e9837cf90cb diff --git a/deps/checksums/LibCURL.v7.61.0-1.x86_64-linux-musl.tar.gz/md5 b/deps/checksums/LibCURL.v7.61.0-1.x86_64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..0d8eb52 --- /dev/null +++ b/deps/checksums/LibCURL.v7.61.0-1.x86_64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +3769a289fa301c2c62b3735d8c136c98 diff --git a/deps/checksums/LibCURL.v7.61.0-1.x86_64-linux-musl.tar.gz/sha512 b/deps/checksums/LibCURL.v7.61.0-1.x86_64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..72202e0 --- /dev/null +++ b/deps/checksums/LibCURL.v7.61.0-1.x86_64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +16f6d5c8f7bda5c1374c4aabe9d8a6e80b2557cf43881cdee72e4b64753eb1732cebfcfee108d7572e244b4696e2ccb972d27deec64e29eddf300f547e899f35 diff --git a/deps/checksums/LibCURL.v7.61.0-1.x86_64-unknown-freebsd11.1.tar.gz/md5 b/deps/checksums/LibCURL.v7.61.0-1.x86_64-unknown-freebsd11.1.tar.gz/md5 new file mode 100644 index 0000000..3db4bb1 --- /dev/null +++ b/deps/checksums/LibCURL.v7.61.0-1.x86_64-unknown-freebsd11.1.tar.gz/md5 @@ -0,0 +1 @@ +b07a8a49e8286d223acffe13c304deda diff --git a/deps/checksums/LibCURL.v7.61.0-1.x86_64-unknown-freebsd11.1.tar.gz/sha512 b/deps/checksums/LibCURL.v7.61.0-1.x86_64-unknown-freebsd11.1.tar.gz/sha512 new file mode 100644 index 0000000..ddcad85 --- /dev/null +++ b/deps/checksums/LibCURL.v7.61.0-1.x86_64-unknown-freebsd11.1.tar.gz/sha512 @@ -0,0 +1 @@ +2d6b221b0c2da6aad25914285e300b38c738dbcd9f9f8ef4c140ad8a9facf48a26769ffabbd2cd2f1dc4e466dd95dd7a116e4fa6f7ddecdf4d19ef283b6d67fc diff --git a/deps/checksums/LibCURL.v7.61.0-1.x86_64-w64-mingw32.tar.gz/md5 b/deps/checksums/LibCURL.v7.61.0-1.x86_64-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..f422a5c --- /dev/null +++ b/deps/checksums/LibCURL.v7.61.0-1.x86_64-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +064cc210e65632f94b9171f190fa3e79 diff --git a/deps/checksums/LibCURL.v7.61.0-1.x86_64-w64-mingw32.tar.gz/sha512 b/deps/checksums/LibCURL.v7.61.0-1.x86_64-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..96da192 --- /dev/null +++ b/deps/checksums/LibCURL.v7.61.0-1.x86_64-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +a51974e5fcc80adc09276f858ac4adf62c46f4b43e42e69b0b8f45f9ee0b696019649c2a65e74abe732c7f7dd0ac1843abf8fa68cabcd9bf1c7cdfac22700f81 diff --git a/deps/checksums/LibGit2.v0.28.2-0.aarch64-linux-gnu.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-0.aarch64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..4a64260 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-0.aarch64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +69e6f91fc215b7a04a2fc33c8d815b02 diff --git a/deps/checksums/LibGit2.v0.28.2-0.aarch64-linux-gnu.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-0.aarch64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..438978c --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-0.aarch64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +0294187cc3aa536d91e30d6074f9d5be11968b0df48d95bcdd39eadad4622520ab8dd1dda525afb1fc10297a3a5b7d3c326d2e6139adcdf7471c50d118bbc984 diff --git a/deps/checksums/LibGit2.v0.28.2-0.aarch64-linux-musl.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-0.aarch64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..da9a261 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-0.aarch64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +0d0a0b92a78a98f79bb4d28270cacaf2 diff --git a/deps/checksums/LibGit2.v0.28.2-0.aarch64-linux-musl.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-0.aarch64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..a659d53 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-0.aarch64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +8e0dd3cf4298df12bcc477f72b1c8e10aefe4ec8966211cc256a6fd677993508258ebcdbdc4b52843fa5f3fcd02650c93f1b4f1cd91357eabe715caa381797c3 diff --git a/deps/checksums/LibGit2.v0.28.2-0.arm-linux-gnueabihf.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-0.arm-linux-gnueabihf.tar.gz/md5 new file mode 100644 index 0000000..12d90c0 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-0.arm-linux-gnueabihf.tar.gz/md5 @@ -0,0 +1 @@ +959fe4c2139aecdba5a7c54914a48c9a diff --git a/deps/checksums/LibGit2.v0.28.2-0.arm-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-0.arm-linux-gnueabihf.tar.gz/sha512 new file mode 100644 index 0000000..45ee823 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-0.arm-linux-gnueabihf.tar.gz/sha512 @@ -0,0 +1 @@ +7607ad7b7825179dfd213092268e455c80a5d12ecb6a5b348dd64ff33410a3f3f00380784df083c251bce648f453687cfe05c3a1bbe208c301f2fee0be01961b diff --git a/deps/checksums/LibGit2.v0.28.2-0.arm-linux-musleabihf.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-0.arm-linux-musleabihf.tar.gz/md5 new file mode 100644 index 0000000..c81caac --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-0.arm-linux-musleabihf.tar.gz/md5 @@ -0,0 +1 @@ +e2b501234f162681c0d13984ed3b2ee6 diff --git a/deps/checksums/LibGit2.v0.28.2-0.arm-linux-musleabihf.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-0.arm-linux-musleabihf.tar.gz/sha512 new file mode 100644 index 0000000..238e8f0 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-0.arm-linux-musleabihf.tar.gz/sha512 @@ -0,0 +1 @@ +766ac41027dfdb3ac126648f3ccfdda5c05b38de59afdc8cbb4eecff52538926ede5969fce13cd069263dc7e1a85b4d5ed9a4d03059d3203d6616e230653bdb4 diff --git a/deps/checksums/LibGit2.v0.28.2-0.i686-linux-gnu.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-0.i686-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..e383d5f --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-0.i686-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +be29004b070454211f78ee056ea3e7a0 diff --git a/deps/checksums/LibGit2.v0.28.2-0.i686-linux-gnu.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-0.i686-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..6df39a0 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-0.i686-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +81efc0d456d7c3527691916259582eaea7e3084b43d271f207f3444c67fe8db7bb1a0d848ee49d3ba4731a4ef91123ed65ad0b7f9c2fc6d91262b25a69beec81 diff --git a/deps/checksums/LibGit2.v0.28.2-0.i686-linux-musl.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-0.i686-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..0945da0 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-0.i686-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +16a48be4d8077f5bff75b4420182c5aa diff --git a/deps/checksums/LibGit2.v0.28.2-0.i686-linux-musl.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-0.i686-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..f3c3f1f --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-0.i686-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +5e9a50afb191ed8a0d007277062c6527c9c246c82969efa6b58ae18967a9ef67d89755331e5c0411c8efb6a9e5321039477dcad22f983a4a7f72c4cb4144a7e5 diff --git a/deps/checksums/LibGit2.v0.28.2-0.i686-w64-mingw32.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-0.i686-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..72d4fde --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-0.i686-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +2f9032cf6489e9beebd62b0a7515aa3d diff --git a/deps/checksums/LibGit2.v0.28.2-0.i686-w64-mingw32.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-0.i686-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..0782a03 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-0.i686-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +9ab9819fcaaabfb5284ab167ece066e144bdc198d61e7d458aad03ebc0a84875a29bf42ed71a87c07e4e6b6c2f3e364f0d27d4b79c8aa0075a2912338dd11f23 diff --git a/deps/checksums/LibGit2.v0.28.2-0.powerpc64le-linux-gnu.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-0.powerpc64le-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..3d5b941 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-0.powerpc64le-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +8b38e077ee7c0fefab9d6fbafae4b9fd diff --git a/deps/checksums/LibGit2.v0.28.2-0.powerpc64le-linux-gnu.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-0.powerpc64le-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..7f6004b --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-0.powerpc64le-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +a40ebae0bffbc72429e8f3211bf5fd6689fbbf207e3cff46c5ca3b368b3c3e3dce646695adf79b25a2bcc507512b8b0ae222c40e1b92dd231b1ddc13be617f62 diff --git a/deps/checksums/LibGit2.v0.28.2-0.x86_64-apple-darwin14.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-0.x86_64-apple-darwin14.tar.gz/md5 new file mode 100644 index 0000000..57a445a --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-0.x86_64-apple-darwin14.tar.gz/md5 @@ -0,0 +1 @@ +b7e43e56116621e39e14992da40b5072 diff --git a/deps/checksums/LibGit2.v0.28.2-0.x86_64-apple-darwin14.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-0.x86_64-apple-darwin14.tar.gz/sha512 new file mode 100644 index 0000000..31ef8ee --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-0.x86_64-apple-darwin14.tar.gz/sha512 @@ -0,0 +1 @@ +1561b498755622ed4a1c9e857b5f684b52a43954cee06b9cbb04dcce65e60a87a54d2d694d7fd67422ee1e436c99e481899a6c3a171db66cbf5409098e7aa724 diff --git a/deps/checksums/LibGit2.v0.28.2-0.x86_64-linux-gnu.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-0.x86_64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..500877f --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-0.x86_64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +174eb178c914cd35e3cd86e434e483c3 diff --git a/deps/checksums/LibGit2.v0.28.2-0.x86_64-linux-gnu.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-0.x86_64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..a4ae42d --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-0.x86_64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +eb3d81ab6bef4abaa95ef57d9f6137834a663ed9484c85e2d3a687f2ec31fd018bb94371e3317ef8422df5738edeb85d3fd0a768d0a5676c1bd243d751e9cafc diff --git a/deps/checksums/LibGit2.v0.28.2-0.x86_64-linux-musl.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-0.x86_64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..90f1057 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-0.x86_64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +6eae54904dc532cb896e6b8353771ba0 diff --git a/deps/checksums/LibGit2.v0.28.2-0.x86_64-linux-musl.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-0.x86_64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..9df286c --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-0.x86_64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +52c850177a1d12ae4afdb8ba0c7b990d85e28e49cddc766b506bcb2b331eea96d1434def121e923b4a19c0c163bd8938b8b0158c0d635da46e10ad4416cb3eef diff --git a/deps/checksums/LibGit2.v0.28.2-0.x86_64-unknown-freebsd11.1.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-0.x86_64-unknown-freebsd11.1.tar.gz/md5 new file mode 100644 index 0000000..01d0e91 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-0.x86_64-unknown-freebsd11.1.tar.gz/md5 @@ -0,0 +1 @@ +dcc9e512ea89ceca7e8909107fc26ebe diff --git a/deps/checksums/LibGit2.v0.28.2-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 new file mode 100644 index 0000000..084cab0 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 @@ -0,0 +1 @@ +f38b6dedec618891757735e849df98930bb0aa53bfb6c8f2cdca4d83fcf747aac520330e83aee8e89463772dadb2c7b9e1e07816d3901b9699e55052ede50763 diff --git a/deps/checksums/LibGit2.v0.28.2-0.x86_64-w64-mingw32.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-0.x86_64-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..deeebbc --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-0.x86_64-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +adeef31a88dec49266b5e2ae1bf74ff6 diff --git a/deps/checksums/LibGit2.v0.28.2-0.x86_64-w64-mingw32.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-0.x86_64-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..50240f4 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-0.x86_64-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +4173ae2455ba2e651b0a98c5fd5e2019a0af76387f715c64e08130579eb8ce587c426af9862f096597dc6d23d76cd12232870ad4598049c313e45b2cb796ca97 diff --git a/deps/checksums/LibOSXUnwind.v0.0.5-0.x86_64-apple-darwin14.tar.gz/md5 b/deps/checksums/LibOSXUnwind.v0.0.5-0.x86_64-apple-darwin14.tar.gz/md5 new file mode 100644 index 0000000..cc1cd67 --- /dev/null +++ b/deps/checksums/LibOSXUnwind.v0.0.5-0.x86_64-apple-darwin14.tar.gz/md5 @@ -0,0 +1 @@ +3e092d738b24f7a121065b46af327f1a diff --git a/deps/checksums/LibOSXUnwind.v0.0.5-0.x86_64-apple-darwin14.tar.gz/sha512 b/deps/checksums/LibOSXUnwind.v0.0.5-0.x86_64-apple-darwin14.tar.gz/sha512 new file mode 100644 index 0000000..6bc69f9 --- /dev/null +++ b/deps/checksums/LibOSXUnwind.v0.0.5-0.x86_64-apple-darwin14.tar.gz/sha512 @@ -0,0 +1 @@ +6c484cba8b5151814764d73a8bc4e56292831ae48eb172c03135c8d83e49571380abe275209fdeb8855d0e6a78d1a4f12e15ef4ab09eebee73056cd406fe017b diff --git a/deps/checksums/LibOSXUnwind.v0.0.5.x86_64-apple-darwin14.tar.gz/md5 b/deps/checksums/LibOSXUnwind.v0.0.5.x86_64-apple-darwin14.tar.gz/md5 new file mode 100644 index 0000000..cc1cd67 --- /dev/null +++ b/deps/checksums/LibOSXUnwind.v0.0.5.x86_64-apple-darwin14.tar.gz/md5 @@ -0,0 +1 @@ +3e092d738b24f7a121065b46af327f1a diff --git a/deps/checksums/LibOSXUnwind.v0.0.5.x86_64-apple-darwin14.tar.gz/sha512 b/deps/checksums/LibOSXUnwind.v0.0.5.x86_64-apple-darwin14.tar.gz/sha512 new file mode 100644 index 0000000..6bc69f9 --- /dev/null +++ b/deps/checksums/LibOSXUnwind.v0.0.5.x86_64-apple-darwin14.tar.gz/sha512 @@ -0,0 +1 @@ +6c484cba8b5151814764d73a8bc4e56292831ae48eb172c03135c8d83e49571380abe275209fdeb8855d0e6a78d1a4f12e15ef4ab09eebee73056cd406fe017b diff --git a/deps/checksums/LibSSH2.v1.9.0-0.aarch64-linux-gnu.tar.gz/md5 b/deps/checksums/LibSSH2.v1.9.0-0.aarch64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..f665b70 --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-0.aarch64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +1de9f0816b41dd7c15640d6fd7bb8d5e diff --git a/deps/checksums/LibSSH2.v1.9.0-0.aarch64-linux-gnu.tar.gz/sha512 b/deps/checksums/LibSSH2.v1.9.0-0.aarch64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..ee6dfc6 --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-0.aarch64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +c2dbbd47befc923c11efa66f05ec1e1d38a3e3da438dcd5da29748f2e6c6ec944ac1d5e8f0062f4c46513faa39e903c8194df8f680b816458e0337d60ccbd9fa diff --git a/deps/checksums/LibSSH2.v1.9.0-0.aarch64-linux-musl.tar.gz/md5 b/deps/checksums/LibSSH2.v1.9.0-0.aarch64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..4d8c62c --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-0.aarch64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +6d7a76ebedd2425b53d23ccebd6a4592 diff --git a/deps/checksums/LibSSH2.v1.9.0-0.aarch64-linux-musl.tar.gz/sha512 b/deps/checksums/LibSSH2.v1.9.0-0.aarch64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..c2fba9a --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-0.aarch64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +b62666ef1f84c8236924188cb31701070356e36f7587d2d3bf13c4049996df06c96e3c4cdf4527fa0091491c5b3103cdfbf07a8fe37601359b86fe87741a2db4 diff --git a/deps/checksums/LibSSH2.v1.9.0-0.arm-linux-gnueabihf.tar.gz/md5 b/deps/checksums/LibSSH2.v1.9.0-0.arm-linux-gnueabihf.tar.gz/md5 new file mode 100644 index 0000000..cba02e5 --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-0.arm-linux-gnueabihf.tar.gz/md5 @@ -0,0 +1 @@ +ecc04e3bd5dfde850c58506320810c6e diff --git a/deps/checksums/LibSSH2.v1.9.0-0.arm-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/LibSSH2.v1.9.0-0.arm-linux-gnueabihf.tar.gz/sha512 new file mode 100644 index 0000000..ae453aa --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-0.arm-linux-gnueabihf.tar.gz/sha512 @@ -0,0 +1 @@ +93f7f3f92834d375da5a1fb2e766a7097628422c2d1dcdcc2f5e2388f31d1fbb2736ad8df3bf68c6a0b17e843c76b27c9c0d01b39bb2ea4df57cf6d5b753d322 diff --git a/deps/checksums/LibSSH2.v1.9.0-0.arm-linux-musleabihf.tar.gz/md5 b/deps/checksums/LibSSH2.v1.9.0-0.arm-linux-musleabihf.tar.gz/md5 new file mode 100644 index 0000000..fd08501 --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-0.arm-linux-musleabihf.tar.gz/md5 @@ -0,0 +1 @@ +d9bc4c981347f8efa5083eaa9be8198a diff --git a/deps/checksums/LibSSH2.v1.9.0-0.arm-linux-musleabihf.tar.gz/sha512 b/deps/checksums/LibSSH2.v1.9.0-0.arm-linux-musleabihf.tar.gz/sha512 new file mode 100644 index 0000000..8052c5f --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-0.arm-linux-musleabihf.tar.gz/sha512 @@ -0,0 +1 @@ +8a24b413f144499d866cace7dd32d3f366e6ba43bd170bf7a4924c985781144fa050573afcef9e1f4cedb0f70ec75625752656f6b71c208d95df2f067d9a04fc diff --git a/deps/checksums/LibSSH2.v1.9.0-0.i686-linux-gnu.tar.gz/md5 b/deps/checksums/LibSSH2.v1.9.0-0.i686-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..38c7017 --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-0.i686-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +65d5f6fa0a77175cf62ddb6a157715d7 diff --git a/deps/checksums/LibSSH2.v1.9.0-0.i686-linux-gnu.tar.gz/sha512 b/deps/checksums/LibSSH2.v1.9.0-0.i686-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..06bc91b --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-0.i686-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +ef93dc51651338e42b23039fbcee8a48e4cfea7e4b9dcd1d0f4049a752e7ec563ed1f0a951157a5402be8b206def82da253f7fa3652c1480a1507bc6faaae382 diff --git a/deps/checksums/LibSSH2.v1.9.0-0.i686-linux-musl.tar.gz/md5 b/deps/checksums/LibSSH2.v1.9.0-0.i686-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..9ec5c9e --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-0.i686-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +de5c00c9e4b0a9a4d26afe0a73d528b9 diff --git a/deps/checksums/LibSSH2.v1.9.0-0.i686-linux-musl.tar.gz/sha512 b/deps/checksums/LibSSH2.v1.9.0-0.i686-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..d88f643 --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-0.i686-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +7c496df3e147991994bd3c37497e2c51c0ce00e709adb86a8789c8b252af7e8148f7e2e71439e435e78fde4cd7695444a8f532e02529698fc8c795f8ebafff10 diff --git a/deps/checksums/LibSSH2.v1.9.0-0.i686-w64-mingw32.tar.gz/md5 b/deps/checksums/LibSSH2.v1.9.0-0.i686-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..88f18fd --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-0.i686-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +6908e848c335c58c8685859916ca840e diff --git a/deps/checksums/LibSSH2.v1.9.0-0.i686-w64-mingw32.tar.gz/sha512 b/deps/checksums/LibSSH2.v1.9.0-0.i686-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..6df03ec --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-0.i686-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +f4a004e919b95bba227f0fa2595a60c7caab168f3558ad5559d6f7ab2777dec70eefb434294e8fbafe7414ea15d6669f6a8d09e882183cb8a6efc59298517b1f diff --git a/deps/checksums/LibSSH2.v1.9.0-0.powerpc64le-linux-gnu.tar.gz/md5 b/deps/checksums/LibSSH2.v1.9.0-0.powerpc64le-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..5a232e0 --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-0.powerpc64le-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +72ab7a7bb11e4961036b935c1dd4dfd7 diff --git a/deps/checksums/LibSSH2.v1.9.0-0.powerpc64le-linux-gnu.tar.gz/sha512 b/deps/checksums/LibSSH2.v1.9.0-0.powerpc64le-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..b873f49 --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-0.powerpc64le-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +aaf4e21768ab22a91848116615aea6eb1d9763894a7080bbd88ca50a40382488ad42dde61e220a730ad30a4062f658c233ef0ac298913e7a35f3de62476a31f6 diff --git a/deps/checksums/LibSSH2.v1.9.0-0.x86_64-apple-darwin14.tar.gz/md5 b/deps/checksums/LibSSH2.v1.9.0-0.x86_64-apple-darwin14.tar.gz/md5 new file mode 100644 index 0000000..94d5cb6 --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-0.x86_64-apple-darwin14.tar.gz/md5 @@ -0,0 +1 @@ +f383418e3c79f80a8b808e5a11653acb diff --git a/deps/checksums/LibSSH2.v1.9.0-0.x86_64-apple-darwin14.tar.gz/sha512 b/deps/checksums/LibSSH2.v1.9.0-0.x86_64-apple-darwin14.tar.gz/sha512 new file mode 100644 index 0000000..23145f8 --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-0.x86_64-apple-darwin14.tar.gz/sha512 @@ -0,0 +1 @@ +5a49877bd90cb314628e1acd120a6c15319e1b10e7b6326cb3f420f6d1921348c0e2331b50af52388fbc31309cf54bdb31235f1c58571f870756fbe3947cfa22 diff --git a/deps/checksums/LibSSH2.v1.9.0-0.x86_64-linux-gnu.tar.gz/md5 b/deps/checksums/LibSSH2.v1.9.0-0.x86_64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..bc272bb --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-0.x86_64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +dcab51c5abd84cea28ab5eb8f279fae4 diff --git a/deps/checksums/LibSSH2.v1.9.0-0.x86_64-linux-gnu.tar.gz/sha512 b/deps/checksums/LibSSH2.v1.9.0-0.x86_64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..81331e0 --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-0.x86_64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +a7888e271801b0992b8b61d1e8d6e4fa3cf69920655bc756d35aafbb685d07df8a2f5af48217b47135edc6bad50748f2c122181bf6c8e7ac77a08ab5c2206ada diff --git a/deps/checksums/LibSSH2.v1.9.0-0.x86_64-linux-musl.tar.gz/md5 b/deps/checksums/LibSSH2.v1.9.0-0.x86_64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..6307958 --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-0.x86_64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +7d64466c8210cd691295ae9ecbb3ae7e diff --git a/deps/checksums/LibSSH2.v1.9.0-0.x86_64-linux-musl.tar.gz/sha512 b/deps/checksums/LibSSH2.v1.9.0-0.x86_64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..9f9385a --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-0.x86_64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +87f0beae3ec9a82cd4df54a480c84b1b04c401fed0cec1515665832aa4a5a2b86923e97beaa37dad8eb259fc1e707b4a47e05747ad88137def12becf1d82a144 diff --git a/deps/checksums/LibSSH2.v1.9.0-0.x86_64-unknown-freebsd11.1.tar.gz/md5 b/deps/checksums/LibSSH2.v1.9.0-0.x86_64-unknown-freebsd11.1.tar.gz/md5 new file mode 100644 index 0000000..dedca8f --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-0.x86_64-unknown-freebsd11.1.tar.gz/md5 @@ -0,0 +1 @@ +82147c4cfe14c5e9213776dc8a3162fe diff --git a/deps/checksums/LibSSH2.v1.9.0-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 b/deps/checksums/LibSSH2.v1.9.0-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 new file mode 100644 index 0000000..8391275 --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 @@ -0,0 +1 @@ +ce64acd2655fc2b6af56ec00c910ff0123b32995bb62af9e1d6cf48194ef994fecb3e4f2b16d0e8060c9740206fae178e3b6fe89cf95992289e7dee8fdccb982 diff --git a/deps/checksums/LibSSH2.v1.9.0-0.x86_64-w64-mingw32.tar.gz/md5 b/deps/checksums/LibSSH2.v1.9.0-0.x86_64-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..5560eb2 --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-0.x86_64-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +5e751cb7817a151a88b828dc077f0982 diff --git a/deps/checksums/LibSSH2.v1.9.0-0.x86_64-w64-mingw32.tar.gz/sha512 b/deps/checksums/LibSSH2.v1.9.0-0.x86_64-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..72d986e --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-0.x86_64-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +8e8c70a993a30e88e06c11eb7042d7ec45f2737790eda7d14bca8e8689d713eda7ca791b4011bbfe5bbec16039c4bfd5cec068bf0194c104fcaf446d48bf0c1a diff --git a/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.aarch64-linux-gnu.tar.gz/md5 b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.aarch64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..68dc7f1 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.aarch64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +f62a26a8747875641cf208c10398f8c3 diff --git a/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.aarch64-linux-gnu.tar.gz/sha512 b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.aarch64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..c7e070a --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.aarch64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +3cd90ede9313f15f8bd0484436722c0233d6462675d9afd9b758c1f2eba545e006c08a10d082ff93a2bcda6db25b7186fc41e2fcb882d8b3b7ca13a29f85efe5 diff --git a/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.aarch64-linux-musl.tar.gz/md5 b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.aarch64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..fdd80da --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.aarch64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +88deb297c61dd5dc23609b5811e8ddf3 diff --git a/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.aarch64-linux-musl.tar.gz/sha512 b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.aarch64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..1a53e81 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.aarch64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +cfdefe8ca33e1fba5a2677f40aff23cbf66fa5c58a5d0aa23fb37c1dc5a18c92d6f0228e42f39370939516fb27b7976bf4f8aa922662b9b2cd28ea9354fb7eb7 diff --git a/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.arm-linux-gnueabihf.tar.gz/md5 b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.arm-linux-gnueabihf.tar.gz/md5 new file mode 100644 index 0000000..4cd4c49 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.arm-linux-gnueabihf.tar.gz/md5 @@ -0,0 +1 @@ +d574f0328b92f89dbad112fb7469b0e3 diff --git a/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.arm-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.arm-linux-gnueabihf.tar.gz/sha512 new file mode 100644 index 0000000..ba6f537 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.arm-linux-gnueabihf.tar.gz/sha512 @@ -0,0 +1 @@ +8e81de23baef1060307f8642ba8bae0c05566d4291f9c876da4a1409db67206527f8afce0b2cb94758f1efcefa3bfd1ca5b7ccb87d5b0bf291c4b154e1ad7c15 diff --git a/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.arm-linux-musleabihf.tar.gz/md5 b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.arm-linux-musleabihf.tar.gz/md5 new file mode 100644 index 0000000..9fb5420 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.arm-linux-musleabihf.tar.gz/md5 @@ -0,0 +1 @@ +f02bd3f826599fd654f75be6ef3c413f diff --git a/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.arm-linux-musleabihf.tar.gz/sha512 b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.arm-linux-musleabihf.tar.gz/sha512 new file mode 100644 index 0000000..4bb2992 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.arm-linux-musleabihf.tar.gz/sha512 @@ -0,0 +1 @@ +ee897ae441f5de0ab31ba272e36620802e1ea09ef8e9982b9fdae3ba55b06e594e0aafe0faad435e1ae8a47335d49decc2bfdd06328a578eaf599881b94726b5 diff --git a/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.i686-linux-gnu.tar.gz/md5 b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.i686-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..18fd79a --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.i686-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +3fa6b5b6c966abc7e2e66392005d9bed diff --git a/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.i686-linux-gnu.tar.gz/sha512 b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.i686-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..1da5d74 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.i686-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +2ef3da931a9758427090a655126515218e95093c5f72bab46eb81da7983f57858ddd4553bd62853fc79bcb9e5ee31ea7f7301e337661752a74113699e5f30eef diff --git a/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.i686-linux-musl.tar.gz/md5 b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.i686-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..25747f6 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.i686-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +c8e5815ad79ce6d9948ab8270fefb8c4 diff --git a/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.i686-linux-musl.tar.gz/sha512 b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.i686-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..0562405 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.i686-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +27ff7670ae35b12f095f972fd4697ca25f3f09b5a58567ac0f4837222a55f47cb53a587d34bfba7d2c694c60a5ef3d938d1a950dbf3c6a05c0b1c95fe7177acf diff --git a/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.i686-w64-mingw32.tar.gz/md5 b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.i686-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..7636855 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.i686-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +68787d2b3f47593d4b9f9a65a06a5b9d diff --git a/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.i686-w64-mingw32.tar.gz/sha512 b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.i686-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..adb18d8 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.i686-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +c49af88c0de1fce898841a11a7eeb03261f1838ad01dadfeec0fa051a6f5a73821208910823d148a2f97e455aab1ae2d5f71928b2bb2e2bdd037a679f2300d3e diff --git a/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.powerpc64le-linux-gnu.tar.gz/md5 b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.powerpc64le-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..6746323 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.powerpc64le-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +8cd161fe79a728f8cfa5bb237164405f diff --git a/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.powerpc64le-linux-gnu.tar.gz/sha512 b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.powerpc64le-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..7ef3217 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.powerpc64le-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +570a5e86bb67afe86509440a62bd3af971496c7fec95b0751796dda16d8b000bdadce781f40992decf886bb2f1b5737b3049774d929d128cf91a3e0cee79cbb9 diff --git a/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-apple-darwin14.tar.gz/md5 b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-apple-darwin14.tar.gz/md5 new file mode 100644 index 0000000..14bc27e --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-apple-darwin14.tar.gz/md5 @@ -0,0 +1 @@ +9a538e613df006bf06e247011b445cf3 diff --git a/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-apple-darwin14.tar.gz/sha512 b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-apple-darwin14.tar.gz/sha512 new file mode 100644 index 0000000..19f9891 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-apple-darwin14.tar.gz/sha512 @@ -0,0 +1 @@ +13a555d4f04c6cbd5d058f737d94da66ffd69fa04093737450c6e537c637126e085a14d97f9ef0c43f62d7f48f94928261f42caef12cb342faf197aed8ad1a35 diff --git a/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-linux-gnu.tar.gz/md5 b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..528f832 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +ab9a8d2010cc91d959878eaafd5cdd56 diff --git a/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-linux-gnu.tar.gz/sha512 b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..556db0f --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +ed73132f846b42d82eea537d0deb17abfd4f60c81535d1a161bf457c2bd00c6befb8ffc287e054c2abbe3bb2ecb890d85639459dc6641c4aa5a291832249dd66 diff --git a/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-linux-musl.tar.gz/md5 b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..bb3ef49 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +fae95b4b5aab54b4cda42d7dfaabdf80 diff --git a/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-linux-musl.tar.gz/sha512 b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..1b52b8b --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +98e21f1068f4863edabbd6648c57c7d7c08c51991010223bef5ee54a6589e56e5151dae33b59cf9de614d6c1e1635bb1effdd480fa03c01bfe5b461dcb12ccc2 diff --git a/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-unknown-freebsd11.1.tar.gz/md5 b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-unknown-freebsd11.1.tar.gz/md5 new file mode 100644 index 0000000..b80a8b9 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-unknown-freebsd11.1.tar.gz/md5 @@ -0,0 +1 @@ +6a9df3e3796e4668ad6309c52aba51a7 diff --git a/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 new file mode 100644 index 0000000..8ae5691 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 @@ -0,0 +1 @@ +a5193c38d2c7e02be34c3e4a77dfb37de46e86c620e7964f177eb2a3bb35741cff1cb58cd4a7b269d45cb6d169a9cdb5a7e805823b062cd6878bec54f6cc2678 diff --git a/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-w64-mingw32.tar.gz/md5 b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..98e6f27 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +32d2b4a5a8edb51ce9c79a32ebbcd5e9 diff --git a/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-w64-mingw32.tar.gz/sha512 b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..7a0a40c --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.x86_64-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +744d72d1047acbf289e826281bac53bf4624d979c1e07c17e7ded11217c1f2dfe566cc6d59e9fbdfd98e181120ebb28411d021f47c3a95d42ab1af0e90802d5f diff --git a/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.aarch64-linux-gnu.tar.gz/md5 b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.aarch64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..8d844af --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.aarch64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +e3c78f70717b1323f300925bf7877a8a diff --git a/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.aarch64-linux-gnu.tar.gz/sha512 b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.aarch64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..0887024 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.aarch64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +236287de60e7e400e79224843217a15735e9873d7473e5bb029bea0462dcf85b39ee0ad8efb85397713e07fbb795d5cc67e4398e4fb700b2c82257be6765035f diff --git a/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.aarch64-linux-musl.tar.gz/md5 b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.aarch64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..ead98d0 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.aarch64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +cb93ad3a9d01e739071a29c02b4bac60 diff --git a/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.aarch64-linux-musl.tar.gz/sha512 b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.aarch64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..f3fb0f6 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.aarch64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +494d9de6e38ba931cdb799a974dd4c781019dfe24ce8813b78c745b4b3a5a68edb45a81d028069f13bcb4353f6ba9f7757ccc2455b612eed068c0595362b6830 diff --git a/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.arm-linux-gnueabihf.tar.gz/md5 b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.arm-linux-gnueabihf.tar.gz/md5 new file mode 100644 index 0000000..8c208c6 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.arm-linux-gnueabihf.tar.gz/md5 @@ -0,0 +1 @@ +ced3ff67ea4ed9b55cac37ae3e1a8ff9 diff --git a/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.arm-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.arm-linux-gnueabihf.tar.gz/sha512 new file mode 100644 index 0000000..1ca301b --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.arm-linux-gnueabihf.tar.gz/sha512 @@ -0,0 +1 @@ +9293cfa079236620d588e1bebddf06864b4f5ca1d19d0a79852325d3c74e8e4b4b1066996c22cf5c8d16346973dcc20fc3fed8b5e53d50075999b5cbe22615e0 diff --git a/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.arm-linux-musleabihf.tar.gz/md5 b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.arm-linux-musleabihf.tar.gz/md5 new file mode 100644 index 0000000..5d2b824 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.arm-linux-musleabihf.tar.gz/md5 @@ -0,0 +1 @@ +5a4d5bd0e2fb7c317d0e7fc079e657f6 diff --git a/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.arm-linux-musleabihf.tar.gz/sha512 b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.arm-linux-musleabihf.tar.gz/sha512 new file mode 100644 index 0000000..5fef3f2 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.arm-linux-musleabihf.tar.gz/sha512 @@ -0,0 +1 @@ +5e930c49987c04b8a9ce5481ffa28c6a0e292b0a6d86f8459258fc79e15b0a4c7402be23e4dda2ca640c7cd0defab60c4e1435df5685e23e0a70a7d25494b9c6 diff --git a/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.i686-linux-gnu.tar.gz/md5 b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.i686-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..09f75ba --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.i686-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +d85e235d6ba2d3a75faeec5a01437a35 diff --git a/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.i686-linux-gnu.tar.gz/sha512 b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.i686-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..6f9aa1a --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.i686-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +cb3227eee7c4d9f8bfee9a5b4101509ffe9570eb064b53ede6d6eb6ef520219ac854276160f1b7fb70dde7ca4eda78cccb93eb68e8a9d5686fbd4f124bf4ca96 diff --git a/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.i686-linux-musl.tar.gz/md5 b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.i686-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..a2dec16 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.i686-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +ef3dddd3aca2a317ba8c2f2fe1a9a4f4 diff --git a/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.i686-linux-musl.tar.gz/sha512 b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.i686-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..871574a --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.i686-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +a32fdb1a55ca7bef2dda81f388cf0c231150dee2323cd349a3fbb1d7da7fe66aaa33f6eff3b34dfff60f64de60986f0d56edcae8b08e9cf7129d6febe395db46 diff --git a/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.i686-w64-mingw32.tar.gz/md5 b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.i686-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..7c0f9fe --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.i686-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +0ee316030df3c6762991564c26efc2f8 diff --git a/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.i686-w64-mingw32.tar.gz/sha512 b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.i686-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..35d49b7 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.i686-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +783dfcb0e0760e8d9b58bd587767ee96abead2577555b5ed5fb84b1c120312e7d5f0d791b1157c5fe1e4ca9612cc56d52b5c644d4312fc0b7491fff25188a264 diff --git a/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.powerpc64le-linux-gnu.tar.gz/md5 b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.powerpc64le-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..ad4df84 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.powerpc64le-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +58f2540cdae3124defbb2f8ddb16631a diff --git a/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.powerpc64le-linux-gnu.tar.gz/sha512 b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.powerpc64le-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..4b6b0f1 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.powerpc64le-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +c66768a860a3c05364022244ab1f3a9e87f242cf3731e63e7461340a564d56133ef9238b9e156ac6b2fe7b23cd143f853cb13befb6b27df0f9b183647a205508 diff --git a/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-apple-darwin14.tar.gz/md5 b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-apple-darwin14.tar.gz/md5 new file mode 100644 index 0000000..07758bf --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-apple-darwin14.tar.gz/md5 @@ -0,0 +1 @@ +2d7b9822dc407d25a00d86e4e545e4e1 diff --git a/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-apple-darwin14.tar.gz/sha512 b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-apple-darwin14.tar.gz/sha512 new file mode 100644 index 0000000..1346be8 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-apple-darwin14.tar.gz/sha512 @@ -0,0 +1 @@ +a7724c477550b13297e6cde70651ce673a2151f698529ecee1e78f76bdf3c4de77d333786caa96da9c8b5e0b38ed323f7aa135f1e4a874608a16f43fe3db0ec7 diff --git a/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-linux-gnu.tar.gz/md5 b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..a96d5e8 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +49c38e6f0d1ee9665bb15d7a12cc0fea diff --git a/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-linux-gnu.tar.gz/sha512 b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..e7a0128 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +9d562e0e4fc304afb7c948aab6d9522ed697216f3c458392b7b5b80a41df8a5a366733b269aa3828d4453dfd770bd0bac817ec4165a8affa568ce57dd450c861 diff --git a/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-linux-musl.tar.gz/md5 b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..8716698 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +49dede722eb6ec52d5b59482fde11ee0 diff --git a/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-linux-musl.tar.gz/sha512 b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..b4683a7 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +ca0b9d6d43f16a2c55805e41c08851b2b54df68f031ca3acef9dfb5f4ceb43cce37f51f34092f6ae67a415fb580adc462a275dfc87f3e9f469df26a1d3ebf6f6 diff --git a/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-unknown-freebsd11.1.tar.gz/md5 b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-unknown-freebsd11.1.tar.gz/md5 new file mode 100644 index 0000000..46f2440 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-unknown-freebsd11.1.tar.gz/md5 @@ -0,0 +1 @@ +72ae8dded9baf8ab0b0a179859734b83 diff --git a/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 new file mode 100644 index 0000000..5378b97 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 @@ -0,0 +1 @@ +26bb930cf161a573910b6c739c9ac3804a021337119037954006b4e8f6cd8eefba2c03988fb0ba4c38fa35a3398ca6ffdf10bc4d5fcce4b3d0f7e5ff41edfd6a diff --git a/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-w64-mingw32.tar.gz/md5 b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..2756dd9 --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +3c04d98d70369a378434c04b3478e93d diff --git a/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-w64-mingw32.tar.gz/sha512 b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..c89874d --- /dev/null +++ b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.x86_64-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +b7abdaad577ec48a28735f1ff7e1092da1c2fd384e51fccb2eb7302a7dc5856cc5e7f95299a7b650bfd784f9ecfc6a311c181e1d42673fe27d69991dcace349b diff --git a/deps/checksums/LibUnwind.v1.3.1-4.aarch64-linux-gnu.tar.gz/md5 b/deps/checksums/LibUnwind.v1.3.1-4.aarch64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..e9c2f0f --- /dev/null +++ b/deps/checksums/LibUnwind.v1.3.1-4.aarch64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +e35f5fb8a303efee449c70d370f01925 diff --git a/deps/checksums/LibUnwind.v1.3.1-4.aarch64-linux-gnu.tar.gz/sha512 b/deps/checksums/LibUnwind.v1.3.1-4.aarch64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..0b8a627 --- /dev/null +++ b/deps/checksums/LibUnwind.v1.3.1-4.aarch64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +da9ad44d45474af8ee0bde497956b6bb6df4b2144c40c562cb8bd20ea113bc48d0c1fbd32d714bced325ea534d74ad6029dda6c2f7e3269a15a1926ecb481dd3 diff --git a/deps/checksums/LibUnwind.v1.3.1-4.aarch64-linux-musl.tar.gz/md5 b/deps/checksums/LibUnwind.v1.3.1-4.aarch64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..7b4a7ae --- /dev/null +++ b/deps/checksums/LibUnwind.v1.3.1-4.aarch64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +25f0c6b4dd4633725545aaec4b988152 diff --git a/deps/checksums/LibUnwind.v1.3.1-4.aarch64-linux-musl.tar.gz/sha512 b/deps/checksums/LibUnwind.v1.3.1-4.aarch64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..19fe226 --- /dev/null +++ b/deps/checksums/LibUnwind.v1.3.1-4.aarch64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +f71f3540c311774f266e52ae463b91be817095a182920adc5b40d93384fa7661ec7f729542e2a6a3e2b9803babcfe13d57781cfb2f1465ffdc3a7dfabe3929ce diff --git a/deps/checksums/LibUnwind.v1.3.1-4.arm-linux-gnueabihf.tar.gz/md5 b/deps/checksums/LibUnwind.v1.3.1-4.arm-linux-gnueabihf.tar.gz/md5 new file mode 100644 index 0000000..555b12b --- /dev/null +++ b/deps/checksums/LibUnwind.v1.3.1-4.arm-linux-gnueabihf.tar.gz/md5 @@ -0,0 +1 @@ +cfba31485165d109f899060ff5c8d86f diff --git a/deps/checksums/LibUnwind.v1.3.1-4.arm-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/LibUnwind.v1.3.1-4.arm-linux-gnueabihf.tar.gz/sha512 new file mode 100644 index 0000000..e0872a1 --- /dev/null +++ b/deps/checksums/LibUnwind.v1.3.1-4.arm-linux-gnueabihf.tar.gz/sha512 @@ -0,0 +1 @@ +1eee02d647b51ab4703c6fe75ca69250c0d70920230d1705307124281f883a18e3485d439dce8376dab46d8d56347bc61e3f11355e0a988a742f8ea527062267 diff --git a/deps/checksums/LibUnwind.v1.3.1-4.arm-linux-musleabihf.tar.gz/md5 b/deps/checksums/LibUnwind.v1.3.1-4.arm-linux-musleabihf.tar.gz/md5 new file mode 100644 index 0000000..bf4e990 --- /dev/null +++ b/deps/checksums/LibUnwind.v1.3.1-4.arm-linux-musleabihf.tar.gz/md5 @@ -0,0 +1 @@ +43e681f53a3886c97fd8496f50310a5c diff --git a/deps/checksums/LibUnwind.v1.3.1-4.arm-linux-musleabihf.tar.gz/sha512 b/deps/checksums/LibUnwind.v1.3.1-4.arm-linux-musleabihf.tar.gz/sha512 new file mode 100644 index 0000000..fff0858 --- /dev/null +++ b/deps/checksums/LibUnwind.v1.3.1-4.arm-linux-musleabihf.tar.gz/sha512 @@ -0,0 +1 @@ +c70abd45910319e19507ae11d7abad202fe73f6e502cbd6b19c3d8d6d6bd05bd954a10cbee65eb16d9cfcbe644e3e7f9aa970dc7fdd0bf800f31a567bd6124b1 diff --git a/deps/checksums/LibUnwind.v1.3.1-4.i686-linux-gnu.tar.gz/md5 b/deps/checksums/LibUnwind.v1.3.1-4.i686-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..b187b1d --- /dev/null +++ b/deps/checksums/LibUnwind.v1.3.1-4.i686-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +b1a3fdc79784f8af6efa7f8b180c6271 diff --git a/deps/checksums/LibUnwind.v1.3.1-4.i686-linux-gnu.tar.gz/sha512 b/deps/checksums/LibUnwind.v1.3.1-4.i686-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..97771c8 --- /dev/null +++ b/deps/checksums/LibUnwind.v1.3.1-4.i686-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +9f7bf582b1b5c3a2fd93ce80996ca6f1a7b5680f07fe6e4ddba90a8782c8b817fdafc742c8dd22936f677075619b1c5ec16f094ae4f577a1be9fcd398d88fcaf diff --git a/deps/checksums/LibUnwind.v1.3.1-4.i686-linux-musl.tar.gz/md5 b/deps/checksums/LibUnwind.v1.3.1-4.i686-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..31fa1f2 --- /dev/null +++ b/deps/checksums/LibUnwind.v1.3.1-4.i686-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +c5aeb572d6fac24a91e71cf4be3a22ca diff --git a/deps/checksums/LibUnwind.v1.3.1-4.i686-linux-musl.tar.gz/sha512 b/deps/checksums/LibUnwind.v1.3.1-4.i686-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..42a24ca --- /dev/null +++ b/deps/checksums/LibUnwind.v1.3.1-4.i686-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +9fe0592b9360cc742b1155a45de19e9aee30f6a88b928d53eaea8cd02126b8ce712f71228d69299370d83d8864ab13ba417c55046bcf0d1632c1032b7996a9c0 diff --git a/deps/checksums/LibUnwind.v1.3.1-4.powerpc64le-linux-gnu.tar.gz/md5 b/deps/checksums/LibUnwind.v1.3.1-4.powerpc64le-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..b30536a --- /dev/null +++ b/deps/checksums/LibUnwind.v1.3.1-4.powerpc64le-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +37cd841d90af1bb438e9a23141abf121 diff --git a/deps/checksums/LibUnwind.v1.3.1-4.powerpc64le-linux-gnu.tar.gz/sha512 b/deps/checksums/LibUnwind.v1.3.1-4.powerpc64le-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..cb81efe --- /dev/null +++ b/deps/checksums/LibUnwind.v1.3.1-4.powerpc64le-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +1fedacfb681e41142ba2814aaf2455980e9993259cbc0aa1604c3490c905c841332de55a24453e6be9c30398c797ec633c73c7d0f3330fd16b6a11b8e303535c diff --git a/deps/checksums/LibUnwind.v1.3.1-4.x86_64-linux-gnu.tar.gz/md5 b/deps/checksums/LibUnwind.v1.3.1-4.x86_64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..a433ffe --- /dev/null +++ b/deps/checksums/LibUnwind.v1.3.1-4.x86_64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +a63ab5e4a513f5fdb136d3485df2cbc4 diff --git a/deps/checksums/LibUnwind.v1.3.1-4.x86_64-linux-gnu.tar.gz/sha512 b/deps/checksums/LibUnwind.v1.3.1-4.x86_64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..a2d558d --- /dev/null +++ b/deps/checksums/LibUnwind.v1.3.1-4.x86_64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +48368371903254799fb38c9a86f722334814fa7e3456466b904dba214607257510dca91ca21e7b6ef81640b031363d2c2414fd364ce878c6f12bd06046560165 diff --git a/deps/checksums/LibUnwind.v1.3.1-4.x86_64-linux-musl.tar.gz/md5 b/deps/checksums/LibUnwind.v1.3.1-4.x86_64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..c9baf75 --- /dev/null +++ b/deps/checksums/LibUnwind.v1.3.1-4.x86_64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +cb3ae0b0367a72ff38180cbbbb82d05c diff --git a/deps/checksums/LibUnwind.v1.3.1-4.x86_64-linux-musl.tar.gz/sha512 b/deps/checksums/LibUnwind.v1.3.1-4.x86_64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..4e81e16 --- /dev/null +++ b/deps/checksums/LibUnwind.v1.3.1-4.x86_64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +2018321394c5babf02cb984214faf0510df74814e95fe95e9b28078016ea0b9d2e5d55f6626c27a1532f5f2405a988c3d6c5922bb7ec00f7fe0a316ef39cf7d7 diff --git a/deps/checksums/LibUnwind.v1.3.1-4.x86_64-unknown-freebsd11.1.tar.gz/md5 b/deps/checksums/LibUnwind.v1.3.1-4.x86_64-unknown-freebsd11.1.tar.gz/md5 new file mode 100644 index 0000000..d8a6ead --- /dev/null +++ b/deps/checksums/LibUnwind.v1.3.1-4.x86_64-unknown-freebsd11.1.tar.gz/md5 @@ -0,0 +1 @@ +2d8ebbc041661ad46fca761fb7860962 diff --git a/deps/checksums/LibUnwind.v1.3.1-4.x86_64-unknown-freebsd11.1.tar.gz/sha512 b/deps/checksums/LibUnwind.v1.3.1-4.x86_64-unknown-freebsd11.1.tar.gz/sha512 new file mode 100644 index 0000000..a0aa565 --- /dev/null +++ b/deps/checksums/LibUnwind.v1.3.1-4.x86_64-unknown-freebsd11.1.tar.gz/sha512 @@ -0,0 +1 @@ +982f8b7c2d649284883bdb5174aea36b6b3bea94b0e561cbfdbf476d9ceb0c00986ca0d6f68e2ab12e3ccd2504605278d6846c3767ed83f2be119b80a2a9042e diff --git a/deps/checksums/MPFR.v4.0.2-0.aarch64-linux-gnu.tar.gz/md5 b/deps/checksums/MPFR.v4.0.2-0.aarch64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..ed9b34c --- /dev/null +++ b/deps/checksums/MPFR.v4.0.2-0.aarch64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +1cda9d8886b5b87a496caaa08a65c087 diff --git a/deps/checksums/MPFR.v4.0.2-0.aarch64-linux-gnu.tar.gz/sha512 b/deps/checksums/MPFR.v4.0.2-0.aarch64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..703f987 --- /dev/null +++ b/deps/checksums/MPFR.v4.0.2-0.aarch64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +481683f1d7abc3346b003c275c44e199b4ff89358ef12c4b2dab7458c68237b0aafb76792de2932ac91e5228c23360b7fa5fa2ef92c0994157e3c33bb24cbea9 diff --git a/deps/checksums/MPFR.v4.0.2-0.aarch64-linux-musl.tar.gz/md5 b/deps/checksums/MPFR.v4.0.2-0.aarch64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..6fbd6c4 --- /dev/null +++ b/deps/checksums/MPFR.v4.0.2-0.aarch64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +108305a6eb6fb76df7ce2ff4f410b786 diff --git a/deps/checksums/MPFR.v4.0.2-0.aarch64-linux-musl.tar.gz/sha512 b/deps/checksums/MPFR.v4.0.2-0.aarch64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..aa922a9 --- /dev/null +++ b/deps/checksums/MPFR.v4.0.2-0.aarch64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +26a82a851ff11e81f33741ae05bb53cfa8bd3af32f466efcf4048e8d6577daa5e8ede67cf19efabfba152b3e93db7eacb5434c6864a0d1ec2c5030d10c5d568e diff --git a/deps/checksums/MPFR.v4.0.2-0.arm-linux-gnueabihf.tar.gz/md5 b/deps/checksums/MPFR.v4.0.2-0.arm-linux-gnueabihf.tar.gz/md5 new file mode 100644 index 0000000..0e58c94 --- /dev/null +++ b/deps/checksums/MPFR.v4.0.2-0.arm-linux-gnueabihf.tar.gz/md5 @@ -0,0 +1 @@ +2fdc41279fc93d071d239a7f6a6cf28c diff --git a/deps/checksums/MPFR.v4.0.2-0.arm-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/MPFR.v4.0.2-0.arm-linux-gnueabihf.tar.gz/sha512 new file mode 100644 index 0000000..f4627e9 --- /dev/null +++ b/deps/checksums/MPFR.v4.0.2-0.arm-linux-gnueabihf.tar.gz/sha512 @@ -0,0 +1 @@ +654566a5a7a540c29fbf4b925f1bc444c6471a3b5b13bdd1a1f927e01dc9f9cd4a8f4d16b35aed6c06b790cdc4fc9703ec63995492bab138e5b4fd2562f95158 diff --git a/deps/checksums/MPFR.v4.0.2-0.arm-linux-musleabihf.tar.gz/md5 b/deps/checksums/MPFR.v4.0.2-0.arm-linux-musleabihf.tar.gz/md5 new file mode 100644 index 0000000..ff1123c --- /dev/null +++ b/deps/checksums/MPFR.v4.0.2-0.arm-linux-musleabihf.tar.gz/md5 @@ -0,0 +1 @@ +87606da70ab6faf6a6d3d918cf58581b diff --git a/deps/checksums/MPFR.v4.0.2-0.arm-linux-musleabihf.tar.gz/sha512 b/deps/checksums/MPFR.v4.0.2-0.arm-linux-musleabihf.tar.gz/sha512 new file mode 100644 index 0000000..61f9a88 --- /dev/null +++ b/deps/checksums/MPFR.v4.0.2-0.arm-linux-musleabihf.tar.gz/sha512 @@ -0,0 +1 @@ +316233ff37ac60a63fd9e3604b1412e2365f5a3a69c0af5557978b937816bb81c5d06e6cba188322e62983d62daee1e56dd1f28b05d6076333fbb5b8bbc56c25 diff --git a/deps/checksums/MPFR.v4.0.2-0.i686-linux-gnu.tar.gz/md5 b/deps/checksums/MPFR.v4.0.2-0.i686-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..b278035 --- /dev/null +++ b/deps/checksums/MPFR.v4.0.2-0.i686-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +86f289a10058ef62cea59ffa063ce989 diff --git a/deps/checksums/MPFR.v4.0.2-0.i686-linux-gnu.tar.gz/sha512 b/deps/checksums/MPFR.v4.0.2-0.i686-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..65c834f --- /dev/null +++ b/deps/checksums/MPFR.v4.0.2-0.i686-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +ff66362191a8307972d5f47c35eafc788067fe2a38be60d38d4a1ee9ae2004dcbd7c3e062db8ebd3914804c97ce46776eaa9723fc9b533813f3476f6e701cbd8 diff --git a/deps/checksums/MPFR.v4.0.2-0.i686-linux-musl.tar.gz/md5 b/deps/checksums/MPFR.v4.0.2-0.i686-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..6e4a6d2 --- /dev/null +++ b/deps/checksums/MPFR.v4.0.2-0.i686-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +3e7a14eb121371420838253b519c2995 diff --git a/deps/checksums/MPFR.v4.0.2-0.i686-linux-musl.tar.gz/sha512 b/deps/checksums/MPFR.v4.0.2-0.i686-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..9f66640 --- /dev/null +++ b/deps/checksums/MPFR.v4.0.2-0.i686-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +76b57dd91f811dc22cab9d530b62150196992af4dd0202544888fd5497cad8587c98ee2868a0af8603d436b5b90cf41879d3c1209c2ace2f86c44018efd1c5fc diff --git a/deps/checksums/MPFR.v4.0.2-0.i686-w64-mingw32.tar.gz/md5 b/deps/checksums/MPFR.v4.0.2-0.i686-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..adfa22e --- /dev/null +++ b/deps/checksums/MPFR.v4.0.2-0.i686-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +bbe44a832c11166ac346abb3e1c6519a diff --git a/deps/checksums/MPFR.v4.0.2-0.i686-w64-mingw32.tar.gz/sha512 b/deps/checksums/MPFR.v4.0.2-0.i686-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..37df8c9 --- /dev/null +++ b/deps/checksums/MPFR.v4.0.2-0.i686-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +d373b6877e398502beb729ccf8a4add6c5307f7b7bc221621ea801f0edd4e558c2e534935354e403a5633ffb24e668c6e10b645beba6fee43fe87d550eb099f8 diff --git a/deps/checksums/MPFR.v4.0.2-0.powerpc64le-linux-gnu.tar.gz/md5 b/deps/checksums/MPFR.v4.0.2-0.powerpc64le-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..41023a5 --- /dev/null +++ b/deps/checksums/MPFR.v4.0.2-0.powerpc64le-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +28739b75f4d7db50fe15712c3b6426e2 diff --git a/deps/checksums/MPFR.v4.0.2-0.powerpc64le-linux-gnu.tar.gz/sha512 b/deps/checksums/MPFR.v4.0.2-0.powerpc64le-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..d215fcb --- /dev/null +++ b/deps/checksums/MPFR.v4.0.2-0.powerpc64le-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +6a8e6eee1f5103e741572571757ce76c9ad6dfa6ade899e8d3cb1de7b252c8113edd36f3ea08031f502ea52662447b49c5385d07dfd864fce466352367dd06bb diff --git a/deps/checksums/MPFR.v4.0.2-0.x86_64-apple-darwin14.tar.gz/md5 b/deps/checksums/MPFR.v4.0.2-0.x86_64-apple-darwin14.tar.gz/md5 new file mode 100644 index 0000000..4ecec2d --- /dev/null +++ b/deps/checksums/MPFR.v4.0.2-0.x86_64-apple-darwin14.tar.gz/md5 @@ -0,0 +1 @@ +94391984c1fffc5a6bf96817dc810aa5 diff --git a/deps/checksums/MPFR.v4.0.2-0.x86_64-apple-darwin14.tar.gz/sha512 b/deps/checksums/MPFR.v4.0.2-0.x86_64-apple-darwin14.tar.gz/sha512 new file mode 100644 index 0000000..d7b579f --- /dev/null +++ b/deps/checksums/MPFR.v4.0.2-0.x86_64-apple-darwin14.tar.gz/sha512 @@ -0,0 +1 @@ +ae272e7e79b92bde359a4ec1abbade2c68f5f13a56f4acc5a15908b59537c5fe2e828bc111845653bffd6c91eef4678bd6a07c60f07500f9e07b233d6c59c6e9 diff --git a/deps/checksums/MPFR.v4.0.2-0.x86_64-linux-gnu.tar.gz/md5 b/deps/checksums/MPFR.v4.0.2-0.x86_64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..6f9ecb6 --- /dev/null +++ b/deps/checksums/MPFR.v4.0.2-0.x86_64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +d95283e761522992ef10fe667bc22d71 diff --git a/deps/checksums/MPFR.v4.0.2-0.x86_64-linux-gnu.tar.gz/sha512 b/deps/checksums/MPFR.v4.0.2-0.x86_64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..794dbc7 --- /dev/null +++ b/deps/checksums/MPFR.v4.0.2-0.x86_64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +f8a58ef2d3475faea97dbffcf4280cc9bb8f61255bf6a0573ff0ffeddbe3b93d764922e2a0d75e20a796cd9821affcd1a4e1672448ef3d0763117db497847c26 diff --git a/deps/checksums/MPFR.v4.0.2-0.x86_64-linux-musl.tar.gz/md5 b/deps/checksums/MPFR.v4.0.2-0.x86_64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..e292815 --- /dev/null +++ b/deps/checksums/MPFR.v4.0.2-0.x86_64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +d2b4ebe667e5b94343579b29d8339775 diff --git a/deps/checksums/MPFR.v4.0.2-0.x86_64-linux-musl.tar.gz/sha512 b/deps/checksums/MPFR.v4.0.2-0.x86_64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..5e9fcee --- /dev/null +++ b/deps/checksums/MPFR.v4.0.2-0.x86_64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +466dc9336ca5edff5f689fe52573b68b1d59caa4421b3911f2a2bff4e7bd2e021f57f6c316b9549822592ac7c8a2d9c2ea21090272bc9546ba15dca973f94088 diff --git a/deps/checksums/MPFR.v4.0.2-0.x86_64-unknown-freebsd11.1.tar.gz/md5 b/deps/checksums/MPFR.v4.0.2-0.x86_64-unknown-freebsd11.1.tar.gz/md5 new file mode 100644 index 0000000..ca76322 --- /dev/null +++ b/deps/checksums/MPFR.v4.0.2-0.x86_64-unknown-freebsd11.1.tar.gz/md5 @@ -0,0 +1 @@ +e33148a1c822b30eb12a807a50630e8b diff --git a/deps/checksums/MPFR.v4.0.2-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 b/deps/checksums/MPFR.v4.0.2-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 new file mode 100644 index 0000000..6839305 --- /dev/null +++ b/deps/checksums/MPFR.v4.0.2-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 @@ -0,0 +1 @@ +cd7a0138818f91f1fa92c48c3f669c187041dc44a5e5612041766ea1ea2b6d706a45d86b36efdd1074a7c0f38b416ef7dd2052c2b13dfbec07316fbd5dd5a80a diff --git a/deps/checksums/MPFR.v4.0.2-0.x86_64-w64-mingw32.tar.gz/md5 b/deps/checksums/MPFR.v4.0.2-0.x86_64-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..5840220 --- /dev/null +++ b/deps/checksums/MPFR.v4.0.2-0.x86_64-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +c0aa9b2115d5abbfc3e9d588d8d88c77 diff --git a/deps/checksums/MPFR.v4.0.2-0.x86_64-w64-mingw32.tar.gz/sha512 b/deps/checksums/MPFR.v4.0.2-0.x86_64-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..ad57a7f --- /dev/null +++ b/deps/checksums/MPFR.v4.0.2-0.x86_64-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +fb7f4df983a29d278cee60770afba369e3a2dccac17634409a670237869d0048c998ab8a3315cde7c351dfcbaaacf0a724456d47f8920ab31e90b1b6e2c397e6 diff --git a/deps/checksums/MbedTLS.v2.16.0-v0.17.0.aarch64-linux-gnu.tar.gz/md5 b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.aarch64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..6f74a62 --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.aarch64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +0f65b4f1db6eb913fc442bfb9a1cacf1 diff --git a/deps/checksums/MbedTLS.v2.16.0-v0.17.0.aarch64-linux-gnu.tar.gz/sha512 b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.aarch64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..09d401f --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.aarch64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +8a370af19cfc19c0d04495291eb0c18793ab029d5eec8c631b0726eadbf2db93fe6c62f40f27f8111fb919f9e5b49e80958800f0ad2cd77fcd061ac94ecfbaa9 diff --git a/deps/checksums/MbedTLS.v2.16.0-v0.17.0.aarch64-linux-musl.tar.gz/md5 b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.aarch64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..158b1de --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.aarch64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +cde903351d0f20805aa7382e3a187f55 diff --git a/deps/checksums/MbedTLS.v2.16.0-v0.17.0.aarch64-linux-musl.tar.gz/sha512 b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.aarch64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..5630995 --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.aarch64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +6db4cd910716fc7c37bbd80e912c9890603f4bd7b63452338eee945e7bf4cec2fe1d989edc064a4562ea1e81062673ab363ab087ba29fcb175800fcaf6f2678d diff --git a/deps/checksums/MbedTLS.v2.16.0-v0.17.0.arm-linux-gnueabihf.tar.gz/md5 b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.arm-linux-gnueabihf.tar.gz/md5 new file mode 100644 index 0000000..ef244b8 --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.arm-linux-gnueabihf.tar.gz/md5 @@ -0,0 +1 @@ +29c08a9e3a32ffbe6a4c4d0929fce3a2 diff --git a/deps/checksums/MbedTLS.v2.16.0-v0.17.0.arm-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.arm-linux-gnueabihf.tar.gz/sha512 new file mode 100644 index 0000000..2095c96 --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.arm-linux-gnueabihf.tar.gz/sha512 @@ -0,0 +1 @@ +24310aa24f55b91c80726344d8844f024a09882b298f806a8af6601e1405e40b78b8ab18893912e01c12537554709a9e781d294d3f42dd501ba7cb01c9f10996 diff --git a/deps/checksums/MbedTLS.v2.16.0-v0.17.0.arm-linux-musleabihf.tar.gz/md5 b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.arm-linux-musleabihf.tar.gz/md5 new file mode 100644 index 0000000..0b42faa --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.arm-linux-musleabihf.tar.gz/md5 @@ -0,0 +1 @@ +078fc8029579eb40c68e7744f0bd6af6 diff --git a/deps/checksums/MbedTLS.v2.16.0-v0.17.0.arm-linux-musleabihf.tar.gz/sha512 b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.arm-linux-musleabihf.tar.gz/sha512 new file mode 100644 index 0000000..03b7d8a --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.arm-linux-musleabihf.tar.gz/sha512 @@ -0,0 +1 @@ +17f3ecfc46799663679562e9dd7021b315db06562687836f376d6168f5778d9ce82d85d4f1a992b3872777931d61585070759b68b213c5f5d6f93a849c772b42 diff --git a/deps/checksums/MbedTLS.v2.16.0-v0.17.0.i686-linux-gnu.tar.gz/md5 b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.i686-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..72cc7b2 --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.i686-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +01c763bd84533879267d18752fc59bb1 diff --git a/deps/checksums/MbedTLS.v2.16.0-v0.17.0.i686-linux-gnu.tar.gz/sha512 b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.i686-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..486eaf7 --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.i686-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +365610732ca83b28ea521004b817688edf145eb3808591d228d2c6926106d5630232dec9be6dff6428d86400d9bf3ace15b65e8269cacfb60556db2ebc9d3c64 diff --git a/deps/checksums/MbedTLS.v2.16.0-v0.17.0.i686-linux-musl.tar.gz/md5 b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.i686-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..b25d8c8 --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.i686-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +a3ebddf1cedc9b5b82266f79f7b35e4a diff --git a/deps/checksums/MbedTLS.v2.16.0-v0.17.0.i686-linux-musl.tar.gz/sha512 b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.i686-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..4e0f524 --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.i686-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +69abbe0e0aa6f276d20e30b2d40824c5c60a41d628eb3f4fc83083eefa775e059ab33176c5177db8dbe911dc90f5baa7aedd1b9ecc075d2781bb906bc237e10f diff --git a/deps/checksums/MbedTLS.v2.16.0-v0.17.0.i686-w64-mingw32.tar.gz/md5 b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.i686-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..c2a374b --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.i686-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +78de097a872a587afcd593e087119bf3 diff --git a/deps/checksums/MbedTLS.v2.16.0-v0.17.0.i686-w64-mingw32.tar.gz/sha512 b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.i686-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..53011b1 --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.i686-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +596f10ae384e1d66e1c3410997857dde5908af83fe6e8f6aa047aa3dd703eab703e9c67c0759d72ba736a45f5813edb933417a5ce48bcdb7d694c08207239291 diff --git a/deps/checksums/MbedTLS.v2.16.0-v0.17.0.powerpc64le-linux-gnu.tar.gz/md5 b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.powerpc64le-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..2c2e028 --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.powerpc64le-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +8df9b36147cf90789d621dd080c5ec9c diff --git a/deps/checksums/MbedTLS.v2.16.0-v0.17.0.powerpc64le-linux-gnu.tar.gz/sha512 b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.powerpc64le-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..7a34668 --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.powerpc64le-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +8bc7dffece424e346664c5edf31531787a7e0ed52509e8211b499df0b396170d3949d813a76f4bfb3ab7d0c60a1914486f915159b1058876994aa3638d0625ee diff --git a/deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-apple-darwin14.tar.gz/md5 b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-apple-darwin14.tar.gz/md5 new file mode 100644 index 0000000..c17a55e --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-apple-darwin14.tar.gz/md5 @@ -0,0 +1 @@ +40ce3e865f293c5772bc67c132777038 diff --git a/deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-apple-darwin14.tar.gz/sha512 b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-apple-darwin14.tar.gz/sha512 new file mode 100644 index 0000000..a45a510 --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-apple-darwin14.tar.gz/sha512 @@ -0,0 +1 @@ +fd34b1330cdae41dd4f492c6feff9a663fc6d373c065d664420e6bb982d653e8877185f90533d651ed1cdbed9722eacc095925833dab36c1e9a87857f9ef4d5a diff --git a/deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-linux-gnu.tar.gz/md5 b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..c5e57dd --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +50ac733ba0dd518cefd3e9b5ec03cdb7 diff --git a/deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-linux-gnu.tar.gz/sha512 b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..040d68b --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +c0a8796673f8a9cd8be9c697228ba2c57991255d277da719209dbf0a830e4d0a09631d6e25618908cc265e3c9c1952743f30e4fb9dbec5023f0fc79215dbbec5 diff --git a/deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-linux-musl.tar.gz/md5 b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..1454830 --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +3cea65e78200e1c5685f1303a97ee98d diff --git a/deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-linux-musl.tar.gz/sha512 b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..9d6ec3e --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +dd08fb48f217b272a1887cbe2afc1c33e218e2d4683ff47a14909829e6781f61eb6667f12b8fb1a6d6d95a7555796bbd156831f1f61f946ed29c58962f3e1989 diff --git a/deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-unknown-freebsd11.1.tar.gz/md5 b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-unknown-freebsd11.1.tar.gz/md5 new file mode 100644 index 0000000..0ccfd52 --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-unknown-freebsd11.1.tar.gz/md5 @@ -0,0 +1 @@ +7dcb4b2f84470fce6a0eb530fb858cc1 diff --git a/deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-unknown-freebsd11.1.tar.gz/sha512 b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-unknown-freebsd11.1.tar.gz/sha512 new file mode 100644 index 0000000..40ce54a --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-unknown-freebsd11.1.tar.gz/sha512 @@ -0,0 +1 @@ +3b84efc17aa2f9030d40a5cd9f605d9e6388557b57279752e2df086128c311be64412b826bd9ef4562398641d773f3293e349bbef5f94cda83493b30df105552 diff --git a/deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-w64-mingw32.tar.gz/md5 b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..8159fab --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +0505e986a2ef7acdd5aa46db0db9087c diff --git a/deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-w64-mingw32.tar.gz/sha512 b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..e12671e --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-v0.17.0.x86_64-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +b8bf73f025d8b2f49498ff4d02fc3c35e37a45af487e49e41da7167c74cd2e70e68fccd32e2e3c2fccb5f0fd72a95312e9fe4abb26271d9fb59f61f8b30abe40 diff --git a/deps/checksums/Objconv.v2.49.0-0.aarch64-linux-gnu.tar.gz/md5 b/deps/checksums/Objconv.v2.49.0-0.aarch64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..7087f1a --- /dev/null +++ b/deps/checksums/Objconv.v2.49.0-0.aarch64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +96565650d0743983f9a17d3e6a932be6 diff --git a/deps/checksums/Objconv.v2.49.0-0.aarch64-linux-gnu.tar.gz/sha512 b/deps/checksums/Objconv.v2.49.0-0.aarch64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..d19d147 --- /dev/null +++ b/deps/checksums/Objconv.v2.49.0-0.aarch64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +6d1295e2c48b9e59f11ef935e7ccbc69ea234cbcfde1aaea9cbbb08ee6d5dcb6c3426ed8ac66ff95f8d2c8cec19767c67b1d9ba7e4698eacbe8d8b5d33cbf178 diff --git a/deps/checksums/Objconv.v2.49.0-0.aarch64-linux-musl.tar.gz/md5 b/deps/checksums/Objconv.v2.49.0-0.aarch64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..acc1937 --- /dev/null +++ b/deps/checksums/Objconv.v2.49.0-0.aarch64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +364900012019f8fd8e33834b43f5de58 diff --git a/deps/checksums/Objconv.v2.49.0-0.aarch64-linux-musl.tar.gz/sha512 b/deps/checksums/Objconv.v2.49.0-0.aarch64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..d4d7c66 --- /dev/null +++ b/deps/checksums/Objconv.v2.49.0-0.aarch64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +a5ac81da5e3d1a0d54a951b444ae5a2a49c00eee3482be1fb835efe1a068ca825bc8d9de6540e5909f04bb5b9c52459487afb6fc549f72aacacd683f82497354 diff --git a/deps/checksums/Objconv.v2.49.0-0.arm-linux-gnueabihf.tar.gz/md5 b/deps/checksums/Objconv.v2.49.0-0.arm-linux-gnueabihf.tar.gz/md5 new file mode 100644 index 0000000..c98ef35 --- /dev/null +++ b/deps/checksums/Objconv.v2.49.0-0.arm-linux-gnueabihf.tar.gz/md5 @@ -0,0 +1 @@ +5f87140ee2b7d2b5c4d7d5e666edb793 diff --git a/deps/checksums/Objconv.v2.49.0-0.arm-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/Objconv.v2.49.0-0.arm-linux-gnueabihf.tar.gz/sha512 new file mode 100644 index 0000000..bc4bdd9 --- /dev/null +++ b/deps/checksums/Objconv.v2.49.0-0.arm-linux-gnueabihf.tar.gz/sha512 @@ -0,0 +1 @@ +cb2b3665d3edcd3b3386101d0ff22002383b25c3e9b58b03eb5387888b5cae2782f984e6a39542d395f2c100c8fd81c89a0aed79e5d7581647bf024eefa5f6c3 diff --git a/deps/checksums/Objconv.v2.49.0-0.arm-linux-musleabihf.tar.gz/md5 b/deps/checksums/Objconv.v2.49.0-0.arm-linux-musleabihf.tar.gz/md5 new file mode 100644 index 0000000..5286ffd --- /dev/null +++ b/deps/checksums/Objconv.v2.49.0-0.arm-linux-musleabihf.tar.gz/md5 @@ -0,0 +1 @@ +8dc7f75d66ece64f490d803751ef2315 diff --git a/deps/checksums/Objconv.v2.49.0-0.arm-linux-musleabihf.tar.gz/sha512 b/deps/checksums/Objconv.v2.49.0-0.arm-linux-musleabihf.tar.gz/sha512 new file mode 100644 index 0000000..46aa7a6 --- /dev/null +++ b/deps/checksums/Objconv.v2.49.0-0.arm-linux-musleabihf.tar.gz/sha512 @@ -0,0 +1 @@ +45f194f2937329fc424fe5abef485e542362b38b17645cdc528f3ce7571445bf4b1f29733d8eef3bdd7b417ea11c8bf45879cc96c4865eaef7fe0baf51d862cd diff --git a/deps/checksums/Objconv.v2.49.0-0.i686-linux-gnu.tar.gz/md5 b/deps/checksums/Objconv.v2.49.0-0.i686-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..e2b3b2c --- /dev/null +++ b/deps/checksums/Objconv.v2.49.0-0.i686-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +85149305b9dd209c1e12f368e9c023aa diff --git a/deps/checksums/Objconv.v2.49.0-0.i686-linux-gnu.tar.gz/sha512 b/deps/checksums/Objconv.v2.49.0-0.i686-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..079df7f --- /dev/null +++ b/deps/checksums/Objconv.v2.49.0-0.i686-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +b909504ac1727ddc4311713320b553e483451123c0706d33a5dea3b2c0454040651ba23654585087ef20f7aa303120362008115e9b52d9a234889624373cb800 diff --git a/deps/checksums/Objconv.v2.49.0-0.i686-linux-musl.tar.gz/md5 b/deps/checksums/Objconv.v2.49.0-0.i686-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..58812bc --- /dev/null +++ b/deps/checksums/Objconv.v2.49.0-0.i686-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +ec5e26c8a2879e991c384b9a36758e60 diff --git a/deps/checksums/Objconv.v2.49.0-0.i686-linux-musl.tar.gz/sha512 b/deps/checksums/Objconv.v2.49.0-0.i686-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..104c1f6 --- /dev/null +++ b/deps/checksums/Objconv.v2.49.0-0.i686-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +43440866002686b9eca5998da7d81e3875da366934b72217fdc26915722b4d446f06a02e7e8b8f5a88ce26faac650be67f1f88b90b520e845a61ca640dd6b1d0 diff --git a/deps/checksums/Objconv.v2.49.0-0.i686-w64-mingw32.tar.gz/md5 b/deps/checksums/Objconv.v2.49.0-0.i686-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..6f8e529 --- /dev/null +++ b/deps/checksums/Objconv.v2.49.0-0.i686-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +84055d1c86579824d4a634adae1cdfd2 diff --git a/deps/checksums/Objconv.v2.49.0-0.i686-w64-mingw32.tar.gz/sha512 b/deps/checksums/Objconv.v2.49.0-0.i686-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..959c460 --- /dev/null +++ b/deps/checksums/Objconv.v2.49.0-0.i686-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +fc980fa01e50f42f01e42fb5fbdb96a89500f757991c07a888f9cb22dee97f1a19e33f7bed43d9372a3ba500d95d0420e59783446ee28230f3f30ac878fa4e36 diff --git a/deps/checksums/Objconv.v2.49.0-0.powerpc64le-linux-gnu.tar.gz/md5 b/deps/checksums/Objconv.v2.49.0-0.powerpc64le-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..5a67171 --- /dev/null +++ b/deps/checksums/Objconv.v2.49.0-0.powerpc64le-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +69b3d436965c139bfcb0e0389d25d73f diff --git a/deps/checksums/Objconv.v2.49.0-0.powerpc64le-linux-gnu.tar.gz/sha512 b/deps/checksums/Objconv.v2.49.0-0.powerpc64le-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..ff113ae --- /dev/null +++ b/deps/checksums/Objconv.v2.49.0-0.powerpc64le-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +f5f849ed48ce4a4a12b59d0ef0bd749d07e323ddacec55039241202b56a3035e546073e5596aa4598f06cd78a8eb9a8d97af3ada448ffab86e6a1d8700956458 diff --git a/deps/checksums/Objconv.v2.49.0-0.x86_64-apple-darwin14.tar.gz/md5 b/deps/checksums/Objconv.v2.49.0-0.x86_64-apple-darwin14.tar.gz/md5 new file mode 100644 index 0000000..e25f214 --- /dev/null +++ b/deps/checksums/Objconv.v2.49.0-0.x86_64-apple-darwin14.tar.gz/md5 @@ -0,0 +1 @@ +3c17020656896f95e3f402cb29429112 diff --git a/deps/checksums/Objconv.v2.49.0-0.x86_64-apple-darwin14.tar.gz/sha512 b/deps/checksums/Objconv.v2.49.0-0.x86_64-apple-darwin14.tar.gz/sha512 new file mode 100644 index 0000000..05add1a --- /dev/null +++ b/deps/checksums/Objconv.v2.49.0-0.x86_64-apple-darwin14.tar.gz/sha512 @@ -0,0 +1 @@ +2cd495304eb4c8ca3f49be8e39b62d835fafd4d8f2bb9ab55fe90b77a1c34cbee240e6bfb1bb47a04eef77029731532eae27e9a1bf530ba2117fad01c6c9cbb7 diff --git a/deps/checksums/Objconv.v2.49.0-0.x86_64-linux-gnu.tar.gz/md5 b/deps/checksums/Objconv.v2.49.0-0.x86_64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..04dbde1 --- /dev/null +++ b/deps/checksums/Objconv.v2.49.0-0.x86_64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +907e188a3ae14dd9f25b298dffc6d3d3 diff --git a/deps/checksums/Objconv.v2.49.0-0.x86_64-linux-gnu.tar.gz/sha512 b/deps/checksums/Objconv.v2.49.0-0.x86_64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..ec491e8 --- /dev/null +++ b/deps/checksums/Objconv.v2.49.0-0.x86_64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +01ef0aeb8dcddaefe2432d1c9d618f647a87bb4bf06ec64207630ad531982c47b7470b6b46b76c3358bf9eb2c28a30509fb2a1197643f936c5034247f7c3cac0 diff --git a/deps/checksums/Objconv.v2.49.0-0.x86_64-linux-musl.tar.gz/md5 b/deps/checksums/Objconv.v2.49.0-0.x86_64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..44ebf4b --- /dev/null +++ b/deps/checksums/Objconv.v2.49.0-0.x86_64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +ff5ce7c16a46a718133578818032c6b8 diff --git a/deps/checksums/Objconv.v2.49.0-0.x86_64-linux-musl.tar.gz/sha512 b/deps/checksums/Objconv.v2.49.0-0.x86_64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..0b004cc --- /dev/null +++ b/deps/checksums/Objconv.v2.49.0-0.x86_64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +b5e2a9fb1ebc3ad2442ca4b636a126924bd9d567261cc8d1d816969454d11dcaeb4f9f1848183c309a63c8a6ad7da8d5bdc111765d8cc1f4c6aabea9eb0fd30a diff --git a/deps/checksums/Objconv.v2.49.0-0.x86_64-unknown-freebsd11.1.tar.gz/md5 b/deps/checksums/Objconv.v2.49.0-0.x86_64-unknown-freebsd11.1.tar.gz/md5 new file mode 100644 index 0000000..6ade781 --- /dev/null +++ b/deps/checksums/Objconv.v2.49.0-0.x86_64-unknown-freebsd11.1.tar.gz/md5 @@ -0,0 +1 @@ +6c86252ee6d60fc171030fd602e16ac7 diff --git a/deps/checksums/Objconv.v2.49.0-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 b/deps/checksums/Objconv.v2.49.0-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 new file mode 100644 index 0000000..0bca4c3 --- /dev/null +++ b/deps/checksums/Objconv.v2.49.0-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 @@ -0,0 +1 @@ +a178fae5a07561092b2f7d392894e52678314f923579cfa23b45ac6501efbd11cccd23a3dc9493ae935dec7b4dcce44b34ddebe0f472575101d03c37e4ee3ce3 diff --git a/deps/checksums/Objconv.v2.49.0-0.x86_64-w64-mingw32.tar.gz/md5 b/deps/checksums/Objconv.v2.49.0-0.x86_64-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..266627f --- /dev/null +++ b/deps/checksums/Objconv.v2.49.0-0.x86_64-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +b7e049750328ea767d2da84477327cf5 diff --git a/deps/checksums/Objconv.v2.49.0-0.x86_64-w64-mingw32.tar.gz/sha512 b/deps/checksums/Objconv.v2.49.0-0.x86_64-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..4595424 --- /dev/null +++ b/deps/checksums/Objconv.v2.49.0-0.x86_64-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +caa18e7101840462ec160e0735481f63dae4294508a1e4010610771310d0194eb284a10ab0937f3c6da41f99055886408910d46892e1e8f4db4090cf88872e9c diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-gnu-gcc4.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-gnu-gcc4.tar.gz/md5 new file mode 100644 index 0000000..0ff730e --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-gnu-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +14d62da838b053ab78c3c84a8df7c429 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-gnu-gcc4.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-gnu-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..b6d2b65 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-gnu-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +0141f903dbc62ccdcf01d8722ce7b03c74d94c661be796c1a242b0f385f891c15daf015331029b8ed692867d2e7f8303fa3d5c443ee65a9cfb9b78e3663617ab diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-gnu-gcc7.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-gnu-gcc7.tar.gz/md5 new file mode 100644 index 0000000..13d9101 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-gnu-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +a16f6e2bffd56d76735d64a36852f650 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-gnu-gcc7.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-gnu-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..a7e5183 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-gnu-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +cc164665889562b2e86c4387f85175bd20bd665fc0ce2842a28107eea7f67d49f58067c2face4d768fffec7eb6ca17450c9f07b927b897bed4114e43bde0e83a diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-gnu-gcc8.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-gnu-gcc8.tar.gz/md5 new file mode 100644 index 0000000..f50a76b --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-gnu-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +8d492b89b0b1c09e620647976df19512 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-gnu-gcc8.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-gnu-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..1c90803 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-gnu-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +ff3de25afefc10577c7860cd3fb9496585b763aad683ee39ccbbb596223910ace4b8411fc1f20a3b5708098cafcfe31b873009cf12777cd9b9c4b8d462122fd6 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-musl-gcc4.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-musl-gcc4.tar.gz/md5 new file mode 100644 index 0000000..6d5f519 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-musl-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +9d7a69fe6ed99592087f3b81c6b1c8a1 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-musl-gcc4.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-musl-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..90cf64b --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-musl-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +7c2029a0e9f9efc59813f99fd220a7c8796989db47758857da31b8c794d92de167f35992d399301d79641f79f3b2831afe83af1e85ad99fe4153248dbe5badaf diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-musl-gcc7.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-musl-gcc7.tar.gz/md5 new file mode 100644 index 0000000..e679b3a --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-musl-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +93a1250c75e7cec00a52c091a6fa89cc diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-musl-gcc7.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-musl-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..f5a13bf --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-musl-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +1949277d8a762c2f0d8bd37fcc10d97420b82d5a96da2987323ec7d6954b36b7eae52732499e7a054adae1b395158e46eda41daf1e0008c23a02fe4f8059ee3c diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-musl-gcc8.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-musl-gcc8.tar.gz/md5 new file mode 100644 index 0000000..e73b73e --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-musl-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +f8e98218f3a832972531924b770cf8fe diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-musl-gcc8.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-musl-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..28b66a9 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.aarch64-linux-musl-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +7a4064f5e79dd11cc14d85dc1e37fd923cebaebc92483a1c90e7a73b072d789a87fb3b0c46bfd0668307e4bb32c2c4af3f190eac2da934f37d61d81b980a878b diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-gnueabihf-gcc4.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-gnueabihf-gcc4.tar.gz/md5 new file mode 100644 index 0000000..425aa69 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-gnueabihf-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +636d2d2c14abc9e3b14d1dc546ad8935 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-gnueabihf-gcc4.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-gnueabihf-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..3305642 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-gnueabihf-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +3394f5ee2f35c168e7f6be9c0653dd157e7da49040b9ec94d6f4d6d1ad2736cdc6f10f6626f08548238c3a11b69ffefa159c5495d1fe58855c0c41c2383641fd diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-gnueabihf-gcc7.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-gnueabihf-gcc7.tar.gz/md5 new file mode 100644 index 0000000..359a29c --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-gnueabihf-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +a336efef9cf11945477b2bb805282810 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-gnueabihf-gcc7.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-gnueabihf-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..35b3788 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-gnueabihf-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +8c0e558aa047f60a5e695229b0f7585363448dd2b61c0a193bdf00d27fffecd3a9c37a0e423821af7beb909c874074fe7a1d4d4cec623bd964205ee20d2e3b23 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-gnueabihf-gcc8.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-gnueabihf-gcc8.tar.gz/md5 new file mode 100644 index 0000000..306b7bd --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-gnueabihf-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +28dd6d175a5a6fe86a4d01f9031b76cf diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-gnueabihf-gcc8.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-gnueabihf-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..0944fab --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-gnueabihf-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +986cabd473a353731479036db91f4a462fe6b389ddee35aacb3f66a15df8e1d38956c385d3ef02e399bde70458a3fc6b769466db652f4495abd7d7d2ff0b26b1 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-musleabihf-gcc4.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-musleabihf-gcc4.tar.gz/md5 new file mode 100644 index 0000000..d7a9892 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-musleabihf-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +1487ee7fb0ff0e2824be40dfa79badff diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-musleabihf-gcc4.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-musleabihf-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..856c33f --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-musleabihf-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +ecd8abcb46bd26e72e6ccda2ef463521629dcf6132dd065079b8da71e7291c0561c801bf7c94a467b6aa2839539ee990ccf4b4ea5160f492fb704b2ffcfe0283 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-musleabihf-gcc7.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-musleabihf-gcc7.tar.gz/md5 new file mode 100644 index 0000000..8b940c0 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-musleabihf-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +c478e1ae81917a27fffbdef01829d79e diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-musleabihf-gcc7.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-musleabihf-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..92d6a17 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-musleabihf-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +22aef28d214c41556fc45ffc3903073bd7f5b44ec6714f4c9d8f50757200c89c3da3e61d96ab587dc353ec4e88b67b8c553c938ecb32261347b1e3c5e8901c0e diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-musleabihf-gcc8.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-musleabihf-gcc8.tar.gz/md5 new file mode 100644 index 0000000..33248b0 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-musleabihf-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +fb6116f48854a9e183d6c6773e889756 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-musleabihf-gcc8.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-musleabihf-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..debea13 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.arm-linux-musleabihf-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +33edfa3fe05f2e6a246eda391813e8db30ed744789032e499cbfb4e1f9f73c67ce6e439dc7ca4795d8b38b9dab6420979f51ef818511e8601398144ef41f7b2e diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-gnu-gcc4.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-gnu-gcc4.tar.gz/md5 new file mode 100644 index 0000000..8815007 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-gnu-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +15c38ae63c6c70bbcf7e52bff16ec915 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-gnu-gcc4.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-gnu-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..86c389b --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-gnu-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +5f2464ad3b56cec0ca343f301460819141dec3217cf15b171da657768571eae77313ce156d56fd5cc76c6d95758597015dc7d04e209c0ffb0dcbeedb99d788e0 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-gnu-gcc7.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-gnu-gcc7.tar.gz/md5 new file mode 100644 index 0000000..8f3b533 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-gnu-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +ad1c639472af0e6245f8faa5cbf65f57 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-gnu-gcc7.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-gnu-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..32f1d34 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-gnu-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +fe85b545b4e0787fc1b6c1f2948a4db09eafe365c1bf52a5979430f014d23a8713bb7c1ff29264fa02e16736b9407c7aaf56416354dd3c1e2e2ae9b32a00f5d2 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-gnu-gcc8.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-gnu-gcc8.tar.gz/md5 new file mode 100644 index 0000000..2811baa --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-gnu-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +e25cb820b2eb0d593fb0c875ae2086a1 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-gnu-gcc8.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-gnu-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..813e18b --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-gnu-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +6b5d4f21609f9382d01b4e5c4b7becbf61b50aa3c20f0db6a78736506ad83b0ec7225790f1127d1601bb96ea3318d3704b18e71fd6c1559006147525cb2b3389 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-musl-gcc4.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-musl-gcc4.tar.gz/md5 new file mode 100644 index 0000000..a25e0ac --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-musl-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +f2c96e84acbded44c263270367b6cf9f diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-musl-gcc4.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-musl-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..ac7fd7a --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-musl-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +3d0d2fac80ec4760c96740e4607086b192d3309ae5bd5b43288655137b5a2a4c78448b283cd50c935f2ad742348bb4770213446c31216972ed1ecf520e34c629 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-musl-gcc7.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-musl-gcc7.tar.gz/md5 new file mode 100644 index 0000000..517b093 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-musl-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +520372fb05206ac794ff2ac5d23234b4 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-musl-gcc7.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-musl-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..0d6b5fc --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-musl-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +92ffc6f2d0fbf6c79dba8f47f44ca7ca11a12e5e6ad43e88d166ba66fcc2b82f62f914ef97ae0831267309ee10c450e191ae0dae51eaf969315e4d3539a8ab57 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-musl-gcc8.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-musl-gcc8.tar.gz/md5 new file mode 100644 index 0000000..dc6f978 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-musl-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +c94c117ba0d7eeeb59df74ffe3733431 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-musl-gcc8.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-musl-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..5ecad97 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.i686-linux-musl-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +dd11c827b50e5983ed7705dcebfb50249ebbaf661a371d7d0e4f2a8d7401d9999def5bf2491c2461a4dd9b582e714d9350c70298ef388cb32a4d38060a124d88 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.i686-w64-mingw32-gcc4.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.i686-w64-mingw32-gcc4.tar.gz/md5 new file mode 100644 index 0000000..8e0ecf2 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.i686-w64-mingw32-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +fb8b0d915623c27936ea293a2eb2850e diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.i686-w64-mingw32-gcc4.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.i686-w64-mingw32-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..c051be3 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.i686-w64-mingw32-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +e3600b813bc7ba5f736899348bf34965f3affead6fadc71555b8bc5e338b06e90a6865a409186604b0fc2d3bdf84ff2e2bf103909cbf7c253dfaff030bbe21fb diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.i686-w64-mingw32-gcc7.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.i686-w64-mingw32-gcc7.tar.gz/md5 new file mode 100644 index 0000000..ca619b7 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.i686-w64-mingw32-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +5b9eabe82ad7de280d739f2d594e8fdc diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.i686-w64-mingw32-gcc7.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.i686-w64-mingw32-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..5382db5 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.i686-w64-mingw32-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +a532481b46d1d7999618676b5d9eba5fd527d25dca5ac2666d9a2258245d77aef742bb2909ff70543d7a06e5182a99fd6c356f3c59c8aff822f9280cbcaa9cf3 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.i686-w64-mingw32-gcc8.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.i686-w64-mingw32-gcc8.tar.gz/md5 new file mode 100644 index 0000000..5d4006d --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.i686-w64-mingw32-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +d02eacbfb41fe7827331865396707b4b diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.i686-w64-mingw32-gcc8.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.i686-w64-mingw32-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..4519f15 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.i686-w64-mingw32-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +8c58b822ecfef7192cf36ff391bf34087eecad299c4417da06fdd990c2e71daf8b717770eabeca7e5b67a020b415b9294d44ac897ccb503a1a34943cd563efc5 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.powerpc64le-linux-gnu-gcc4.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.powerpc64le-linux-gnu-gcc4.tar.gz/md5 new file mode 100644 index 0000000..5a551db --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.powerpc64le-linux-gnu-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +d1e4a5d1a43e018b67532fa98081206e diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.powerpc64le-linux-gnu-gcc4.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.powerpc64le-linux-gnu-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..a9738af --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.powerpc64le-linux-gnu-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +4357a9f323739c90e6f1e0797be0d8f11e36fbfa9f8a64a6050833244a786166a8391e2c73b1f8130fa83db3d57b58fd48bf2081ba79992b452ffec57d1759a2 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.powerpc64le-linux-gnu-gcc7.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.powerpc64le-linux-gnu-gcc7.tar.gz/md5 new file mode 100644 index 0000000..9e59162 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.powerpc64le-linux-gnu-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +2c7930c84ea6cd11c69aa25941087f5c diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.powerpc64le-linux-gnu-gcc7.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.powerpc64le-linux-gnu-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..fc2938f --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.powerpc64le-linux-gnu-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +a53d47e1d7ff25258b20394195e374c9f986b2e7edb0dd66c314a5f12a7d28b4518e5ab87fe5f0feaac99c2f2961292b1ce1d5436ea83104bf089d3d08607d93 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.powerpc64le-linux-gnu-gcc8.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.powerpc64le-linux-gnu-gcc8.tar.gz/md5 new file mode 100644 index 0000000..9de6c1c --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.powerpc64le-linux-gnu-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +1b881141039ba34a7f2c29cef9305062 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.powerpc64le-linux-gnu-gcc8.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.powerpc64le-linux-gnu-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..809915d --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.powerpc64le-linux-gnu-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +23ab0524b4403e002bdf280f83fb955de11176fdf7cfaed3f9197993f7c09ad5c1fea14dc6402f1756272aa5e427ffd904418db4de02ea9efbc96de60baf6ee5 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-apple-darwin14-gcc4.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-apple-darwin14-gcc4.tar.gz/md5 new file mode 100644 index 0000000..8e4794e --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-apple-darwin14-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +eda2584e55dc066e5549048b84b35f05 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-apple-darwin14-gcc4.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-apple-darwin14-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..8a2877a --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-apple-darwin14-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +3034f82dc2e06f8f3474ffd6c9692d58186edb59f5f32578de9c21777fbff9def9abe000ca79ae5044be82825f70fab4a9612bbc8df7b0d23dfc77fa28b8f363 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-apple-darwin14-gcc7.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-apple-darwin14-gcc7.tar.gz/md5 new file mode 100644 index 0000000..31a0759 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-apple-darwin14-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +aa649a88f904208bc3e979af833c9ecd diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-apple-darwin14-gcc7.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-apple-darwin14-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..ec02f2f --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-apple-darwin14-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +c2d7c3a718106de87777beea3addd658ce06f7f1ed5973c5d17fcf97d958741a19df114f62771afc77a7f2c5e25f8302471772cafa30ee40ef7cdfebcec8477b diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-apple-darwin14-gcc8.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-apple-darwin14-gcc8.tar.gz/md5 new file mode 100644 index 0000000..6b0b046 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-apple-darwin14-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +f3c53bfaabc5027cd95838524ed1ab4e diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-apple-darwin14-gcc8.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-apple-darwin14-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..2ac1f2a --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-apple-darwin14-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +cd40f1510f66071551ef1d752a64dada6cf82128b725030d8bab7a3cd4b25397b3504763f165498c7231f9c2577c5eddee4cd8b7d8810526221b15d3647ad9cc diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-gnu-gcc4.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-gnu-gcc4.tar.gz/md5 new file mode 100644 index 0000000..29cb2f9 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-gnu-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +d6452ff5d4fd1cf1d77239d22cf6a1b5 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-gnu-gcc4.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-gnu-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..75e512a --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-gnu-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +801c374ee8e8f296e6b84536d2d719bee35e1f0380d947ea119d0a5ebfa7151e358a6228078b516338a4957580de63b3fa083d5be08be2930832c105286cfc49 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-gnu-gcc7.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-gnu-gcc7.tar.gz/md5 new file mode 100644 index 0000000..6abba7c --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-gnu-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +124282a831a7d39036a7d3b391d513ec diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-gnu-gcc7.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-gnu-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..f197900 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-gnu-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +2ff54c2700e5c1c1dd8bca51cbe8654aa364e5b57f871b6f2701f4a5412ff0ad64ce173fbd9ce7443b6625177e4d645991736966bfbf3fdbdc66bc96fc22d4a5 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-gnu-gcc8.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-gnu-gcc8.tar.gz/md5 new file mode 100644 index 0000000..5fdab5a --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-gnu-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +e0822060b13864f9b0a4416149d89336 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-gnu-gcc8.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-gnu-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..1364968 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-gnu-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +8beca6be0853117eae41b84e984601fd61ef15fd5f6555f1d71375d60a22693cdbfbfac93ec76dd14fa476a6d474b29e8c7efe96a4acb2e90433fe8f325690e1 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-musl-gcc4.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-musl-gcc4.tar.gz/md5 new file mode 100644 index 0000000..0b8f5e8 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-musl-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +4c3dffaacf774f5838cd2d0f178a4e67 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-musl-gcc4.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-musl-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..65b9bd3 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-musl-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +9552f805840c48ed24d65ac858ac13db158cb3579753400c778a3aabac39b20d47a59ab36c86293e5c8b15837e8567e8635792f5ee426be83d26cc6de7bfdcb0 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-musl-gcc7.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-musl-gcc7.tar.gz/md5 new file mode 100644 index 0000000..e3e2178 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-musl-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +d0724e5fd3ffdb759e293724f4b9fa73 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-musl-gcc7.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-musl-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..b0ea95e --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-musl-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +00b6d2eb995e0c1d973e6d30bd70b92d7d2c5f6d14f9cb1788b16d772e91faef8b095e3ae13729c168088fe005caf2e303d51b9414c243b4e12d8beee7547d3d diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-musl-gcc8.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-musl-gcc8.tar.gz/md5 new file mode 100644 index 0000000..f7b5eba --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-musl-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +36258813bf0e72e1566bd73816bd7b6a diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-musl-gcc8.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-musl-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..f7c773c --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-linux-musl-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +289903f344b251e122dd76bb0c9e097323e2da1f3995391dc9882098346355767c8d4933e6d1cc98074f3d85bb326aac5785b716dd6198d122e48f2c7fd072d4 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-unknown-freebsd11.1-gcc4.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-unknown-freebsd11.1-gcc4.tar.gz/md5 new file mode 100644 index 0000000..3f7656c --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-unknown-freebsd11.1-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +457e4f86c5f482d777f41a24eb9642e1 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-unknown-freebsd11.1-gcc4.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-unknown-freebsd11.1-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..7b67ada --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-unknown-freebsd11.1-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +acd3f8b3d42e1fa4c46d82bc9c60042d91985d41b440fd7259b4370b93b19287792d8b14fa7d5a5510945b5201f225aadbbc96ff2edee0ab2c1a254624647247 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-unknown-freebsd11.1-gcc7.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-unknown-freebsd11.1-gcc7.tar.gz/md5 new file mode 100644 index 0000000..9132655 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-unknown-freebsd11.1-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +e57e4cb36402d9acb3d1a89a88e37826 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-unknown-freebsd11.1-gcc7.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-unknown-freebsd11.1-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..ba5d1fb --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-unknown-freebsd11.1-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +b372b1d6fbaacb2e4c9baf585e1c6fa85a72ff37e9dc644a27308586edffa5e1c5c7e0be4245fb965e7cd0c6686f284d0112be60d5456e0892b097737756f4cf diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-unknown-freebsd11.1-gcc8.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-unknown-freebsd11.1-gcc8.tar.gz/md5 new file mode 100644 index 0000000..a9ec847 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-unknown-freebsd11.1-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +61b60d24090c00e38cf8b6af83fd884f diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-unknown-freebsd11.1-gcc8.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-unknown-freebsd11.1-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..47d269f --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-unknown-freebsd11.1-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +7f6cf4818134353462b08d23ed1adf4360653e58eb848b56bf49aee3c3f591ec91999048f4c1540001a35d15bc5e7795eb09ad8cb549663d56f7613c987b4b7c diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-w64-mingw32-gcc4.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-w64-mingw32-gcc4.tar.gz/md5 new file mode 100644 index 0000000..cb82203 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-w64-mingw32-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +f5c0d5d809ddcf721ae1dc68eaf2c0c4 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-w64-mingw32-gcc4.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-w64-mingw32-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..7e8c760 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-w64-mingw32-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +395bdc4a316cf55206966c858bf7320d0f66cdf2f7d6393cc8b3dfd74fc4983a59dc522ac8c18534c6ecde5bf877af57dbc088c83fa6dd8270326f587ceecd6a diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-w64-mingw32-gcc7.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-w64-mingw32-gcc7.tar.gz/md5 new file mode 100644 index 0000000..a6c82f8 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-w64-mingw32-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +f40e5bc882602ea1298089a92350bf5a diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-w64-mingw32-gcc7.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-w64-mingw32-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..72ce16a --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-w64-mingw32-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +c4bc4b6922a57a9806ad007bd3e0655206717acf722e4e794b709f8b06efd6106d4b4e943276061a7632e7191e307317e3f3a205ff46e8ef3b9c0ea8634fe4f3 diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-w64-mingw32-gcc8.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-w64-mingw32-gcc8.tar.gz/md5 new file mode 100644 index 0000000..09801e6 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-w64-mingw32-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +617c4587e25dc495446d389fa323c45f diff --git a/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-w64-mingw32-gcc8.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-w64-mingw32-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..38bc2a3 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.5-2.x86_64-w64-mingw32-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +63938a2e8ee30807cbeca92589d79d3c438f2aed3eb818735c3ab28e6a6d9835fb7cf250ea7c05fdc8949ba32769f394e6981be46c71b19a389a9b00501962ee diff --git a/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-gnu-gcc4.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-gnu-gcc4.tar.gz/md5 new file mode 100644 index 0000000..d57eecc --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-gnu-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +1117d1b3e15c8fff4fee698202e8178e diff --git a/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-gnu-gcc4.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-gnu-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..53e6e68 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-gnu-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +4b4be8479ee75340fa6f7c01f2ced125d71bc9793cad1e4dc1ce206958e7b3c4c1312e67aa1b4a456b3a3611f787c0e0f25ed16ec5627dd40ce2573f8eb544bf diff --git a/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-gnu-gcc7.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-gnu-gcc7.tar.gz/md5 new file mode 100644 index 0000000..efae8c3 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-gnu-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +77473487f950d8ee33306c51462fb759 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-gnu-gcc7.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-gnu-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..c77387d --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-gnu-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +146db2beb0b1495fdd70f7f8cae1afc4be842c3fc7df11e52107f528c13388ca702ca40dd9599659a48b02e79d17bab9891a27206e60533b29faca7d05b9df1a diff --git a/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-gnu-gcc8.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-gnu-gcc8.tar.gz/md5 new file mode 100644 index 0000000..ee626d1 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-gnu-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +dbc3c4d0816a387857a56754cfed25bb diff --git a/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-gnu-gcc8.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-gnu-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..1f922c1 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-gnu-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +b9e0b0be1b3ac00fda3cfb955137402cde755e81922cb50fb826228543e3f1962fd7ef8359370de3592494d685eb5cc02a17dddc413a50c1dee42c966ee5fdf5 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-musl-gcc4.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-musl-gcc4.tar.gz/md5 new file mode 100644 index 0000000..c3a0dad --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-musl-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +dd2a256463f9b400d818008b585bea1b diff --git a/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-musl-gcc4.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-musl-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..103caf7 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-musl-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +7030890349111d2d2b4bab5b6868b74b714c469aeb8f1335d53b87faa664a8560a28751a24806ffe8f1bcc2eeb1e1624ba416ee9c2b4942707538bc583593cfa diff --git a/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-musl-gcc7.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-musl-gcc7.tar.gz/md5 new file mode 100644 index 0000000..db468ca --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-musl-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +ea8e3ab255e3687d0e9835b91824d948 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-musl-gcc7.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-musl-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..ef40c86 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-musl-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +a74915465d4c1751d27d290e79fb874090bd0fb66657def7ac9ddf9448c896da6fb2b437f9637dd37fe36450dc39bc8b0e63325bcf3a4a898c31e0e147583fe2 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-musl-gcc8.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-musl-gcc8.tar.gz/md5 new file mode 100644 index 0000000..99a5745 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-musl-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +5fe52cccd3dcfd9a67c33ef9078abeda diff --git a/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-musl-gcc8.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-musl-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..819f2f3 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.aarch64-linux-musl-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +954887033bc20c5d23d1ba7d0ef4d735aeeec4954026c71e64c62e5f4244a4e3b8c7c0dec7dfdf24a2a7827af809bdb20cee71f8a4e063dc350904ceefe45893 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-gnueabihf-gcc4.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-gnueabihf-gcc4.tar.gz/md5 new file mode 100644 index 0000000..ce8386d --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-gnueabihf-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +918341811a5eaedfcd46a56fcb9008d6 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-gnueabihf-gcc4.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-gnueabihf-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..c40fa10 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-gnueabihf-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +50a7de214ef4fc2fe65783f88e4422d43c11a4c1de4e6d42b4aa0dc7589fb5266773f0240bfb01ca25283c0ad661f9885a6bbd5fb6683e3799f1c4fb1a3127a2 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-gnueabihf-gcc7.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-gnueabihf-gcc7.tar.gz/md5 new file mode 100644 index 0000000..4c99ddf --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-gnueabihf-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +59ca0415f810118511f54568503ee0a3 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-gnueabihf-gcc7.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-gnueabihf-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..b34b102 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-gnueabihf-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +a989c4d0980071469a08e8713b6a9d5635068d267581e627592d139ccf29c29b510ea2af1b10f32d87cdde35260116d4e10bd25d5938f4873d3ae8db8a55ec51 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-gnueabihf-gcc8.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-gnueabihf-gcc8.tar.gz/md5 new file mode 100644 index 0000000..3109091 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-gnueabihf-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +794ae33a021c9e7ae9f90957067a5693 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-gnueabihf-gcc8.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-gnueabihf-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..8f9a623 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-gnueabihf-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +0da14244ab753ab82705c1a48ad6a9c710b2b86b95f7f194e1b979a920bee57848953a57a157516f85a4d2a94bcba69ab5b0bd805e6cc3fcf26a060e1a593a9e diff --git a/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-musleabihf-gcc4.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-musleabihf-gcc4.tar.gz/md5 new file mode 100644 index 0000000..ce804d7 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-musleabihf-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +b4a630cb32bbd8fe82ef0aecfd2e2f00 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-musleabihf-gcc4.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-musleabihf-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..9ff98f9 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-musleabihf-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +acabac34d3c8df652d1260255b6ce957803023c3e3bdafec86a8438aff222094092d8327b9fc67d89bf3a6e18651e060f4b8c473aa77249cca7d65edc9fb4d94 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-musleabihf-gcc7.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-musleabihf-gcc7.tar.gz/md5 new file mode 100644 index 0000000..468d3fb --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-musleabihf-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +7dc17d77bcf0e605b39284681767cdcf diff --git a/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-musleabihf-gcc7.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-musleabihf-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..d5eed89 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-musleabihf-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +0baba12c0c2b28a7c5aa9e9ad0a34ab1e7f607ff99b3d007efb08c42e28f616b480f29da1cbb1c57429cf7341ae2acaed74fd93dbf997630aae2f72ff90d96bd diff --git a/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-musleabihf-gcc8.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-musleabihf-gcc8.tar.gz/md5 new file mode 100644 index 0000000..69655b4 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-musleabihf-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +98bc938b24e22ed46950000d44082c0d diff --git a/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-musleabihf-gcc8.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-musleabihf-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..b6a9505 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.arm-linux-musleabihf-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +7b4772ed7751ccfad37750476006e10c17a6e4eed1e03c6b8e61f5449548fa201db93565a15b6ebbab6fc5cd64799dbbbfda00a08b69ccfce8c14fa9fe2bbc3d diff --git a/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-gnu-gcc4.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-gnu-gcc4.tar.gz/md5 new file mode 100644 index 0000000..896a236 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-gnu-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +72e2fdf60bc37208f7a9e39396289115 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-gnu-gcc4.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-gnu-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..eda1113 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-gnu-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +2f9a5501ea51f2af1e7abfe0bad12988e23724888cf79beb0bae82b8e152783ceb195af31c1eb2d22554087bdfa7b0a1166d3a649441d6dcc52fdfe503f965a1 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-gnu-gcc7.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-gnu-gcc7.tar.gz/md5 new file mode 100644 index 0000000..c8c286d --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-gnu-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +fc9ca5de49e3f77cbd41715de003bc38 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-gnu-gcc7.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-gnu-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..a35e556 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-gnu-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +d2c3536ad05cd675e2710c3bb67b096529c387923c7516c9e98d701a9ae01ae2486e7e88d3e47b115f0a6a5824db3dc64672b873282f24ccd64e85391449ad2c diff --git a/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-gnu-gcc8.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-gnu-gcc8.tar.gz/md5 new file mode 100644 index 0000000..bf72040 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-gnu-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +5df5081e7802ee5310094acd6958f672 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-gnu-gcc8.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-gnu-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..9b33db2 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-gnu-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +91a0a36dca0f46914b25d840fda06e83e906e435436851745c5c3a87a1682df641c86a2bbf21963f2cef2c492fcdd69777c38edad1df68d89770a79277716c67 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-musl-gcc4.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-musl-gcc4.tar.gz/md5 new file mode 100644 index 0000000..aeed18a --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-musl-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +0f892d5f2ef3252a79d4cd83b54f6c6c diff --git a/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-musl-gcc4.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-musl-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..22edbde --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-musl-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +d18df828d9a2c493053b464b80d6c6c2db33fceeb5f831594f8738d300905e16aaae94ee2176348b045412debb6953aba1a639528fcd830ef492862b73b9083d diff --git a/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-musl-gcc7.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-musl-gcc7.tar.gz/md5 new file mode 100644 index 0000000..011154b --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-musl-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +32a79e3a1047073454516ff9d3f7fdb1 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-musl-gcc7.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-musl-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..9bd0a2d --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-musl-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +155ce50abd96a42b170a87928599d2c101a2ff7d40d87ca8085f02383451aa2f191c0aa65cb1c8a1f4d6a2ae9be42f85d7e76aeb70508e63e962b0389dcf3af1 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-musl-gcc8.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-musl-gcc8.tar.gz/md5 new file mode 100644 index 0000000..72b0087 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-musl-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +2a5cd6c75365bf76d03e375cd17b6cbd diff --git a/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-musl-gcc8.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-musl-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..7d223b5 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.i686-linux-musl-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +fe834a214a7a6522846885e9177ff6f1e712d31f909c847e72729f4ab920e57e80a03040a19b79d6ca5472c37500f9e4566adb0651aff0c9c6c46a4937c8d10b diff --git a/deps/checksums/OpenLibm.v0.6.0-0.i686-w64-mingw32-gcc4.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.i686-w64-mingw32-gcc4.tar.gz/md5 new file mode 100644 index 0000000..5984144 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.i686-w64-mingw32-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +86ed6710d4d1d5c2eae40ed5017893c8 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.i686-w64-mingw32-gcc4.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.i686-w64-mingw32-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..4c91d4b --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.i686-w64-mingw32-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +45f9a78e787378b936335b63f644bb149d031b911db8eecca2fdf5d945e30eaca98606f65a0ad3ba380d6737b56063b97014569fcfafc07402103e21a19fc2bd diff --git a/deps/checksums/OpenLibm.v0.6.0-0.i686-w64-mingw32-gcc7.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.i686-w64-mingw32-gcc7.tar.gz/md5 new file mode 100644 index 0000000..389483e --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.i686-w64-mingw32-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +363d1c81d5f233bd034485cdfda8ac36 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.i686-w64-mingw32-gcc7.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.i686-w64-mingw32-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..036eec7 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.i686-w64-mingw32-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +22e47797c3698f4dd301436cf2d45ad22cd21278daafb14050c6e9aff3db35e07809c15cd6fab08e28f48d9d32ed19228fa80df189e5c8c5c6fd8e236113763f diff --git a/deps/checksums/OpenLibm.v0.6.0-0.i686-w64-mingw32-gcc8.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.i686-w64-mingw32-gcc8.tar.gz/md5 new file mode 100644 index 0000000..7c00c10 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.i686-w64-mingw32-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +bd79a7c3b977987fde79e075c41a0d7e diff --git a/deps/checksums/OpenLibm.v0.6.0-0.i686-w64-mingw32-gcc8.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.i686-w64-mingw32-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..bfe35d0 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.i686-w64-mingw32-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +6c59499dbae39218ec271ca743f2fdc845ac7bbab95602bda18dc9329f48cf741fc94df1d5999e1f53812b18bd998ca2ba464e1b2b6ea8c1656527829062b9c8 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.powerpc64le-linux-gnu-gcc4.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.powerpc64le-linux-gnu-gcc4.tar.gz/md5 new file mode 100644 index 0000000..018a415 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.powerpc64le-linux-gnu-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +e642d95a77a37d05ab4fffa58edaddc0 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.powerpc64le-linux-gnu-gcc4.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.powerpc64le-linux-gnu-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..eeb9a60 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.powerpc64le-linux-gnu-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +9cf468d33c4ddc0b15e8604c2a0b166f1ab3835d1a50e9a5d8725db783b9fe161f2e046ef4cf886143209042803054a45c0d6f8281c36c2c818cc8ff1fbddc45 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.powerpc64le-linux-gnu-gcc7.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.powerpc64le-linux-gnu-gcc7.tar.gz/md5 new file mode 100644 index 0000000..74b10fb --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.powerpc64le-linux-gnu-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +1473a5f10e983bc922ec7f064ebdaf1c diff --git a/deps/checksums/OpenLibm.v0.6.0-0.powerpc64le-linux-gnu-gcc7.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.powerpc64le-linux-gnu-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..61bbd2d --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.powerpc64le-linux-gnu-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +2f3cb562825038d15611997577d4bcb28e5807d5e9e72f46a89e97c04bdf3bcef0ba8222a0a4263b76e219072a5955e493ec3f1c901126312bcae4dff64986fb diff --git a/deps/checksums/OpenLibm.v0.6.0-0.powerpc64le-linux-gnu-gcc8.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.powerpc64le-linux-gnu-gcc8.tar.gz/md5 new file mode 100644 index 0000000..531a789 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.powerpc64le-linux-gnu-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +609bc19af3b58517c9c686a12d0baefc diff --git a/deps/checksums/OpenLibm.v0.6.0-0.powerpc64le-linux-gnu-gcc8.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.powerpc64le-linux-gnu-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..b88854a --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.powerpc64le-linux-gnu-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +06a005e38ac92a0c033e71a64f136ae07d68f4e431e32e9cce34736753f6e8160cf2dbd8e8b6ae23a3301c6ad541d9cad25a099bffcb2778230cb92733e10862 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.x86_64-apple-darwin14-gcc4.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-apple-darwin14-gcc4.tar.gz/md5 new file mode 100644 index 0000000..11a0eb9 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-apple-darwin14-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +ff498089028ea0d3b3a3190b2a29325c diff --git a/deps/checksums/OpenLibm.v0.6.0-0.x86_64-apple-darwin14-gcc4.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-apple-darwin14-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..01d4a93 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-apple-darwin14-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +13cb6851ebe0b838b2259e7beb634a3a71b71cddf1d03de24ed9fe96db5a62aa6231140e22442a9df4424b9b1c34bc13ad919f94bc8fdf6bd381e2d6dcb8b532 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.x86_64-apple-darwin14-gcc7.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-apple-darwin14-gcc7.tar.gz/md5 new file mode 100644 index 0000000..bccb8e5 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-apple-darwin14-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +2d162bac3bd10b2e5ac76f775aaa01c0 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.x86_64-apple-darwin14-gcc7.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-apple-darwin14-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..08d9b88 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-apple-darwin14-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +c4b4918675ed659bb4c8fa9711cceb53a827815cb2d325966f340eee20e2d15661f439dc282ca20607a94d032caa05229810c4a203d3b1b844c668348fd09923 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.x86_64-apple-darwin14-gcc8.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-apple-darwin14-gcc8.tar.gz/md5 new file mode 100644 index 0000000..5af608c --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-apple-darwin14-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +4854d0e4e9e001079d92b182b445a531 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.x86_64-apple-darwin14-gcc8.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-apple-darwin14-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..97d958f --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-apple-darwin14-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +f88b24b2605f5b23633da2220e1e23b427dae6be8c5037a1218e74e9e2e7d6b60343756e6b09c6ae6db0bf6403ce3cf300a999de268c4a9f90bf2dcd2ab624d0 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-gnu-gcc4.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-gnu-gcc4.tar.gz/md5 new file mode 100644 index 0000000..52c6151 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-gnu-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +fe26259e2d84ed1fb2f6cef2d4902849 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-gnu-gcc4.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-gnu-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..cb69a21 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-gnu-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +b4e55f1a34534bddd5775c06fe54a782ede5c0e656d8a121539980375935ff2f524ba9988ee4756742eb7adb09c60365121844037a84247be41330293e7efe0f diff --git a/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-gnu-gcc7.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-gnu-gcc7.tar.gz/md5 new file mode 100644 index 0000000..aa97efa --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-gnu-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +83bfa603134be3a478dd2e92ed91d844 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-gnu-gcc7.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-gnu-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..dd5275e --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-gnu-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +4aeff5ea9a02b3c2a6f7c763ae1bc25c33de211ec6e91652f442ed60fa7ad6e0b58453e99f93148553bafacb3267825222b48537329c181bd57af902cf32ec65 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-gnu-gcc8.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-gnu-gcc8.tar.gz/md5 new file mode 100644 index 0000000..7618f83 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-gnu-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +702f9c7ad0584ad54f96d62021222705 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-gnu-gcc8.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-gnu-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..15552b4 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-gnu-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +5b303b50652905ad3467ad8838d098d6801ba1e9d2eb288e0e970ae6ded50cef6afb7938283b27e13292568d1d904eaf0382e68f657799ed2551b70f2e6a9f49 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-musl-gcc4.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-musl-gcc4.tar.gz/md5 new file mode 100644 index 0000000..d13f6d8 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-musl-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +ecd1fee33d239c5517bf5af19ce31224 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-musl-gcc4.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-musl-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..fc80095 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-musl-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +30035898bc54ead4a3c4db2e3a7330ff971ae4579a0dca75aca1e33a5cc7cb440d3998496b5368aa836d422d2c52f7e0339df94e07717f56f2fbac8f9420693c diff --git a/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-musl-gcc7.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-musl-gcc7.tar.gz/md5 new file mode 100644 index 0000000..cabb506 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-musl-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +ae9eb7b38f0448faf5cd909a14050d6a diff --git a/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-musl-gcc7.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-musl-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..5a1766a --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-musl-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +4e43f8d7328d08c6bf9688d562ecc118da17827015603f78db58122a9cca3629b16e0998cfe48534a67d321290641a6fb165b0206d4824740b3b15ae6e6d9f21 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-musl-gcc8.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-musl-gcc8.tar.gz/md5 new file mode 100644 index 0000000..f96bbe9 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-musl-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +1d1b710c8afa04ea3f0a5bebc52fa68c diff --git a/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-musl-gcc8.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-musl-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..1f0d3ea --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-linux-musl-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +390f8de6169c3aaeff30121ba1988ab55f13e8bdfba4e8ef6733774643e26b92f3a71fd8951400fa52143fd794cf27544a2b415130ad5373d045ec16e5b09cc1 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.x86_64-unknown-freebsd11.1-gcc4.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-unknown-freebsd11.1-gcc4.tar.gz/md5 new file mode 100644 index 0000000..d74587b --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-unknown-freebsd11.1-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +ff872f815b2ffc3b748174c31e537203 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.x86_64-unknown-freebsd11.1-gcc4.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-unknown-freebsd11.1-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..80611e1 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-unknown-freebsd11.1-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +6f91d6cbcb7e8ae339ab368815b86e7b66ac48f8fe909d971478b1bf052fd2f86166217e2b1983a599d27038ed195c0d1502c2ca90d576a904f0bb6d867e73fd diff --git a/deps/checksums/OpenLibm.v0.6.0-0.x86_64-unknown-freebsd11.1-gcc7.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-unknown-freebsd11.1-gcc7.tar.gz/md5 new file mode 100644 index 0000000..c9493d9 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-unknown-freebsd11.1-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +d34838fc8fd64cc7c1aaee220f6209c1 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.x86_64-unknown-freebsd11.1-gcc7.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-unknown-freebsd11.1-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..015096c --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-unknown-freebsd11.1-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +535c2f5f5632aea4c02fce2dd7d7443201079b6c76dfb48d4d70831285b6b360c41974b218922b0fb758970d1e29ce5242e13c7030092583d6812d463461a076 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.x86_64-unknown-freebsd11.1-gcc8.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-unknown-freebsd11.1-gcc8.tar.gz/md5 new file mode 100644 index 0000000..a815978 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-unknown-freebsd11.1-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +94f4c4cb094357346d6c95d2f474abe8 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.x86_64-unknown-freebsd11.1-gcc8.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-unknown-freebsd11.1-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..52a0fbd --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-unknown-freebsd11.1-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +6f3ebf843dcf15a3f9f9323fc3b1bd9128fa624f4c01cb385fe8da364f3250c0f4dcc487537d7e51aa867bc8bf667d16434d43fc1fbe38bab786ba6d98eae48c diff --git a/deps/checksums/OpenLibm.v0.6.0-0.x86_64-w64-mingw32-gcc4.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-w64-mingw32-gcc4.tar.gz/md5 new file mode 100644 index 0000000..a11d214 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-w64-mingw32-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +d456a8f85469c6d9a63797f8f0cfbe78 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.x86_64-w64-mingw32-gcc4.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-w64-mingw32-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..ec18465 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-w64-mingw32-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +89231240804e3d1c180501930bf75c72ea9c6523469272fcf0f9bb9a8dc7c59a2522525c97f887ec30cd79bf91234059afc21ca48310f7ece16a6491b381b9ac diff --git a/deps/checksums/OpenLibm.v0.6.0-0.x86_64-w64-mingw32-gcc7.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-w64-mingw32-gcc7.tar.gz/md5 new file mode 100644 index 0000000..306160c --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-w64-mingw32-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +0c8281ccb6e80790c895d00c25c64cdf diff --git a/deps/checksums/OpenLibm.v0.6.0-0.x86_64-w64-mingw32-gcc7.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-w64-mingw32-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..ba0132c --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-w64-mingw32-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +593a0a590a3eeb503eb624937cf5b48e44a74e99f97bd7aac5e04b8ff741994a133fb4452343311416726985d48e74f4bd239b41c57b686b82e48724e4fd3288 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.x86_64-w64-mingw32-gcc8.tar.gz/md5 b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-w64-mingw32-gcc8.tar.gz/md5 new file mode 100644 index 0000000..038b3b3 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-w64-mingw32-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +c18942244da474b81e852455dc589571 diff --git a/deps/checksums/OpenLibm.v0.6.0-0.x86_64-w64-mingw32-gcc8.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-w64-mingw32-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..db234c1 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.6.0-0.x86_64-w64-mingw32-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +1b17d78919c1df296acc3fb7870b301a88a522dd25e076c09d98b185ce63c46947ab31b2bda8d020e62c1c4b4a4babee7aa25e1dd717f4c59461e8008c75d13a diff --git a/deps/checksums/PCRE2.v10.31.0-0.aarch64-linux-gnu.tar.gz/md5 b/deps/checksums/PCRE2.v10.31.0-0.aarch64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..fd805c3 --- /dev/null +++ b/deps/checksums/PCRE2.v10.31.0-0.aarch64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +54e4d21dddc9165ebe7fd736fc9ebe0d diff --git a/deps/checksums/PCRE2.v10.31.0-0.aarch64-linux-gnu.tar.gz/sha512 b/deps/checksums/PCRE2.v10.31.0-0.aarch64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..3ff166e --- /dev/null +++ b/deps/checksums/PCRE2.v10.31.0-0.aarch64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +3845c21cd009303533f0c9ca9eda1bb80d2273e2547a3c8fed95f65902b185a06e5da51452d52b1c595e451acf2ef91284e31eb2dddf87b95649ffccea3b2ece diff --git a/deps/checksums/PCRE2.v10.31.0-0.aarch64-linux-musl.tar.gz/md5 b/deps/checksums/PCRE2.v10.31.0-0.aarch64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..0fe889a --- /dev/null +++ b/deps/checksums/PCRE2.v10.31.0-0.aarch64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +ab4ae6dfe099b8c718c24a18056daef8 diff --git a/deps/checksums/PCRE2.v10.31.0-0.aarch64-linux-musl.tar.gz/sha512 b/deps/checksums/PCRE2.v10.31.0-0.aarch64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..7d4828a --- /dev/null +++ b/deps/checksums/PCRE2.v10.31.0-0.aarch64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +359ac7fac9eb6e3a72d40c548602c5f3e0e88f866282b8487ff113f3347d71171e75547675ac214f35ff8e9d76891d9eea762c30d96e66f2ad2ee3804c7d255b diff --git a/deps/checksums/PCRE2.v10.31.0-0.arm-linux-gnueabihf.tar.gz/md5 b/deps/checksums/PCRE2.v10.31.0-0.arm-linux-gnueabihf.tar.gz/md5 new file mode 100644 index 0000000..c940a87 --- /dev/null +++ b/deps/checksums/PCRE2.v10.31.0-0.arm-linux-gnueabihf.tar.gz/md5 @@ -0,0 +1 @@ +8a138c21e69e2ef27e97df2393a24f62 diff --git a/deps/checksums/PCRE2.v10.31.0-0.arm-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/PCRE2.v10.31.0-0.arm-linux-gnueabihf.tar.gz/sha512 new file mode 100644 index 0000000..4691d6d --- /dev/null +++ b/deps/checksums/PCRE2.v10.31.0-0.arm-linux-gnueabihf.tar.gz/sha512 @@ -0,0 +1 @@ +aae6e19f3d8464cf195612e6c5bb1d79ea14d2c2f03207ee1796aa8e5374197ff1693e8f170620f5f5769aa2ed6f0d4b84220475376d85d111f5f9d7c33e135a diff --git a/deps/checksums/PCRE2.v10.31.0-0.arm-linux-musleabihf.tar.gz/md5 b/deps/checksums/PCRE2.v10.31.0-0.arm-linux-musleabihf.tar.gz/md5 new file mode 100644 index 0000000..1a590e3 --- /dev/null +++ b/deps/checksums/PCRE2.v10.31.0-0.arm-linux-musleabihf.tar.gz/md5 @@ -0,0 +1 @@ +a2d5325168a333f60dac0dd083022224 diff --git a/deps/checksums/PCRE2.v10.31.0-0.arm-linux-musleabihf.tar.gz/sha512 b/deps/checksums/PCRE2.v10.31.0-0.arm-linux-musleabihf.tar.gz/sha512 new file mode 100644 index 0000000..f9b9209 --- /dev/null +++ b/deps/checksums/PCRE2.v10.31.0-0.arm-linux-musleabihf.tar.gz/sha512 @@ -0,0 +1 @@ +49cbb7ad7a1efeb61aaa79906b846f69e7c98eb8b65857a6fcd30f4995398f0589a8bc6e4cb21816f0089d5904a743a6b3af2265c2bbd4b0cb25788a74b82bc5 diff --git a/deps/checksums/PCRE2.v10.31.0-0.i686-linux-gnu.tar.gz/md5 b/deps/checksums/PCRE2.v10.31.0-0.i686-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..85ded73 --- /dev/null +++ b/deps/checksums/PCRE2.v10.31.0-0.i686-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +442b052ac108a31c33a89f9727a3b49b diff --git a/deps/checksums/PCRE2.v10.31.0-0.i686-linux-gnu.tar.gz/sha512 b/deps/checksums/PCRE2.v10.31.0-0.i686-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..49723e3 --- /dev/null +++ b/deps/checksums/PCRE2.v10.31.0-0.i686-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +8776e35682738eb5b958954573f79dfbcbace923b00a9d70cddee6728454dc389f54d91e7a00beff759a6247a5a7fdfbe2f8a66321a2fd642a379910ca391cbf diff --git a/deps/checksums/PCRE2.v10.31.0-0.i686-linux-musl.tar.gz/md5 b/deps/checksums/PCRE2.v10.31.0-0.i686-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..7647c98 --- /dev/null +++ b/deps/checksums/PCRE2.v10.31.0-0.i686-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +24101edc95ebcf77a22fe5e4d7d8799d diff --git a/deps/checksums/PCRE2.v10.31.0-0.i686-linux-musl.tar.gz/sha512 b/deps/checksums/PCRE2.v10.31.0-0.i686-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..79a20cc --- /dev/null +++ b/deps/checksums/PCRE2.v10.31.0-0.i686-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +8561f6a775e5e552d77aabaa4b4f7f6098cfcfaab06051385896214eb1b8a4d5a0cc2f3cd0d6e9c2f5fbb64928a3328a61db699d5108d553574bdcb52353da3f diff --git a/deps/checksums/PCRE2.v10.31.0-0.i686-w64-mingw32.tar.gz/md5 b/deps/checksums/PCRE2.v10.31.0-0.i686-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..4cbe1bc --- /dev/null +++ b/deps/checksums/PCRE2.v10.31.0-0.i686-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +c6b7b3402e7126d9d9aef281c09d58a6 diff --git a/deps/checksums/PCRE2.v10.31.0-0.i686-w64-mingw32.tar.gz/sha512 b/deps/checksums/PCRE2.v10.31.0-0.i686-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..adcd04c --- /dev/null +++ b/deps/checksums/PCRE2.v10.31.0-0.i686-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +f46f56756d1d71fa0b9b767a18d47d9cd5966d63ac7806d90289311007717ec8580ff5989bf9f3fc8192bea581f2598f1083d182f1e8474a7e29977be5751f90 diff --git a/deps/checksums/PCRE2.v10.31.0-0.powerpc64le-linux-gnu.tar.gz/md5 b/deps/checksums/PCRE2.v10.31.0-0.powerpc64le-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..c2ff7c7 --- /dev/null +++ b/deps/checksums/PCRE2.v10.31.0-0.powerpc64le-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +31cef7ec68cabd292aa63873f517f3ee diff --git a/deps/checksums/PCRE2.v10.31.0-0.powerpc64le-linux-gnu.tar.gz/sha512 b/deps/checksums/PCRE2.v10.31.0-0.powerpc64le-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..f39d426 --- /dev/null +++ b/deps/checksums/PCRE2.v10.31.0-0.powerpc64le-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +de165bc120d90553435f8955b7afd467163c66d83b6661593682bf7ae7c160622e6a98a459bcf862ba1eb14b224912cfaca6971a4f1c125a0d75170d1e13d5e5 diff --git a/deps/checksums/PCRE2.v10.31.0-0.x86_64-apple-darwin14.tar.gz/md5 b/deps/checksums/PCRE2.v10.31.0-0.x86_64-apple-darwin14.tar.gz/md5 new file mode 100644 index 0000000..94b7033 --- /dev/null +++ b/deps/checksums/PCRE2.v10.31.0-0.x86_64-apple-darwin14.tar.gz/md5 @@ -0,0 +1 @@ +10bdb7281fe9899a65e4d058ce3b62a0 diff --git a/deps/checksums/PCRE2.v10.31.0-0.x86_64-apple-darwin14.tar.gz/sha512 b/deps/checksums/PCRE2.v10.31.0-0.x86_64-apple-darwin14.tar.gz/sha512 new file mode 100644 index 0000000..40cf63c --- /dev/null +++ b/deps/checksums/PCRE2.v10.31.0-0.x86_64-apple-darwin14.tar.gz/sha512 @@ -0,0 +1 @@ +74f603c84f48a8fd24b80146be215bd30356e1fc1b46c33a0b9df105f3afd5cebcfaac4a6f018e06d0279bdfc369ea0ec22cab38a1cdcf97ce49f56a0fc86509 diff --git a/deps/checksums/PCRE2.v10.31.0-0.x86_64-linux-gnu.tar.gz/md5 b/deps/checksums/PCRE2.v10.31.0-0.x86_64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..59d8bb3 --- /dev/null +++ b/deps/checksums/PCRE2.v10.31.0-0.x86_64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +7b5a398e37554f764fc33b384f2588ab diff --git a/deps/checksums/PCRE2.v10.31.0-0.x86_64-linux-gnu.tar.gz/sha512 b/deps/checksums/PCRE2.v10.31.0-0.x86_64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..751eb51 --- /dev/null +++ b/deps/checksums/PCRE2.v10.31.0-0.x86_64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +6d4e42bdc851d905a7c4ff626dc6da58bfcc6361d578123c54f616d571ef0056dfc27789ea3520b816ed80b1501307461cb6fd6a7eecb032da4f42e70b555128 diff --git a/deps/checksums/PCRE2.v10.31.0-0.x86_64-linux-musl.tar.gz/md5 b/deps/checksums/PCRE2.v10.31.0-0.x86_64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..ee1f0e8 --- /dev/null +++ b/deps/checksums/PCRE2.v10.31.0-0.x86_64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +b4a0541fcb70459046492ea61e784af5 diff --git a/deps/checksums/PCRE2.v10.31.0-0.x86_64-linux-musl.tar.gz/sha512 b/deps/checksums/PCRE2.v10.31.0-0.x86_64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..79c011b --- /dev/null +++ b/deps/checksums/PCRE2.v10.31.0-0.x86_64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +71e2485144b6d5d6527777e96b8677ce49eac6ee8bddba2aa792d7d153d073b401e590c947f30fbdb14e34bbf1f339ed9da4d28c32e906fb2156b9c21e5b3928 diff --git a/deps/checksums/PCRE2.v10.31.0-0.x86_64-unknown-freebsd11.1.tar.gz/md5 b/deps/checksums/PCRE2.v10.31.0-0.x86_64-unknown-freebsd11.1.tar.gz/md5 new file mode 100644 index 0000000..bfa6d25 --- /dev/null +++ b/deps/checksums/PCRE2.v10.31.0-0.x86_64-unknown-freebsd11.1.tar.gz/md5 @@ -0,0 +1 @@ +12c462d17c6133a43224e7aa0dbf6ab8 diff --git a/deps/checksums/PCRE2.v10.31.0-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 b/deps/checksums/PCRE2.v10.31.0-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 new file mode 100644 index 0000000..71f7eba --- /dev/null +++ b/deps/checksums/PCRE2.v10.31.0-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 @@ -0,0 +1 @@ +558bd5705ecf2621d6a3da0aa3ad76abd750a64566bd4e08f251fdb09057a245b2ad97fa50160baa62eb92a52118fac592ebfd562bec23de1eb45e3de9fcd02c diff --git a/deps/checksums/PCRE2.v10.31.0-0.x86_64-w64-mingw32.tar.gz/md5 b/deps/checksums/PCRE2.v10.31.0-0.x86_64-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..bcca7ab --- /dev/null +++ b/deps/checksums/PCRE2.v10.31.0-0.x86_64-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +ccb3b55a39f06475cdc20e243b64c822 diff --git a/deps/checksums/PCRE2.v10.31.0-0.x86_64-w64-mingw32.tar.gz/sha512 b/deps/checksums/PCRE2.v10.31.0-0.x86_64-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..d809ff9 --- /dev/null +++ b/deps/checksums/PCRE2.v10.31.0-0.x86_64-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +8fed96378ce5567dd40a29831c92c21c6627dce5ab468d43d8bdf6447bd43b01201360e8b89b3ec0380248eb2ef46df6be40109d683a3af16d264a2e4300eab6 diff --git a/deps/checksums/Pkg-f71e2c5a119b9c850f9b357fc8c56068f5b51cc0.tar.gz/md5 b/deps/checksums/Pkg-f71e2c5a119b9c850f9b357fc8c56068f5b51cc0.tar.gz/md5 new file mode 100644 index 0000000..8235b52 --- /dev/null +++ b/deps/checksums/Pkg-f71e2c5a119b9c850f9b357fc8c56068f5b51cc0.tar.gz/md5 @@ -0,0 +1 @@ +054eb913fe9bcbe6080c2305980a6c6e diff --git a/deps/checksums/Pkg-f71e2c5a119b9c850f9b357fc8c56068f5b51cc0.tar.gz/sha512 b/deps/checksums/Pkg-f71e2c5a119b9c850f9b357fc8c56068f5b51cc0.tar.gz/sha512 new file mode 100644 index 0000000..62d0ba9 --- /dev/null +++ b/deps/checksums/Pkg-f71e2c5a119b9c850f9b357fc8c56068f5b51cc0.tar.gz/sha512 @@ -0,0 +1 @@ +31c71b702b2831efe08911cab73b78aa2988524a5563720da902d1012e621c45e6bcad7310abc291e934be0d3f481d884af83e3cb4c63e284d8351938faf211f diff --git a/deps/checksums/SuiteSparse-5.4.0.tar.gz/md5 b/deps/checksums/SuiteSparse-5.4.0.tar.gz/md5 new file mode 100644 index 0000000..645597b --- /dev/null +++ b/deps/checksums/SuiteSparse-5.4.0.tar.gz/md5 @@ -0,0 +1 @@ +4a6d4e74fc44c503f52996ae95cad03a diff --git a/deps/checksums/SuiteSparse-5.4.0.tar.gz/sha512 b/deps/checksums/SuiteSparse-5.4.0.tar.gz/sha512 new file mode 100644 index 0000000..74cc152 --- /dev/null +++ b/deps/checksums/SuiteSparse-5.4.0.tar.gz/sha512 @@ -0,0 +1 @@ +8328bcc2ef5eb03febf91b9c71159f091ff405c1ba7522e53714120fcf857ceab2d2ecf8bf9a2e1fc45e1a934665a341e3a47f954f87b59934f4fce6164775d6 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-gnu-gcc4.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-gnu-gcc4.tar.gz/md5 new file mode 100644 index 0000000..0eb883a --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-gnu-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +33802fc0807417d49abe7b9457a1d356 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-gnu-gcc4.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-gnu-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..d894abd --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-gnu-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +0c7eedc18fd36b7f2f933b258c240d26d5dff4d93ff83e3e8d7bbe8321c1cada5c0bc672744ff3a18e5f91770a3b011c117072a44703c9679a90a4d0b61e985e diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-gnu-gcc7.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-gnu-gcc7.tar.gz/md5 new file mode 100644 index 0000000..2c57de0 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-gnu-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +86a2cad460097eccd3cb293f1f50da6b diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-gnu-gcc7.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-gnu-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..ca0073b --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-gnu-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +319b394082697de4127a46be37432bfec3c518d95f51753ffdb2c8b445e8644cf1ec7dfd8f361a9a9a0898ec9763807b754229988987bc2cf55a38d10829a9a8 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-gnu-gcc8.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-gnu-gcc8.tar.gz/md5 new file mode 100644 index 0000000..9c59943 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-gnu-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +341dbf803639461111a071c5c99c67ed diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-gnu-gcc8.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-gnu-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..ebad303 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-gnu-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +3f4d5288d0ae59cd7595074f08e471306ddd8559d2e0546d77c8a236f295eeace7ca11de77c6b598c35b85c539b3f919c30aed48fc6afbdc3409a1b0e08da6c9 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-musl-gcc4.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-musl-gcc4.tar.gz/md5 new file mode 100644 index 0000000..c9e8e8b --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-musl-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +3bd99240bf3b1928e031d6a147c9a9d3 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-musl-gcc4.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-musl-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..df2950a --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-musl-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +2bf9a2709fffe257ab0931dde4cd380d8095acea0b7b7acf877d3d0b82cd03bb31d9ef12862297b39fd635632323ccaeedfde7b67b527cb523056580384761c7 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-musl-gcc7.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-musl-gcc7.tar.gz/md5 new file mode 100644 index 0000000..33a569e --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-musl-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +10e1889da8fda4af214891a8a3336098 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-musl-gcc7.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-musl-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..a66c56b --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-musl-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +e190428ff4449c7e0032017845a866aaff8921d95aa5470417499cc09518af95047f65305c473ccc40f8b9c6295f2d2896bb50d3083f3b05e8c57bc00198f88f diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-musl-gcc8.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-musl-gcc8.tar.gz/md5 new file mode 100644 index 0000000..780f499 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-musl-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +0bfb020eb2cecc934c429ed816013663 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-musl-gcc8.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-musl-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..f6056b6 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.aarch64-linux-musl-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +a940fecb380c6e9591a2df02e0d3bdf0b5cb544615015595feb2afae075156e4e58b00db7a8c0bc87e09e11429329e5bc343cd7b67dd1f1ec108259f485eb527 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-gnueabihf-gcc4.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-gnueabihf-gcc4.tar.gz/md5 new file mode 100644 index 0000000..5a51023 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-gnueabihf-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +65c8a77030c1bf97c9ecaff5d2ef0a9a diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-gnueabihf-gcc4.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-gnueabihf-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..247734e --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-gnueabihf-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +0cb84eebc24f81c5ae8eff91463f40de21e4720e1fb8af9293624de0c45a67c213e914650b2ba27bfd7550422b30294d27c17023089ac8bf295bdab959592538 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-gnueabihf-gcc7.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-gnueabihf-gcc7.tar.gz/md5 new file mode 100644 index 0000000..54bfff7 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-gnueabihf-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +d4f083a1b9bd7798f6cbdbc46d462796 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-gnueabihf-gcc7.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-gnueabihf-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..a754084 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-gnueabihf-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +1b93a2652ec2bca334b20509a01f87d166442c8171e9ae2d52696c64937201ac1eb6e5847b4c60ff2faa46169ffa7c132c78e9aecafcf520f775c3492bc77b8b diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-gnueabihf-gcc8.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-gnueabihf-gcc8.tar.gz/md5 new file mode 100644 index 0000000..1b55672 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-gnueabihf-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +cedef7d2bbf47910d02b194c749ea2e1 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-gnueabihf-gcc8.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-gnueabihf-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..21fa8b5 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-gnueabihf-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +c82da88ab436d7ff032b9947181e33700ee21f227a7afb819ebe59da0d1862eda91d021f75f04d382e41b41ac1caf88d144231ca087a2af8f3c38e2584b0d4fc diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-musleabihf-gcc4.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-musleabihf-gcc4.tar.gz/md5 new file mode 100644 index 0000000..1c06ee5 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-musleabihf-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +15013619182f33bf162e8822976aa172 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-musleabihf-gcc4.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-musleabihf-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..67c7027 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-musleabihf-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +f2bc71b9700d7b22c382edd275768523252b92fe6bc2d0b33cdaa69092a802205c2ecc96a4bb66a56d22be093d2d4f9170273919c32c9aafd1ab949d735eb2f0 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-musleabihf-gcc7.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-musleabihf-gcc7.tar.gz/md5 new file mode 100644 index 0000000..c7c4bf1 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-musleabihf-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +f736d19b00b2973e0ed4f93bfcbf35fb diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-musleabihf-gcc7.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-musleabihf-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..885914c --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-musleabihf-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +375ebe191963f9c834bf7e49f3d4d2d5d064c341ae81e913b34baa49222da0b5fd4f8e2036e90f74a55bee081f148ff13ac3d6651e18417aebf8df1a3a66b84b diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-musleabihf-gcc8.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-musleabihf-gcc8.tar.gz/md5 new file mode 100644 index 0000000..bb9f66c --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-musleabihf-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +6f65cc6930839e1370f315a7a3000cae diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-musleabihf-gcc8.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-musleabihf-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..ee16b9e --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.arm-linux-musleabihf-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +a7974da5e369976ca83af6324df8337a2bc8a5ae3c2297d4ad108fac784c378a4e9bb151da220cf5a3ef003016b5a318148eef845f4203217ec6c7c7eecc192e diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-gnu-gcc4.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-gnu-gcc4.tar.gz/md5 new file mode 100644 index 0000000..b10d9d8 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-gnu-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +2db4891a429a6d2b8bfaffa1d9cd0efd diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-gnu-gcc4.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-gnu-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..334e830 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-gnu-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +5808f20823cc82d4cc07da4a2aec79762209f5f209714ff20c6347e727b4f4d8a1978cd39e6d7513984410fd8aa68ab736da1164ed8d0bee9ef49773cefb1fb9 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-gnu-gcc7.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-gnu-gcc7.tar.gz/md5 new file mode 100644 index 0000000..0326268 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-gnu-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +456292494c7f0d93edadb154cb924a34 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-gnu-gcc7.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-gnu-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..b265346 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-gnu-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +59d9373ed2bcf64949816c02efe4f297126dee577ef7e5284ef648bb7722a68fd32295bb2dc150785f078431843faec943297b7a8fb25afea9dcfb45ea5637c7 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-gnu-gcc8.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-gnu-gcc8.tar.gz/md5 new file mode 100644 index 0000000..ec291cc --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-gnu-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +d475f17eb7dc9bf0391ee372f055ea82 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-gnu-gcc8.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-gnu-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..281ed23 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-gnu-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +b66dd84a2f08a437d1ebe82642cd451bee3251c4c8120b47ec2942fbb5ba21f0772c5c34ea1d750c869913f8c144be58c47d9749d5ceb766876bbd1b51a441d6 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-musl-gcc4.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-musl-gcc4.tar.gz/md5 new file mode 100644 index 0000000..5c07fde --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-musl-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +c58f7cc55ccf7fda770ee3059853ba43 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-musl-gcc4.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-musl-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..a254244 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-musl-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +62023c8af152a93e5a287337cc923775923a164291f9e91ceb8f2982e7b4ea52069039f178ea2c017439b5891edbf6f512e64abf3de89985d47c70320c55ea35 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-musl-gcc7.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-musl-gcc7.tar.gz/md5 new file mode 100644 index 0000000..28b2d8d --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-musl-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +47dd149f3260f04ec6ca641c4accd671 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-musl-gcc7.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-musl-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..20ccf1f --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-musl-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +13ab1dcaf06a804bebb3b5be9fc7c5a7bd0d238afac2c89a9f02f8527ac738369513f3b82eee2371536a85aadd940cea29b071e603a192e573da46e1cdc85da8 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-musl-gcc8.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-musl-gcc8.tar.gz/md5 new file mode 100644 index 0000000..b06d52a --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-musl-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +d2193e3c71f7967e10cd4ee9f4a8fb20 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-musl-gcc8.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-musl-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..ced6f0b --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.i686-linux-musl-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +e155e0ed62fdd8dc48c50bc31b69df87e9ad3d1e298cbe5b18622a82407066a651a2906b104ed941c7fa8eaedd2ddfd2d08d2d4b2ca547c45c1c80d174a94f3e diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.i686-w64-mingw32-gcc4.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.i686-w64-mingw32-gcc4.tar.gz/md5 new file mode 100644 index 0000000..3009b72 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.i686-w64-mingw32-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +c0a8ad36a88378cd5af4c3f0e3e702e1 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.i686-w64-mingw32-gcc4.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.i686-w64-mingw32-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..f3d575f --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.i686-w64-mingw32-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +e1914f96b3bd4027c36001d14b1d1e588d4c36fe5342e265329bb687678f5a5e9d55f11862d8ae609c0eed64cd5b6dd7b6fbeb10a2610fed9674313a0b49e574 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.i686-w64-mingw32-gcc7.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.i686-w64-mingw32-gcc7.tar.gz/md5 new file mode 100644 index 0000000..02696d2 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.i686-w64-mingw32-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +832e3888e5e3e5e88e587b8d9ce6854b diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.i686-w64-mingw32-gcc7.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.i686-w64-mingw32-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..443008e --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.i686-w64-mingw32-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +49a6774dcf281ea1e0bb518b3e50ff1c5cdc357f5a6de38e20e3d7d6bd01067ef8cc192137c063d9dd2cfd955324cd51fd4c3045cd775747f112374832bda231 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.i686-w64-mingw32-gcc8.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.i686-w64-mingw32-gcc8.tar.gz/md5 new file mode 100644 index 0000000..37f9efd --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.i686-w64-mingw32-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +0ebae94bc5e4acb92be1bebbf8ead972 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.i686-w64-mingw32-gcc8.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.i686-w64-mingw32-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..dd4c3b4 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.i686-w64-mingw32-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +4938e865cab6304e27e33bf30b4c45c55e2416f4ad8dc2218f635136282f8be05c44c1413c027433e933bbac2c350255871cb57550839e6e06ae4bcac0bccb7f diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.powerpc64le-linux-gnu-gcc4.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.powerpc64le-linux-gnu-gcc4.tar.gz/md5 new file mode 100644 index 0000000..5a6da73 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.powerpc64le-linux-gnu-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +5e1ecdb356352fad2bdf0998ce0a11a5 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.powerpc64le-linux-gnu-gcc4.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.powerpc64le-linux-gnu-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..38842db --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.powerpc64le-linux-gnu-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +ccbb6d78e05d3536247d734320e74112c93e7461a7f93b2a19dd2537bf6b181405190100ec7244cb56e79f72cc5639e0dd297b67fb97400fe528b1f5ef2e189c diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.powerpc64le-linux-gnu-gcc7.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.powerpc64le-linux-gnu-gcc7.tar.gz/md5 new file mode 100644 index 0000000..1bc1cf3 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.powerpc64le-linux-gnu-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +16ab7487eb5bad9ab569ac892bb94ef7 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.powerpc64le-linux-gnu-gcc7.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.powerpc64le-linux-gnu-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..e171a57 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.powerpc64le-linux-gnu-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +346c2a570ef2b007b4218f70192a257ad784065a28bdb684c6bd1957c138920a223d54278ba508d1707e5356c58e3eabb930f4ec1e408974c9e251a43b793c37 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.powerpc64le-linux-gnu-gcc8.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.powerpc64le-linux-gnu-gcc8.tar.gz/md5 new file mode 100644 index 0000000..73eab94 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.powerpc64le-linux-gnu-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +c572e9f037f1de515c354b2049718e69 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.powerpc64le-linux-gnu-gcc8.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.powerpc64le-linux-gnu-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..aa37f97 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.powerpc64le-linux-gnu-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +d591b7228376497b93032cefc8763e8017db0247c33ed4651ee6706bbc8bca8fd8dd2dcb66622cda4a7bf853dfcf62421655fc4b13c3ff1697e047775ac1d830 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-apple-darwin14-gcc4.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-apple-darwin14-gcc4.tar.gz/md5 new file mode 100644 index 0000000..af62125 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-apple-darwin14-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +d58a378917e0660b61874ee7153af90b diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-apple-darwin14-gcc4.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-apple-darwin14-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..d0432c6 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-apple-darwin14-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +4667a9aaab3abe48d2b4cfb355606d9101a51153c8cd039a37bec50825b7544ad9c0bf8a77c06f832b3d9538b02e8bbf5070f18fa554405f762757c4cc218bc5 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-apple-darwin14-gcc7.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-apple-darwin14-gcc7.tar.gz/md5 new file mode 100644 index 0000000..d7abc72 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-apple-darwin14-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +55cc3aee91a1870d66e07ae5ae89a33d diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-apple-darwin14-gcc7.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-apple-darwin14-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..dffca08 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-apple-darwin14-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +6b870b963954f15112f5259ef92e97eb494edf529cc79ed35db0a71a73e80d5f83c99c10f5d4030517be200f702a1af5c30afd51e7dbcfae4961a1738115d5d6 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-apple-darwin14-gcc8.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-apple-darwin14-gcc8.tar.gz/md5 new file mode 100644 index 0000000..92488a3 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-apple-darwin14-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +eacc60bd53a2836eacf25e3da66b515d diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-apple-darwin14-gcc8.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-apple-darwin14-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..9fe179e --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-apple-darwin14-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +f3c0c61debd1a61dbacfc1c1c75cfe9ad4cad66bd6cbed5df6c9216a09aa9e07c6285993b4cda2aea29d6321ca18689963bebe9d0676d55f7019d75678475ff6 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-gnu-gcc4.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-gnu-gcc4.tar.gz/md5 new file mode 100644 index 0000000..e528ac3 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-gnu-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +ce3cbb0c43d1ba9aee2f530638dba41b diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-gnu-gcc4.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-gnu-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..cb8a8a8 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-gnu-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +d04d962704c670eb887bd0be1c059677337056d238690df816591fb30a23118d0179ef52eecd99d37a3f63f0fc7ba46df2d5fe72a39bf33657f8f87bc2b47d56 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-gnu-gcc7.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-gnu-gcc7.tar.gz/md5 new file mode 100644 index 0000000..435dee5 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-gnu-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +370ccfbf711b302c4422ebb7ec8a1a23 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-gnu-gcc7.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-gnu-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..597c671 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-gnu-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +53fb0cbdc145d655c41d9d6c5209f0dfb5883691ca0277ae2b29272703e6d56e48dc4a51a0f54142944dbfa94903c2c01463bfbf01dda3902c20e0fb9f9b4639 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-gnu-gcc8.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-gnu-gcc8.tar.gz/md5 new file mode 100644 index 0000000..eeee251 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-gnu-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +86758655145158b9894f6165f19834fa diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-gnu-gcc8.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-gnu-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..19212df --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-gnu-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +8e704caa6d5023469aa453c6c5704465a81c4c9ee5b5679e0aa43e331bd45d1f2a921548b7cd2d080fa25e6e3035470f098347a602e7327b7ab6f943aa971c2b diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-musl-gcc4.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-musl-gcc4.tar.gz/md5 new file mode 100644 index 0000000..e883f5b --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-musl-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +fc56c9c3ce78494fc2cfddfc3b40a4a8 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-musl-gcc4.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-musl-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..39a9566 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-musl-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +3bbf6176c7ebfa7098fa2b30ff9a3f8cd45f5e0d394e35240695ccac6644686e53e04b69e2d464c3f1eec12a813be7e5c7c52345241dae59e52d5b0f1d2f73c5 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-musl-gcc7.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-musl-gcc7.tar.gz/md5 new file mode 100644 index 0000000..ab54e9a --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-musl-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +e3b1e1d8af656cde81d2befeefd4972d diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-musl-gcc7.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-musl-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..57cef93 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-musl-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +b9823d1655c3014c38405ac5d4348e57fbabdfc0299c05c9f7af3145e64dda32b57218bd2b786e0293d997cef56b9b3a83ae7ed4423ffd7bb4a32b2a6ea11bbe diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-musl-gcc8.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-musl-gcc8.tar.gz/md5 new file mode 100644 index 0000000..a4e9d4e --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-musl-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +4b612c7a888ad37d25d0dae69f2a7673 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-musl-gcc8.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-musl-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..4f4e998 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-linux-musl-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +54a1097aecde4235741bd3207d93d11b34521a0d66b5c6a59a03394812be11b96c0b5ffeb80673b325b7a61baeca6652ebf969c371def567decb192290305ac2 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-unknown-freebsd11.1-gcc4.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-unknown-freebsd11.1-gcc4.tar.gz/md5 new file mode 100644 index 0000000..56975e1 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-unknown-freebsd11.1-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +2fa695b19c357c9086f8c46a7c1bae77 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-unknown-freebsd11.1-gcc4.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-unknown-freebsd11.1-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..d0b8228 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-unknown-freebsd11.1-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +5efed959f24fca19b7e1697b9e5510e3aeb4c5ce9a36d79217c9673c715f1b7d93dfb2b051708556430aff483549e8e89a002e8115815bd08836aa22e421f328 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-unknown-freebsd11.1-gcc7.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-unknown-freebsd11.1-gcc7.tar.gz/md5 new file mode 100644 index 0000000..77af8ef --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-unknown-freebsd11.1-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +9b7299fc0c230990601c70b0dfc8ba1c diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-unknown-freebsd11.1-gcc7.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-unknown-freebsd11.1-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..8a75cec --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-unknown-freebsd11.1-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +6de8ca419ea11e4b55273dc23ee3581e76b1c3459d7f5159a61efe0c485520e6d3e604eb937c5e477e83dbe52d734a462d37dc0d4385aac151f3120d0b0564d1 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-unknown-freebsd11.1-gcc8.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-unknown-freebsd11.1-gcc8.tar.gz/md5 new file mode 100644 index 0000000..b6ed567 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-unknown-freebsd11.1-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +dd2b5517ff2d34dbdbe37b97faa0ee75 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-unknown-freebsd11.1-gcc8.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-unknown-freebsd11.1-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..fb62348 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-unknown-freebsd11.1-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +4a9a1cbedf1e1a29d246a7c800916ad822b450bd8064d45d1f7850e3637600eb073454e4943bb92d7096d44e945bb0d4294d7bc733d6a415afcb7135dc42fd08 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-w64-mingw32-gcc4.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-w64-mingw32-gcc4.tar.gz/md5 new file mode 100644 index 0000000..1c68ae4 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-w64-mingw32-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +bdbf2315d86856cde07b34ebeeb48fd5 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-w64-mingw32-gcc4.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-w64-mingw32-gcc4.tar.gz/sha512 new file mode 100644 index 0000000..29b28b1 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-w64-mingw32-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +67a83ca4ce9a4c4e39c2d1146fd44b0af7b7ab5abbfaf1519de4c9037c473163c5f76ac9f19178b4e73072ec06d38adee8ccc008a564661033ced05540eeab80 diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-w64-mingw32-gcc7.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-w64-mingw32-gcc7.tar.gz/md5 new file mode 100644 index 0000000..9a57b45 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-w64-mingw32-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +3adc6ddc72b36ea3b77eddf9ee4d6cfd diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-w64-mingw32-gcc7.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-w64-mingw32-gcc7.tar.gz/sha512 new file mode 100644 index 0000000..b5f694b --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-w64-mingw32-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +71ca221448bbde73f277433ac4a51d96a58bec7ffffa19f0cf18208bd84429921abaa1a7b3b198d69bc165e3372fe15182a3f007bbc67584128d38963dd220dc diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-w64-mingw32-gcc8.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-w64-mingw32-gcc8.tar.gz/md5 new file mode 100644 index 0000000..8ba47f0 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-w64-mingw32-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +aa15f78375b270e87e51c628da2163ef diff --git a/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-w64-mingw32-gcc8.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-w64-mingw32-gcc8.tar.gz/sha512 new file mode 100644 index 0000000..20f7361 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-2.x86_64-w64-mingw32-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +f69a68890b563a0c71c6642754c0836387a24b316694022120347905f55bb9f0b6a9f2bb3235470a7ec30c452590fab7ebd0fc13ca53fe3a5a102c26c4a47371 diff --git a/deps/checksums/UnicodeData.txt/md5 b/deps/checksums/UnicodeData.txt/md5 new file mode 100644 index 0000000..e68ac81 --- /dev/null +++ b/deps/checksums/UnicodeData.txt/md5 @@ -0,0 +1 @@ +dde25b1cf9bbb4ba1140ac12e4128b0b diff --git a/deps/checksums/UnicodeData.txt/sha512 b/deps/checksums/UnicodeData.txt/sha512 new file mode 100644 index 0000000..5832a6b --- /dev/null +++ b/deps/checksums/UnicodeData.txt/sha512 @@ -0,0 +1 @@ +43eaf66d9cb3748012b2dfd77da1b41f667c5c7602a56bea8186b796b215bde82d555d79ab053378c2222521396354dcce5cf23a78fa3b1456062c47771c8433 diff --git a/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-gnu.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..00da64e --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +9f518cd093311ea2c8ff624b06aaa4cd diff --git a/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-gnu.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..e8006fe --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +8ca001e4cdc0de7c3224b92b6e454102c511225757b1c54f32734c65eb11cd57cb2fffc50b7e4d12c9b9cb243bdf87f3b215a2fa3ce538336d1852820b241ebf diff --git a/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-musl.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..216075e --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +5157dfaf0a62241553783d38d6fa565c diff --git a/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-musl.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..631e470 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +30199abf69bcee900087ae548de0303ae3ac5fa1fba24436ef406ad81675407eefb7157922e4701b2297d139eb8b4e3630a92c01b309de6b0205a98d60f2d63b diff --git a/deps/checksums/Zlib.v1.2.11-6.arm-linux-gnueabihf.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.arm-linux-gnueabihf.tar.gz/md5 new file mode 100644 index 0000000..4485a55 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.arm-linux-gnueabihf.tar.gz/md5 @@ -0,0 +1 @@ +2d42d6a3899b974f9c56c4fabfa34459 diff --git a/deps/checksums/Zlib.v1.2.11-6.arm-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.arm-linux-gnueabihf.tar.gz/sha512 new file mode 100644 index 0000000..31a1f61 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.arm-linux-gnueabihf.tar.gz/sha512 @@ -0,0 +1 @@ +138254be838d7a9e356632785e5af30a562b498605e366e932011d9a41778e5a81c9073aa698529f95aa1101f2a7642e7ae81449f734d5e7bf0e3b4f26fedcae diff --git a/deps/checksums/Zlib.v1.2.11-6.arm-linux-musleabihf.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.arm-linux-musleabihf.tar.gz/md5 new file mode 100644 index 0000000..4dbcf69 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.arm-linux-musleabihf.tar.gz/md5 @@ -0,0 +1 @@ +5db5f13979777caac31c830b66e75e8f diff --git a/deps/checksums/Zlib.v1.2.11-6.arm-linux-musleabihf.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.arm-linux-musleabihf.tar.gz/sha512 new file mode 100644 index 0000000..c47463b --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.arm-linux-musleabihf.tar.gz/sha512 @@ -0,0 +1 @@ +6088b1a14ff372a2b7d28187edcfe85b613242b812ae23cbaea88da1e580105f38f65f70b2e80833711e2e99ac1683bdf451d55e5dc9283bb7e8dd582680acfb diff --git a/deps/checksums/Zlib.v1.2.11-6.i686-linux-gnu.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.i686-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..b5d6d15 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.i686-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +46d03f5f9190cb787e867c3eb7edea78 diff --git a/deps/checksums/Zlib.v1.2.11-6.i686-linux-gnu.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.i686-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..0087950 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.i686-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +ca08a089148260c2b6d81a87a1e00a4e3a881c6d8bdad0d00b3e42a56cc91a7b72b542a748fc01a49ce207785053636587417916c6ec0d112fae49c55f34fb65 diff --git a/deps/checksums/Zlib.v1.2.11-6.i686-linux-musl.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.i686-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..811bce7 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.i686-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +34725ad0598db3cd7817e3578453f1d8 diff --git a/deps/checksums/Zlib.v1.2.11-6.i686-linux-musl.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.i686-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..1936e8f --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.i686-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +b2706f68cecc21ab5013b07c226eda8fd1a68101bd17fcd79b991ac3b74d9b9e63e54f804a48a86ea448c5695c3284ac11c07592ae76a10b4fd6f5881c3ee277 diff --git a/deps/checksums/Zlib.v1.2.11-6.i686-w64-mingw32.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.i686-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..2074ae5 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.i686-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +89611cfd8f7c987de8b8c8cfd9eaeae6 diff --git a/deps/checksums/Zlib.v1.2.11-6.i686-w64-mingw32.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.i686-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..482a799 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.i686-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +128f0a2db6045c7e886dc283c93a3bff3f7bd64d2b2bcea0e4e67aac6c8e76ef9abcdac10cc56f5880ae32160ffe5ee4b42fdc9bc1fd3e07104776e16dc05aa0 diff --git a/deps/checksums/Zlib.v1.2.11-6.powerpc64le-linux-gnu.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.powerpc64le-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..e7b88b4 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.powerpc64le-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +1c32a314230b46db6eb02e72bd8b8b47 diff --git a/deps/checksums/Zlib.v1.2.11-6.powerpc64le-linux-gnu.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.powerpc64le-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..9be88af --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.powerpc64le-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +69e785a356eec5c12cb4fe2d3b2a5b6ced16152d51e28c6e4eb646cad07a951d73cfcfd015c233f0164ed28d9ad772403230dcee581526fa4d34fefb95c2bb13 diff --git a/deps/checksums/Zlib.v1.2.11-6.x86_64-apple-darwin14.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.x86_64-apple-darwin14.tar.gz/md5 new file mode 100644 index 0000000..5316272 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.x86_64-apple-darwin14.tar.gz/md5 @@ -0,0 +1 @@ +0e14dd815045e47dd722ff55434428d1 diff --git a/deps/checksums/Zlib.v1.2.11-6.x86_64-apple-darwin14.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.x86_64-apple-darwin14.tar.gz/sha512 new file mode 100644 index 0000000..7b21de4 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.x86_64-apple-darwin14.tar.gz/sha512 @@ -0,0 +1 @@ +4111e99386cab30de2f1c625079991576e97446c8a85bc37c05629ddf7b867bc32a74b94ed5552c2b057c4cf14f85cbc06b62459ed800cd9faa5a40382a6e958 diff --git a/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-gnu.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..77123e3 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +9fcc23ef8cf1740418a147905f7aa45c diff --git a/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-gnu.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..089693b --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +2da223cb96bcd1845c8b84b44f779c8d464fd8dc21a8c8d7b065da07e7d995db627b9a2df1dc5247f70d1cd9e23f31c118288a0b3047884b19edd886e1dd6f2b diff --git a/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-musl.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..5380815 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +b3e690fe03ab5f57118ae2cff0e82f35 diff --git a/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-musl.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..74786b8 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +cea679067b995a5503922e81c50dda603b0b9634bb07b54073f74e2d0ccd638ec6c10ab26ae09c5a3566c44d4ee78419abc99f22da55c8131901aa72f790cc5c diff --git a/deps/checksums/Zlib.v1.2.11-6.x86_64-unknown-freebsd11.1.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.x86_64-unknown-freebsd11.1.tar.gz/md5 new file mode 100644 index 0000000..85a8c96 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.x86_64-unknown-freebsd11.1.tar.gz/md5 @@ -0,0 +1 @@ +34a32be8fed7b26b3cef2f7c367241b8 diff --git a/deps/checksums/Zlib.v1.2.11-6.x86_64-unknown-freebsd11.1.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.x86_64-unknown-freebsd11.1.tar.gz/sha512 new file mode 100644 index 0000000..5b03347 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.x86_64-unknown-freebsd11.1.tar.gz/sha512 @@ -0,0 +1 @@ +ba351d5415c86457b487e0078b8a86f2429e8c2aef8f3edb48647de8a2b7d05accf9582e8b58f49b46a67684b59c1bfa9ebda6ec93724747128c93f9efd69fa1 diff --git a/deps/checksums/Zlib.v1.2.11-6.x86_64-w64-mingw32.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.x86_64-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..0110426 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.x86_64-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +66292bfea89453db4945639d454fa739 diff --git a/deps/checksums/Zlib.v1.2.11-6.x86_64-w64-mingw32.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.x86_64-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..a9cdbe9 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.x86_64-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +2f2ec74977e5a713c2336a0ef5f2553ec5393285ae3a6914ab8d9b535337ad36d9f07fc180df10db302d96dc6b9ca2d7d178a9f42b906276b97b597953dc4782 diff --git a/deps/checksums/arpack-ng-3.3.0-testA.mtx/md5 b/deps/checksums/arpack-ng-3.3.0-testA.mtx/md5 new file mode 100644 index 0000000..8e47827 --- /dev/null +++ b/deps/checksums/arpack-ng-3.3.0-testA.mtx/md5 @@ -0,0 +1 @@ +2826846e98bcb009d339fb69973951d3 diff --git a/deps/checksums/arpack-ng-3.3.0-testA.mtx/sha512 b/deps/checksums/arpack-ng-3.3.0-testA.mtx/sha512 new file mode 100644 index 0000000..70eec33 --- /dev/null +++ b/deps/checksums/arpack-ng-3.3.0-testA.mtx/sha512 @@ -0,0 +1 @@ +00af7f2353441c4197c52d105d3670fe250a312b8e67ae2794246f2ce8cd0b63585e5c5ab764921d357efd9ad685fcc0ee5b8b8ee7ab9af2bea26ccbb97c50ba diff --git a/deps/checksums/arpack-ng-3.3.0.tar.gz/md5 b/deps/checksums/arpack-ng-3.3.0.tar.gz/md5 new file mode 100644 index 0000000..81b2c9e --- /dev/null +++ b/deps/checksums/arpack-ng-3.3.0.tar.gz/md5 @@ -0,0 +1 @@ +343db685ebedbb9690e4ea5f9d62c19a diff --git a/deps/checksums/arpack-ng-3.3.0.tar.gz/sha512 b/deps/checksums/arpack-ng-3.3.0.tar.gz/sha512 new file mode 100644 index 0000000..fd2a13c --- /dev/null +++ b/deps/checksums/arpack-ng-3.3.0.tar.gz/sha512 @@ -0,0 +1 @@ +8d0e526831d62200af9d939e77e41548875caacdf39543c3f33a00321bb18fc04ffa625d9c4103f84c1f6a03a3c362dc1df8571db9a525d46487e5d1ae682474 diff --git a/deps/checksums/cacert-2019-10-16.pem/md5 b/deps/checksums/cacert-2019-10-16.pem/md5 new file mode 100644 index 0000000..5286d34 --- /dev/null +++ b/deps/checksums/cacert-2019-10-16.pem/md5 @@ -0,0 +1 @@ +5805059ab9e4646e4803ce1e007eb8ba diff --git a/deps/checksums/cacert-2019-10-16.pem/sha512 b/deps/checksums/cacert-2019-10-16.pem/sha512 new file mode 100644 index 0000000..e017d0f --- /dev/null +++ b/deps/checksums/cacert-2019-10-16.pem/sha512 @@ -0,0 +1 @@ +49778472e46ce3b86b3930f4df5731ac86daf4d8602d418af1c89dc35df5f98c4557aa6c6eb280558c61139ead4b96cbb457a259f72640452f28a2fecd4ccb89 diff --git a/deps/checksums/cfe-6.0.0.src.tar.xz/md5 b/deps/checksums/cfe-6.0.0.src.tar.xz/md5 new file mode 100644 index 0000000..27d1ed0 --- /dev/null +++ b/deps/checksums/cfe-6.0.0.src.tar.xz/md5 @@ -0,0 +1 @@ +121b3896cb0c7765d690acc5d9495d24 diff --git a/deps/checksums/cfe-6.0.0.src.tar.xz/sha512 b/deps/checksums/cfe-6.0.0.src.tar.xz/sha512 new file mode 100644 index 0000000..25505f2 --- /dev/null +++ b/deps/checksums/cfe-6.0.0.src.tar.xz/sha512 @@ -0,0 +1 @@ +e886dd27448503bbfc7fd4f68eb089c19b2f2be4f0e5b26d3df253833f60b91d70b472a6b530063386e2252075b110ce9f5942800feddf6c34b94a75cf7bd5c6 diff --git a/deps/checksums/compiler-rt-6.0.0.src.tar.xz/md5 b/deps/checksums/compiler-rt-6.0.0.src.tar.xz/md5 new file mode 100644 index 0000000..54ea354 --- /dev/null +++ b/deps/checksums/compiler-rt-6.0.0.src.tar.xz/md5 @@ -0,0 +1 @@ +ba6368e894b5528e527d86a69d8533c6 diff --git a/deps/checksums/compiler-rt-6.0.0.src.tar.xz/sha512 b/deps/checksums/compiler-rt-6.0.0.src.tar.xz/sha512 new file mode 100644 index 0000000..2b5443d --- /dev/null +++ b/deps/checksums/compiler-rt-6.0.0.src.tar.xz/sha512 @@ -0,0 +1 @@ +717bed116ef43ebb2e18daf6fb737472edf57564947f53fe6368d3bbb080f63e986c0d1b94dbd087be998196ad7be54f4361854f8eb5214600b82449ba02c9c1 diff --git a/deps/checksums/curl-7.61.0.tar.bz2/md5 b/deps/checksums/curl-7.61.0.tar.bz2/md5 new file mode 100644 index 0000000..8ee98c0 --- /dev/null +++ b/deps/checksums/curl-7.61.0.tar.bz2/md5 @@ -0,0 +1 @@ +31d0a9f48dc796a7db351898a1e5058a diff --git a/deps/checksums/curl-7.61.0.tar.bz2/sha512 b/deps/checksums/curl-7.61.0.tar.bz2/sha512 new file mode 100644 index 0000000..95547b9 --- /dev/null +++ b/deps/checksums/curl-7.61.0.tar.bz2/sha512 @@ -0,0 +1 @@ +4907234c75a9e52a5b81cf895bcc811d7a69f1db84a9ae1adc3af360e8cc4371f58c00925ce6bc5170f2a8072848da47a52c41f4bfedcf14274ec75802afcddd diff --git a/deps/checksums/dSFMT.v2.2.3-0.aarch64-linux-gnu.tar.gz/md5 b/deps/checksums/dSFMT.v2.2.3-0.aarch64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..6c4c474 --- /dev/null +++ b/deps/checksums/dSFMT.v2.2.3-0.aarch64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +f6a70363ea796a63ed1aa2e0c68cca9f diff --git a/deps/checksums/dSFMT.v2.2.3-0.aarch64-linux-gnu.tar.gz/sha512 b/deps/checksums/dSFMT.v2.2.3-0.aarch64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..efa055e --- /dev/null +++ b/deps/checksums/dSFMT.v2.2.3-0.aarch64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +f4980b557041922dadb2dc89732a332dfac360471194d37e30c85518dff5072412d28213f802ed0f5d791717e3bdebba7ca0bfe11a52b2ef16e633adfe13a4a3 diff --git a/deps/checksums/dSFMT.v2.2.3-0.aarch64-linux-musl.tar.gz/md5 b/deps/checksums/dSFMT.v2.2.3-0.aarch64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..a063795 --- /dev/null +++ b/deps/checksums/dSFMT.v2.2.3-0.aarch64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +d349f16c392747610de2b1e30f5dc844 diff --git a/deps/checksums/dSFMT.v2.2.3-0.aarch64-linux-musl.tar.gz/sha512 b/deps/checksums/dSFMT.v2.2.3-0.aarch64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..fe63670 --- /dev/null +++ b/deps/checksums/dSFMT.v2.2.3-0.aarch64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +2694a265d05acb82e1be3dbe8f1ad533c83af038c3a8f184cf0a0c8088a09ebfeffae02c1821cbcacf2a34a2fff091d21a094a900ee1ab8fead8a78bd3e0a70e diff --git a/deps/checksums/dSFMT.v2.2.3-0.arm-linux-gnueabihf.tar.gz/md5 b/deps/checksums/dSFMT.v2.2.3-0.arm-linux-gnueabihf.tar.gz/md5 new file mode 100644 index 0000000..0933516 --- /dev/null +++ b/deps/checksums/dSFMT.v2.2.3-0.arm-linux-gnueabihf.tar.gz/md5 @@ -0,0 +1 @@ +10b22af1aafd1d06efcfb9bad249d99d diff --git a/deps/checksums/dSFMT.v2.2.3-0.arm-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/dSFMT.v2.2.3-0.arm-linux-gnueabihf.tar.gz/sha512 new file mode 100644 index 0000000..287054b --- /dev/null +++ b/deps/checksums/dSFMT.v2.2.3-0.arm-linux-gnueabihf.tar.gz/sha512 @@ -0,0 +1 @@ +545a8801d8e176448306ed77b482393f62f97a0e7a3e169ee08a868d5a405b5e87f4698e2915740d56587173117f0894686b59d0f81d29f3c1668a40d0526ad8 diff --git a/deps/checksums/dSFMT.v2.2.3-0.arm-linux-musleabihf.tar.gz/md5 b/deps/checksums/dSFMT.v2.2.3-0.arm-linux-musleabihf.tar.gz/md5 new file mode 100644 index 0000000..0a401a0 --- /dev/null +++ b/deps/checksums/dSFMT.v2.2.3-0.arm-linux-musleabihf.tar.gz/md5 @@ -0,0 +1 @@ +eb01530f3e4be7d2a0b7b3480e71dadd diff --git a/deps/checksums/dSFMT.v2.2.3-0.arm-linux-musleabihf.tar.gz/sha512 b/deps/checksums/dSFMT.v2.2.3-0.arm-linux-musleabihf.tar.gz/sha512 new file mode 100644 index 0000000..7a5874e --- /dev/null +++ b/deps/checksums/dSFMT.v2.2.3-0.arm-linux-musleabihf.tar.gz/sha512 @@ -0,0 +1 @@ +28cdc41f3e30efcfb1327b2ea5ce2405a0cb72238ee8722b2da925bbec4d683ded161621001f64d028268abd969d1ed655a9ec9758c9a6c1bacd96e4d9c25f13 diff --git a/deps/checksums/dSFMT.v2.2.3-0.i686-linux-gnu.tar.gz/md5 b/deps/checksums/dSFMT.v2.2.3-0.i686-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..388ef52 --- /dev/null +++ b/deps/checksums/dSFMT.v2.2.3-0.i686-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +1b78e8985d28b42ad083a35668d27ee8 diff --git a/deps/checksums/dSFMT.v2.2.3-0.i686-linux-gnu.tar.gz/sha512 b/deps/checksums/dSFMT.v2.2.3-0.i686-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..9b7802f --- /dev/null +++ b/deps/checksums/dSFMT.v2.2.3-0.i686-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +757a74dbab1c483e524085fbe12d5b961282a412b86a6c3ef4be9d5a88bd3165d761eae7c39f49b3d3069253bb3a35704f9400b0377c16b360041e7929355b87 diff --git a/deps/checksums/dSFMT.v2.2.3-0.i686-linux-musl.tar.gz/md5 b/deps/checksums/dSFMT.v2.2.3-0.i686-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..80b917f --- /dev/null +++ b/deps/checksums/dSFMT.v2.2.3-0.i686-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +52c2f2399832fbc68e1a3585b0fd2ee8 diff --git a/deps/checksums/dSFMT.v2.2.3-0.i686-linux-musl.tar.gz/sha512 b/deps/checksums/dSFMT.v2.2.3-0.i686-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..631a9bc --- /dev/null +++ b/deps/checksums/dSFMT.v2.2.3-0.i686-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +3132e3a17cc6dc9cd47c1f602ec7ab3620d526c09c0fda1c77bcf49e42671132853b6acf4e917bd327afcbbdf19ed0e8590ba5b3d560c72d7b9be465152dd4a0 diff --git a/deps/checksums/dSFMT.v2.2.3-0.i686-w64-mingw32.tar.gz/md5 b/deps/checksums/dSFMT.v2.2.3-0.i686-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..5c14478 --- /dev/null +++ b/deps/checksums/dSFMT.v2.2.3-0.i686-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +23b2ca964fcb40f2eff7140cb185d3ee diff --git a/deps/checksums/dSFMT.v2.2.3-0.i686-w64-mingw32.tar.gz/sha512 b/deps/checksums/dSFMT.v2.2.3-0.i686-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..b3abe02 --- /dev/null +++ b/deps/checksums/dSFMT.v2.2.3-0.i686-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +d1061dbe44ec777cf9303295d92eae31d80f13a5e2a79710928ae96ff6899ed4aa155037af93c369d1880e13725294276556c32c174f4e94566b7ad898c3636b diff --git a/deps/checksums/dSFMT.v2.2.3-0.powerpc64le-linux-gnu.tar.gz/md5 b/deps/checksums/dSFMT.v2.2.3-0.powerpc64le-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..d23749c --- /dev/null +++ b/deps/checksums/dSFMT.v2.2.3-0.powerpc64le-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +69ed63fb5d330b216107670224c9d751 diff --git a/deps/checksums/dSFMT.v2.2.3-0.powerpc64le-linux-gnu.tar.gz/sha512 b/deps/checksums/dSFMT.v2.2.3-0.powerpc64le-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..753e61d --- /dev/null +++ b/deps/checksums/dSFMT.v2.2.3-0.powerpc64le-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +e7668fc13899a727b54ab034383686c05c7903a90e5eef257928218f6114e2cfabb1bbcef230e4f4f36b131976fc6e36193192666ea781330de115dacd00fa29 diff --git a/deps/checksums/dSFMT.v2.2.3-0.x86_64-apple-darwin14.tar.gz/md5 b/deps/checksums/dSFMT.v2.2.3-0.x86_64-apple-darwin14.tar.gz/md5 new file mode 100644 index 0000000..fe759a7 --- /dev/null +++ b/deps/checksums/dSFMT.v2.2.3-0.x86_64-apple-darwin14.tar.gz/md5 @@ -0,0 +1 @@ +9a978d0d59b85fffdf8edec751c4ada4 diff --git a/deps/checksums/dSFMT.v2.2.3-0.x86_64-apple-darwin14.tar.gz/sha512 b/deps/checksums/dSFMT.v2.2.3-0.x86_64-apple-darwin14.tar.gz/sha512 new file mode 100644 index 0000000..527bc5a --- /dev/null +++ b/deps/checksums/dSFMT.v2.2.3-0.x86_64-apple-darwin14.tar.gz/sha512 @@ -0,0 +1 @@ +1517dca3c35d96a3dae563cbf06325a8ea68689b59ed74eaf7775e021a4e853df2f023559cb730ced48691a8688cb8214dbbb18397c5944ea838ac42a2e42984 diff --git a/deps/checksums/dSFMT.v2.2.3-0.x86_64-linux-gnu.tar.gz/md5 b/deps/checksums/dSFMT.v2.2.3-0.x86_64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..6f2b1f3 --- /dev/null +++ b/deps/checksums/dSFMT.v2.2.3-0.x86_64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +1eb95aa2ae1aa96207964f0106a76355 diff --git a/deps/checksums/dSFMT.v2.2.3-0.x86_64-linux-gnu.tar.gz/sha512 b/deps/checksums/dSFMT.v2.2.3-0.x86_64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..9e03fa4 --- /dev/null +++ b/deps/checksums/dSFMT.v2.2.3-0.x86_64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +b4230d8b5b505de81f7a44406df70ad1a7318caac461bca656fcf06251f4af5a7d063b7f139c083858d674c22bfb02c8069a752d21acf0e99d0f0e97fac6f287 diff --git a/deps/checksums/dSFMT.v2.2.3-0.x86_64-linux-musl.tar.gz/md5 b/deps/checksums/dSFMT.v2.2.3-0.x86_64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..bed0a5a --- /dev/null +++ b/deps/checksums/dSFMT.v2.2.3-0.x86_64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +33aae580ccada0715cd462299221f94b diff --git a/deps/checksums/dSFMT.v2.2.3-0.x86_64-linux-musl.tar.gz/sha512 b/deps/checksums/dSFMT.v2.2.3-0.x86_64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..5db27fe --- /dev/null +++ b/deps/checksums/dSFMT.v2.2.3-0.x86_64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +2888eb510f7fb89110357d8590b751c3a80f9ee659f202d25e233fb03531676b7c40bd754e4a57e592f74323d41265b24768621f1900908cd3825e95b7e61db8 diff --git a/deps/checksums/dSFMT.v2.2.3-0.x86_64-unknown-freebsd11.1.tar.gz/md5 b/deps/checksums/dSFMT.v2.2.3-0.x86_64-unknown-freebsd11.1.tar.gz/md5 new file mode 100644 index 0000000..2c60431 --- /dev/null +++ b/deps/checksums/dSFMT.v2.2.3-0.x86_64-unknown-freebsd11.1.tar.gz/md5 @@ -0,0 +1 @@ +6e388e1e437cfeb020b67f47201b8f66 diff --git a/deps/checksums/dSFMT.v2.2.3-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 b/deps/checksums/dSFMT.v2.2.3-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 new file mode 100644 index 0000000..6d682eb --- /dev/null +++ b/deps/checksums/dSFMT.v2.2.3-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 @@ -0,0 +1 @@ +15e9ddc274e582775fafb4ccb0b824dab44afad2c950481d4cac71fa94f335fb91f1cf94967e321a2f2ded3f10a02470ef79108f598a0948e6903dbbd5be082d diff --git a/deps/checksums/dSFMT.v2.2.3-0.x86_64-w64-mingw32.tar.gz/md5 b/deps/checksums/dSFMT.v2.2.3-0.x86_64-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..7adfb50 --- /dev/null +++ b/deps/checksums/dSFMT.v2.2.3-0.x86_64-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +731892f1ec7857ed0d199aac541c9f8a diff --git a/deps/checksums/dSFMT.v2.2.3-0.x86_64-w64-mingw32.tar.gz/sha512 b/deps/checksums/dSFMT.v2.2.3-0.x86_64-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..e04a600 --- /dev/null +++ b/deps/checksums/dSFMT.v2.2.3-0.x86_64-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +014a86aee1c76ac818cc16d2bb494b6d028321d701c238ac656ed3309b68d8105216e7574b2984cda79b293345a493fef5051da943fcf5ce5c7eeadb58c72772 diff --git a/deps/checksums/dsfmt-2.2.3.tar.gz/md5 b/deps/checksums/dsfmt-2.2.3.tar.gz/md5 new file mode 100644 index 0000000..bb1c6f1 --- /dev/null +++ b/deps/checksums/dsfmt-2.2.3.tar.gz/md5 @@ -0,0 +1 @@ +057c5a11d28296825fba584f561a4369 diff --git a/deps/checksums/dsfmt-2.2.3.tar.gz/sha512 b/deps/checksums/dsfmt-2.2.3.tar.gz/sha512 new file mode 100644 index 0000000..6914bfe --- /dev/null +++ b/deps/checksums/dsfmt-2.2.3.tar.gz/sha512 @@ -0,0 +1 @@ +6d8ddcd01aab3f9039f4f0288a8af53e290cc2f293ed8c4a8c9f3b3f12398e7eedfce8016117b425766ba89c61c86c13b3e420035b5f9bf91315b8b814c03662 diff --git a/deps/checksums/gmp-6.1.2.tar.bz2/md5 b/deps/checksums/gmp-6.1.2.tar.bz2/md5 new file mode 100644 index 0000000..afce61b --- /dev/null +++ b/deps/checksums/gmp-6.1.2.tar.bz2/md5 @@ -0,0 +1 @@ +8ddbb26dc3bd4e2302984debba1406a5 diff --git a/deps/checksums/gmp-6.1.2.tar.bz2/sha512 b/deps/checksums/gmp-6.1.2.tar.bz2/sha512 new file mode 100644 index 0000000..82e0e2b --- /dev/null +++ b/deps/checksums/gmp-6.1.2.tar.bz2/sha512 @@ -0,0 +1 @@ +268db88447174617f5746d9a6ba2b105940cc1a5e73155eb23b6eedf55f8e7724eda05d161b2de19aca9e794956d226ba9ed6f23124c7c82f7e1872e32b003cf diff --git a/deps/checksums/i686-4.9.2-release-win32-sjlj-rt_v4-rev3.7z/md5 b/deps/checksums/i686-4.9.2-release-win32-sjlj-rt_v4-rev3.7z/md5 new file mode 100644 index 0000000..e297faa --- /dev/null +++ b/deps/checksums/i686-4.9.2-release-win32-sjlj-rt_v4-rev3.7z/md5 @@ -0,0 +1 @@ +6217dae4a1016b37b12aeed1cc950187 diff --git a/deps/checksums/i686-4.9.2-release-win32-sjlj-rt_v4-rev3.7z/sha512 b/deps/checksums/i686-4.9.2-release-win32-sjlj-rt_v4-rev3.7z/sha512 new file mode 100644 index 0000000..2cc5d15 --- /dev/null +++ b/deps/checksums/i686-4.9.2-release-win32-sjlj-rt_v4-rev3.7z/sha512 @@ -0,0 +1 @@ +1f38cc758725f124552285940cde6d9e072a594298a09f7a542a6cc28cb25d119d80d73918e4e9207694819825787abb6eba4a83cee5892e5acaf6350221b97f diff --git a/deps/checksums/lapack-3.5.0.tgz/md5 b/deps/checksums/lapack-3.5.0.tgz/md5 new file mode 100644 index 0000000..a1e6c43 --- /dev/null +++ b/deps/checksums/lapack-3.5.0.tgz/md5 @@ -0,0 +1 @@ +b1d3e3e425b2e44a06760ff173104bdf diff --git a/deps/checksums/lapack-3.5.0.tgz/sha512 b/deps/checksums/lapack-3.5.0.tgz/sha512 new file mode 100644 index 0000000..de7df8c --- /dev/null +++ b/deps/checksums/lapack-3.5.0.tgz/sha512 @@ -0,0 +1 @@ +b948a0a0db032bda455ec4f519d4e89e4c29e29cecb5b6258ca61f68faaeeac9fdf4ece5c39ffcd0154c5505facbc392c7d09c8348b1d60bdd2685153ab2543f diff --git a/deps/checksums/libgit2-b3e1a56ebb2b9291e82dc027ba9cbcfc3ead54d3.tar.gz/md5 b/deps/checksums/libgit2-b3e1a56ebb2b9291e82dc027ba9cbcfc3ead54d3.tar.gz/md5 new file mode 100644 index 0000000..50d86a7 --- /dev/null +++ b/deps/checksums/libgit2-b3e1a56ebb2b9291e82dc027ba9cbcfc3ead54d3.tar.gz/md5 @@ -0,0 +1 @@ +4319ad9364f35bf2f4d2df523ff51437 diff --git a/deps/checksums/libgit2-b3e1a56ebb2b9291e82dc027ba9cbcfc3ead54d3.tar.gz/sha512 b/deps/checksums/libgit2-b3e1a56ebb2b9291e82dc027ba9cbcfc3ead54d3.tar.gz/sha512 new file mode 100644 index 0000000..1599b90 --- /dev/null +++ b/deps/checksums/libgit2-b3e1a56ebb2b9291e82dc027ba9cbcfc3ead54d3.tar.gz/sha512 @@ -0,0 +1 @@ +7384a7eeeb36ac79d4e7e1336546e48f8fc820653f887f944c3902fcaae4e59d855679b39c5279b7372995934e549e34c526e1459a82032206913f9747b3dd08 diff --git a/deps/checksums/libosxunwind-0.0.5.tar.gz/md5 b/deps/checksums/libosxunwind-0.0.5.tar.gz/md5 new file mode 100644 index 0000000..200fa07 --- /dev/null +++ b/deps/checksums/libosxunwind-0.0.5.tar.gz/md5 @@ -0,0 +1 @@ +8d59d311421f228dc941b4311e5db3f8 diff --git a/deps/checksums/libosxunwind-0.0.5.tar.gz/sha512 b/deps/checksums/libosxunwind-0.0.5.tar.gz/sha512 new file mode 100644 index 0000000..8d788b0 --- /dev/null +++ b/deps/checksums/libosxunwind-0.0.5.tar.gz/sha512 @@ -0,0 +1 @@ +db4a93732d2ea09c06be3578ac5a4e92b1662137bfc1c0047b3ba9e035b3268dca2cbf01781ce6b80566d3cad579ae84ec869a3231b6ae8bb0344c7e5e4734ab diff --git a/deps/checksums/libssh2-42d37aa63129a1b2644bf6495198923534322d64.tar.gz/md5 b/deps/checksums/libssh2-42d37aa63129a1b2644bf6495198923534322d64.tar.gz/md5 new file mode 100644 index 0000000..f50f001 --- /dev/null +++ b/deps/checksums/libssh2-42d37aa63129a1b2644bf6495198923534322d64.tar.gz/md5 @@ -0,0 +1 @@ +253c2c3eef1f5c9c54be3e677512ea28 diff --git a/deps/checksums/libssh2-42d37aa63129a1b2644bf6495198923534322d64.tar.gz/sha512 b/deps/checksums/libssh2-42d37aa63129a1b2644bf6495198923534322d64.tar.gz/sha512 new file mode 100644 index 0000000..ad3f190 --- /dev/null +++ b/deps/checksums/libssh2-42d37aa63129a1b2644bf6495198923534322d64.tar.gz/sha512 @@ -0,0 +1 @@ +1fb3202ab793024dc83a419bc7138c4bce9aa37f1bd6aa199388df9a7d78f3838c1fba38b5a82fe2b912f2cf742f476f0c8f2e30a253d1313317d844ac315606 diff --git a/deps/checksums/libunwind-1.3.1.tar.gz/md5 b/deps/checksums/libunwind-1.3.1.tar.gz/md5 new file mode 100644 index 0000000..c42ea47 --- /dev/null +++ b/deps/checksums/libunwind-1.3.1.tar.gz/md5 @@ -0,0 +1 @@ +a04f69d66d8e16f8bf3ab72a69112cd6 diff --git a/deps/checksums/libunwind-1.3.1.tar.gz/sha512 b/deps/checksums/libunwind-1.3.1.tar.gz/sha512 new file mode 100644 index 0000000..a168f65 --- /dev/null +++ b/deps/checksums/libunwind-1.3.1.tar.gz/sha512 @@ -0,0 +1 @@ +3110d0aed4f5c781ef1ff72c9337e59793c02c42066209a4ac44f50eff1c0b0e02a5ff9f66891e62016de14af065a47975763970b839b700c0ff2e9f415c8def diff --git a/deps/checksums/libuv-35b1504507a7a4168caae3d78db54d1121b121e1.tar.gz/md5 b/deps/checksums/libuv-35b1504507a7a4168caae3d78db54d1121b121e1.tar.gz/md5 new file mode 100644 index 0000000..e53ddf1 --- /dev/null +++ b/deps/checksums/libuv-35b1504507a7a4168caae3d78db54d1121b121e1.tar.gz/md5 @@ -0,0 +1 @@ +679a3ee8272f92fa06ca3150f7d30eeb diff --git a/deps/checksums/libuv-35b1504507a7a4168caae3d78db54d1121b121e1.tar.gz/sha512 b/deps/checksums/libuv-35b1504507a7a4168caae3d78db54d1121b121e1.tar.gz/sha512 new file mode 100644 index 0000000..87afaf5 --- /dev/null +++ b/deps/checksums/libuv-35b1504507a7a4168caae3d78db54d1121b121e1.tar.gz/sha512 @@ -0,0 +1 @@ +5932e9105346e952fd0f51f79898e5b9c993261ec1558408437647ab3b3ce282562f1e0a16db5704cb66e7aa7a4c6a75dd7f0da498a98b1b6af48da88b4869f0 diff --git a/deps/checksums/libwhich-81e9723c0273d78493dc8c8ed570f68d9ce7e89e.tar.gz/md5 b/deps/checksums/libwhich-81e9723c0273d78493dc8c8ed570f68d9ce7e89e.tar.gz/md5 new file mode 100644 index 0000000..817a55b --- /dev/null +++ b/deps/checksums/libwhich-81e9723c0273d78493dc8c8ed570f68d9ce7e89e.tar.gz/md5 @@ -0,0 +1 @@ +22fd8368c7b40209dada50e3205c1294 diff --git a/deps/checksums/libwhich-81e9723c0273d78493dc8c8ed570f68d9ce7e89e.tar.gz/sha512 b/deps/checksums/libwhich-81e9723c0273d78493dc8c8ed570f68d9ce7e89e.tar.gz/sha512 new file mode 100644 index 0000000..9b9ae68 --- /dev/null +++ b/deps/checksums/libwhich-81e9723c0273d78493dc8c8ed570f68d9ce7e89e.tar.gz/sha512 @@ -0,0 +1 @@ +6fb77b715d70d9bc95a8546c3bf97bd3677c7ea344b88bb5bc3bbfac9dceabe8a8cde7a0f64dec884cde802e4a3000e30837d3f824b5a9242348c4fe061526a3 diff --git a/deps/checksums/llvm-6.0.1.src.tar.xz/md5 b/deps/checksums/llvm-6.0.1.src.tar.xz/md5 new file mode 100644 index 0000000..0c7151e --- /dev/null +++ b/deps/checksums/llvm-6.0.1.src.tar.xz/md5 @@ -0,0 +1 @@ +c88c98709300ce2c285391f387fecce0 diff --git a/deps/checksums/llvm-6.0.1.src.tar.xz/sha512 b/deps/checksums/llvm-6.0.1.src.tar.xz/sha512 new file mode 100644 index 0000000..2e0ff6e --- /dev/null +++ b/deps/checksums/llvm-6.0.1.src.tar.xz/sha512 @@ -0,0 +1 @@ +cbbb00eb99cfeb4aff623ee1a5ba075e7b5a76fc00c5f9f539ff28c108598f5708a0369d5bd92683def5a20c2fe60cab7827b42d628dbfcc79b57e0e91b84dd9 diff --git a/deps/checksums/llvm-7.0.1.src.tar.xz/md5 b/deps/checksums/llvm-7.0.1.src.tar.xz/md5 new file mode 100644 index 0000000..e934f0c --- /dev/null +++ b/deps/checksums/llvm-7.0.1.src.tar.xz/md5 @@ -0,0 +1 @@ +79f1256f97d52a054da8660706deb5f6 diff --git a/deps/checksums/llvm-7.0.1.src.tar.xz/sha512 b/deps/checksums/llvm-7.0.1.src.tar.xz/sha512 new file mode 100644 index 0000000..f6f980b --- /dev/null +++ b/deps/checksums/llvm-7.0.1.src.tar.xz/sha512 @@ -0,0 +1 @@ +ac43a3cb71a53deb55e3693653847cf20bf6f5d9056f224e6956c96d63bc59ebee9404f088eec9cabe65337b4607a905ef931354b373cf64e0004c6905a6b5df diff --git a/deps/checksums/llvm-8.0.0.src.tar.xz/md5 b/deps/checksums/llvm-8.0.0.src.tar.xz/md5 new file mode 100644 index 0000000..61146bb --- /dev/null +++ b/deps/checksums/llvm-8.0.0.src.tar.xz/md5 @@ -0,0 +1 @@ +74818f431563603515a62be1ee69a142 diff --git a/deps/checksums/llvm-8.0.0.src.tar.xz/sha512 b/deps/checksums/llvm-8.0.0.src.tar.xz/sha512 new file mode 100644 index 0000000..bda5127 --- /dev/null +++ b/deps/checksums/llvm-8.0.0.src.tar.xz/sha512 @@ -0,0 +1 @@ +1602343b451b964f5d8c2d6b0654d89384c80d45883498c5f0e2f4196168dd4a1ed2a4dadb752076020243df42ffe46cb31d82ffc145d8e5874163cbb9686a1f diff --git a/deps/checksums/mbedtls-2.16.0-apache.tgz/md5 b/deps/checksums/mbedtls-2.16.0-apache.tgz/md5 new file mode 100644 index 0000000..fd5553e --- /dev/null +++ b/deps/checksums/mbedtls-2.16.0-apache.tgz/md5 @@ -0,0 +1 @@ +eae9cf16114f4491dbbc0fe36bb7e6c3 diff --git a/deps/checksums/mbedtls-2.16.0-apache.tgz/sha512 b/deps/checksums/mbedtls-2.16.0-apache.tgz/sha512 new file mode 100644 index 0000000..6e4c5a7 --- /dev/null +++ b/deps/checksums/mbedtls-2.16.0-apache.tgz/sha512 @@ -0,0 +1 @@ +765303bc0572b904e05990b254ed160eb18e300a1bbbb4aa5176e5cdbf5464a944d5ad06b712183e2396a48f0c7ae529e0c820303c3995f7d6b9c3f24535d004 diff --git a/deps/checksums/mbedtls-2.16.0-gpl.tgz/md5 b/deps/checksums/mbedtls-2.16.0-gpl.tgz/md5 new file mode 100644 index 0000000..a8999db --- /dev/null +++ b/deps/checksums/mbedtls-2.16.0-gpl.tgz/md5 @@ -0,0 +1 @@ +899b8385c0b47fb3e0e52baca16c93c8 diff --git a/deps/checksums/mbedtls-2.16.0-gpl.tgz/sha512 b/deps/checksums/mbedtls-2.16.0-gpl.tgz/sha512 new file mode 100644 index 0000000..1306601 --- /dev/null +++ b/deps/checksums/mbedtls-2.16.0-gpl.tgz/sha512 @@ -0,0 +1 @@ +6bd72a3c08d2b6bb15d7eae33d36f07ac5ee6089ddd9e26d89ae373539a1e15462d5d06d01dd9e6d8872aa03b38ab20acc90d70bceede01253a58476e76b0e20 diff --git a/deps/checksums/mpfr-4.0.2.tar.bz2/md5 b/deps/checksums/mpfr-4.0.2.tar.bz2/md5 new file mode 100644 index 0000000..8639e86 --- /dev/null +++ b/deps/checksums/mpfr-4.0.2.tar.bz2/md5 @@ -0,0 +1 @@ +6d8a8bb46fe09ff44e21cdbf84f5cdac diff --git a/deps/checksums/mpfr-4.0.2.tar.bz2/sha512 b/deps/checksums/mpfr-4.0.2.tar.bz2/sha512 new file mode 100644 index 0000000..d073360 --- /dev/null +++ b/deps/checksums/mpfr-4.0.2.tar.bz2/sha512 @@ -0,0 +1 @@ +18bb3a87123d02b7537bc298d41bdbb33e58b8c196cc4040578e3b470e86c6c89e1bd8ab8b3919d106fe5b86922ef8999dc1aba7c521ee90a69f690be288a30d diff --git a/deps/checksums/nsis-3.04-setup.exe/md5 b/deps/checksums/nsis-3.04-setup.exe/md5 new file mode 100644 index 0000000..dd2ee55 --- /dev/null +++ b/deps/checksums/nsis-3.04-setup.exe/md5 @@ -0,0 +1 @@ +3c93427c56714478bb5a7a4bbaab934f diff --git a/deps/checksums/nsis-3.04-setup.exe/sha512 b/deps/checksums/nsis-3.04-setup.exe/sha512 new file mode 100644 index 0000000..4216a8c --- /dev/null +++ b/deps/checksums/nsis-3.04-setup.exe/sha512 @@ -0,0 +1 @@ +55142f5eeada65f18f6de950ead5342d78e1a4a01b7620d02dae2fbd3f0d963db482f5a63939131e81a78b458c5306f600578ab96891b0661cb0b74be1f7636d diff --git a/deps/checksums/openblas-eebc18928715775c9ed254684edee16e4efe0342.tar.gz/md5 b/deps/checksums/openblas-eebc18928715775c9ed254684edee16e4efe0342.tar.gz/md5 new file mode 100644 index 0000000..b45937f --- /dev/null +++ b/deps/checksums/openblas-eebc18928715775c9ed254684edee16e4efe0342.tar.gz/md5 @@ -0,0 +1 @@ +26116d324e3601b370a696f331621a84 diff --git a/deps/checksums/openblas-eebc18928715775c9ed254684edee16e4efe0342.tar.gz/sha512 b/deps/checksums/openblas-eebc18928715775c9ed254684edee16e4efe0342.tar.gz/sha512 new file mode 100644 index 0000000..3db2b67 --- /dev/null +++ b/deps/checksums/openblas-eebc18928715775c9ed254684edee16e4efe0342.tar.gz/sha512 @@ -0,0 +1 @@ +15520c3eca79b002f1afbee64257afa24705dec667a89bd46acfcdae426e29342816fb586db98ff256fcc9d0fd744b14b347fed23995958e7646a5e3e7310cf4 diff --git a/deps/checksums/openlibm-ce69bf1f32d3e2e9791da36c9e33ba38670d5576.tar.gz/md5 b/deps/checksums/openlibm-ce69bf1f32d3e2e9791da36c9e33ba38670d5576.tar.gz/md5 new file mode 100644 index 0000000..dcfe80f --- /dev/null +++ b/deps/checksums/openlibm-ce69bf1f32d3e2e9791da36c9e33ba38670d5576.tar.gz/md5 @@ -0,0 +1 @@ +ae65adb8753163eb8afa841abde42605 diff --git a/deps/checksums/openlibm-ce69bf1f32d3e2e9791da36c9e33ba38670d5576.tar.gz/sha512 b/deps/checksums/openlibm-ce69bf1f32d3e2e9791da36c9e33ba38670d5576.tar.gz/sha512 new file mode 100644 index 0000000..ec8fdb1 --- /dev/null +++ b/deps/checksums/openlibm-ce69bf1f32d3e2e9791da36c9e33ba38670d5576.tar.gz/sha512 @@ -0,0 +1 @@ +2d5ccd975c9f5c5fd8a03f1d84d5120a025c09de9c03b178dada15ed3f853a7f1e526a5c0f167f1dda9dd7801e44a826ae79135fe8472fda1f7485c50e634ddd diff --git a/deps/checksums/openspecfun-39699a1c1824bf88410cabb8a7438af91ea98f4c.tar.gz/md5 b/deps/checksums/openspecfun-39699a1c1824bf88410cabb8a7438af91ea98f4c.tar.gz/md5 new file mode 100644 index 0000000..c667bb8 --- /dev/null +++ b/deps/checksums/openspecfun-39699a1c1824bf88410cabb8a7438af91ea98f4c.tar.gz/md5 @@ -0,0 +1 @@ +00130a0879f691240df5f3ea25e6f0ba diff --git a/deps/checksums/openspecfun-39699a1c1824bf88410cabb8a7438af91ea98f4c.tar.gz/sha512 b/deps/checksums/openspecfun-39699a1c1824bf88410cabb8a7438af91ea98f4c.tar.gz/sha512 new file mode 100644 index 0000000..6848380 --- /dev/null +++ b/deps/checksums/openspecfun-39699a1c1824bf88410cabb8a7438af91ea98f4c.tar.gz/sha512 @@ -0,0 +1 @@ +8681fc2f31696de2a6850cb4318c14ad39d3514742b066cf16058da87966d08e0d197fbb5104229fa19b61819a12ae5c76ea0279749f4338923cd4741ba31132 diff --git a/deps/checksums/p7zip-16.2.0.tar.bz2/md5 b/deps/checksums/p7zip-16.2.0.tar.bz2/md5 new file mode 100644 index 0000000..145a487 --- /dev/null +++ b/deps/checksums/p7zip-16.2.0.tar.bz2/md5 @@ -0,0 +1 @@ +a0128d661cfe7cc8c121e73519c54fbf diff --git a/deps/checksums/p7zip-16.2.0.tar.bz2/sha512 b/deps/checksums/p7zip-16.2.0.tar.bz2/sha512 new file mode 100644 index 0000000..45e4603 --- /dev/null +++ b/deps/checksums/p7zip-16.2.0.tar.bz2/sha512 @@ -0,0 +1 @@ +d2c4d53817f96bb4c7683f42045198d4cd509cfc9c3e2cb85c8d9dc4ab6dfa7496449edeac4e300ecf986a9cbbc90bd8f8feef8156895d94617c04e507add55f diff --git a/deps/checksums/p7zip.v16.2.0-1.aarch64-linux-gnu.tar.gz/md5 b/deps/checksums/p7zip.v16.2.0-1.aarch64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..c1eef0e --- /dev/null +++ b/deps/checksums/p7zip.v16.2.0-1.aarch64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +97011a65a2d0fafd74e81bc3445654dc diff --git a/deps/checksums/p7zip.v16.2.0-1.aarch64-linux-gnu.tar.gz/sha512 b/deps/checksums/p7zip.v16.2.0-1.aarch64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..a35cb6a --- /dev/null +++ b/deps/checksums/p7zip.v16.2.0-1.aarch64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +9de77aab797e8c9a1785e121a1a33d0f007f58529b2107b181a22f1032f836e374a937214c0cabf72bb1f8ca32b70b71e3e1a12d895b301cdfe35ac3ba1fcb10 diff --git a/deps/checksums/p7zip.v16.2.0-1.aarch64-linux-musl.tar.gz/md5 b/deps/checksums/p7zip.v16.2.0-1.aarch64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..4d22d47 --- /dev/null +++ b/deps/checksums/p7zip.v16.2.0-1.aarch64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +90d4ebc185099fd1425607bc18118df2 diff --git a/deps/checksums/p7zip.v16.2.0-1.aarch64-linux-musl.tar.gz/sha512 b/deps/checksums/p7zip.v16.2.0-1.aarch64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..0181b58 --- /dev/null +++ b/deps/checksums/p7zip.v16.2.0-1.aarch64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +aa2d6c285076de80e1f0b91860bdee7dfba7074f8abd5a95b8a9d66a8419b2a0ed5f835daeb3dc2a23c5ba33edb451710cc4aa8998005fc40536a5950c65d635 diff --git a/deps/checksums/p7zip.v16.2.0-1.arm-linux-gnueabihf.tar.gz/md5 b/deps/checksums/p7zip.v16.2.0-1.arm-linux-gnueabihf.tar.gz/md5 new file mode 100644 index 0000000..1327706 --- /dev/null +++ b/deps/checksums/p7zip.v16.2.0-1.arm-linux-gnueabihf.tar.gz/md5 @@ -0,0 +1 @@ +3e170698971fbe88c72933f1b5040588 diff --git a/deps/checksums/p7zip.v16.2.0-1.arm-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/p7zip.v16.2.0-1.arm-linux-gnueabihf.tar.gz/sha512 new file mode 100644 index 0000000..3e5eda9 --- /dev/null +++ b/deps/checksums/p7zip.v16.2.0-1.arm-linux-gnueabihf.tar.gz/sha512 @@ -0,0 +1 @@ +5a1026a5540a40ec6b5b7cbf1660cca7b6b8b50693b7737ced333e137e7a881c7eb0f1d0f4b3f82d92c07b7fec754375caabd1399196bde731bc9505cd1c5ada diff --git a/deps/checksums/p7zip.v16.2.0-1.arm-linux-musleabihf.tar.gz/md5 b/deps/checksums/p7zip.v16.2.0-1.arm-linux-musleabihf.tar.gz/md5 new file mode 100644 index 0000000..3067103 --- /dev/null +++ b/deps/checksums/p7zip.v16.2.0-1.arm-linux-musleabihf.tar.gz/md5 @@ -0,0 +1 @@ +da71ac0e0a16b11fca6953b8dbaf921c diff --git a/deps/checksums/p7zip.v16.2.0-1.arm-linux-musleabihf.tar.gz/sha512 b/deps/checksums/p7zip.v16.2.0-1.arm-linux-musleabihf.tar.gz/sha512 new file mode 100644 index 0000000..10e3faa --- /dev/null +++ b/deps/checksums/p7zip.v16.2.0-1.arm-linux-musleabihf.tar.gz/sha512 @@ -0,0 +1 @@ +7655a1501ea4fea55165945da394f8aad366b453d2da00f585eeebfb9cc2e421545f3bfabbc5eb7b9859d6470a9d652a0503f2bdbfcd72f94553e149b11ff92d diff --git a/deps/checksums/p7zip.v16.2.0-1.i686-linux-gnu.tar.gz/md5 b/deps/checksums/p7zip.v16.2.0-1.i686-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..a182ecf --- /dev/null +++ b/deps/checksums/p7zip.v16.2.0-1.i686-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +951385b34aba01cdcef4e07e66942951 diff --git a/deps/checksums/p7zip.v16.2.0-1.i686-linux-gnu.tar.gz/sha512 b/deps/checksums/p7zip.v16.2.0-1.i686-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..e5c1e74 --- /dev/null +++ b/deps/checksums/p7zip.v16.2.0-1.i686-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +2d655e6d3d5d26bed81817bfbcece8084bfcbf4fc5e491c6ebec72490ccca4db72cc1f962a3ef5ceab5c5abe2cc4e9b9e9524d25c158ed8af32b6aff4b722d00 diff --git a/deps/checksums/p7zip.v16.2.0-1.i686-linux-musl.tar.gz/md5 b/deps/checksums/p7zip.v16.2.0-1.i686-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..0c59387 --- /dev/null +++ b/deps/checksums/p7zip.v16.2.0-1.i686-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +4e005a19cef586bc407953a0d03caf6d diff --git a/deps/checksums/p7zip.v16.2.0-1.i686-linux-musl.tar.gz/sha512 b/deps/checksums/p7zip.v16.2.0-1.i686-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..1873498 --- /dev/null +++ b/deps/checksums/p7zip.v16.2.0-1.i686-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +6c55576e1f3907435fea1aa6db0c124dee155ab1dac6cdf72ebe1ad4380bcd585d0a8d755b86621a498c63a95523dbf56053076c3f7398e6c0411db0fc1099be diff --git a/deps/checksums/p7zip.v16.2.0-1.i686-w64-mingw32.tar.gz/md5 b/deps/checksums/p7zip.v16.2.0-1.i686-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..6a2ac19 --- /dev/null +++ b/deps/checksums/p7zip.v16.2.0-1.i686-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +a193ac18bf72645e8132c0303d285aab diff --git a/deps/checksums/p7zip.v16.2.0-1.i686-w64-mingw32.tar.gz/sha512 b/deps/checksums/p7zip.v16.2.0-1.i686-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..f0a4fa0 --- /dev/null +++ b/deps/checksums/p7zip.v16.2.0-1.i686-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +eb82dd1b1cbacd76b2440a73b3c2a3167d5c8e88afddd27e0912f4d43d21040112a130f32efbe8af978dda5f698310e5253091c2b144b50384117e362d711256 diff --git a/deps/checksums/p7zip.v16.2.0-1.powerpc64le-linux-gnu.tar.gz/md5 b/deps/checksums/p7zip.v16.2.0-1.powerpc64le-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..000e1fd --- /dev/null +++ b/deps/checksums/p7zip.v16.2.0-1.powerpc64le-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +33ee9c368767a7dc4ecc176b107a1344 diff --git a/deps/checksums/p7zip.v16.2.0-1.powerpc64le-linux-gnu.tar.gz/sha512 b/deps/checksums/p7zip.v16.2.0-1.powerpc64le-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..6f90953 --- /dev/null +++ b/deps/checksums/p7zip.v16.2.0-1.powerpc64le-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +2f801688be550705f10061e1be92c53a4388c90138159763ab63835730fb794cea54928dd3da72d203fcfc047ecf4c6b10644f914c849436058013e24381126d diff --git a/deps/checksums/p7zip.v16.2.0-1.x86_64-apple-darwin14.tar.gz/md5 b/deps/checksums/p7zip.v16.2.0-1.x86_64-apple-darwin14.tar.gz/md5 new file mode 100644 index 0000000..043f55b --- /dev/null +++ b/deps/checksums/p7zip.v16.2.0-1.x86_64-apple-darwin14.tar.gz/md5 @@ -0,0 +1 @@ +dbcfff4fd3ee79040fcee45ad70e8115 diff --git a/deps/checksums/p7zip.v16.2.0-1.x86_64-apple-darwin14.tar.gz/sha512 b/deps/checksums/p7zip.v16.2.0-1.x86_64-apple-darwin14.tar.gz/sha512 new file mode 100644 index 0000000..e6c2ee6 --- /dev/null +++ b/deps/checksums/p7zip.v16.2.0-1.x86_64-apple-darwin14.tar.gz/sha512 @@ -0,0 +1 @@ +66ca063b9b7ffb1e36db3fb71de7ef0a0b22782dabafa1b7f6ba89dd7931bcb4491920cf77970c3ebc2f14741ce21c3c28e7c8ae57ad14d6c2dc54676f853177 diff --git a/deps/checksums/p7zip.v16.2.0-1.x86_64-linux-gnu.tar.gz/md5 b/deps/checksums/p7zip.v16.2.0-1.x86_64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..a94b25b --- /dev/null +++ b/deps/checksums/p7zip.v16.2.0-1.x86_64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +e4ba040e655fc4a82a16e41620171233 diff --git a/deps/checksums/p7zip.v16.2.0-1.x86_64-linux-gnu.tar.gz/sha512 b/deps/checksums/p7zip.v16.2.0-1.x86_64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..0bc4c5b --- /dev/null +++ b/deps/checksums/p7zip.v16.2.0-1.x86_64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +fdc322926de636060699daf87dd69003e092a10cc7125a0963a94fa588f0af9562dd5da2adcf6aa4fdf74e1c01ab46c25ff5e36c6f5665a1781a8b8240ac5624 diff --git a/deps/checksums/p7zip.v16.2.0-1.x86_64-linux-musl.tar.gz/md5 b/deps/checksums/p7zip.v16.2.0-1.x86_64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..08266da --- /dev/null +++ b/deps/checksums/p7zip.v16.2.0-1.x86_64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +14875b7aa0e3d6a6c8773dce64abdcb2 diff --git a/deps/checksums/p7zip.v16.2.0-1.x86_64-linux-musl.tar.gz/sha512 b/deps/checksums/p7zip.v16.2.0-1.x86_64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..cddd982 --- /dev/null +++ b/deps/checksums/p7zip.v16.2.0-1.x86_64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +9cbadcc3b52ec36c9885c04156ec6521d4959f5de78afc0ed867c308ad33994da4fa39ec63f455403587e8b21c6269f1c88c98980744628c4a37470693012e81 diff --git a/deps/checksums/p7zip.v16.2.0-1.x86_64-unknown-freebsd11.1.tar.gz/md5 b/deps/checksums/p7zip.v16.2.0-1.x86_64-unknown-freebsd11.1.tar.gz/md5 new file mode 100644 index 0000000..28c34c3 --- /dev/null +++ b/deps/checksums/p7zip.v16.2.0-1.x86_64-unknown-freebsd11.1.tar.gz/md5 @@ -0,0 +1 @@ +6fff391800713934b3f008906349ddb2 diff --git a/deps/checksums/p7zip.v16.2.0-1.x86_64-unknown-freebsd11.1.tar.gz/sha512 b/deps/checksums/p7zip.v16.2.0-1.x86_64-unknown-freebsd11.1.tar.gz/sha512 new file mode 100644 index 0000000..42bf971 --- /dev/null +++ b/deps/checksums/p7zip.v16.2.0-1.x86_64-unknown-freebsd11.1.tar.gz/sha512 @@ -0,0 +1 @@ +887feb7e197c7cdf3ec65a8fde1809c2c82500b6ddf8a114400ace9bbbd9cb22fc947d3397ae189b6c5d76b55fc4b98c39e02aec298edcdeca997115828af080 diff --git a/deps/checksums/p7zip.v16.2.0-1.x86_64-w64-mingw32.tar.gz/md5 b/deps/checksums/p7zip.v16.2.0-1.x86_64-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..1e28dd2 --- /dev/null +++ b/deps/checksums/p7zip.v16.2.0-1.x86_64-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +1732e69ed7e9d5201637421c7dad93cd diff --git a/deps/checksums/p7zip.v16.2.0-1.x86_64-w64-mingw32.tar.gz/sha512 b/deps/checksums/p7zip.v16.2.0-1.x86_64-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..7a98c0a --- /dev/null +++ b/deps/checksums/p7zip.v16.2.0-1.x86_64-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +8354d68e07e77d0f9679f5daa27a373013b6edefaafff070629ca260a001f1160850637563f16419f37a59fb397b3b273ca4293bc38a72371feff1ecd17c2fc1 diff --git a/deps/checksums/patchelf-0.9.tar.gz/md5 b/deps/checksums/patchelf-0.9.tar.gz/md5 new file mode 100644 index 0000000..085d1ff --- /dev/null +++ b/deps/checksums/patchelf-0.9.tar.gz/md5 @@ -0,0 +1 @@ +3c265508526760f233620f35d79c79fc diff --git a/deps/checksums/patchelf-0.9.tar.gz/sha512 b/deps/checksums/patchelf-0.9.tar.gz/sha512 new file mode 100644 index 0000000..87e5794 --- /dev/null +++ b/deps/checksums/patchelf-0.9.tar.gz/sha512 @@ -0,0 +1 @@ +715db21156e6bd91cfa626f5201b32a6619e51532f5635ef52396da8193738ba66113485b61cc1e218b16737e66f72cc2e4bb3a7a33e73061ac2ef2c6330a299 diff --git a/deps/checksums/pcre2-10.31.tar.bz2/md5 b/deps/checksums/pcre2-10.31.tar.bz2/md5 new file mode 100644 index 0000000..83b9943 --- /dev/null +++ b/deps/checksums/pcre2-10.31.tar.bz2/md5 @@ -0,0 +1 @@ +e0b91c891a3c49050f7fd15de33d0ba4 diff --git a/deps/checksums/pcre2-10.31.tar.bz2/sha512 b/deps/checksums/pcre2-10.31.tar.bz2/sha512 new file mode 100644 index 0000000..a51f234 --- /dev/null +++ b/deps/checksums/pcre2-10.31.tar.bz2/sha512 @@ -0,0 +1 @@ +44d7db2513d9415dcdf6541366fea585e016f572f3e4379f6e959a38114b2337851092049ab4a1576ae8f19b9de413edbcfa62f434c77fc8470747ee5413e967 diff --git a/deps/checksums/utf8proc-5c632c57426f2e4246e3b64dd2fd088d3920f9e5.tar.gz/md5 b/deps/checksums/utf8proc-5c632c57426f2e4246e3b64dd2fd088d3920f9e5.tar.gz/md5 new file mode 100644 index 0000000..5efb522 --- /dev/null +++ b/deps/checksums/utf8proc-5c632c57426f2e4246e3b64dd2fd088d3920f9e5.tar.gz/md5 @@ -0,0 +1 @@ +798b422f22d617129ca1aec2a7199c50 diff --git a/deps/checksums/utf8proc-5c632c57426f2e4246e3b64dd2fd088d3920f9e5.tar.gz/sha512 b/deps/checksums/utf8proc-5c632c57426f2e4246e3b64dd2fd088d3920f9e5.tar.gz/sha512 new file mode 100644 index 0000000..5349cd0 --- /dev/null +++ b/deps/checksums/utf8proc-5c632c57426f2e4246e3b64dd2fd088d3920f9e5.tar.gz/sha512 @@ -0,0 +1 @@ +dec5d7e976bd808bc8edc39dda62b85ecb462b98b6059ace4acee27ec75f6839e0a2899a9baa225379ae5ffb5b568c63a666f52c9958075e5af0376f8a284534 diff --git a/deps/checksums/x86_64-4.9.2-release-win32-seh-rt_v4-rev3.7z/md5 b/deps/checksums/x86_64-4.9.2-release-win32-seh-rt_v4-rev3.7z/md5 new file mode 100644 index 0000000..c574a25 --- /dev/null +++ b/deps/checksums/x86_64-4.9.2-release-win32-seh-rt_v4-rev3.7z/md5 @@ -0,0 +1 @@ +b138576c92c425e8fa171a9d98e13b86 diff --git a/deps/checksums/x86_64-4.9.2-release-win32-seh-rt_v4-rev3.7z/sha512 b/deps/checksums/x86_64-4.9.2-release-win32-seh-rt_v4-rev3.7z/sha512 new file mode 100644 index 0000000..ed37b9b --- /dev/null +++ b/deps/checksums/x86_64-4.9.2-release-win32-seh-rt_v4-rev3.7z/sha512 @@ -0,0 +1 @@ +50dcb3aaab3e53d5ff4561079272daf473bacc8d7a2a3720e7c594ae6e402123cb3cc6f3eabaf67df8d94fa5c01bb3e45b5bb1bc7cbb87cd2beb6b5664e02072 diff --git a/deps/checksums/zlib-cacf7f1d4e3d44d871b605da3b647f07d718623f.tar.gz/md5 b/deps/checksums/zlib-cacf7f1d4e3d44d871b605da3b647f07d718623f.tar.gz/md5 new file mode 100644 index 0000000..5cd6bae --- /dev/null +++ b/deps/checksums/zlib-cacf7f1d4e3d44d871b605da3b647f07d718623f.tar.gz/md5 @@ -0,0 +1 @@ +93d10d4dd040f14ae63417070d1346e8 diff --git a/deps/checksums/zlib-cacf7f1d4e3d44d871b605da3b647f07d718623f.tar.gz/sha512 b/deps/checksums/zlib-cacf7f1d4e3d44d871b605da3b647f07d718623f.tar.gz/sha512 new file mode 100644 index 0000000..365e160 --- /dev/null +++ b/deps/checksums/zlib-cacf7f1d4e3d44d871b605da3b647f07d718623f.tar.gz/sha512 @@ -0,0 +1 @@ +a1e9c5a2963266a582192d0fe88c179f5239245f11c4df4427dda755ad77d31e1fcf045d7d3fe49141090f4ff8da13d9a2e8d8d317fe6460a5f3e9bdea29b883 diff --git a/deps/curl.mk b/deps/curl.mk new file mode 100644 index 0000000..7d1e5dd --- /dev/null +++ b/deps/curl.mk @@ -0,0 +1,77 @@ +## CURL ## + +ifeq ($(USE_SYSTEM_LIBSSH2), 0) +$(BUILDDIR)/curl-$(CURL_VER)/build-configured: | $(build_prefix)/manifest/libssh2 +endif + +ifeq ($(USE_SYSTEM_MBEDTLS), 0) +$(BUILDDIR)/curl-$(CURL_VER)/build-configured: | $(build_prefix)/manifest/mbedtls +endif + +ifneq ($(USE_BINARYBUILDER_CURL),1) +CURL_LDFLAGS := $(RPATH_ESCAPED_ORIGIN) + +# On older Linuces (those that use OpenSSL < 1.1) we include `libpthread` explicitly. +# It doesn't hurt to include it explicitly elsewhere, so we do so. +ifeq ($(OS),Linux) +CURL_LDFLAGS += -lpthread +endif + +$(SRCCACHE)/curl-$(CURL_VER).tar.bz2: | $(SRCCACHE) + $(JLDOWNLOAD) $@ https://curl.haxx.se/download/curl-$(CURL_VER).tar.bz2 + +$(SRCCACHE)/curl-$(CURL_VER)/source-extracted: $(SRCCACHE)/curl-$(CURL_VER).tar.bz2 + $(JLCHECKSUM) $< + cd $(dir $<) && $(TAR) jxf $(notdir $<) + touch -c $(SRCCACHE)/curl-$(CURL_VER)/configure # old target + echo 1 > $@ + +$(BUILDDIR)/curl-$(CURL_VER)/build-configured: $(SRCCACHE)/curl-$(CURL_VER)/source-extracted + mkdir -p $(dir $@) + cd $(dir $@) && \ + $(dir $<)/configure $(CONFIGURE_COMMON) --includedir=$(build_includedir) \ + --without-ssl --without-gnutls --without-gssapi --without-zlib \ + --without-libidn --without-libidn2 --without-libmetalink --without-librtmp \ + --without-nghttp2 --without-nss --without-polarssl \ + --without-spnego --without-libpsl --disable-ares \ + --disable-ldap --disable-ldaps --without-zsh-functions-dir \ + --with-libssh2=$(build_prefix) --with-mbedtls=$(build_prefix) \ + CFLAGS="$(CFLAGS) $(CURL_CFLAGS)" LDFLAGS="$(LDFLAGS) $(CURL_LDFLAGS)" + echo 1 > $@ + +$(BUILDDIR)/curl-$(CURL_VER)/build-compiled: $(BUILDDIR)/curl-$(CURL_VER)/build-configured + $(MAKE) -C $(dir $<) $(LIBTOOL_CCLD) + echo 1 > $@ + +$(BUILDDIR)/curl-$(CURL_VER)/build-checked: $(BUILDDIR)/curl-$(CURL_VER)/build-compiled +ifeq ($(OS),$(BUILD_OS)) + $(MAKE) -C $(dir $@) check +endif + echo 1 > $@ + +$(eval $(call staged-install, \ + curl,curl-$$(CURL_VER), \ + MAKE_INSTALL,$$(LIBTOOL_CCLD),, \ + $$(INSTALL_NAME_CMD)libcurl.$$(SHLIB_EXT) $$(build_shlibdir)/libcurl.$$(SHLIB_EXT))) + +clean-curl: + -rm $(BUILDDIR)/curl-$(CURL_VER)/build-configured $(BUILDDIR)/curl-$(CURL_VER)/build-compiled + -$(MAKE) -C $(BUILDDIR)/curl-$(CURL_VER) clean + +distclean-curl: + -rm -rf $(SRCCACHE)/curl-$(CURL_VER).tar.bz2 $(SRCCACHE)/curl-$(CURL_VER) $(BUILDDIR)/curl-$(CURL_VER) + +get-curl: $(SRCCACHE)/curl-$(CURL_VER).tar.bz2 +extract-curl: $(SRCCACHE)/curl-$(CURL_VER)/source-extracted +configure-curl: $(BUILDDIR)/curl-$(CURL_VER)/build-configured +compile-curl: $(BUILDDIR)/curl-$(CURL_VER)/build-compiled +fastcheck-curl: #none +check-curl: $(BUILDDIR)/curl-$(CURL_VER)/build-checked + +else # USE_BINARYBUILDER_CURL + +CURL_BB_URL_BASE := https://github.com/JuliaPackaging/Yggdrasil/releases/download/LibCURL-v$(CURL_VER)-$(CURL_BB_REL) +CURL_BB_NAME := LibCURL.v$(CURL_VER) + +$(eval $(call bb-install,curl,CURL,false)) +endif diff --git a/deps/dsfmt.mk b/deps/dsfmt.mk new file mode 100644 index 0000000..c3e4a60 --- /dev/null +++ b/deps/dsfmt.mk @@ -0,0 +1,72 @@ +## DSFMT ## + +ifneq ($(USE_BINARYBUILDER_DSFMT),1) + +DSFMT_CFLAGS := $(CFLAGS) -DNDEBUG -DDSFMT_MEXP=19937 $(fPIC) -DDSFMT_DO_NOT_USE_OLD_NAMES +ifneq ($(USEMSVC), 1) +DSFMT_CFLAGS += -O3 -finline-functions -fomit-frame-pointer -fno-strict-aliasing \ + --param max-inline-insns-single=1800 -Wmissing-prototypes -Wall -std=c99 -shared +else +DSFMT_CFLAGS += -Wl,-dll,-def:../../libdSFMT.def +endif +ifeq ($(ARCH), x86_64) +DSFMT_CFLAGS += -msse2 -DHAVE_SSE2 +endif + +$(SRCCACHE)/dsfmt-$(DSFMT_VER).tar.gz: | $(SRCCACHE) + $(JLDOWNLOAD) $@ http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/dSFMT-src-$(DSFMT_VER).tar.gz + touch -c $@ + +$(BUILDDIR)/dsfmt-$(DSFMT_VER)/source-extracted: $(SRCCACHE)/dsfmt-$(DSFMT_VER).tar.gz + $(JLCHECKSUM) $< + -rm -r $(dir $@) + mkdir -p $(dir $@) + $(TAR) -C $(dir $@) --strip-components 1 -xf $< + cd $(dir $@) && patch < $(SRCDIR)/patches/dSFMT.h.patch + cd $(dir $@) && patch < $(SRCDIR)/patches/dSFMT.c.patch + echo 1 > $@ + +$(BUILDDIR)/dsfmt-$(DSFMT_VER)/build-compiled: $(BUILDDIR)/dsfmt-$(DSFMT_VER)/source-extracted + cd $(dir $<) && \ + $(CC) $(CPPFLAGS) $(DSFMT_CFLAGS) $(LDFLAGS) dSFMT.c -o libdSFMT.$(SHLIB_EXT) + echo 1 > $@ + +$(BUILDDIR)/dsfmt-$(DSFMT_VER)/build-checked: $(BUILDDIR)/dsfmt-$(DSFMT_VER)/build-compiled +ifeq ($(OS),$(BUILD_OS)) + $(MAKE) -C $(dir $@) std-check sse2-check +endif + echo 1 > $@ + +define DSFMT_INSTALL + mkdir -p $2/$$(build_includedir) + mkdir -p $2/$$(build_shlibdir) + cp $1/dSFMT.h $2/$$(build_includedir) + cp $1/libdSFMT.$$(SHLIB_EXT) $2/$$(build_shlibdir) +endef +$(eval $(call staged-install, \ + dsfmt,dsfmt-$(DSFMT_VER), \ + DSFMT_INSTALL,, \ + $$(DSFMT_OBJ_TARGET), \ + $$(INSTALL_NAME_CMD)libdSFMT.$$(SHLIB_EXT) $$(build_shlibdir)/libdSFMT.$$(SHLIB_EXT))) + +clean-dsfmt: + -rm $(BUILDDIR)/dsfmt-$(DSFMT_VER)/build-compiled + -rm $(BUILDDIR)/dsfmt-$(DSFMT_VER)/libdSFMT.$(SHLIB_EXT) + +distclean-dsfmt: + -rm -rf $(SRCCACHE)/dsfmt*.tar.gz $(SRCCACHE)/dsfmt-$(DSFMT_VER) $(BUILDDIR)/dsfmt-$(DSFMT_VER) + +get-dsfmt: $(SRCCACHE)/dsfmt-$(DSFMT_VER).tar.gz +extract-dsfmt: $(BUILDDIR)/dsfmt-$(DSFMT_VER)/source-extracted +configure-dsfmt: extract-dsfmt +compile-dsfmt: $(BUILDDIR)/dsfmt-$(DSFMT_VER)/build-compiled +fastcheck-dsfmt: check-dsfmt +check-dsfmt: $(BUILDDIR)/dsfmt-$(DSFMT_VER)/build-checked + +else + +DSFMT_BB_URL_BASE := https://github.com/JuliaPackaging/Yggdrasil/releases/download/dSFMT-v$(DSFMT_VER)-$(DSFMT_BB_REL) +DSFMT_BB_NAME := dSFMT.v$(DSFMT_VER) +$(eval $(call bb-install,dsfmt,DSFMT,false)) + +endif # USE_BINARYBUILDER_DSFMT diff --git a/deps/gfortblas.alias b/deps/gfortblas.alias new file mode 100644 index 0000000..19f4615 --- /dev/null +++ b/deps/gfortblas.alias @@ -0,0 +1,50 @@ +_sasum_ _sasum +_sasum_ _SASUM +_sasum_ _SASUM_ +_sasum_ _sasum_gfort_ + +_scasum_ _scasum +_scasum_ _SCASUM +_scasum_ _SCASUM_ +_scasum_ _scasum_gfort_ + +_scnrm2_ _scnrm2 +_scnrm2_ _SCNRM2 +_scnrm2_ _SCNRM2_ +_scnrm2_ _scnrm2_gfort_ + +_sdot_ _sdot +_sdot_ _SDOT +_sdot_ _SDOT_ +_sdot_ _sdot_gfort_ + +_sdsdot_ _sdsdot +_sdsdot_ _SDSDOT +_sdsdot_ _SDSDOT_ +_sdsdot_ _sdsdot_gfort_ + +_snrm2_ _snrm2 +_snrm2_ _SNRM2 +_snrm2_ _SNRM2_ +_snrm2_ _snrm2_gfort_ + + +_cdotc_ _cdotc +_cdotc_ _CDOTC +_cdotc_ _CDOTC_ +_cdotc_ _cdotc_gfort_ + +_cdotu_ _cdotu +_cdotu_ _CDOTU +_cdotu_ _CDOTU_ +_cdotu_ _cdotu_gfort_ + +_zdotc_ _zdotc +_zdotc_ _ZDOTC +_zdotc_ _ZDOTC_ +_zdotc_ _zdotc_gfort_ + +_zdotu_ _zdotu +_zdotu_ _ZDOTU +_zdotu_ _ZDOTU_ +_zdotu_ _zdotu_gfort_ diff --git a/deps/gfortblas.c b/deps/gfortblas.c new file mode 100644 index 0000000..4133a97 --- /dev/null +++ b/deps/gfortblas.c @@ -0,0 +1,122 @@ +/** + * Copyright (C) 2013 JuliaLang Project / Jameson Nash + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * + * Inspired by blaswrap.c by Jarno Rajahalme 2011 (written for Octave) + * + * Wrapper for Apple libBLAS.dylib for the gfortran calling convention + * + * At least on the versions of OSX 10.6 so far (up and including 10.6.6) + * these libraries are incompatible with 64 bit builds, as some functions + * in libBLAS.dylib are not conforming to F2C calling conventions, as + * they should. This breaks them in 64-bit builds on the x86_64 + * architecture. + * + * Newer gfortran compoilers no longer default to the F2C calling + * convention. These wrappers map the F2C conformant functions in + * libBLAS and libLAPACK to the native gfortran calling convention, so + * that the libraries can be used with software built for x86_64 + * architecture. + * + * These wrappers are as efficient as the Apple functions of the same name. +**/ + +#include +#include + +float sasum_(int *N, float *SX, int *INCX) { + return cblas_sasum(*N, SX, *INCX); +} +float scasum_(int *N, void *SX, int *INCX) { + return cblas_scasum(*N, SX, *INCX); +} +float scnrm2_(int *N, void *X, int *INCX) { + return cblas_scnrm2(*N, X, *INCX); +} +float sdot_(int *N, float *SX, int *INCX, float *SY, int *INCY) { + return cblas_sdot(*N, SX, *INCX, SY, *INCY); +} +float sdsdot_(int *N, float *SB, float *SX, int *INCX, float *SY, int *INCY) { + return cblas_sdsdot(*N, *SB, SX, *INCX, SY, *INCY); +} +float snrm2_(int *N, float *X, int *INCX) { + return cblas_snrm2(*N, X, *INCX); +} +/* +float scabs1_(void *Z) { + this function is part of BLAS spec, but is not provided by Apple nor required by LAPACK +} +*/ + +complex float cdotc_(int *N, void *CX, int *INCX, void *CY, int *INCY) { + complex float dotc; + cblas_cdotc_sub(*N, CX, *INCX, CY, *INCY, &dotc); + return dotc; +} +complex float cdotu_(int *N, void *CX, int *INCX, void *CY, int *INCY) { + complex float dotu; + cblas_cdotu_sub(*N, CX, *INCX, CY, *INCY, &dotu); + return dotu; +} +complex double zdotc_(int *N, void *CX, int *INCX, void *CY, int *INCY) { + complex double dotc; + cblas_zdotc_sub(*N, CX, *INCX, CY, *INCY, &dotc); + return dotc; +} +complex double zdotu_(int *N, void *CX, int *INCX, void *CY, int *INCY) { + complex double dotu; + cblas_zdotu_sub(*N, CX, *INCX, CY, *INCY, &dotu); + return dotu; +} + +//#include +//#include +//extern void xerbl2_(const char*, const int*) __attribute__((weak)); +void BLASParamErrorProcNULL( + const char *funcName, + const char *paramName, + const int *paramPos, + const int *paramValue) { +// //cblas_xerbla(*paramPos, (char*)funcName, " param %s\n", paramName); +// if (funcName && !strncmp(funcName, "cblas_", 6)) funcName += 6; +// size_t i, len = strlen(funcName); +// char funcName2[7]; +// for (i = 0; i < len; i++) +// funcName2[i] = toupper(funcName[i]); +// for (i = len; i < 6; i++) +// funcName2[i] = ' '; +// funcName2[i] = 0; +// int paramPos2 = *paramPos-1; +// if (xerbl2_) xerbl2_(funcName2, ¶mPos2); + /* default BLAS error handler calls abort() on invalid parameters, but we want to handle these failures in Julia */ +} + +__attribute__((constructor)) +static void init(void) { + SetBLASParamErrorProc(BLASParamErrorProcNULL); +} + +__attribute__((destructor)) +static void fini(void) { + SetBLASParamErrorProc(NULL); /* restore default handler */ +} + diff --git a/deps/gmp.mk b/deps/gmp.mk new file mode 100644 index 0000000..dbc21c7 --- /dev/null +++ b/deps/gmp.mk @@ -0,0 +1,85 @@ +## GMP ## + +ifeq ($(SANITIZE),1) +GMP_CONFIGURE_OPTS += --disable-assembly +endif + +ifeq ($(BUILD_OS),WINNT) +GMP_CONFIGURE_OPTS += --srcdir="$(subst \,/,$(call mingw_to_dos,$(SRCCACHE)/gmp-$(GMP_VER)))" +endif + +ifneq ($(USE_BINARYBUILDER_GMP),1) + +$(SRCCACHE)/gmp-$(GMP_VER).tar.bz2: | $(SRCCACHE) + $(JLDOWNLOAD) $@ https://gmplib.org/download/gmp/$(notdir $@) + +$(SRCCACHE)/gmp-$(GMP_VER)/source-extracted: $(SRCCACHE)/gmp-$(GMP_VER).tar.bz2 + $(JLCHECKSUM) $< + cd $(dir $<) && $(TAR) -jxf $< + touch -c $(SRCCACHE)/gmp-$(GMP_VER)/configure # old target + echo 1 > $@ + +$(SRCCACHE)/gmp-$(GMP_VER)/build-patched: $(SRCCACHE)/gmp-$(GMP_VER)/source-extracted + cp $(SRCDIR)/patches/config.sub $(SRCCACHE)/gmp-$(GMP_VER)/configfsf.sub + cd $(dir $@) && patch < $(SRCDIR)/patches/gmp-exception.patch + cd $(dir $@) && patch -p1 < $(SRCDIR)/patches/gmp_alloc_overflow_func.patch + echo 1 > $@ + +$(SRCCACHE)/gmp-$(GMP_VER)/gmp-config-ldflags.patch-applied: | $(SRCCACHE)/gmp-$(GMP_VER)/build-patched + cd $(dir $@) && patch -p1 < $(SRCDIR)/patches/gmp-config-ldflags.patch + echo 1 > $@ + +$(BUILDDIR)/gmp-$(GMP_VER)/build-configured: $(SRCCACHE)/gmp-$(GMP_VER)/gmp-config-ldflags.patch-applied + +$(BUILDDIR)/gmp-$(GMP_VER)/build-configured: $(SRCCACHE)/gmp-$(GMP_VER)/source-extracted + mkdir -p $(dir $@) + cd $(dir $@) && \ + $(dir $<)/configure $(CONFIGURE_COMMON) F77= --enable-shared --disable-static $(GMP_CONFIGURE_OPTS) + echo 1 > $@ + +$(BUILDDIR)/gmp-$(GMP_VER)/build-compiled: $(BUILDDIR)/gmp-$(GMP_VER)/build-configured + $(MAKE) -C $(dir $<) $(LIBTOOL_CCLD) + echo 1 > $@ + +$(BUILDDIR)/gmp-$(GMP_VER)/build-checked: $(BUILDDIR)/gmp-$(GMP_VER)/build-compiled +ifeq ($(OS),$(BUILD_OS)) + $(MAKE) -C $(dir $@) $(LIBTOOL_CCLD) check +endif + echo 1 > $@ + +define GMP_INSTALL + mkdir -p $2/$(build_shlibdir) $2/$(build_includedir) +ifeq ($(BUILD_OS),WINNT) + -mv $1/.libs/gmp.dll $1/.libs/libgmp.dll +endif + $(INSTALL_M) $1/.libs/libgmp.*$(SHLIB_EXT)* $2/$(build_shlibdir) + $(INSTALL_F) $1/gmp.h $2/$(build_includedir) +endef +$(eval $(call staged-install, \ + gmp,gmp-$(GMP_VER), \ + GMP_INSTALL,,, \ + $$(INSTALL_NAME_CMD)libgmp.$$(SHLIB_EXT) $$(build_shlibdir)/libgmp.$$(SHLIB_EXT))) + +clean-gmp: + -rm $(BUILDDIR)/gmp-$(GMP_VER)/build-configured $(BUILDDIR)/gmp-$(GMP_VER)/build-compiled + -$(MAKE) -C $(BUILDDIR)/gmp-$(GMP_VER) clean + +distclean-gmp: + -rm -rf $(SRCCACHE)/gmp-$(GMP_VER).tar.bz2 \ + $(SRCCACHE)/gmp-$(GMP_VER) \ + $(BUILDDIR)/gmp-$(GMP_VER) + +get-gmp: $(SRCCACHE)/gmp-$(GMP_VER).tar.bz2 +extract-gmp: $(SRCCACHE)/gmp-$(GMP_VER)/source-extracted +configure-gmp: $(BUILDDIR)/gmp-$(GMP_VER)/build-configured +compile-gmp: $(BUILDDIR)/gmp-$(GMP_VER)/build-compiled +fastcheck-gmp: check-gmp +check-gmp: $(BUILDDIR)/gmp-$(GMP_VER)/build-checked + +else # USE_BINARYBUILDER_GMP + +GMP_BB_URL_BASE := https://github.com/JuliaBinaryWrappers/GMP_jll.jl/releases/download/GMP-v$(GMP_VER)+$(GMP_BB_REL) +GMP_BB_NAME := GMP.v$(GMP_VER) + +$(eval $(call bb-install,gmp,GMP,false)) +endif diff --git a/deps/libdSFMT.def b/deps/libdSFMT.def new file mode 100644 index 0000000..7388fc8 --- /dev/null +++ b/deps/libdSFMT.def @@ -0,0 +1,30 @@ +LIBRARY "libdSFMT.dll" +EXPORTS +dsfmt_chk_init_by_array +dsfmt_chk_init_gen_rand +dsfmt_fill_array_close1_open2 +dsfmt_fill_array_close_open +dsfmt_fill_array_open_close +dsfmt_fill_array_open_open +dsfmt_gen_rand_all +dsfmt_genrand_close1_open2 +dsfmt_genrand_close_open +dsfmt_genrand_open_close +dsfmt_genrand_open_open +dsfmt_genrand_uint32 +dsfmt_get_idstring +dsfmt_get_min_array_size +dsfmt_global_data DATA +dsfmt_gv_fill_array_close1_open2 +dsfmt_gv_fill_array_close_open +dsfmt_gv_fill_array_open_close +dsfmt_gv_fill_array_open_open +dsfmt_gv_genrand_close1_open2 +dsfmt_gv_genrand_close_open +dsfmt_gv_genrand_open_close +dsfmt_gv_genrand_open_open +dsfmt_gv_genrand_uint32 +dsfmt_gv_init_by_array +dsfmt_gv_init_gen_rand +dsfmt_init_by_array +dsfmt_init_gen_rand diff --git a/deps/libgit2.mk b/deps/libgit2.mk new file mode 100644 index 0000000..ae63454 --- /dev/null +++ b/deps/libgit2.mk @@ -0,0 +1,120 @@ +## libgit2 + +LIBGIT2_GIT_URL := git://github.com/libgit2/libgit2.git +LIBGIT2_TAR_URL = https://api.github.com/repos/libgit2/libgit2/tarball/$1 +$(eval $(call git-external,libgit2,LIBGIT2,CMakeLists.txt,,$(SRCCACHE))) + +ifeq ($(USE_SYSTEM_LIBSSH2), 0) +$(BUILDDIR)/$(LIBGIT2_SRC_DIR)/build-configured: | $(build_prefix)/manifest/libssh2 +endif + +ifeq ($(USE_SYSTEM_MBEDTLS), 0) +$(BUILDDIR)/$(LIBGIT2_SRC_DIR)/build-configured: | $(build_prefix)/manifest/mbedtls +endif + +ifneq ($(OS),WINNT) +ifeq ($(USE_SYSTEM_CURL), 0) +$(BUILDDIR)/$(LIBGIT2_SRC_DIR)/build-configured: | $(build_prefix)/manifest/curl +endif +endif + +ifneq ($(USE_BINARYBUILDER_LIBGIT2),1) + +LIBGIT2_OPTS := $(CMAKE_COMMON) -DCMAKE_BUILD_TYPE=Release -DTHREADSAFE=ON -DUSE_BUNDLED_ZLIB=ON +ifeq ($(OS),WINNT) +LIBGIT2_OPTS += -DWIN32=ON -DMINGW=ON +ifneq ($(ARCH),x86_64) +ifneq ($(USECLANG),1) +LIBGIT2_OPTS += -DCMAKE_C_FLAGS="-mincoming-stack-boundary=2" +endif +endif +ifeq ($(BUILD_OS),WINNT) +LIBGIT2_OPTS += -G"MSYS Makefiles" +else +LIBGIT2_OPTS += -DBUILD_CLAR=OFF -DDLLTOOL=`which $(CROSS_COMPILE)dlltool` +LIBGIT2_OPTS += -DCMAKE_FIND_ROOT_PATH=/usr/$(XC_HOST) -DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY +endif +else +LIBGIT2_OPTS += -DCURL_INCLUDE_DIRS=$(build_includedir) -DCURL_LIBRARIES="curl" +endif + +ifneq (,$(findstring $(OS),Linux FreeBSD)) +LIBGIT2_OPTS += -DUSE_HTTPS="mbedTLS" -DSHA1_BACKEND="CollisionDetection" -DCMAKE_INSTALL_RPATH="\$$ORIGIN" +endif + +LIBGIT2_SRC_PATH := $(SRCCACHE)/$(LIBGIT2_SRC_DIR) + +$(LIBGIT2_SRC_PATH)/libgit2-agent-nonfatal.patch-applied: $(LIBGIT2_SRC_PATH)/source-extracted + cd $(LIBGIT2_SRC_PATH) && \ + patch -p1 -f < $(SRCDIR)/patches/libgit2-agent-nonfatal.patch + echo 1 > $@ + +$(BUILDDIR)/$(LIBGIT2_SRC_DIR)/build-configured: \ + $(LIBGIT2_SRC_PATH)/libgit2-agent-nonfatal.patch-applied \ + +$(BUILDDIR)/$(LIBGIT2_SRC_DIR)/build-configured: $(LIBGIT2_SRC_PATH)/source-extracted + mkdir -p $(dir $@) + cd $(dir $@) && \ + $(CMAKE) $(dir $<) $(LIBGIT2_OPTS) + echo 1 > $@ + +$(BUILDDIR)/$(LIBGIT2_SRC_DIR)/build-compiled: $(BUILDDIR)/$(LIBGIT2_SRC_DIR)/build-configured + $(MAKE) -C $(dir $<) + echo 1 > $@ + +$(BUILDDIR)/$(LIBGIT2_SRC_DIR)/build-checked: $(BUILDDIR)/$(LIBGIT2_SRC_DIR)/build-compiled +ifeq ($(OS),$(BUILD_OS)) + $(MAKE) -C $(dir $@) test +endif + echo 1 > $@ + +define LIBGIT2_INSTALL + +ifeq ($$(OS),WINNT) + mkdir -p $2/$$(build_shlibdir) + cp $1/libgit2.$$(SHLIB_EXT) $2/$$(build_shlibdir)/libgit2.$$(SHLIB_EXT) +else + $(call MAKE_INSTALL,$1,$2,$3) +endif +endef +$(eval $(call staged-install, \ + libgit2,$(LIBGIT2_SRC_DIR), \ + LIBGIT2_INSTALL,,, \ + $$(INSTALL_NAME_CMD)libgit2.$$(SHLIB_EXT) $$(build_shlibdir)/libgit2.$$(SHLIB_EXT))) + +clean-libgit2: + -rm $(build_datarootdir)/julia/cert.pem + -rm $(BUILDDIR)/$(LIBGIT2_SRC_DIR)/build-configured $(BUILDDIR)/$(LIBGIT2_SRC_DIR)/build-compiled + -$(MAKE) -C $(BUILDDIR)/$(LIBGIT2_SRC_DIR) clean + +get-libgit2: $(LIBGIT2_SRC_FILE) +extract-libgit2: $(SRCCACHE)/$(LIBGIT2_SRC_DIR)/source-extracted +configure-libgit2: $(BUILDDIR)/$(LIBGIT2_SRC_DIR)/build-configured +compile-libgit2: $(BUILDDIR)/$(LIBGIT2_SRC_DIR)/build-compiled +fastcheck-libgit2: #none +check-libgit2: $(BUILDDIR)/$(LIBGIT2_SRC_DIR)/build-checked +$(build_prefix)/manifest/libgit2: $(build_datarootdir)/julia/cert.pem # use libgit2 install status + +else # USE_BINARYBUILDER_LIBGIT2 + +LIBGIT2_BB_URL_BASE := https://github.com/JuliaPackaging/Yggdrasil/releases/download/LibGit2-v$(LIBGIT2_VER)+$(LIBGIT2_BB_REL) +LIBGIT2_BB_NAME := LibGit2.v$(LIBGIT2_VER) +$(eval $(call bb-install,libgit2,LIBGIT2,false)) + +# BB tarball doesn't create a manifest, so directly depend the `install` target +install-libgit2: $(build_datarootdir)/julia/cert.pem +endif + +# Also download and install a cacert.pem file, regardless of whether or not +# we're using BinaryBuilder-sourced binaries +$(SRCCACHE)/cacert-$(MOZILLA_CACERT_VERSION).pem: + $(JLDOWNLOAD) $@ https://curl.haxx.se/ca/cacert-$(MOZILLA_CACERT_VERSION).pem + +$(build_datarootdir)/julia/cert.pem: $(SRCCACHE)/cacert-$(MOZILLA_CACERT_VERSION).pem | $(build_datarootdir) + $(JLCHECKSUM) $< + mkdir -p $(build_datarootdir)/julia + cp $< $@ + +# When "get"'ing libgit2, download the .pem +get-libgit2: $(SRCCACHE)/cacert-$(MOZILLA_CACERT_VERSION).pem + diff --git a/deps/libgit2.version b/deps/libgit2.version new file mode 100644 index 0000000..f67bedc --- /dev/null +++ b/deps/libgit2.version @@ -0,0 +1,2 @@ +LIBGIT2_BRANCH=v0.28.2 +LIBGIT2_SHA1=b3e1a56ebb2b9291e82dc027ba9cbcfc3ead54d3 diff --git a/deps/libssh2.mk b/deps/libssh2.mk new file mode 100644 index 0000000..5d05fb7 --- /dev/null +++ b/deps/libssh2.mk @@ -0,0 +1,71 @@ +## libssh2 + +LIBSSH2_GIT_URL := git://github.com/libssh2/libssh2.git +LIBSSH2_TAR_URL = https://api.github.com/repos/libssh2/libssh2/tarball/$1 +$(eval $(call git-external,libssh2,LIBSSH2,CMakeLists.txt,,$(SRCCACHE))) + +ifeq ($(USE_SYSTEM_MBEDTLS), 0) +$(BUILDDIR)/$(LIBSSH2_SRC_DIR)/build-configured: | $(build_prefix)/manifest/mbedtls +endif + +ifneq ($(USE_BINARYBUILDER_LIBSSH2), 1) +LIBSSH2_OPTS := $(CMAKE_COMMON) -DBUILD_SHARED_LIBS=ON -DBUILD_EXAMPLES=OFF \ + -DCMAKE_BUILD_TYPE=Release + +ifeq ($(OS),WINNT) +LIBSSH2_OPTS += -DCRYPTO_BACKEND=WinCNG -DENABLE_ZLIB_COMPRESSION=OFF +ifeq ($(BUILD_OS),WINNT) +LIBSSH2_OPTS += -G"MSYS Makefiles" +endif +else +LIBSSH2_OPTS += -DCRYPTO_BACKEND=mbedTLS -DENABLE_ZLIB_COMPRESSION=OFF +endif + +ifneq (,$(findstring $(OS),Linux FreeBSD)) +LIBSSH2_OPTS += -DCMAKE_INSTALL_RPATH="\$$ORIGIN" +endif + +ifeq ($(LIBSSH2_ENABLE_TESTS), 0) +LIBSSH2_OPTS += -DBUILD_TESTING=OFF +endif + +$(BUILDDIR)/$(LIBSSH2_SRC_DIR)/build-configured: $(SRCCACHE)/$(LIBSSH2_SRC_DIR)/source-extracted + mkdir -p $(dir $@) + cd $(dir $@) && \ + $(CMAKE) $(dir $<) $(LIBSSH2_OPTS) + echo 1 > $@ + +$(BUILDDIR)/$(LIBSSH2_SRC_DIR)/build-compiled: $(BUILDDIR)/$(LIBSSH2_SRC_DIR)/build-configured + $(MAKE) -C $(dir $<) libssh2 + echo 1 > $@ + +$(BUILDDIR)/$(LIBSSH2_SRC_DIR)/build-checked: $(BUILDDIR)/$(LIBSSH2_SRC_DIR)/build-compiled +ifeq ($(OS),$(BUILD_OS)) + $(MAKE) -C $(dir $@) test +endif + echo 1 > $@ + +$(eval $(call staged-install, \ + libssh2,$(LIBSSH2_SRC_DIR), \ + MAKE_INSTALL,,, \ + $$(INSTALL_NAME_CMD)libssh2.$$(SHLIB_EXT) $$(build_shlibdir)/libssh2.$$(SHLIB_EXT))) + +clean-libssh2: + -rm $(BUILDDIR)/$(LIBSSH2_SRC_DIR)/build-configured $(BUILDDIR)/$(LIBSSH2_SRC_DIR)/build-compiled + -$(MAKE) -C $(BUILDDIR)/$(LIBSSH2_SRC_DIR) clean + + +get-libssh2: $(LIBSSH2_SRC_FILE) +extract-libssh2: $(SRCCACHE)/$(LIBSSH2_SRC_DIR)/source-extracted +configure-libssh2: $(BUILDDIR)/$(LIBSSH2_SRC_DIR)/build-configured +compile-libssh2: $(BUILDDIR)/$(LIBSSH2_SRC_DIR)/build-compiled +fastcheck-libssh2: check-libssh2 +check-libssh2: $(BUILDDIR)/$(LIBSSH2_SRC_DIR)/build-checked + +else # USE_BINARYBUILDER_LIBSSH2 + +LIBSSH2_BB_URL_BASE := https://github.com/JuliaPackaging/Yggdrasil/releases/download/LibSSH2-v$(LIBSSH2_VER)+$(LIBSSH2_BB_REL) +LIBSSH2_BB_NAME := LibSSH2.v$(LIBSSH2_VER) + +$(eval $(call bb-install,libssh2,LIBSSH2,false)) +endif diff --git a/deps/libssh2.version b/deps/libssh2.version new file mode 100644 index 0000000..0902351 --- /dev/null +++ b/deps/libssh2.version @@ -0,0 +1,2 @@ +LIBSSH2_BRANCH=libssh2-1.9.0 +LIBSSH2_SHA1=42d37aa63129a1b2644bf6495198923534322d64 diff --git a/deps/libuv.mk b/deps/libuv.mk new file mode 100644 index 0000000..977a6a7 --- /dev/null +++ b/deps/libuv.mk @@ -0,0 +1,71 @@ +## LIBUV ## +LIBUV_GIT_URL:=git://github.com/JuliaLang/libuv.git +LIBUV_TAR_URL=https://api.github.com/repos/JuliaLang/libuv/tarball/$1 +$(eval $(call git-external,libuv,LIBUV,configure,,$(SRCCACHE))) + +ifneq ($(USE_BINARYBUILDER_LIBUV),1) + +UV_CFLAGS := -O2 +ifeq ($(USEMSVC), 1) +UV_CFLAGS += -DBUILDING_UV_SHARED +endif +ifeq ($(USEICC), 1) +UV_CFLAGS += -static-intel +endif + +UV_FLAGS := LDFLAGS="$(LDFLAGS) $(CLDFLAGS) -v" +ifneq ($(UV_CFLAGS),) +UV_FLAGS += CFLAGS="$(CFLAGS) $(UV_CFLAGS)" +endif +ifeq ($(USEMSVC), 1) +UV_FLAGS += --disable-shared +endif + +ifneq ($(VERBOSE), 0) +UV_MFLAGS += V=1 +endif + +LIBUV_BUILDDIR := $(BUILDDIR)/$(LIBUV_SRC_DIR) + +$(LIBUV_BUILDDIR)/build-configured: $(SRCCACHE)/$(LIBUV_SRC_DIR)/source-extracted + touch -c $(SRCCACHE)/$(LIBUV_SRC_DIR)/aclocal.m4 # touch a few files to prevent autogen from getting called + touch -c $(SRCCACHE)/$(LIBUV_SRC_DIR)/Makefile.in + touch -c $(SRCCACHE)/$(LIBUV_SRC_DIR)/configure + mkdir -p $(dir $@) + cd $(dir $@) && \ + $(dir $<)/configure --with-pic $(CONFIGURE_COMMON) $(UV_FLAGS) + echo 1 > $@ + +$(LIBUV_BUILDDIR)/build-compiled: $(LIBUV_BUILDDIR)/build-configured + $(MAKE) -C $(dir $<) $(UV_MFLAGS) + echo 1 > $@ + +$(LIBUV_BUILDDIR)/build-checked: $(LIBUV_BUILDDIR)/build-compiled +ifeq ($(OS),$(BUILD_OS)) + -$(MAKE) -C $(dir $@) check +endif + echo 1 > $@ + +$(eval $(call staged-install, \ + libuv,$$(LIBUV_SRC_DIR), \ + MAKE_INSTALL,,, \ + $$(INSTALL_NAME_CMD)libuv.$$(SHLIB_EXT) $$(build_shlibdir)/libuv.$$(SHLIB_EXT))) + +clean-libuv: + -rm -rf $(LIBUV_BUILDDIR)/build-configured $(LIBUV_BUILDDIR)/build-compiled + -$(MAKE) -C $(LIBUV_BUILDDIR) clean + + +get-libuv: $(LIBUV_SRC_FILE) +extract-libuv: $(SRCCACHE)/$(LIBUV_SRC_DIR)/source-extracted +configure-libuv: $(LIBUV_BUILDDIR)/build-configured +compile-libuv: $(LIBUV_BUILDDIR)/build-compiled +fastcheck-libuv: #none +check-libuv: $(LIBUV_BUILDDIR)/build-checked + +else # USE_BINARYBUILDER_LIBUV +LIBUV_BB_URL_BASE := https://github.com/JuliaPackaging/Yggdrasil/releases/download/LibUV-v2+$(LIBUV_VER)-julia+$(LIBUV_BB_REL) +LIBUV_BB_NAME := LibUV.v2.0.0+$(LIBUV_VER)-julia + +$(eval $(call bb-install,libuv,LIBUV,false)) +endif diff --git a/deps/libuv.version b/deps/libuv.version new file mode 100644 index 0000000..cb4d6b8 --- /dev/null +++ b/deps/libuv.version @@ -0,0 +1,2 @@ +LIBUV_BRANCH=julia-uv2-1.29.1 +LIBUV_SHA1=35b1504507a7a4168caae3d78db54d1121b121e1 diff --git a/deps/libwhich.mk b/deps/libwhich.mk new file mode 100644 index 0000000..5f4a50c --- /dev/null +++ b/deps/libwhich.mk @@ -0,0 +1,36 @@ +## LIBWHICH ## +LIBWHICH_GIT_URL := git://github.com/vtjnash/libwhich.git +LIBWHICH_TAR_URL = https://api.github.com/repos/vtjnash/libwhich/tarball/$1 +$(eval $(call git-external,libwhich,LIBWHICH,,,$(BUILDDIR))) + +LIBWHICH_OBJ_LIB := $(build_depsbindir)/libwhich +LIBWHICH_MFLAGS := CC="$(CC)" + +$(BUILDDIR)/$(LIBWHICH_SRC_DIR)/build-compiled: $(BUILDDIR)/$(LIBWHICH_SRC_DIR)/source-extracted + $(MAKE) -C $(dir $<) $(LIBWHICH_MFLAGS) libwhich + echo 1 > $@ + +$(BUILDDIR)/$(LIBWHICH_SRC_DIR)/build-checked: $(BUILDDIR)/$(LIBWHICH_SRC_DIR)/build-compiled +ifeq ($(OS),$(BUILD_OS)) + $(MAKE) -C $(dir $@) $(LIBWHICH_MFLAGS) check +endif + echo 1 > $@ + +define LIBWHICH_INSTALL + mkdir -p $2/$$(build_depsbindir) + cp $1/libwhich $2/$$(build_depsbindir) +endef +$(eval $(call staged-install, \ + libwhich,$(LIBWHICH_SRC_DIR), \ + LIBWHICH_INSTALL,,,)) + +clean-libwhich: + -rm $(BUILDDIR)/$(LIBWHICH_SRC_DIR)/build-compiled + -$(MAKE) -C $(BUILDDIR)/$(LIBWHICH_SRC_DIR) clean + +get-libwhich: $(LIBWHICH_SRC_FILE) +extract-libwhich: $(BUILDDIR)/$(LIBWHICH_SRC_DIR)/source-extracted +configure-libwhich: extract-libwhich +compile-libwhich: $(BUILDDIR)/$(LIBWHICH_SRC_DIR)/build-compiled +fastcheck-libwhich: check-libwhich +check-libwhich: $(BUILDDIR)/$(LIBWHICH_SRC_DIR)/build-checked diff --git a/deps/libwhich.version b/deps/libwhich.version new file mode 100644 index 0000000..0fa7130 --- /dev/null +++ b/deps/libwhich.version @@ -0,0 +1,2 @@ +LIBWHICH_BRANCH=master +LIBWHICH_SHA1=81e9723c0273d78493dc8c8ed570f68d9ce7e89e diff --git a/deps/llvm-options.mk b/deps/llvm-options.mk new file mode 100644 index 0000000..14ae820 --- /dev/null +++ b/deps/llvm-options.mk @@ -0,0 +1,25 @@ +ifneq ($(LLVM_DEBUG),0) +ifeq ($(LLVM_DEBUG),1) +LLVM_BUILDTYPE := Debug +else +LLVM_BUILDTYPE := RelWithDebInfo +endif +else +LLVM_BUILDTYPE := Release +endif +LLVM_CMAKE_BUILDTYPE := $(LLVM_BUILDTYPE) +ifeq ($(LLVM_ASSERTIONS),1) +LLVM_BUILDTYPE := $(LLVM_BUILDTYPE)+Asserts +endif +LLVM_FLAVOR := $(LLVM_BUILDTYPE) +ifeq ($(LLVM_SANITIZE),1) +ifeq ($(SANITIZE_MEMORY),1) +LLVM_BUILDTYPE := $(LLVM_BUILDTYPE)+MSAN +else +LLVM_BUILDTYPE := $(LLVM_BUILDTYPE)+ASAN +endif +endif + +LLVM_SRC_DIR:=$(SRCCACHE)/llvm-$(LLVM_VER) +LLVM_BUILD_DIR:=$(BUILDDIR)/llvm-$(LLVM_VER) +LLVM_BUILDDIR_withtype := $(LLVM_BUILD_DIR)/build_$(LLVM_BUILDTYPE) diff --git a/deps/llvm-ver.make b/deps/llvm-ver.make new file mode 100644 index 0000000..c2c7f2b --- /dev/null +++ b/deps/llvm-ver.make @@ -0,0 +1,12 @@ +LLVM_VER_MAJ:=$(word 1, $(subst ., ,$(LLVM_VER))) +LLVM_VER_MIN:=$(word 2, $(subst ., ,$(LLVM_VER))) +# define a "short" LLVM version for easy comparisons +ifeq ($(LLVM_VER),svn) +LLVM_VER_SHORT:=svn +else +LLVM_VER_SHORT:=$(LLVM_VER_MAJ).$(LLVM_VER_MIN) +endif +LLVM_VER_PATCH:=$(word 3, $(subst ., ,$(LLVM_VER))) +ifeq ($(LLVM_VER_PATCH),) +LLVM_VER_PATCH := 0 +endif diff --git a/deps/llvm.mk b/deps/llvm.mk new file mode 100644 index 0000000..9ad3f35 --- /dev/null +++ b/deps/llvm.mk @@ -0,0 +1,573 @@ +## LLVM ## +include $(SRCDIR)/llvm-ver.make + +ifneq ($(USE_BINARYBUILDER_LLVM), 1) +LLVM_GIT_URL_BASE ?= http://llvm.org/git +LLVM_GIT_URL_LLVM ?= $(LLVM_GIT_URL_BASE)/llvm.git +LLVM_GIT_URL_CLANG ?= $(LLVM_GIT_URL_BASE)/clang.git +LLVM_GIT_URL_COMPILER_RT ?= $(LLVM_GIT_URL_BASE)/compiler-rt.git +LLVM_GIT_URL_LLDB ?= $(LLVM_GIT_URL_BASE)/lldb.git +LLVM_GIT_URL_LIBCXX ?= $(LLVM_GIT_URL_BASE)/libcxx.git +LLVM_GIT_URL_LIBCXXABI ?= $(LLVM_GIT_URL_BASE)/libcxxabi.git +LLVM_GIT_URL_POLLY ?= $(LLVM_GIT_URL_BASE)/polly.git + +ifeq ($(BUILD_LLDB), 1) +BUILD_LLVM_CLANG := 1 +# because it's a build requirement +endif + +ifeq ($(USE_POLLY),1) +ifeq ($(USE_SYSTEM_LLVM),0) +ifneq ($(LLVM_VER),svn) +$(error USE_POLLY=1 requires LLVM_VER=svn) +endif +endif +endif + +include $(SRCDIR)/llvm-options.mk +LLVM_LIB_FILE := libLLVMCodeGen.a + +LLVM_TAR_EXT:=$(LLVM_VER).src.tar.xz + +ifneq ($(LLVM_VER),svn) +LLVM_TAR:=$(SRCCACHE)/llvm-$(LLVM_TAR_EXT) + +ifeq ($(BUILD_LLDB),1) +LLVM_LLDB_TAR:=$(SRCCACHE)/lldb-$(LLVM_TAR_EXT) +endif # BUILD_LLDB + +ifeq ($(BUILD_LLVM_CLANG),1) +LLVM_CLANG_TAR:=$(SRCCACHE)/cfe-$(LLVM_TAR_EXT) +LLVM_COMPILER_RT_TAR:=$(SRCCACHE)/compiler-rt-$(LLVM_TAR_EXT) +else +LLVM_CLANG_TAR:= +LLVM_COMPILER_RT_TAR:= +LLVM_LIBCXX_TAR:= +endif # BUILD_LLVM_CLANG + +ifeq ($(BUILD_CUSTOM_LIBCXX),1) +LLVM_LIBCXX_TAR:=$(SRCCACHE)/libcxx-$(LLVM_TAR_EXT) +endif +endif # LLVM_VER != svn + +# Figure out which targets to build +LLVM_TARGETS := host;NVPTX;AMDGPU +LLVM_EXPERIMENTAL_TARGETS := WebAssembly + +LLVM_CFLAGS := +LLVM_CXXFLAGS := +LLVM_CPPFLAGS := +LLVM_LDFLAGS := +LLVM_CMAKE := + +# Allow adding LLVM specific flags +LLVM_CFLAGS += $(CFLAGS) +LLVM_CXXFLAGS += $(CXXFLAGS) +LLVM_CPPFLAGS += $(CPPFLAGS) +LLVM_LDFLAGS += $(LDFLAGS) +LLVM_CMAKE += -DLLVM_TARGETS_TO_BUILD:STRING="$(LLVM_TARGETS)" -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD="$(LLVM_EXPERIMENTAL_TARGETS)" -DCMAKE_BUILD_TYPE="$(LLVM_CMAKE_BUILDTYPE)" +LLVM_CMAKE += -DLLVM_ENABLE_ZLIB=OFF -DLLVM_ENABLE_LIBXML2=OFF -DLLVM_HOST_TRIPLE="$(or $(XC_HOST),$(BUILD_MACHINE))" +ifeq ($(USE_POLLY_ACC),1) +LLVM_CMAKE += -DPOLLY_ENABLE_GPGPU_CODEGEN=ON +endif +LLVM_CMAKE += -DLLVM_TOOLS_INSTALL_DIR=$(shell $(JULIAHOME)/contrib/relative_path.sh $(build_prefix) $(build_depsbindir)) +LLVM_CMAKE += -DLLVM_UTILS_INSTALL_DIR=$(shell $(JULIAHOME)/contrib/relative_path.sh $(build_prefix) $(build_depsbindir)) +LLVM_CMAKE += -DLLVM_INCLUDE_UTILS=ON -DLLVM_INSTALL_UTILS=ON +LLVM_CMAKE += -DLLVM_BINDINGS_LIST="" -DLLVM_INCLUDE_DOCS=Off -DLLVM_ENABLE_TERMINFO=Off -DHAVE_HISTEDIT_H=Off -DHAVE_LIBEDIT=Off +ifeq ($(LLVM_ASSERTIONS), 1) +LLVM_CMAKE += -DLLVM_ENABLE_ASSERTIONS:BOOL=ON +endif # LLVM_ASSERTIONS +ifeq ($(LLVM_DEBUG), 1) +ifeq ($(OS), WINNT) +LLVM_CXXFLAGS += -Wa,-mbig-obj +endif # OS == WINNT +endif # LLVM_DEBUG +ifeq ($(OS), WINNT) +LLVM_CPPFLAGS += -D__USING_SJLJ_EXCEPTIONS__ -D__CRT__NO_INLINE +ifneq ($(BUILD_OS),WINNT) +LLVM_CMAKE += -DCROSS_TOOLCHAIN_FLAGS_NATIVE=-DCMAKE_TOOLCHAIN_FILE=$(SRCDIR)/NATIVE.cmake +endif # BUILD_OS != WINNT +endif # OS == WINNT +ifeq ($(OS), emscripten) +LLVM_CMAKE += -DCMAKE_TOOLCHAIN_FILE=$(EMSCRIPTEN)/cmake/Modules/Platform/Emscripten.cmake -DCROSS_TOOLCHAIN_FLAGS_NATIVE=-DCMAKE_TOOLCHAIN_FILE=$(SRCDIR)/NATIVE.cmake -DLLVM_INCLUDE_TOOLS=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_ENABLE_THREADS=OFF -DLLVM_BUILD_UTILS=OFF +endif # OS == emscripten +ifeq ($(USE_LLVM_SHLIB),1) +# NOTE: we could also --disable-static here (on the condition we link tools +# against libLLVM) but there doesn't seem to be a CMake counterpart option +LLVM_CMAKE += -DLLVM_BUILD_LLVM_DYLIB:BOOL=ON -DLLVM_LINK_LLVM_DYLIB:BOOL=ON +endif +ifeq ($(USE_INTEL_JITEVENTS), 1) +LLVM_CMAKE += -DLLVM_USE_INTEL_JITEVENTS:BOOL=ON +endif # USE_INTEL_JITEVENTS + +ifeq ($(USE_OPROFILE_JITEVENTS), 1) +LLVM_CMAKE += -DLLVM_USE_OPROFILE:BOOL=ON +endif # USE_OPROFILE_JITEVENTS + +ifeq ($(USE_PERF_JITEVENTS), 1) + LLVM_CMAKE += -DLLVM_USE_PERF:BOOL=ON +endif # USE_PERF_JITEVENTS + +ifeq ($(BUILD_LLDB),1) +ifeq ($(USECLANG),0) +LLVM_CXXFLAGS += -std=c++0x +endif # USECLANG +ifeq ($(LLDB_DISABLE_PYTHON),1) +LLVM_CXXFLAGS += -DLLDB_DISABLE_PYTHON +LLVM_CMAKE += -DLLDB_DISABLE_PYTHON=ON +endif # LLDB_DISABLE_PYTHON +endif # BUILD_LLDB + +ifneq (,$(filter $(ARCH), powerpc64le ppc64le)) +LLVM_CXXFLAGS += -mminimal-toc +endif + +ifeq ($(LLVM_SANITIZE),1) +ifeq ($(SANITIZE_MEMORY),1) +LLVM_CFLAGS += -fsanitize=memory -fsanitize-memory-track-origins +LLVM_LDFLAGS += -fsanitize=memory -fsanitize-memory-track-origins +LLVM_CXXFLAGS += -fsanitize=memory -fsanitize-memory-track-origins +LLVM_CMAKE += -DLLVM_USE_SANITIZER="MemoryWithOrigins" +else +LLVM_CFLAGS += -fsanitize=address +LLVM_LDFLAGS += -fsanitize=address +LLVM_CXXFLAGS += -fsanitize=address +LLVM_CMAKE += -DLLVM_USE_SANITIZER="Address" +endif +endif # LLVM_SANITIZE + +ifeq ($(LLVM_LTO),1) +LLVM_CPPFLAGS += -flto +LLVM_LDFLAGS += -flto +endif # LLVM_LTO + +ifeq ($(fPIC),) +LLVM_CMAKE += -DLLVM_ENABLE_PIC=OFF +endif + +ifeq ($(BUILD_CUSTOM_LIBCXX),1) +LLVM_LDFLAGS += -Wl,-rpath,$(build_libdir) +LLVM_CPPFLAGS += -I$(build_includedir) +# We don't want to link to libc++ while trying to build it, so we define these +# flags separately so that we can still pass them to the main LLVM build +LLVM_LIBCXX_LDFLAGS := -lc++ -lc++abi +ifeq ($(USEICC),1) +LLVM_LDFLAGS += -no_cpprt +endif # USEICC +else +LLVM_LIBCXX_LDFLAGS := +endif # BUILD_CUSTOM_LIBCXX + +LLVM_CMAKE += -DCMAKE_C_FLAGS="$(LLVM_CPPFLAGS) $(LLVM_CFLAGS)" \ + -DCMAKE_CXX_FLAGS="$(LLVM_CPPFLAGS) $(LLVM_CXXFLAGS)" + +ifeq ($(BUILD_LLVM_CLANG),0) +# block default building of Clang +LLVM_CMAKE += -DLLVM_TOOL_CLANG_BUILD=OFF +LLVM_CMAKE += -DLLVM_TOOL_COMPILER_RT_BUILD=OFF +endif +ifeq ($(BUILD_LLDB),0) +# block default building of lldb +LLVM_CMAKE += -DLLVM_TOOL_LLDB_BUILD=OFF +endif + +LLVM_SRC_URL := http://releases.llvm.org/$(LLVM_VER) + +ifneq ($(LLVM_CLANG_TAR),) +$(LLVM_CLANG_TAR): | $(SRCCACHE) + $(JLDOWNLOAD) $@ $(LLVM_SRC_URL)/$(notdir $@) +endif +ifneq ($(LLVM_COMPILER_RT_TAR),) +$(LLVM_COMPILER_RT_TAR): | $(SRCCACHE) + $(JLDOWNLOAD) $@ $(LLVM_SRC_URL)/$(notdir $@) +endif + +ifneq ($(LLVM_LIBCXX_TAR),) +$(LLVM_LIBCXX_TAR): | $(SRCCACHE) + $(JLDOWNLOAD) $@ $(LLVM_SRC_URL)/$(notdir $@) +endif +ifneq ($(LLVM_VER),svn) +$(LLVM_TAR): | $(SRCCACHE) + $(JLDOWNLOAD) $@ $(LLVM_SRC_URL)/$(notdir $@) +endif + +ifneq ($(LLVM_LLDB_TAR),) +$(LLVM_LLDB_TAR): | $(SRCCACHE) + $(JLDOWNLOAD) $@ $(LLVM_SRC_URL)/$(notdir $@) +endif +ifeq ($(BUILD_LLDB),1) +$(LLVM_SRC_DIR)/tools/lldb: +$(LLVM_SRC_DIR)/source-extracted: $(LLVM_SRC_DIR)/tools/lldb +endif + +# LLDB still relies on plenty of python 2.x infrastructure, without checking +llvm_python_location=$(shell /usr/bin/env python2 -c 'import sys; print(sys.executable)') +llvm_python_workaround=$(SRCCACHE)/python2_path +$(llvm_python_workaround): + mkdir -p $@ + -python -c 'import sys; sys.exit(not sys.version_info > (3, 0))' && \ + /usr/bin/env python2 -c 'import sys; sys.exit(not sys.version_info < (3, 0))' && \ + ln -sf $(llvm_python_location) "$@/python" && \ + ln -sf $(llvm_python_location)-config "$@/python-config" + +ifeq ($(BUILD_CUSTOM_LIBCXX),1) + +# Take a snapshot of the CMake flags before linking to -lc++ and -lc++abi +# These are added to the LLVM CMake flags further down +LLVM_CMAKE_LIBCXX := $(LLVM_CMAKE) \ + -DCMAKE_EXE_LINKER_FLAGS="$(LLVM_LDFLAGS)" \ + -DCMAKE_SHARED_LINKER_FLAGS="$(LLVM_LDFLAGS)" + +ifeq ($(USEICC),1) +LIBCXX_EXTRA_FLAGS := -Bstatic -lirc -Bdynamic +endif + +# These libraries require unwind.h from the libunwind dependency +ifeq ($(USE_SYSTEM_LIBUNWIND),0) +ifeq ($(OS),Darwin) +BUILT_UNWIND := $(build_prefix)/manifest/osxunwind +else +BUILT_UNWIND := $(build_prefix)/manifest/unwind +endif # Darwin +else +BUILT_UNWIND := +endif # Building libunwind + +$(LLVM_SRC_DIR)/projects/libcxx: $(LLVM_LIBCXX_TAR) | $(LLVM_SRC_DIR)/source-extracted + ([ ! -d $@ ] && \ + git clone $(LLVM_GIT_URL_LIBCXX) $@ ) || \ + (cd $@ && \ + git pull --ff-only) +$(LLVM_SRC_DIR)/projects/libcxx/.git/HEAD: | $(LLVM_SRC_DIR)/projects/libcxx +$(LLVM_SRC_DIR)/projects/libcxxabi: $(LLVM_LIBCXXABI_TAR) | $(LLVM_SRC_DIR)/source-extracted + ([ ! -d $@ ] && \ + git clone $(LLVM_GIT_URL_LIBCXXABI) $@ ) || \ + (cd $@ && \ + git pull --ff-only) +$(LLVM_SRC_DIR)/projects/libcxxabi/.git/HEAD: | $(LLVM_SRC_DIR)/projects/libcxxabi +$(LLVM_BUILD_DIR)/libcxx-build/Makefile: | $(LLVM_SRC_DIR)/projects/libcxx $(LLVM_SRC_DIR)/projects/libcxxabi $(BUILT_UNWIND) + mkdir -p $(dir $@) + cd $(dir $@) && \ + $(CMAKE) -G "Unix Makefiles" $(CMAKE_COMMON) $(LLVM_CMAKE_LIBCXX) -DLIBCXX_CXX_ABI=libcxxabi -DLIBCXX_CXX_ABI_INCLUDE_PATHS="$(LLVM_SRC_DIR)/projects/libcxxabi/include" $(LLVM_SRC_DIR)/projects/libcxx -DCMAKE_SHARED_LINKER_FLAGS="$(LDFLAGS) -L$(build_libdir) $(LIBCXX_EXTRA_FLAGS)" +$(LLVM_BUILD_DIR)/libcxxabi-build/Makefile: | $(LLVM_SRC_DIR)/projects/libcxxabi $(LLVM_SRC_DIR)/projects/libcxx $(BUILT_UNWIND) + mkdir -p $(dir $@) + cd $(dir $@) && \ + $(CMAKE) -G "Unix Makefiles" $(CMAKE_COMMON) $(LLVM_CMAKE_LIBCXX) -DLLVM_ABI_BREAKING_CHECKS="WITH_ASSERTS" -DLLVM_PATH="$(LLVM_SRC_DIR)" $(LLVM_SRC_DIR)/projects/libcxxabi -DLIBCXXABI_CXX_ABI_LIBRARIES="$(LIBCXX_EXTRA_FLAGS)" -DCMAKE_CXX_FLAGS="$(LLVM_CPPFLAGS) $(LLVM_CXXFLAGS) -std=c++11" +$(LLVM_BUILD_DIR)/libcxxabi-build/lib/libc++abi.so.1.0: $(LLVM_BUILD_DIR)/libcxxabi-build/Makefile $(LLVM_SRC_DIR)/projects/libcxxabi/.git/HEAD + $(MAKE) -C $(LLVM_BUILD_DIR)/libcxxabi-build + touch -c $@ +$(build_libdir)/libc++abi.so.1.0: $(LLVM_BUILD_DIR)/libcxxabi-build/lib/libc++abi.so.1.0 + $(MAKE) -C $(LLVM_BUILD_DIR)/libcxxabi-build install + touch -c $@ + # Building this library installs these headers, which breaks other dependencies + -rm -rf $(build_includedir)/c++ +$(LLVM_BUILD_DIR)/libcxx-build/lib/libc++.so.1.0: $(build_libdir)/libc++abi.so.1.0 $(LLVM_BUILD_DIR)/libcxx-build/Makefile $(LLVM_SRC_DIR)/projects/libcxx/.git/HEAD + $(MAKE) -C $(LLVM_BUILD_DIR)/libcxx-build +$(build_libdir)/libc++.so.1.0: $(LLVM_BUILD_DIR)/libcxx-build/lib/libc++.so.1.0 + $(MAKE) -C $(LLVM_BUILD_DIR)/libcxx-build install + touch -c $@ + # Building this library installs these headers, which breaks other dependencies + -rm -rf $(build_includedir)/c++ +get-libcxx: $(LLVM_SRC_DIR)/projects/libcxx +get-libcxxabi: $(LLVM_SRC_DIR)/projects/libcxxabi +install-libcxxabi: $(build_libdir)/libc++abi.so.1.0 +install-libcxx: $(build_libdir)/libc++.so.1.0 +endif # BUILD_CUSTOM_LIBCXX + +# We want to be able to clean without having to pass BUILD_CUSTOM_LIBCXX=1, so define these +# outside of the conditional above +clean-libcxx: + -$(MAKE) -C $(LLVM_BUILD_DIR)/libcxx-build clean +clean-libcxxabi: + -$(MAKE) -C $(LLVM_BUILD_DIR)/libcxxabi-build clean +distclean-libcxx: + -rm -rf $(LLVM_LIBCXX_TAR) $(LLVM_SRC_DIR)/projects/libcxx $(LLVM_BUILD_DIR)/libcxx-build +distclean-libcxxabi: + -rm -rf $(LLVM_LIBCXXABI_TAR) $(LLVM_SRC_DIR)/projects/libcxxabi $(LLVM_BUILD_DIR)/libcxxabi-build + +# We want to ensure that the libcxx linking flags don't get passed to the libcxx build, since it will +# error on a fresh build +LLVM_CMAKE += -DCMAKE_EXE_LINKER_FLAGS="$(LLVM_LDFLAGS) $(LLVM_LIBCXX_LDFLAGS)" \ + -DCMAKE_SHARED_LINKER_FLAGS="$(LLVM_LDFLAGS) $(LLVM_LIBCXX_LDFLAGS)" + +ifeq ($(BUILD_CUSTOM_LIBCXX),1) +LIBCXX_DEPENDENCY := $(build_libdir)/libc++abi.so.1.0 $(build_libdir)/libc++.so.1.0 +get-llvm: get-libcxx get-libcxxabi +endif + +$(LLVM_SRC_DIR)/source-extracted: | $(LLVM_TAR) $(LLVM_CLANG_TAR) $(LLVM_COMPILER_RT_TAR) $(LLVM_LIBCXX_TAR) $(LLVM_LLDB_TAR) +ifneq ($(LLVM_CLANG_TAR),) + $(JLCHECKSUM) $(LLVM_CLANG_TAR) +endif +ifneq ($(LLVM_COMPILER_RT_TAR),) + $(JLCHECKSUM) $(LLVM_COMPILER_RT_TAR) +endif +ifneq ($(LLVM_LIBCXX_TAR),) + $(JLCHECKSUM) $(LLVM_LIBCXX_TAR) +endif +ifneq ($(LLVM_VER),svn) + $(JLCHECKSUM) $(LLVM_TAR) +endif +ifneq ($(LLVM_LLDB_TAR),) + $(JLCHECKSUM) $(LLVM_LLDB_TAR) +endif + -rm -rf $(LLVM_SRC_DIR) +ifneq ($(LLVM_VER),svn) + mkdir -p $(LLVM_SRC_DIR) + $(TAR) -C $(LLVM_SRC_DIR) --strip-components 1 -xf $(LLVM_TAR) +else + ([ ! -d $(LLVM_SRC_DIR) ] && \ + git clone $(LLVM_GIT_URL_LLVM) $(LLVM_SRC_DIR) ) || \ + (cd $(LLVM_SRC_DIR) && \ + git pull --ff-only) +ifneq ($(LLVM_GIT_VER),) + (cd $(LLVM_SRC_DIR) && \ + git checkout $(LLVM_GIT_VER)) +endif # LLVM_GIT_VER + # Debug output only. Disable pager and ignore error. + (cd $(LLVM_SRC_DIR) && \ + git show HEAD --stat | cat) || true +endif # LLVM_VER +ifneq ($(LLVM_VER),svn) +ifneq ($(LLVM_CLANG_TAR),) + mkdir -p $(LLVM_SRC_DIR)/tools/clang + $(TAR) -C $(LLVM_SRC_DIR)/tools/clang --strip-components 1 -xf $(LLVM_CLANG_TAR) +endif # LLVM_CLANG_TAR +ifneq ($(LLVM_COMPILER_RT_TAR),) + mkdir -p $(LLVM_SRC_DIR)/projects/compiler-rt + $(TAR) -C $(LLVM_SRC_DIR)/projects/compiler-rt --strip-components 1 -xf $(LLVM_COMPILER_RT_TAR) +endif # LLVM_COMPILER_RT_TAR +ifneq ($(LLVM_LLDB_TAR),) + mkdir -p $(LLVM_SRC_DIR)/tools/lldb + $(TAR) -C $(LLVM_SRC_DIR)/tools/lldb --strip-components 1 -xf $(LLVM_LLDB_TAR) +endif # LLVM_LLDB_TAR +else # LLVM_VER +ifeq ($(BUILD_LLVM_CLANG),1) + ([ ! -d $(LLVM_SRC_DIR)/tools/clang ] && \ + git clone $(LLVM_GIT_URL_CLANG) $(LLVM_SRC_DIR)/tools/clang ) || \ + (cd $(LLVM_SRC_DIR)/tools/clang && \ + git pull --ff-only) + ([ ! -d $(LLVM_SRC_DIR)/projects/compiler-rt ] && \ + git clone $(LLVM_GIT_URL_COMPILER_RT) $(LLVM_SRC_DIR)/projects/compiler-rt ) || \ + (cd $(LLVM_SRC_DIR)/projects/compiler-rt && \ + git pull --ff-only) +ifneq ($(LLVM_GIT_VER_CLANG),) + (cd $(LLVM_SRC_DIR)/tools/clang && \ + git checkout $(LLVM_GIT_VER_CLANG)) +endif # LLVM_GIT_VER_CLANG +endif # BUILD_LLVM_CLANG +ifeq ($(BUILD_LLDB),1) + ([ ! -d $(LLVM_SRC_DIR)/tools/lldb ] && \ + git clone $(LLVM_GIT_URL_LLDB) $(LLVM_SRC_DIR)/tools/lldb ) || \ + (cd $(LLVM_SRC_DIR)/tools/lldb && \ + git pull --ff-only) +ifneq ($(LLVM_GIT_VER_LLDB),) + (cd $(LLVM_SRC_DIR)/tools/lldb && \ + git checkout $(LLVM_GIT_VER_LLDB)) +endif # LLVM_GIT_VER_CLANG +endif # BUILD_LLDB +ifeq ($(USE_POLLY),1) + ([ ! -d $(LLVM_SRC_DIR)/tools/polly ] && \ + git clone $(LLVM_GIT_URL_POLLY) $(LLVM_SRC_DIR)/tools/polly ) || \ + (cd $(LLVM_SRC_DIR)/tools/polly && \ + git pull --ff-only) +ifneq ($(LLVM_GIT_VER_POLLY),) + (cd $(LLVM_SRC_DIR)/tools/polly && \ + git checkout $(LLVM_GIT_VER_POLLY)) +endif # LLVM_GIT_VER_POLLY +endif # USE_POLLY +endif # LLVM_VER + # touch some extra files to ensure bisect works pretty well + touch -c $(LLVM_SRC_DIR).extracted + touch -c $(LLVM_SRC_DIR)/configure + touch -c $(LLVM_SRC_DIR)/CMakeLists.txt + echo 1 > $@ + +# Apply version-specific LLVM patches sequentially +LLVM_PATCH_PREV := +define LLVM_PATCH +$$(LLVM_SRC_DIR)/$1.patch-applied: $$(LLVM_SRC_DIR)/source-extracted | $$(SRCDIR)/patches/$1.patch $$(LLVM_PATCH_PREV) + cd $$(LLVM_SRC_DIR) && patch -p1 < $$(SRCDIR)/patches/$1.patch + echo 1 > $$@ +# declare that applying any patch must re-run the compile step +$$(LLVM_BUILDDIR_withtype)/build-compiled: $$(LLVM_SRC_DIR)/$1.patch-applied +LLVM_PATCH_PREV := $$(LLVM_SRC_DIR)/$1.patch-applied +endef + +ifeq ($(LLVM_VER_SHORT),6.0) +ifeq ($(LLVM_VER_PATCH), 0) +$(eval $(call LLVM_PATCH,llvm-D27629-AArch64-large_model_4.0)) +else +$(eval $(call LLVM_PATCH,llvm-D27629-AArch64-large_model_6.0.1)) +endif +$(eval $(call LLVM_PATCH,llvm-D34078-vectorize-fdiv)) +$(eval $(call LLVM_PATCH,llvm-6.0-NVPTX-addrspaces)) # NVPTX +$(eval $(call LLVM_PATCH,llvm-D42262-jumpthreading-not-i1)) # remove for 7.0 +$(eval $(call LLVM_PATCH,llvm-PPC-addrspaces)) # remove for 7.0 +ifeq ($(LLVM_VER_PATCH), 0) +$(eval $(call LLVM_PATCH,llvm-D42260)) # remove for 7.0 +$(eval $(call LLVM_PATCH,llvm-rL326843-missing-header)) # remove for 7.0 +$(eval $(call LLVM_PATCH,llvm-6.0-r327540)) # remove for 7.0 +endif +$(eval $(call LLVM_PATCH,llvm-6.0.0_D27296-libssp)) # remove for 7.0 +$(eval $(call LLVM_PATCH,llvm-6.0-D44650)) # mingw32 build fix +ifeq ($(LLVM_VER_PATCH), 0) +$(eval $(call LLVM_PATCH,llvm-D45008)) # remove for 7.0 +$(eval $(call LLVM_PATCH,llvm-D45070)) # remove for 7.0 +$(eval $(call LLVM_PATCH,llvm-6.0.0-ifconv-D45819)) # remove for 7.0 +endif +$(eval $(call LLVM_PATCH,llvm-D46460)) +ifeq ($(LLVM_VER_PATCH), 0) +$(eval $(call LLVM_PATCH,llvm-rL332680)) # remove for 7.0 +$(eval $(call LLVM_PATCH,llvm-rL332682)) # remove for 7.0 +$(eval $(call LLVM_PATCH,llvm-rL332302)) # remove for 7.0 +$(eval $(call LLVM_PATCH,llvm-rL332694)) # remove for 7.0 +endif +$(eval $(call LLVM_PATCH,llvm-rL327898)) # remove for 7.0 +$(eval $(call LLVM_PATCH,llvm-6.0-DISABLE_ABI_CHECKS)) +$(eval $(call LLVM_PATCH,llvm-OProfile-line-num)) +$(eval $(call LLVM_PATCH,llvm-D44892-Perf-integration)) +$(eval $(call LLVM_PATCH,llvm-D49832-SCEVPred)) # Remove for 7.0 +$(eval $(call LLVM_PATCH,llvm-rL323946-LSRTy)) # Remove for 7.0 +$(eval $(call LLVM_PATCH,llvm-D50010-VNCoercion-ni)) +$(eval $(call LLVM_PATCH,llvm-D50167-scev-umin)) +$(eval $(call LLVM_PATCH,llvm-rL326967-aligned-load)) # remove for 7.0 +ifeq ($(LLVM_VER_PATCH), 0) +$(eval $(call LLVM_PATCH,llvm-windows-race)) +endif +$(eval $(call LLVM_PATCH,llvm-D51842-win64-byval-cc)) +$(eval $(call LLVM_PATCH,llvm-D57118-powerpc)) +$(eval $(call LLVM_PATCH,llvm-r355582-avxminmax)) # remove for 8.0 +$(eval $(call LLVM_PATCH,llvm-rL349068-llvm-config)) # remove for 8.0 +$(eval $(call LLVM_PATCH,llvm-6.0-D63688-wasm-isLocal)) +$(eval $(call LLVM_PATCH,llvm-6.0-D64032-cmake-cross)) +$(eval $(call LLVM_PATCH,llvm-6.0-D64225-cmake-cross2)) +$(eval $(call LLVM_PATCH,llvm6-WASM-addrspaces)) # WebAssembly +endif # LLVM_VER 6.0 + +ifeq ($(LLVM_VER_SHORT),7.0) +$(eval $(call LLVM_PATCH,llvm-D27629-AArch64-large_model_6.0.1)) +$(eval $(call LLVM_PATCH,llvm-D34078-vectorize-fdiv)) +$(eval $(call LLVM_PATCH,llvm-6.0-NVPTX-addrspaces)) # NVPTX -- warning: this fails check-llvm-codegen-nvptx +$(eval $(call LLVM_PATCH,llvm-7.0-D44650)) # mingw32 build fix +$(eval $(call LLVM_PATCH,llvm-D46460)) +$(eval $(call LLVM_PATCH,llvm-6.0-DISABLE_ABI_CHECKS)) +$(eval $(call LLVM_PATCH,llvm7-D50010-VNCoercion-ni)) +$(eval $(call LLVM_PATCH,llvm-7.0-D50167-scev-umin)) +$(eval $(call LLVM_PATCH,llvm7-windows-race)) +$(eval $(call LLVM_PATCH,llvm7-D51842-win64-byval-cc)) # remove for 8.0 +$(eval $(call LLVM_PATCH,llvm-D57118-powerpc)) +$(eval $(call LLVM_PATCH,llvm-rL349068-llvm-config)) # remove for 8.0 +$(eval $(call LLVM_PATCH,llvm7-WASM-addrspaces)) # WebAssembly +endif # LLVM_VER 7.0 + +ifeq ($(LLVM_VER_SHORT),8.0) +$(eval $(call LLVM_PATCH,llvm-D27629-AArch64-large_model_6.0.1)) +$(eval $(call LLVM_PATCH,llvm8-D34078-vectorize-fdiv)) +$(eval $(call LLVM_PATCH,llvm-6.0-NVPTX-addrspaces)) # NVPTX -- warning: this fails check-llvm-codegen-nvptx +$(eval $(call LLVM_PATCH,llvm-7.0-D44650)) # mingw32 build fix +$(eval $(call LLVM_PATCH,llvm-6.0-DISABLE_ABI_CHECKS)) +$(eval $(call LLVM_PATCH,llvm7-D50010-VNCoercion-ni)) +$(eval $(call LLVM_PATCH,llvm-8.0-D50167-scev-umin)) +$(eval $(call LLVM_PATCH,llvm7-windows-race)) +$(eval $(call LLVM_PATCH,llvm-D57118-powerpc)) # remove for 9.0 +$(eval $(call LLVM_PATCH,llvm8-WASM-addrspaces)) # WebAssembly +endif # LLVM_VER 8.0 + +# Add a JL prefix to the version map. DO NOT REMOVE +ifneq ($(LLVM_VER), svn) +ifeq ($(LLVM_VER_SHORT), 6.0) +$(eval $(call LLVM_PATCH,llvm-symver-jlprefix)) +else +$(eval $(call LLVM_PATCH,llvm7-symver-jlprefix)) +endif +endif + +# declare that all patches must be applied before running ./configure +$(LLVM_BUILDDIR_withtype)/build-configured: | $(LLVM_PATCH_PREV) + +$(LLVM_BUILDDIR_withtype)/build-configured: $(LLVM_SRC_DIR)/source-extracted | $(llvm_python_workaround) $(LIBCXX_DEPENDENCY) + mkdir -p $(dir $@) + cd $(dir $@) && \ + export PATH=$(llvm_python_workaround):"$$PATH" && \ + $(CMAKE) $(LLVM_SRC_DIR) $(CMAKE_GENERATOR_COMMAND) $(CMAKE_COMMON) $(LLVM_CMAKE) \ + || { echo '*** To install a newer version of cmake, run contrib/download_cmake.sh ***' && false; } + echo 1 > $@ + +$(LLVM_BUILDDIR_withtype)/build-compiled: $(LLVM_BUILDDIR_withtype)/build-configured | $(llvm_python_workaround) + cd $(LLVM_BUILDDIR_withtype) && \ + export PATH=$(llvm_python_workaround):"$$PATH" && \ + $(if $(filter $(CMAKE_GENERATOR),make), \ + $(MAKE), \ + $(CMAKE) --build .) + echo 1 > $@ + +$(LLVM_BUILDDIR_withtype)/build-checked: $(LLVM_BUILDDIR_withtype)/build-compiled | $(llvm_python_workaround) +ifeq ($(OS),$(BUILD_OS)) + cd $(LLVM_BUILDDIR_withtype) && \ + export PATH=$(llvm_python_workaround):"$$PATH" && \ + $(CMAKE) --build . --target check +endif + echo 1 > $@ + +$(build_prefix)/manifest/llvm: | $(llvm_python_workaround) + +LLVM_INSTALL = \ + cd $1 && mkdir -p $2$$(build_depsbindir) && \ + cp -r $$(LLVM_SRC_DIR)/utils/lit $2$$(build_depsbindir)/ && \ + $$(CMAKE) -DCMAKE_INSTALL_PREFIX="$2$$(build_prefix)" -P cmake_install.cmake +ifeq ($(OS), WINNT) +LLVM_INSTALL += && cp $2$$(build_shlibdir)/LLVM.dll $2$$(build_depsbindir) +endif +ifeq ($(OS),Darwin) +# https://github.com/JuliaLang/julia/issues/29981 +LLVM_INSTALL += && ln -s libLLVM.dylib $2$$(build_shlibdir)/libLLVM-$$(LLVM_VER_SHORT).dylib +endif + +$(eval $(call staged-install,llvm,llvm-$$(LLVM_VER)/build_$$(LLVM_BUILDTYPE), \ + LLVM_INSTALL,,,)) + +clean-llvm: clean-libcxx clean-libcxxabi + -rm $(LLVM_BUILDDIR_withtype)/build-configured $(LLVM_BUILDDIR_withtype)/build-compiled + -$(MAKE) -C $(LLVM_BUILDDIR_withtype) clean + +distclean-llvm: distclean-libcxx distclean-libcxxabi + -rm -rf $(LLVM_TAR) $(LLVM_CLANG_TAR) \ + $(LLVM_COMPILER_RT_TAR) $(LLVM_LIBCXX_TAR) $(LLVM_LLDB_TAR) \ + $(LLVM_SRC_DIR) $(LLVM_BUILDDIR_withtype) + + +ifneq ($(LLVM_VER),svn) +get-llvm: $(LLVM_TAR) $(LLVM_CLANG_TAR) $(LLVM_COMPILER_RT_TAR) $(LLVM_LIBCXX_TAR) $(LLVM_LLDB_TAR) +else +get-llvm: $(LLVM_SRC_DIR)/source-extracted +endif +extract-llvm: $(LLVM_SRC_DIR)/source-extracted +configure-llvm: $(LLVM_BUILDDIR_withtype)/build-configured +compile-llvm: $(LLVM_BUILDDIR_withtype)/build-compiled +fastcheck-llvm: #none +check-llvm: $(LLVM_BUILDDIR_withtype)/build-checked +#todo: LLVM make check target is broken on julia.mit.edu (and really slow elsewhere) + + +ifeq ($(LLVM_VER),svn) +update-llvm: + (cd $(LLVM_SRC_DIR); git pull --ff-only) + ([ -d "$(LLVM_SRC_DIR)/tools/clang" ] || exit 0; cd $(LLVM_SRC_DIR)/tools/clang; git pull --ff-only) + ([ -d "$(LLVM_SRC_DIR)/projects/compiler-rt" ] || exit 0; cd $(LLVM_SRC_DIR)/projects/compiler-rt; git pull --ff-only) + ([ -d "$(LLVM_SRC_DIR)/tools/lldb" ] || exit 0; cd $(LLVM_SRC_DIR)/tools/lldb; git pull --ff-only) +ifeq ($(USE_POLLY),1) + ([ -d "$(LLVM_SRC_DIR)/tools/polly" ] || exit 0; cd $(LLVM_SRC_DIR)/tools/polly; git pull --ff-only) +endif +endif +else # USE_BINARYBUILDER_LLVM +LLVM_BB_URL_BASE := https://github.com/JuliaPackaging/Yggdrasil/releases/download/LLVM-v$(LLVM_VER)-$(LLVM_BB_REL) +ifneq ($(BINARYBUILDER_LLVM_ASSERTS), 1) +LLVM_BB_NAME := LLVM.v$(LLVM_VER) +else +LLVM_BB_NAME := LLVM.asserts.v$(LLVM_VER) +endif + +$(eval $(call bb-install,llvm,LLVM,true)) + +endif # USE_BINARYBUILDER_LLVM diff --git a/deps/mbedtls.mk b/deps/mbedtls.mk new file mode 100644 index 0000000..677d9a1 --- /dev/null +++ b/deps/mbedtls.mk @@ -0,0 +1,98 @@ +## mbedtls + +ifneq ($(USE_BINARYBUILDER_MBEDTLS), 1) +ifeq ($(USE_GPL_LIBS), 1) +MBEDTLS_SRC = mbedtls-$(MBEDTLS_VER)-gpl +else +MBEDTLS_SRC = mbedtls-$(MBEDTLS_VER)-apache +endif +MBEDTLS_URL = https://tls.mbed.org/download/$(MBEDTLS_SRC).tgz + +MBEDTLS_OPTS := $(CMAKE_COMMON) -DUSE_SHARED_MBEDTLS_LIBRARY=ON \ + -DUSE_STATIC_MBEDTLS_LIBRARY=OFF -DENABLE_PROGRAMS=OFF -DCMAKE_BUILD_TYPE=Release + +MBEDTLS_OPTS += -DENABLE_ZLIB_SUPPORT=OFF +ifeq ($(BUILD_OS),WINNT) +MBEDTLS_OPTS += -G"MSYS Makefiles" +endif + +ifneq (,$(findstring $(OS),Linux FreeBSD)) +MBEDTLS_OPTS += -DCMAKE_INSTALL_RPATH="\$$ORIGIN" +endif + +$(SRCCACHE)/$(MBEDTLS_SRC).tgz: | $(SRCCACHE) + $(JLDOWNLOAD) $@ $(MBEDTLS_URL) + +$(SRCCACHE)/$(MBEDTLS_SRC)/source-extracted: $(SRCCACHE)/$(MBEDTLS_SRC).tgz + $(JLCHECKSUM) $< + mkdir -p $(dir $@) && \ + $(TAR) -C $(dir $@) --strip-components 1 -xf $< + touch -c $(SRCCACHE)/$(MBEDTLS_SRC)/CMakeLists.txt # old target + echo 1 > $@ + +$(BUILDDIR)/$(MBEDTLS_SRC)/build-configured: $(SRCCACHE)/$(MBEDTLS_SRC)/source-extracted + mkdir -p $(dir $@) + cd $(dir $@) && \ + $(CMAKE) $(dir $<) $(MBEDTLS_OPTS) + echo 1 > $@ + +$(BUILDDIR)/$(MBEDTLS_SRC)/build-compiled: $(BUILDDIR)/$(MBEDTLS_SRC)/build-configured + $(MAKE) -C $(dir $<) + echo 1 > $@ + +$(BUILDDIR)/$(MBEDTLS_SRC)/build-checked: $(BUILDDIR)/$(MBEDTLS_SRC)/build-compiled +ifeq ($(OS),$(BUILD_OS)) + $(MAKE) -C $(dir $@) test +endif + echo 1 > $@ + +ifeq ($(OS),WINNT) +define MBEDTLS_INSTALL + mkdir -p $2/$$(build_shlibdir) + cp $1/library/libmbedcrypto.$$(SHLIB_EXT) $2/$$(build_shlibdir) + cp $1/library/libmbedx509.$$(SHLIB_EXT) $2/$$(build_shlibdir) + cp $1/library/libmbedtls.$$(SHLIB_EXT) $2/$$(build_shlibdir) +endef +else +define MBEDTLS_INSTALL + $(call MAKE_INSTALL,$1,$2,) +endef +endif +$(eval $(call staged-install, \ + mbedtls,$(MBEDTLS_SRC), \ + MBEDTLS_INSTALL,,, \ + $$(INSTALL_NAME_CMD)libmbedx509.$$(SHLIB_EXT) $$(build_shlibdir)/libmbedx509.$$(SHLIB_EXT) && \ + $$(INSTALL_NAME_CMD)libmbedtls.$$(SHLIB_EXT) $$(build_shlibdir)/libmbedtls.$$(SHLIB_EXT) && \ + $$(INSTALL_NAME_CHANGE_CMD) libmbedx509.0.dylib @rpath/libmbedx509.$$(SHLIB_EXT) $$(build_shlibdir)/libmbedtls.$$(SHLIB_EXT) && \ + $$(INSTALL_NAME_CHANGE_CMD) libmbedcrypto.3.dylib @rpath/libmbedcrypto.$$(SHLIB_EXT) $$(build_shlibdir)/libmbedtls.$$(SHLIB_EXT) && \ + $$(INSTALL_NAME_CHANGE_CMD) libmbedcrypto.3.dylib @rpath/libmbedcrypto.$$(SHLIB_EXT) $$(build_shlibdir)/libmbedx509.$$(SHLIB_EXT) && \ + $$(INSTALL_NAME_CMD)libmbedcrypto.$$(SHLIB_EXT) $$(build_shlibdir)/libmbedcrypto.$$(SHLIB_EXT))) + + +clean-mbedtls: + -rm $(BUILDDIR)/$(MBEDTLS_SRC)/build-configured \ + $(BUILDDIR)/$(MBEDTLS_SRC)/build-compiled + -$(MAKE) -C $(BUILDDIR)/$(MBEDTLS_SRC) clean + +distclean-mbedtls: + -rm -rf $(SRCCACHE)/$(MBEDTLS_SRC).tgz \ + $(SRCCACHE)/$(MBEDTLS_SRC) \ + $(BUILDDIR)/$(MBEDTLS_SRC) + + +get-mbedtls: $(SRCCACHE)/$(MBEDTLS_SRC).tgz +extract-mbedtls: $(SRCCACHE)/$(MBEDTLS_SRC)/source-extracted +configure-mbedtls: $(BUILDDIR)/$(MBEDTLS_SRC)/build-configured +compile-mbedtls: $(BUILDDIR)/$(MBEDTLS_SRC)/build-compiled +# tests disabled since they are known to fail +fastcheck-mbedtls: #check-mbedtls +check-mbedtls: $(BUILDDIR)/$(MBEDTLS_SRC)/build-checked + +else # USE_BINARYBUILDER_MBEDTLS + +MBEDTLS_BB_URL_BASE := https://github.com/JuliaWeb/MbedTLSBuilder/releases/download/$(MBEDTLS_BB_REL) +MBEDTLS_BB_NAME := MbedTLS.v$(MBEDTLS_VER) + +$(eval $(call bb-install,mbedtls,MBEDTLS,false)) + +endif diff --git a/deps/mpfr.mk b/deps/mpfr.mk new file mode 100644 index 0000000..b2ea878 --- /dev/null +++ b/deps/mpfr.mk @@ -0,0 +1,82 @@ +## MPFR ## + +ifeq ($(USE_SYSTEM_GMP), 0) +$(BUILDDIR)/mpfr-$(MPFR_VER)/build-configured: | $(build_prefix)/manifest/gmp +endif + +ifneq ($(USE_BINARYBUILDER_MPFR),1) + +ifeq ($(USE_SYSTEM_MPFR), 0) +ifeq ($(USE_SYSTEM_GMP), 0) +MPFR_OPTS := --with-gmp-include=$(abspath $(build_includedir)) --with-gmp-lib=$(abspath $(build_shlibdir)) +endif +endif +ifeq ($(BUILD_OS),WINNT) +ifeq ($(OS),WINNT) +MPFR_OPTS += --disable-thread-safe CFLAGS="$(CFLAGS) -DNPRINTF_L -DNPRINTF_T -DNPRINTF_J" +endif +endif + + +ifeq ($(OS),Darwin) +MPFR_CHECK_MFLAGS := LDFLAGS="$(LDFLAGS) -Wl,-rpath,'$(build_libdir)'" +endif + +ifeq ($(SANITIZE),1) +# Force generic C build +MPFR_OPTS += --host=none-unknown-linux +endif + +$(SRCCACHE)/mpfr-$(MPFR_VER).tar.bz2: | $(SRCCACHE) + $(JLDOWNLOAD) $@ http://www.mpfr.org/mpfr-$(MPFR_VER)/$(notdir $@) +$(SRCCACHE)/mpfr-$(MPFR_VER)/source-extracted: $(SRCCACHE)/mpfr-$(MPFR_VER).tar.bz2 + $(JLCHECKSUM) $< + cd $(dir $<) && $(TAR) -jxf $< + cp $(SRCDIR)/patches/config.sub $(SRCCACHE)/mpfr-$(MPFR_VER)/config.sub + touch -c $(SRCCACHE)/mpfr-$(MPFR_VER)/configure # old target + echo 1 > $@ + +$(BUILDDIR)/mpfr-$(MPFR_VER)/build-configured: $(SRCCACHE)/mpfr-$(MPFR_VER)/source-extracted + mkdir -p $(dir $@) + cd $(dir $@) && \ + $(dir $<)/configure $(CONFIGURE_COMMON) $(MPFR_OPTS) F77= --enable-shared --disable-static + echo 1 > $@ + +$(BUILDDIR)/mpfr-$(MPFR_VER)/build-compiled: $(BUILDDIR)/mpfr-$(MPFR_VER)/build-configured + $(MAKE) -C $(dir $<) $(LIBTOOL_CCLD) + echo 1 > $@ + +$(BUILDDIR)/mpfr-$(MPFR_VER)/build-checked: $(BUILDDIR)/mpfr-$(MPFR_VER)/build-compiled +ifeq ($(OS),$(BUILD_OS)) + $(MAKE) -C $(dir $@) $(LIBTOOL_CCLD) check $(MPFR_CHECK_MFLAGS) +endif + echo 1 > $@ + +$(eval $(call staged-install, \ + mpfr,mpfr-$(MPFR_VER), \ + MAKE_INSTALL,$$(LIBTOOL_CCLD),, \ + $$(INSTALL_NAME_CMD)libmpfr.$$(SHLIB_EXT) $$(build_shlibdir)/libmpfr.$$(SHLIB_EXT))) + +clean-mpfr: + -rm $(BUILDDIR)/mpfr-$(MPFR_VER)/build-configured $(BUILDDIR)/mpfr-$(MPFR_VER)/build-compiled + -$(MAKE) -C $(BUILDDIR)/mpfr-$(MPFR_VER) clean + +distclean-mpfr: + -rm -rf $(SRCCACHE)/mpfr-$(MPFR_VER).tar.bz2 \ + $(SRCCACHE)/mpfr-$(MPFR_VER) \ + $(BUILDDIR)/mpfr-$(MPFR_VER) + +get-mpfr: $(SRCCACHE)/mpfr-$(MPFR_VER).tar.bz2 +extract-mpfr: $(SRCCACHE)/mpfr-$(MPFR_VER)/source-extracted +configure-mpfr: $(BUILDDIR)/mpfr-$(MPFR_VER)/build-configured +compile-mpfr: $(BUILDDIR)/mpfr-$(MPFR_VER)/build-compiled +fastcheck-mpfr: check-mpfr +check-mpfr: $(BUILDDIR)/mpfr-$(MPFR_VER)/build-checked + +else # USE_BINARYBUILDER_MPFR + +MPFR_BB_URL_BASE := https://github.com/JuliaBinaryWrappers/MPFR_jll.jl/releases/download/MPFR-v$(MPFR_VER)+$(MPFR_BB_REL) +MPFR_BB_NAME := MPFR.v$(MPFR_VER) + +$(eval $(call bb-install,mpfr,MPFR,false)) +endif diff --git a/deps/objconv.mk b/deps/objconv.mk new file mode 100644 index 0000000..355cf39 --- /dev/null +++ b/deps/objconv.mk @@ -0,0 +1,43 @@ +## objconv ## + +ifneq ($(USE_BINARYBUILDER_OBJCONV),1) + +$(SRCCACHE)/objconv.zip: | $(SRCCACHE) + $(JLDOWNLOAD) $@ http://www.agner.org/optimize/objconv.zip + +$(BUILDDIR)/objconv/source-extracted: $(SRCCACHE)/objconv.zip + -rm -r $(dir $@) + mkdir -p $(BUILDDIR) + unzip -d $(dir $@) $< + cd $(dir $@) && unzip source.zip + echo 1 > $@ + +$(BUILDDIR)/objconv/build-compiled: $(BUILDDIR)/objconv/source-extracted + cd $(dir $<) && $(CXX) -o objconv -O2 *.cpp + echo 1 > $@ + +$(eval $(call staged-install, \ + objconv,objconv, \ + BINFILE_INSTALL,$$(BUILDDIR)/objconv/objconv,,)) + +clean-objconv: + -rm $(BUILDDIR)/objconv/build-compiled $(build_depsbindir)/objconv + +distclean-objconv: + -rm -rf $(SRCCACHE)/objconv.zip $(BUILDDIR)/objconv + + +get-objconv: $(SRCCACHE)/objconv.zip +extract-objconv: $(BUILDDIR)/objconv/source-extracted +configure-objconv: extract-objconv +compile-objconv: $(BUILDDIR)/objconv/build-compiled +fastcheck-objconv: check-objconv +check-objconv: compile-objconv + +else + +OBJCONV_BB_URL_BASE := https://github.com/JuliaPackaging/Yggdrasil/releases/download/Objconv-v$(OBJCONV_VER)-$(OBJCONV_BB_REL) +OBJCONV_BB_NAME := Objconv.v$(OBJCONV_VER) +$(eval $(call bb-install,objconv,OBJCONV,false)) + +endif diff --git a/deps/openblas.version b/deps/openblas.version new file mode 100644 index 0000000..283bc6d --- /dev/null +++ b/deps/openblas.version @@ -0,0 +1,2 @@ +OPENBLAS_BRANCH=v0.3.5 +OPENBLAS_SHA1=eebc18928715775c9ed254684edee16e4efe0342 diff --git a/deps/openlibm.mk b/deps/openlibm.mk new file mode 100644 index 0000000..36e2e62 --- /dev/null +++ b/deps/openlibm.mk @@ -0,0 +1,38 @@ +## openlibm ## + + +OPENLIBM_GIT_URL := git://github.com/JuliaMath/openlibm.git +OPENLIBM_TAR_URL = https://api.github.com/repos/JuliaMath/openlibm/tarball/$1 +$(eval $(call git-external,openlibm,OPENLIBM,,,$(BUILDDIR))) + +OPENLIBM_FLAGS := ARCH="$(ARCH)" REAL_ARCH="$(MARCH)" CC="$(CC)" FC="$(FC)" AR="$(AR)" OS="$(OS)" USECLANG=$(USECLANG) USEGCC=$(USEGCC) + +ifneq ($(USE_BINARYBUILDER_OPENLIBM), 1) +$(BUILDDIR)/$(OPENLIBM_SRC_DIR)/build-compiled: $(BUILDDIR)/$(OPENLIBM_SRC_DIR)/source-extracted + $(MAKE) -C $(dir $<) $(OPENLIBM_FLAGS) $(MAKE_COMMON) + echo 1 > $@ + +$(eval $(call staged-install, \ + openlibm,$$(OPENLIBM_SRC_DIR), \ + MAKE_INSTALL,$$(OPENLIBM_FLAGS),, \ + $(INSTALL_NAME_CMD)libopenlibm.$(SHLIB_EXT) $(build_shlibdir)/libopenlibm.$(SHLIB_EXT))) + +clean-openlibm: + -rm $(BUILDDIR)/$(OPENLIBM_SRC_DIR)/build-compiled $(build_libdir)/libopenlibm.a + -$(MAKE) -C $(BUILDDIR)/$(OPENLIBM_SRC_DIR) distclean $(OPENLIBM_FLAGS) + + +get-openlibm: $(OPENLIBM_SRC_FILE) +extract-openlibm: $(BUILDDIR)/$(OPENLIBM_SRC_DIR)/source-extracted +configure-openlibm: extract-openlibm +compile-openlibm: $(BUILDDIR)/$(OPENLIBM_SRC_DIR)/build-compiled +fastcheck-openlibm: check-openlibm +check-openlibm: compile-openlibm + +else # USE_BINARYBUILDER_SUITESPARSE + +OPENLIBM_BB_URL_BASE := https://github.com/JuliaPackaging/Yggdrasil/releases/download/OpenLibm-v$(OPENLIBM_VER)-$(OPENLIBM_BB_REL) +OPENLIBM_BB_NAME := OpenLibm.v$(OPENLIBM_VER) + +$(eval $(call bb-install,openlibm,OPENLIBM,true)) +endif diff --git a/deps/openlibm.version b/deps/openlibm.version new file mode 100644 index 0000000..1bad33f --- /dev/null +++ b/deps/openlibm.version @@ -0,0 +1,2 @@ +OPENLIBM_BRANCH=v0.6.0 +OPENLIBM_SHA1=ce69bf1f32d3e2e9791da36c9e33ba38670d5576 diff --git a/deps/p7zip.mk b/deps/p7zip.mk new file mode 100644 index 0000000..8ad38b2 --- /dev/null +++ b/deps/p7zip.mk @@ -0,0 +1,65 @@ +## p7zip ## + +ifneq ($(USE_BINARYBUILDER_P7ZIP),1) +# Force optimization for P7ZIP flags (Issue #11668) +$(SRCCACHE)/p7zip-$(P7ZIP_VER).tar.bz2: | $(SRCCACHE) + $(JLDOWNLOAD) $@ https://downloads.sourceforge.net/project/p7zip/p7zip/16.02/p7zip_16.02_src_all.tar.bz2 + +$(BUILDDIR)/p7zip-$(P7ZIP_VER)/source-extracted: $(SRCCACHE)/p7zip-$(P7ZIP_VER).tar.bz2 + $(JLCHECKSUM) $< + mkdir -p $(dir $@) + cd $(dir $@) && $(TAR) --strip-components 1 -jxf $< + echo $1 > $@ + +$(BUILDDIR)/p7zip-$(P7ZIP_VER)/p7zip-12-CVE-2016-9296.patch-applied: $(BUILDDIR)/p7zip-$(P7ZIP_VER)/source-extracted + cd $(dir $@) && patch -p1 -f < $(SRCDIR)/patches/p7zip-12-CVE-2016-9296.patch + echo 1 > $@ + +$(BUILDDIR)/p7zip-$(P7ZIP_VER)/p7zip-13-CVE-2017-17969.patch-applied: $(BUILDDIR)/p7zip-$(P7ZIP_VER)/p7zip-12-CVE-2016-9296.patch-applied + cd $(dir $@) && patch -p1 -f < $(SRCDIR)/patches/p7zip-13-CVE-2017-17969.patch + echo 1 > $@ + +$(BUILDDIR)/p7zip-$(P7ZIP_VER)/p7zip-15-Enhanced-encryption-strength.patch-applied: $(BUILDDIR)/p7zip-$(P7ZIP_VER)/p7zip-13-CVE-2017-17969.patch-applied + cd $(dir $@) && patch -p4 -f < $(SRCDIR)/patches/p7zip-15-Enhanced-encryption-strength.patch + echo 1 > $@ + +$(BUILDDIR)/p7zip-$(P7ZIP_VER)/p7zip-Windows_ErrorMsg.patch-applied: $(BUILDDIR)/p7zip-$(P7ZIP_VER)/p7zip-15-Enhanced-encryption-strength.patch-applied + cd $(dir $@) && patch -p0 -f < $(SRCDIR)/patches/p7zip-Windows_ErrorMsg.patch + echo 1 > $@ + +$(BUILDDIR)/p7zip-$(P7ZIP_VER)/build-configured: $(BUILDDIR)/p7zip-$(P7ZIP_VER)/p7zip-Windows_ErrorMsg.patch-applied +$(BUILDDIR)/p7zip-$(P7ZIP_VER)/build-compiled: $(BUILDDIR)/p7zip-$(P7ZIP_VER)/build-configured + $(MAKE) -C $(dir $<) $(MAKE_COMMON) CC="$(CC)" CXX="$(CXX)" 7za + echo 1 > $@ + +define P7ZIP_INSTALL + mkdir -p $2/$$(build_bindir) + cp -a $1/bin/7za $2/$$(build_bindir)/7z +endef +$(eval $(call staged-install, \ + p7zip,p7zip-$(P7ZIP_VER), \ + P7ZIP_INSTALL,,,)) + +clean-p7zip: + -rm $(BUILDDIR)/p7zip-$(P7ZIP_VER)/build-configured $(BUILDDIR)/p7zip-$(P7ZIP_VER)/build-compiled + -rm $(build_bindir)/7za + -$(MAKE) -C $(BUILDDIR)/p7zip-$(P7ZIP_VER) clean + +distclean-p7zip: + -rm -rf $(SRCCACHE)/p7zip-$(P7ZIP_VER).tar.bz2 $(SRCCACHE)/p7zip-$(P7ZIP_VER) $(BUILDDIR)/p7zip-$(P7ZIP_VER) + + +get-p7zip: $(SRCCACHE)/p7zip-$(P7ZIP_VER).tar.bz2 +extract-p7zip: $(SRCCACHE)/p7zip-$(P7ZIP_VER)/source-extracted +configure-p7zip: $(BUILDDIR)/p7zip-$(P7ZIP_VER)/build-configured +compile-p7zip: $(BUILDDIR)/p7zip-$(P7ZIP_VER)/build-compiled +fastcheck-p7zip: check-p7zip +check-p7zip: compile-p7zip + + +else # USE_BINARYBUILDER_P7ZIP +P7ZIP_BB_URL_BASE := https://github.com/JuliaBinaryWrappers/p7zip_jll.jl/releases/download/p7zip-v$(P7ZIP_VER)+$(P7ZIP_BB_REL) +P7ZIP_BB_NAME := p7zip.v$(P7ZIP_VER) +$(eval $(call bb-install,p7zip,P7ZIP,false)) + +endif diff --git a/deps/patchelf.mk b/deps/patchelf.mk new file mode 100644 index 0000000..89ad1da --- /dev/null +++ b/deps/patchelf.mk @@ -0,0 +1,48 @@ +## patchelf ## + +$(SRCCACHE)/patchelf-$(PATCHELF_VER).tar.gz: | $(SRCCACHE) + $(JLDOWNLOAD) $@ https://nixos.org/releases/patchelf/patchelf-$(PATCHELF_VER)/patchelf-$(PATCHELF_VER).tar.gz + +$(SRCCACHE)/patchelf-$(PATCHELF_VER)/source-extracted: $(SRCCACHE)/patchelf-$(PATCHELF_VER).tar.gz + $(JLCHECKSUM) $< + cd $(dir $<) && $(TAR) zxf $< + touch -c $(SRCCACHE)/patchelf-$(PATCHELF_VER)/configure # old target + echo 1 > $@ + +$(BUILDDIR)/patchelf-$(PATCHELF_VER)/build-configured: $(SRCCACHE)/patchelf-$(PATCHELF_VER)/source-extracted | $(LIBCXX_DEPENDENCY) + mkdir -p $(dir $@) + cd $(dir $@) && \ + $(dir $<)/configure $(CONFIGURE_COMMON) LDFLAGS="$(CXXLDFLAGS)" CPPFLAGS="$(CPPFLAGS)" + echo 1 > $@ + +$(BUILDDIR)/patchelf-$(PATCHELF_VER)/build-compiled: $(BUILDDIR)/patchelf-$(PATCHELF_VER)/build-configured + $(MAKE) -C $(dir $<) + echo 1 > $@ + +$(BUILDDIR)/patchelf-$(PATCHELF_VER)/build-checked: $(BUILDDIR)/patchelf-$(PATCHELF_VER)/build-compiled +ifeq ($(OS),$(BUILD_OS)) + # disabled due to bug in v0.6 + #$(MAKE) -C $(dir $@) check +endif + echo 1 > $@ + +$(eval $(call staged-install, \ + patchelf,patchelf-$(PATCHELF_VER), \ + MAKE_INSTALL,$$(LIBTOOL_CCLD),,)) + +clean-patchelf: + -rm $(BUILDDIR)/patchelf-$(PATCHELF_VER)/build-configured \ + $(BUILDDIR)/patchelf-$(PATCHELF_VER)/build-compiled + -$(MAKE) -C $(BUILDDIR)/patchelf-$(PATCHELF_VER) clean + +distclean-patchelf: + -rm -rf $(SRCCACHE)/patchelf-$(PATCHELF_VER).tar.gz \ + $(SRCCACHE)/patchelf-$(PATCHELF_VER) \ + $(BUILDDIR)/patchelf-$(PATCHELF_VER) + + +get-patchelf: $(SRCCACHE)/patchelf-$(PATCHELF_VER).tar.gz +extract-patchelf: $(SRCCACHE)/patchelf-$(PATCHELF_VER)/source-extracted +configure-patchelf: $(BUILDDIR)/patchelf-$(PATCHELF_VER)/build-configured +compile-patchelf: $(BUILDDIR)/patchelf-$(PATCHELF_VER)/build-compiled +check-patchelf: $(BUILDDIR)/patchelf-$(PATCHELF_VER)/build-checked diff --git a/deps/patches/SuiteSparse-shlib.patch b/deps/patches/SuiteSparse-shlib.patch new file mode 100644 index 0000000..fe25aa1 --- /dev/null +++ b/deps/patches/SuiteSparse-shlib.patch @@ -0,0 +1,43 @@ +--- a/SuiteSparse_config/SuiteSparse_config.mk 2018-12-22 22:15:58.000000000 -0500 ++++ b/SuiteSparse_config/SuiteSparse_config.mk 2019-02-28 01:40:51.768394370 -0500 +@@ -357,8 +357,8 @@ + #--------------------------------------------------------------------------- + + ifeq ($(UNAME),Linux) +- # add the realtime library, librt, and SuiteSparse/lib +- LDLIBS += -lrt -Wl,-rpath=$(INSTALL_LIB) ++ # add the posix realtime extensions library: librt ++ LDLIBS += -lrt + endif + + #--------------------------------------------------------------------------- +@@ -447,11 +447,14 @@ + SO_OPTS = $(LDFLAGS) + + ifeq ($(UNAME),Windows) +- # Cygwin Make on Windows (untested) ++ # Cygwin Make on Windows + AR_TARGET = $(LIBRARY).lib +- SO_PLAIN = $(LIBRARY).dll ++ SO_TARGET = $(LIBRARY).dll ++ # The following two links are just garbage copies of the real target ++ # they aren't actually supported by this OS + SO_MAIN = $(LIBRARY).$(SO_VERSION).dll +- SO_TARGET = $(LIBRARY).$(VERSION).dll ++ SO_PLAIN = $(LIBRARY).$(VERSION).dll ++ SO_OPTS += -shared + SO_INSTALL_NAME = echo + else + # Mac or Linux/Unix +@@ -472,8 +475,9 @@ + SO_PLAIN = $(LIBRARY).so + SO_MAIN = $(LIBRARY).so.$(SO_VERSION) + SO_TARGET = $(LIBRARY).so.$(VERSION) +- SO_OPTS += -shared -Wl,-soname -Wl,$(SO_MAIN) -Wl,--no-undefined +- # Linux/Unix *.so files can be moved without modification: ++ SO_OPTS += -shared -Wl,-soname -Wl,$(SO_MAIN) -Wl,--no-undefined \ ++ -Wl,-rpath,'$$ORIGIN' -Wl,-z,origin ++ # Use rpath ORIGIN so that Linux/Unix *.so files can be moved without modification: + SO_INSTALL_NAME = echo + endif + endif diff --git a/deps/patches/SuiteSparse-winclang.patch b/deps/patches/SuiteSparse-winclang.patch new file mode 100644 index 0000000..8bfb64d --- /dev/null +++ b/deps/patches/SuiteSparse-winclang.patch @@ -0,0 +1,14 @@ +--- SuiteSparse_config/SuiteSparse_config.h 2015-07-15 03:26:41.000000000 +0000 ++++ SuiteSparse_config/SuiteSparse_config.h 2016-07-01 00:55:57.157465600 +0000 +@@ -54,7 +54,11 @@ + #ifdef _WIN64 + + #define SuiteSparse_long __int64 ++#ifdef _MSVC_VER + #define SuiteSparse_long_max _I64_MAX ++#else ++#define SuiteSparse_long_max LLONG_MAX ++#endif + #define SuiteSparse_long_idd "I64d" + + #else diff --git a/deps/patches/clang-D28477.patch b/deps/patches/clang-D28477.patch new file mode 100644 index 0000000..9080721 --- /dev/null +++ b/deps/patches/clang-D28477.patch @@ -0,0 +1,13 @@ +Index: projects/clang/lib/CodeGen/BackendUtil.cpp +=================================================================== +--- a/tools/clang/lib/CodeGen/BackendUtil.cpp ++++ b/tools/clang/lib/CodeGen/BackendUtil.cpp +@@ -262,7 +262,7 @@ + TLII->disableAllFunctions(); + else { + // Disable individual libc/libm calls in TargetLibraryInfo. +- LibFunc::Func F; ++ LibFunc F; + for (auto &FuncName : CodeGenOpts.getNoBuiltinFuncs()) + if (TLII->getLibFunc(FuncName, F)) + TLII->setUnavailable(F); diff --git a/deps/patches/config.sub b/deps/patches/config.sub new file mode 100755 index 0000000..3d579cf --- /dev/null +++ b/deps/patches/config.sub @@ -0,0 +1,1791 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright 1992-2018 Free Software Foundation, Inc. + +timestamp='2018-12-31' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see . +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). + + +# Please send patches to . +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# You can get the latest version of this script from: +# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS + +Canonicalize a configuration name. + +Options: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright 1992-2018 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo "$1" + exit ;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Split fields of configuration type +IFS="-" read field1 field2 field3 field4 <&2 + exit 1 + ;; + *-*-*-*) + basic_machine=$field1-$field2 + os=$field3-$field4 + ;; + *-*-*) + # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two + # parts + maybe_os=$field2-$field3 + case $maybe_os in + nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc \ + | linux-newlib* | linux-musl* | linux-uclibc* | uclinux-uclibc* \ + | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \ + | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \ + | storm-chaos* | os2-emx* | rtmk-nova*) + basic_machine=$field1 + os=$maybe_os + ;; + android-linux) + basic_machine=$field1-unknown + os=linux-android + ;; + *) + basic_machine=$field1-$field2 + os=$field3 + ;; + esac + ;; + *-*) + # A lone config we happen to match not fitting any pattern + case $field1-$field2 in + decstation-3100) + basic_machine=mips-dec + os= + ;; + *-*) + # Second component is usually, but not always the OS + case $field2 in + # Prevent following clause from handling this valid os + sun*os*) + basic_machine=$field1 + os=$field2 + ;; + # Manufacturers + dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \ + | att* | 7300* | 3300* | delta* | motorola* | sun[234]* \ + | unicom* | ibm* | next | hp | isi* | apollo | altos* \ + | convergent* | ncr* | news | 32* | 3600* | 3100* \ + | hitachi* | c[123]* | convex* | sun | crds | omron* | dg \ + | ultra | tti* | harris | dolphin | highlevel | gould \ + | cbm | ns | masscomp | apple | axis | knuth | cray \ + | microblaze* | sim | cisco \ + | oki | wec | wrs | winbond) + basic_machine=$field1-$field2 + os= + ;; + *) + basic_machine=$field1 + os=$field2 + ;; + esac + ;; + esac + ;; + *) + # Convert single-component short-hands not valid as part of + # multi-component configurations. + case $field1 in + 386bsd) + basic_machine=i386-pc + os=bsd + ;; + a29khif) + basic_machine=a29k-amd + os=udi + ;; + adobe68k) + basic_machine=m68010-adobe + os=scout + ;; + alliant) + basic_machine=fx80-alliant + os= + ;; + altos | altos3068) + basic_machine=m68k-altos + os= + ;; + am29k) + basic_machine=a29k-none + os=bsd + ;; + amdahl) + basic_machine=580-amdahl + os=sysv + ;; + amiga) + basic_machine=m68k-unknown + os= + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=bsd + ;; + aros) + basic_machine=i386-pc + os=aros + ;; + aux) + basic_machine=m68k-apple + os=aux + ;; + balance) + basic_machine=ns32k-sequent + os=dynix + ;; + blackfin) + basic_machine=bfin-unknown + os=linux + ;; + cegcc) + basic_machine=arm-unknown + os=cegcc + ;; + convex-c1) + basic_machine=c1-convex + os=bsd + ;; + convex-c2) + basic_machine=c2-convex + os=bsd + ;; + convex-c32) + basic_machine=c32-convex + os=bsd + ;; + convex-c34) + basic_machine=c34-convex + os=bsd + ;; + convex-c38) + basic_machine=c38-convex + os=bsd + ;; + cray) + basic_machine=j90-cray + os=unicos + ;; + crds | unos) + basic_machine=m68k-crds + os= + ;; + da30) + basic_machine=m68k-da30 + os= + ;; + decstation | pmax | pmin | dec3100 | decstatn) + basic_machine=mips-dec + os= + ;; + delta88) + basic_machine=m88k-motorola + os=sysv3 + ;; + dicos) + basic_machine=i686-pc + os=dicos + ;; + djgpp) + basic_machine=i586-pc + os=msdosdjgpp + ;; + ebmon29k) + basic_machine=a29k-amd + os=ebmon + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=ose + ;; + gmicro) + basic_machine=tron-gmicro + os=sysv + ;; + go32) + basic_machine=i386-pc + os=go32 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=hms + ;; + harris) + basic_machine=m88k-harris + os=sysv3 + ;; + hp300) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=hpux + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=proelf + ;; + i386mach) + basic_machine=i386-mach + os=mach + ;; + vsta) + basic_machine=i386-pc + os=vsta + ;; + isi68 | isi) + basic_machine=m68k-isi + os=sysv + ;; + m68knommu) + basic_machine=m68k-unknown + os=linux + ;; + magnum | m3230) + basic_machine=mips-mips + os=sysv + ;; + merlin) + basic_machine=ns32k-utek + os=sysv + ;; + mingw64) + basic_machine=x86_64-pc + os=mingw64 + ;; + mingw32) + basic_machine=i686-pc + os=mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + os=mingw32ce + ;; + monitor) + basic_machine=m68k-rom68k + os=coff + ;; + morphos) + basic_machine=powerpc-unknown + os=morphos + ;; + moxiebox) + basic_machine=moxie-unknown + os=moxiebox + ;; + msdos) + basic_machine=i386-pc + os=msdos + ;; + msys) + basic_machine=i686-pc + os=msys + ;; + mvs) + basic_machine=i370-ibm + os=mvs + ;; + nacl) + basic_machine=le32-unknown + os=nacl + ;; + ncr3000) + basic_machine=i486-ncr + os=sysv4 + ;; + netbsd386) + basic_machine=i386-pc + os=netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=newsos + ;; + news1000) + basic_machine=m68030-sony + os=newsos + ;; + necv70) + basic_machine=v70-nec + os=sysv + ;; + nh3000) + basic_machine=m68k-harris + os=cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=cxux + ;; + nindy960) + basic_machine=i960-intel + os=nindy + ;; + mon960) + basic_machine=i960-intel + os=mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=nonstopux + ;; + os400) + basic_machine=powerpc-ibm + os=os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=ose + ;; + os68k) + basic_machine=m68k-none + os=os68k + ;; + paragon) + basic_machine=i860-intel + os=osf + ;; + parisc) + basic_machine=hppa-unknown + os=linux + ;; + pw32) + basic_machine=i586-unknown + os=pw32 + ;; + rdos | rdos64) + basic_machine=x86_64-pc + os=rdos + ;; + rdos32) + basic_machine=i386-pc + os=rdos + ;; + rom68k) + basic_machine=m68k-rom68k + os=coff + ;; + sa29200) + basic_machine=a29k-amd + os=udi + ;; + sei) + basic_machine=mips-sei + os=seiux + ;; + sequent) + basic_machine=i386-sequent + os= + ;; + sps7) + basic_machine=m68k-bull + os=sysv2 + ;; + st2000) + basic_machine=m68k-tandem + os= + ;; + stratus) + basic_machine=i860-stratus + os=sysv4 + ;; + sun2) + basic_machine=m68000-sun + os= + ;; + sun2os3) + basic_machine=m68000-sun + os=sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=sunos4 + ;; + sun3) + basic_machine=m68k-sun + os= + ;; + sun3os3) + basic_machine=m68k-sun + os=sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=sunos4 + ;; + sun4) + basic_machine=sparc-sun + os= + ;; + sun4os3) + basic_machine=sparc-sun + os=sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=solaris2 + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + os= + ;; + sv1) + basic_machine=sv1-cray + os=unicos + ;; + symmetry) + basic_machine=i386-sequent + os=dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=unicos + ;; + t90) + basic_machine=t90-cray + os=unicos + ;; + toad1) + basic_machine=pdp10-xkl + os=tops20 + ;; + tpf) + basic_machine=s390x-ibm + os=tpf + ;; + udi29k) + basic_machine=a29k-amd + os=udi + ;; + ultra3) + basic_machine=a29k-nyu + os=sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=none + ;; + vaxv) + basic_machine=vax-dec + os=sysv + ;; + vms) + basic_machine=vax-dec + os=vms + ;; + vxworks960) + basic_machine=i960-wrs + os=vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=vxworks + ;; + xbox) + basic_machine=i686-pc + os=mingw32 + ;; + ymp) + basic_machine=ymp-cray + os=unicos + ;; + *) + basic_machine=$1 + os= + ;; + esac + ;; +esac + +# Decode 1-component or ad-hoc basic machines +case $basic_machine in + # Here we handle the default manufacturer of certain CPU types. It is in + # some cases the only manufacturer, in others, it is the most popular. + w89k) + cpu=hppa1.1 + vendor=winbond + ;; + op50n) + cpu=hppa1.1 + vendor=oki + ;; + op60c) + cpu=hppa1.1 + vendor=oki + ;; + ibm*) + cpu=i370 + vendor=ibm + ;; + orion105) + cpu=clipper + vendor=highlevel + ;; + mac | mpw | mac-mpw) + cpu=m68k + vendor=apple + ;; + pmac | pmac-mpw) + cpu=powerpc + vendor=apple + ;; + + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + cpu=m68000 + vendor=att + ;; + 3b*) + cpu=we32k + vendor=att + ;; + bluegene*) + cpu=powerpc + vendor=ibm + os=cnk + ;; + decsystem10* | dec10*) + cpu=pdp10 + vendor=dec + os=tops10 + ;; + decsystem20* | dec20*) + cpu=pdp10 + vendor=dec + os=tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + cpu=m68k + vendor=motorola + ;; + dpx2*) + cpu=m68k + vendor=bull + os=sysv3 + ;; + encore | umax | mmax) + cpu=ns32k + vendor=encore + ;; + elxsi) + cpu=elxsi + vendor=elxsi + os=${os:-bsd} + ;; + fx2800) + cpu=i860 + vendor=alliant + ;; + genix) + cpu=ns32k + vendor=ns + ;; + h3050r* | hiux*) + cpu=hppa1.1 + vendor=hitachi + os=hiuxwe2 + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + cpu=hppa1.0 + vendor=hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + cpu=m68000 + vendor=hp + ;; + hp9k3[2-9][0-9]) + cpu=m68k + vendor=hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + cpu=hppa1.0 + vendor=hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + cpu=hppa1.1 + vendor=hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + cpu=hppa1.1 + vendor=hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + cpu=hppa1.1 + vendor=hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + cpu=hppa1.1 + vendor=hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + cpu=hppa1.0 + vendor=hp + ;; + i*86v32) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + os=sysv32 + ;; + i*86v4*) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + os=sysv4 + ;; + i*86v) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + os=sysv + ;; + i*86sol2) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + os=solaris2 + ;; + j90 | j90-cray) + cpu=j90 + vendor=cray + os=${os:-unicos} + ;; + iris | iris4d) + cpu=mips + vendor=sgi + case $os in + irix*) + ;; + *) + os=irix4 + ;; + esac + ;; + miniframe) + cpu=m68000 + vendor=convergent + ;; + *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*) + cpu=m68k + vendor=atari + os=mint + ;; + news-3600 | risc-news) + cpu=mips + vendor=sony + os=newsos + ;; + next | m*-next) + cpu=m68k + vendor=next + case $os in + nextstep* ) + ;; + ns2*) + os=nextstep2 + ;; + *) + os=nextstep3 + ;; + esac + ;; + np1) + cpu=np1 + vendor=gould + ;; + op50n-* | op60c-*) + cpu=hppa1.1 + vendor=oki + os=proelf + ;; + pa-hitachi) + cpu=hppa1.1 + vendor=hitachi + os=hiuxwe2 + ;; + pbd) + cpu=sparc + vendor=tti + ;; + pbb) + cpu=m68k + vendor=tti + ;; + pc532) + cpu=ns32k + vendor=pc532 + ;; + pn) + cpu=pn + vendor=gould + ;; + power) + cpu=power + vendor=ibm + ;; + ps2) + cpu=i386 + vendor=ibm + ;; + rm[46]00) + cpu=mips + vendor=siemens + ;; + rtpc | rtpc-*) + cpu=romp + vendor=ibm + ;; + sde) + cpu=mipsisa32 + vendor=sde + os=${os:-elf} + ;; + simso-wrs) + cpu=sparclite + vendor=wrs + os=vxworks + ;; + tower | tower-32) + cpu=m68k + vendor=ncr + ;; + vpp*|vx|vx-*) + cpu=f301 + vendor=fujitsu + ;; + w65) + cpu=w65 + vendor=wdc + ;; + w89k-*) + cpu=hppa1.1 + vendor=winbond + os=proelf + ;; + none) + cpu=none + vendor=none + ;; + leon|leon[3-9]) + cpu=sparc + vendor=$basic_machine + ;; + leon-*|leon[3-9]-*) + cpu=sparc + vendor=`echo "$basic_machine" | sed 's/-.*//'` + ;; + + *-*) + IFS="-" read cpu vendor <&2 + exit 1 + ;; + esac + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $vendor in + digital*) + vendor=dec + ;; + commodore*) + vendor=cbm + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x$os != x ] +then +case $os in + # First match some system type aliases that might get confused + # with valid system types. + # solaris* is a basic system type, with this one exception. + auroraux) + os=auroraux + ;; + bluegene*) + os=cnk + ;; + solaris1 | solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + solaris) + os=solaris2 + ;; + unixware*) + os=sysv4.2uw + ;; + gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # es1800 is here to avoid being matched by es* (a different OS) + es1800*) + os=ose + ;; + # Some version numbers need modification + chorusos*) + os=chorusos + ;; + isc) + os=isc2.2 + ;; + sco6) + os=sco5v6 + ;; + sco5) + os=sco3.2v5 + ;; + sco4) + os=sco3.2v4 + ;; + sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + ;; + sco3.2v[4-9]* | sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + ;; + scout) + # Don't match below + ;; + sco*) + os=sco3.2v2 + ;; + psos*) + os=psos + ;; + # Now accept the basic system types. + # The portable systems comes first. + # Each alternative MUST end in a * to match a version number. + # sysv* is not here because it comes later, after sysvr4. + gnu* | bsd* | mach* | minix* | genix* | ultrix* | irix* \ + | *vms* | esix* | aix* | cnk* | sunos | sunos[34]*\ + | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \ + | sym* | kopensolaris* | plan9* \ + | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ + | aos* | aros* | cloudabi* | sortix* \ + | nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \ + | clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \ + | knetbsd* | mirbsd* | netbsd* \ + | bitrig* | openbsd* | solidbsd* | libertybsd* \ + | ekkobsd* | kfreebsd* | freebsd* | riscix* | lynxos* \ + | bosx* | nextstep* | cxux* | aout* | elf* | oabi* \ + | ptx* | coff* | ecoff* | winnt* | domain* | vsta* \ + | udi* | eabi* | lites* | ieee* | go32* | aux* | hcos* \ + | chorusrdb* | cegcc* | glidix* \ + | cygwin* | msys* | pe* | moss* | proelf* | rtems* \ + | midipix* | mingw32* | mingw64* | linux-gnu* | linux-android* \ + | linux-newlib* | linux-musl* | linux-uclibc* \ + | uxpv* | beos* | mpeix* | udk* | moxiebox* \ + | interix* | uwin* | mks* | rhapsody* | darwin* \ + | openstep* | oskit* | conix* | pw32* | nonstopux* \ + | storm-chaos* | tops10* | tenex* | tops20* | its* \ + | os2* | vos* | palmos* | uclinux* | nucleus* \ + | morphos* | superux* | rtmk* | windiss* \ + | powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \ + | skyos* | haiku* | rdos* | toppers* | drops* | es* \ + | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \ + | midnightbsd* | amdhsa* | unleashed* | emscripten*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + qnx*) + case $cpu in + x86 | i*86) + ;; + *) + os=nto-$os + ;; + esac + ;; + hiux*) + os=hiuxwe2 + ;; + nto-qnx*) + ;; + nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + sim | xray | os68k* | v88r* \ + | windows* | osx | abug | netware* | os9* \ + | macos* | mpw* | magic* | mmixware* | mon960* | lnews*) + ;; + linux-dietlibc) + os=linux-dietlibc + ;; + linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + lynx*178) + os=lynxos178 + ;; + lynx*5) + os=lynxos5 + ;; + lynx*) + os=lynxos + ;; + mac*) + os=`echo "$os" | sed -e 's|mac|macos|'` + ;; + opened*) + os=openedition + ;; + os400*) + os=os400 + ;; + sunos5*) + os=`echo "$os" | sed -e 's|sunos5|solaris2|'` + ;; + sunos6*) + os=`echo "$os" | sed -e 's|sunos6|solaris3|'` + ;; + wince*) + os=wince + ;; + utek*) + os=bsd + ;; + dynix*) + os=bsd + ;; + acis*) + os=aos + ;; + atheos*) + os=atheos + ;; + syllable*) + os=syllable + ;; + 386bsd) + os=bsd + ;; + ctix* | uts*) + os=sysv + ;; + nova*) + os=rtmk-nova + ;; + ns2) + os=nextstep2 + ;; + nsk*) + os=nsk + ;; + # Preserve the version number of sinix5. + sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + sinix*) + os=sysv4 + ;; + tpf*) + os=tpf + ;; + triton*) + os=sysv3 + ;; + oss*) + os=sysv3 + ;; + svr4*) + os=sysv4 + ;; + svr3) + os=sysv3 + ;; + sysvr4) + os=sysv4 + ;; + # This must come after sysvr4. + sysv*) + ;; + ose*) + os=ose + ;; + *mint | mint[0-9]* | *MiNT | MiNT[0-9]*) + os=mint + ;; + zvmoe) + os=zvmoe + ;; + dicos*) + os=dicos + ;; + pikeos*) + # Until real need of OS specific support for + # particular features comes up, bare metal + # configurations are quite functional. + case $cpu in + arm*) + os=eabi + ;; + *) + os=elf + ;; + esac + ;; + nacl*) + ;; + ios) + ;; + none) + ;; + *-eabi) + ;; + *) + echo Invalid configuration \`"$1"\': system \`"$os"\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $cpu-$vendor in + score-*) + os=elf + ;; + spu-*) + os=elf + ;; + *-acorn) + os=riscix1.2 + ;; + arm*-rebel) + os=linux + ;; + arm*-semi) + os=aout + ;; + c4x-* | tic4x-*) + os=coff + ;; + c8051-*) + os=elf + ;; + clipper-intergraph) + os=clix + ;; + hexagon-*) + os=elf + ;; + tic54x-*) + os=coff + ;; + tic55x-*) + os=coff + ;; + tic6x-*) + os=coff + ;; + # This must come before the *-dec entry. + pdp10-*) + os=tops20 + ;; + pdp11-*) + os=none + ;; + *-dec | vax-*) + os=ultrix4.2 + ;; + m68*-apollo) + os=domain + ;; + i386-sun) + os=sunos4.0.2 + ;; + m68000-sun) + os=sunos3 + ;; + m68*-cisco) + os=aout + ;; + mep-*) + os=elf + ;; + mips*-cisco) + os=elf + ;; + mips*-*) + os=elf + ;; + or32-*) + os=coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=sysv3 + ;; + sparc-* | *-sun) + os=sunos4.1.1 + ;; + pru-*) + os=elf + ;; + *-be) + os=beos + ;; + *-ibm) + os=aix + ;; + *-knuth) + os=mmixware + ;; + *-wec) + os=proelf + ;; + *-winbond) + os=proelf + ;; + *-oki) + os=proelf + ;; + *-hp) + os=hpux + ;; + *-hitachi) + os=hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=sysv + ;; + *-cbm) + os=amigaos + ;; + *-dg) + os=dgux + ;; + *-dolphin) + os=sysv3 + ;; + m68k-ccur) + os=rtu + ;; + m88k-omron*) + os=luna + ;; + *-next) + os=nextstep + ;; + *-sequent) + os=ptx + ;; + *-crds) + os=unos + ;; + *-ns) + os=genix + ;; + i370-*) + os=mvs + ;; + *-gould) + os=sysv + ;; + *-highlevel) + os=bsd + ;; + *-encore) + os=bsd + ;; + *-sgi) + os=irix + ;; + *-siemens) + os=sysv4 + ;; + *-masscomp) + os=rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=uxpv + ;; + *-rom68k) + os=coff + ;; + *-*bug) + os=coff + ;; + *-apple) + os=macos + ;; + *-atari*) + os=mint + ;; + *-wrs) + os=vxworks + ;; + *) + os=none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +case $vendor in + unknown) + case $os in + riscix*) + vendor=acorn + ;; + sunos*) + vendor=sun + ;; + cnk*|-aix*) + vendor=ibm + ;; + beos*) + vendor=be + ;; + hpux*) + vendor=hp + ;; + mpeix*) + vendor=hp + ;; + hiux*) + vendor=hitachi + ;; + unos*) + vendor=crds + ;; + dgux*) + vendor=dg + ;; + luna*) + vendor=omron + ;; + genix*) + vendor=ns + ;; + clix*) + vendor=intergraph + ;; + mvs* | opened*) + vendor=ibm + ;; + os400*) + vendor=ibm + ;; + ptx*) + vendor=sequent + ;; + tpf*) + vendor=ibm + ;; + vxsim* | vxworks* | windiss*) + vendor=wrs + ;; + aux*) + vendor=apple + ;; + hms*) + vendor=hitachi + ;; + mpw* | macos*) + vendor=apple + ;; + *mint | mint[0-9]* | *MiNT | MiNT[0-9]*) + vendor=atari + ;; + vos*) + vendor=stratus + ;; + esac + ;; +esac + +echo "$cpu-$vendor-$os" +exit + +# Local variables: +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/deps/patches/dSFMT.c.patch b/deps/patches/dSFMT.c.patch new file mode 100644 index 0000000..0554831 --- /dev/null +++ b/deps/patches/dSFMT.c.patch @@ -0,0 +1,100 @@ +--- dsfmt-2.2/dSFMT.c 2012-06-29 03:24:27.000000000 -0400 ++++ dSFMT-patched.c 2012-12-20 12:45:45.000000000 -0500 +@@ -32,13 +32,13 @@ + inline static uint32_t ini_func1(uint32_t x); + inline static uint32_t ini_func2(uint32_t x); + inline static void gen_rand_array_c1o2(dsfmt_t *dsfmt, w128_t *array, +- int size); ++ ptrdiff_t size); + inline static void gen_rand_array_c0o1(dsfmt_t *dsfmt, w128_t *array, +- int size); ++ ptrdiff_t size); + inline static void gen_rand_array_o0c1(dsfmt_t *dsfmt, w128_t *array, +- int size); ++ ptrdiff_t size); + inline static void gen_rand_array_o0o1(dsfmt_t *dsfmt, w128_t *array, +- int size); ++ ptrdiff_t size); + inline static int idxof(int i); + static void initial_mask(dsfmt_t *dsfmt); + static void period_certification(dsfmt_t *dsfmt); +@@ -142,8 +142,8 @@ + * @param size number of 128-bit pseudorandom numbers to be generated. + */ + inline static void gen_rand_array_c1o2(dsfmt_t *dsfmt, w128_t *array, +- int size) { +- int i, j; ++ ptrdiff_t size) { ++ ptrdiff_t i, j; + w128_t lung; + + lung = dsfmt->status[DSFMT_N]; +@@ -180,8 +180,8 @@ + * @param size number of 128-bit pseudorandom numbers to be generated. + */ + inline static void gen_rand_array_c0o1(dsfmt_t *dsfmt, w128_t *array, +- int size) { +- int i, j; ++ ptrdiff_t size) { ++ ptrdiff_t i, j; + w128_t lung; + + lung = dsfmt->status[DSFMT_N]; +@@ -223,8 +223,8 @@ + * @param size number of 128-bit pseudorandom numbers to be generated. + */ + inline static void gen_rand_array_o0o1(dsfmt_t *dsfmt, w128_t *array, +- int size) { +- int i, j; ++ ptrdiff_t size) { ++ ptrdiff_t i, j; + w128_t lung; + + lung = dsfmt->status[DSFMT_N]; +@@ -266,8 +266,8 @@ + * @param size number of 128-bit pseudorandom numbers to be generated. + */ + inline static void gen_rand_array_o0c1(dsfmt_t *dsfmt, w128_t *array, +- int size) { +- int i, j; ++ ptrdiff_t size) { ++ ptrdiff_t i, j; + w128_t lung; + + lung = dsfmt->status[DSFMT_N]; +@@ -453,7 +453,7 @@ + * memory. Mac OSX doesn't have these functions, but \b malloc of OSX + * returns the pointer to the aligned memory block. + */ +-void dsfmt_fill_array_close1_open2(dsfmt_t *dsfmt, double array[], int size) { ++void dsfmt_fill_array_close1_open2(dsfmt_t *dsfmt, double array[], ptrdiff_t size) { + assert(size % 2 == 0); + assert(size >= DSFMT_N64); + gen_rand_array_c1o2(dsfmt, (w128_t *)array, size / 2); +@@ -471,7 +471,7 @@ + * @param size the number of pseudorandom numbers to be generated. + * see also \sa fill_array_close1_open2() + */ +-void dsfmt_fill_array_open_close(dsfmt_t *dsfmt, double array[], int size) { ++void dsfmt_fill_array_open_close(dsfmt_t *dsfmt, double array[], ptrdiff_t size) { + assert(size % 2 == 0); + assert(size >= DSFMT_N64); + gen_rand_array_o0c1(dsfmt, (w128_t *)array, size / 2); +@@ -489,7 +489,7 @@ + * @param size the number of pseudorandom numbers to be generated. + * see also \sa fill_array_close1_open2() + */ +-void dsfmt_fill_array_close_open(dsfmt_t *dsfmt, double array[], int size) { ++void dsfmt_fill_array_close_open(dsfmt_t *dsfmt, double array[], ptrdiff_t size) { + assert(size % 2 == 0); + assert(size >= DSFMT_N64); + gen_rand_array_c0o1(dsfmt, (w128_t *)array, size / 2); +@@ -507,7 +507,7 @@ + * @param size the number of pseudorandom numbers to be generated. + * see also \sa fill_array_close1_open2() + */ +-void dsfmt_fill_array_open_open(dsfmt_t *dsfmt, double array[], int size) { ++void dsfmt_fill_array_open_open(dsfmt_t *dsfmt, double array[], ptrdiff_t size) { + assert(size % 2 == 0); + assert(size >= DSFMT_N64); + gen_rand_array_o0o1(dsfmt, (w128_t *)array, size / 2); diff --git a/deps/patches/dSFMT.h.patch b/deps/patches/dSFMT.h.patch new file mode 100644 index 0000000..b35a7fe --- /dev/null +++ b/deps/patches/dSFMT.h.patch @@ -0,0 +1,362 @@ +--- dsfmt-2.2/dSFMT.h 2012-06-29 03:24:27.000000000 -0400 ++++ dSFMT-patched.h 2012-12-20 12:35:44.000000000 -0500 +@@ -40,6 +40,7 @@ + + #include + #include ++#include + + #if !defined(DSFMT_MEXP) + #ifdef __GNUC__ +@@ -178,16 +179,17 @@ + extern const int dsfmt_global_mexp; + + void dsfmt_gen_rand_all(dsfmt_t *dsfmt); +-void dsfmt_fill_array_open_close(dsfmt_t *dsfmt, double array[], int size); +-void dsfmt_fill_array_close_open(dsfmt_t *dsfmt, double array[], int size); +-void dsfmt_fill_array_open_open(dsfmt_t *dsfmt, double array[], int size); +-void dsfmt_fill_array_close1_open2(dsfmt_t *dsfmt, double array[], int size); ++void dsfmt_fill_array_open_close(dsfmt_t *dsfmt, double array[], ptrdiff_t size); ++void dsfmt_fill_array_close_open(dsfmt_t *dsfmt, double array[], ptrdiff_t size); ++void dsfmt_fill_array_open_open(dsfmt_t *dsfmt, double array[], ptrdiff_t size); ++void dsfmt_fill_array_close1_open2(dsfmt_t *dsfmt, double array[], ptrdiff_t size); + void dsfmt_chk_init_gen_rand(dsfmt_t *dsfmt, uint32_t seed, int mexp); + void dsfmt_chk_init_by_array(dsfmt_t *dsfmt, uint32_t init_key[], + int key_length, int mexp); + const char *dsfmt_get_idstring(void); + int dsfmt_get_min_array_size(void); + ++/* + #if defined(__GNUC__) + # define DSFMT_PRE_INLINE inline static + # define DSFMT_PST_INLINE __attribute__((always_inline)) +@@ -198,6 +200,10 @@ + # define DSFMT_PRE_INLINE inline static + # define DSFMT_PST_INLINE + #endif ++*/ ++#define DSFMT_PRE_INLINE ++#define DSFMT_PST_INLINE ++ + DSFMT_PRE_INLINE uint32_t dsfmt_genrand_uint32(dsfmt_t *dsfmt) DSFMT_PST_INLINE; + DSFMT_PRE_INLINE double dsfmt_genrand_close1_open2(dsfmt_t *dsfmt) + DSFMT_PST_INLINE; +@@ -212,13 +218,13 @@ + DSFMT_PRE_INLINE double dsfmt_gv_genrand_close_open(void) DSFMT_PST_INLINE; + DSFMT_PRE_INLINE double dsfmt_gv_genrand_open_close(void) DSFMT_PST_INLINE; + DSFMT_PRE_INLINE double dsfmt_gv_genrand_open_open(void) DSFMT_PST_INLINE; +-DSFMT_PRE_INLINE void dsfmt_gv_fill_array_open_close(double array[], int size) ++DSFMT_PRE_INLINE void dsfmt_gv_fill_array_open_close(double array[], ptrdiff_t size) + DSFMT_PST_INLINE; +-DSFMT_PRE_INLINE void dsfmt_gv_fill_array_close_open(double array[], int size) ++DSFMT_PRE_INLINE void dsfmt_gv_fill_array_close_open(double array[], ptrdiff_t size) + DSFMT_PST_INLINE; +-DSFMT_PRE_INLINE void dsfmt_gv_fill_array_open_open(double array[], int size) ++DSFMT_PRE_INLINE void dsfmt_gv_fill_array_open_open(double array[], ptrdiff_t size) + DSFMT_PST_INLINE; +-DSFMT_PRE_INLINE void dsfmt_gv_fill_array_close1_open2(double array[], int size) ++DSFMT_PRE_INLINE void dsfmt_gv_fill_array_close1_open2(double array[], ptrdiff_t size) + DSFMT_PST_INLINE; + DSFMT_PRE_INLINE void dsfmt_gv_init_gen_rand(uint32_t seed) DSFMT_PST_INLINE; + DSFMT_PRE_INLINE void dsfmt_gv_init_by_array(uint32_t init_key[], +@@ -236,7 +242,7 @@ + * @param dsfmt dsfmt internal state date + * @return double precision floating point pseudorandom number + */ +-inline static uint32_t dsfmt_genrand_uint32(dsfmt_t *dsfmt) { ++uint32_t dsfmt_genrand_uint32(dsfmt_t *dsfmt) { + uint32_t r; + uint64_t *psfmt64 = &dsfmt->status[0].u[0]; + +@@ -257,7 +263,7 @@ + * @param dsfmt dsfmt internal state date + * @return double precision floating point pseudorandom number + */ +-inline static double dsfmt_genrand_close1_open2(dsfmt_t *dsfmt) { ++double dsfmt_genrand_close1_open2(dsfmt_t *dsfmt) { + double r; + double *psfmt64 = &dsfmt->status[0].d[0]; + +@@ -276,7 +282,7 @@ + * before this function. This function uses \b global variables. + * @return double precision floating point pseudorandom number + */ +-inline static uint32_t dsfmt_gv_genrand_uint32(void) { ++uint32_t dsfmt_gv_genrand_uint32(void) { + return dsfmt_genrand_uint32(&dsfmt_global_data); + } + +@@ -287,7 +293,7 @@ + * before this function. This function uses \b global variables. + * @return double precision floating point pseudorandom number + */ +-inline static double dsfmt_gv_genrand_close1_open2(void) { ++double dsfmt_gv_genrand_close1_open2(void) { + return dsfmt_genrand_close1_open2(&dsfmt_global_data); + } + +@@ -299,7 +305,7 @@ + * @param dsfmt dsfmt internal state date + * @return double precision floating point pseudorandom number + */ +-inline static double dsfmt_genrand_close_open(dsfmt_t *dsfmt) { ++double dsfmt_genrand_close_open(dsfmt_t *dsfmt) { + return dsfmt_genrand_close1_open2(dsfmt) - 1.0; + } + +@@ -310,7 +316,7 @@ + * before this function. This function uses \b global variables. + * @return double precision floating point pseudorandom number + */ +-inline static double dsfmt_gv_genrand_close_open(void) { ++double dsfmt_gv_genrand_close_open(void) { + return dsfmt_gv_genrand_close1_open2() - 1.0; + } + +@@ -322,7 +328,7 @@ + * @param dsfmt dsfmt internal state date + * @return double precision floating point pseudorandom number + */ +-inline static double dsfmt_genrand_open_close(dsfmt_t *dsfmt) { ++double dsfmt_genrand_open_close(dsfmt_t *dsfmt) { + return 2.0 - dsfmt_genrand_close1_open2(dsfmt); + } + +@@ -333,7 +339,7 @@ + * before this function. This function uses \b global variables. + * @return double precision floating point pseudorandom number + */ +-inline static double dsfmt_gv_genrand_open_close(void) { ++double dsfmt_gv_genrand_open_close(void) { + return 2.0 - dsfmt_gv_genrand_close1_open2(); + } + +@@ -345,7 +351,7 @@ + * @param dsfmt dsfmt internal state date + * @return double precision floating point pseudorandom number + */ +-inline static double dsfmt_genrand_open_open(dsfmt_t *dsfmt) { ++double dsfmt_genrand_open_open(dsfmt_t *dsfmt) { + double *dsfmt64 = &dsfmt->status[0].d[0]; + union { + double d; +@@ -368,7 +374,7 @@ + * before this function. This function uses \b global variables. + * @return double precision floating point pseudorandom number + */ +-inline static double dsfmt_gv_genrand_open_open(void) { ++double dsfmt_gv_genrand_open_open(void) { + return dsfmt_genrand_open_open(&dsfmt_global_data); + } + +@@ -383,7 +389,7 @@ + * @param size the number of pseudorandom numbers to be generated. + * see also \sa dsfmt_fill_array_close1_open2() + */ +-inline static void dsfmt_gv_fill_array_close1_open2(double array[], int size) { ++void dsfmt_gv_fill_array_close1_open2(double array[], ptrdiff_t size) { + dsfmt_fill_array_close1_open2(&dsfmt_global_data, array, size); + } + +@@ -399,7 +405,7 @@ + * see also \sa dsfmt_fill_array_close1_open2() and \sa + * dsfmt_gv_fill_array_close1_open2() + */ +-inline static void dsfmt_gv_fill_array_open_close(double array[], int size) { ++void dsfmt_gv_fill_array_open_close(double array[], ptrdiff_t size) { + dsfmt_fill_array_open_close(&dsfmt_global_data, array, size); + } + +@@ -415,7 +421,7 @@ + * see also \sa dsfmt_fill_array_close1_open2() \sa + * dsfmt_gv_fill_array_close1_open2() + */ +-inline static void dsfmt_gv_fill_array_close_open(double array[], int size) { ++void dsfmt_gv_fill_array_close_open(double array[], ptrdiff_t size) { + dsfmt_fill_array_close_open(&dsfmt_global_data, array, size); + } + +@@ -431,7 +437,7 @@ + * see also \sa dsfmt_fill_array_close1_open2() \sa + * dsfmt_gv_fill_array_close1_open2() + */ +-inline static void dsfmt_gv_fill_array_open_open(double array[], int size) { ++void dsfmt_gv_fill_array_open_open(double array[], ptrdiff_t size) { + dsfmt_fill_array_open_open(&dsfmt_global_data, array, size); + } + +@@ -441,7 +447,7 @@ + * @param dsfmt dsfmt state vector. + * @param seed a 32-bit integer used as the seed. + */ +-inline static void dsfmt_init_gen_rand(dsfmt_t *dsfmt, uint32_t seed) { ++void dsfmt_init_gen_rand(dsfmt_t *dsfmt, uint32_t seed) { + dsfmt_chk_init_gen_rand(dsfmt, seed, DSFMT_MEXP); + } + +@@ -451,7 +457,7 @@ + * @param seed a 32-bit integer used as the seed. + * see also \sa dsfmt_init_gen_rand() + */ +-inline static void dsfmt_gv_init_gen_rand(uint32_t seed) { ++void dsfmt_gv_init_gen_rand(uint32_t seed) { + dsfmt_init_gen_rand(&dsfmt_global_data, seed); + } + +@@ -462,7 +468,7 @@ + * @param init_key the array of 32-bit integers, used as a seed. + * @param key_length the length of init_key. + */ +-inline static void dsfmt_init_by_array(dsfmt_t *dsfmt, uint32_t init_key[], ++void dsfmt_init_by_array(dsfmt_t *dsfmt, uint32_t init_key[], + int key_length) { + dsfmt_chk_init_by_array(dsfmt, init_key, key_length, DSFMT_MEXP); + } +@@ -475,7 +481,7 @@ + * @param key_length the length of init_key. + * see also \sa dsfmt_init_by_array() + */ +-inline static void dsfmt_gv_init_by_array(uint32_t init_key[], int key_length) { ++void dsfmt_gv_init_by_array(uint32_t init_key[], int key_length) { + dsfmt_init_by_array(&dsfmt_global_data, init_key, key_length); + } + +@@ -489,13 +495,13 @@ + DSFMT_PRE_INLINE double genrand_close_open(void) DSFMT_PST_INLINE; + DSFMT_PRE_INLINE double genrand_open_close(void) DSFMT_PST_INLINE; + DSFMT_PRE_INLINE double genrand_open_open(void) DSFMT_PST_INLINE; +-DSFMT_PRE_INLINE void fill_array_open_close(double array[], int size) ++DSFMT_PRE_INLINE void fill_array_open_close(double array[], ptrdiff_t size) + DSFMT_PST_INLINE; +-DSFMT_PRE_INLINE void fill_array_close_open(double array[], int size) ++DSFMT_PRE_INLINE void fill_array_close_open(double array[], ptrdiff_t size) + DSFMT_PST_INLINE; +-DSFMT_PRE_INLINE void fill_array_open_open(double array[], int size) ++DSFMT_PRE_INLINE void fill_array_open_open(double array[], ptrdiff_t size) + DSFMT_PST_INLINE; +-DSFMT_PRE_INLINE void fill_array_close1_open2(double array[], int size) ++DSFMT_PRE_INLINE void fill_array_close1_open2(double array[], ptrdiff_t size) + DSFMT_PST_INLINE; + + /** +@@ -503,7 +509,7 @@ + * @return id string. + * see also \sa dsfmt_get_idstring() + */ +-inline static const char *get_idstring(void) { ++const char *get_idstring(void) { + return dsfmt_get_idstring(); + } + +@@ -512,7 +518,7 @@ + * @return minimum size of array used for fill_array functions. + * see also \sa dsfmt_get_min_array_size() + */ +-inline static int get_min_array_size(void) { ++int get_min_array_size(void) { + return dsfmt_get_min_array_size(); + } + +@@ -521,7 +527,7 @@ + * @param seed a 32-bit integer used as the seed. + * see also \sa dsfmt_gv_init_gen_rand(), \sa dsfmt_init_gen_rand(). + */ +-inline static void init_gen_rand(uint32_t seed) { ++void init_gen_rand(uint32_t seed) { + dsfmt_gv_init_gen_rand(seed); + } + +@@ -531,7 +537,7 @@ + * @param key_length the length of init_key. + * see also \sa dsfmt_gv_init_by_array(), \sa dsfmt_init_by_array(). + */ +-inline static void init_by_array(uint32_t init_key[], int key_length) { ++void init_by_array(uint32_t init_key[], int key_length) { + dsfmt_gv_init_by_array(init_key, key_length); + } + +@@ -541,7 +547,7 @@ + * see also \sa dsfmt_genrand_close1_open2() \sa + * dsfmt_gv_genrand_close1_open2() + */ +-inline static double genrand_close1_open2(void) { ++double genrand_close1_open2(void) { + return dsfmt_gv_genrand_close1_open2(); + } + +@@ -551,7 +557,7 @@ + * see also \sa dsfmt_genrand_close_open() \sa + * dsfmt_gv_genrand_close_open() + */ +-inline static double genrand_close_open(void) { ++double genrand_close_open(void) { + return dsfmt_gv_genrand_close_open(); + } + +@@ -561,7 +567,7 @@ + * see also \sa dsfmt_genrand_open_close() \sa + * dsfmt_gv_genrand_open_close() + */ +-inline static double genrand_open_close(void) { ++double genrand_open_close(void) { + return dsfmt_gv_genrand_open_close(); + } + +@@ -571,7 +577,7 @@ + * see also \sa dsfmt_genrand_open_open() \sa + * dsfmt_gv_genrand_open_open() + */ +-inline static double genrand_open_open(void) { ++double genrand_open_open(void) { + return dsfmt_gv_genrand_open_open(); + } + +@@ -584,7 +590,7 @@ + * dsfmt_fill_array_close1_open2(), \sa + * dsfmt_gv_fill_array_close1_open2() + */ +-inline static void fill_array_open_close(double array[], int size) { ++void fill_array_open_close(double array[], ptrdiff_t size) { + dsfmt_gv_fill_array_open_close(array, size); + } + +@@ -597,7 +603,7 @@ + * dsfmt_fill_array_close1_open2(), \sa + * dsfmt_gv_fill_array_close1_open2() + */ +-inline static void fill_array_close_open(double array[], int size) { ++void fill_array_close_open(double array[], ptrdiff_t size) { + dsfmt_gv_fill_array_close_open(array, size); + } + +@@ -610,7 +616,7 @@ + * dsfmt_fill_array_close1_open2(), \sa + * dsfmt_gv_fill_array_close1_open2() + */ +-inline static void fill_array_open_open(double array[], int size) { ++void fill_array_open_open(double array[], ptrdiff_t size) { + dsfmt_gv_fill_array_open_open(array, size); + } + +@@ -622,7 +628,7 @@ + * see also \sa dsfmt_fill_array_close1_open2(), \sa + * dsfmt_gv_fill_array_close1_open2() + */ +-inline static void fill_array_close1_open2(double array[], int size) { ++void fill_array_close1_open2(double array[], ptrdiff_t size) { + dsfmt_gv_fill_array_close1_open2(array, size); + } + #endif /* DSFMT_DO_NOT_USE_OLD_NAMES */ +diff --git a/test.c b/test.c +index 82d55db..d65db9a 100644 +--- a/test.c ++++ b/test.c +@@ -4,7 +4,7 @@ + #include + #include + #define DSFMT_DO_NOT_USE_OLD_NAMES +-#include "dSFMT.h" ++#include "dSFMT.h.orig" + + #define NUM_RANDS 50000 + #define TIC_MAG 1 diff --git a/deps/patches/gmp-config-ldflags.patch b/deps/patches/gmp-config-ldflags.patch new file mode 100644 index 0000000..fb89fa6 --- /dev/null +++ b/deps/patches/gmp-config-ldflags.patch @@ -0,0 +1,381 @@ +--- gmp-6.1.2/configure 2019-03-25 17:58:41.928471374 -0400 ++++ gmp-6.1.2-LDFLAGS/configure 2019-03-26 13:08:07.756316866 -0400 +@@ -5880,7 +5880,7 @@ if test "$gmp_prog_cc_works" = yes; then + int main () { return 0; } + EOF + echo "Test compile: " >&5 +- gmp_compile="$cc $cflags $cppflags conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -5934,7 +5934,7 @@ void *f() { return g(); } + int main () { return 0; } + EOF + echo "Test compile: function pointer return" >&5 +- gmp_compile="$cc $cflags $cppflags conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -5990,7 +5990,7 @@ int cmov () { return (n >= 0 ? n : 0); } + int main () { return 0; } + EOF + echo "Test compile: cmov instruction" >&5 +- gmp_compile="$cc $cflags $cppflags conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -6047,7 +6047,7 @@ unsigned long gcc303 () { return (unsign + int main () { return 0; } + EOF + echo "Test compile: double -> ulong conversion" >&5 +- gmp_compile="$cc $cflags $cppflags conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -6102,7 +6102,7 @@ unsigned long fneg () { return -fneg_dat + int main () { return 0; } + EOF + echo "Test compile: double negation" >&5 +- gmp_compile="$cc $cflags $cppflags conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -6158,7 +6158,7 @@ float ftod () { return (float) ftod_data + int main () { return 0; } + EOF + echo "Test compile: double -> float conversion" >&5 +- gmp_compile="$cc $cflags $cppflags conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -6243,7 +6243,7 @@ param_init () + int main () { return 0; } + EOF + echo "Test compile: gnupro alpha ev6 char spilling" >&5 +- gmp_compile="$cc $cflags $cppflags conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -6294,7 +6294,7 @@ if test "$gmp_prog_cc_works" = yes; then + int k; int foo () { __builtin_alloca (k); } + EOF + echo "Test compile: __builtin_alloca availability" >&5 +- gmp_compile="$cc $cflags $cppflags conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -6340,7 +6340,7 @@ int foo () + int main () { return 0; } + EOF + echo "Test compile: alloca array" >&5 +- gmp_compile="$cc $cflags $cppflags conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -6418,7 +6418,7 @@ int f () + int main () { return 0; } + EOF + echo "Test compile: abs int -> double conversion" >&5 +- gmp_compile="$cc $cflags $cppflags conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -6483,7 +6483,7 @@ int dummy; + int main () { return 0; } + EOF + echo "Test compile: long long reliability test 1" >&5 +- gmp_compile="$cc $cflags $cppflags conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -6544,7 +6544,7 @@ int dummy; + int main () { return 0; } + EOF + echo "Test compile: long long reliability test 2" >&5 +- gmp_compile="$cc $cflags $cppflags conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -6605,7 +6605,7 @@ int dummy; + int main () { return 0; } + EOF + echo "Test compile: freebsd hacked gcc" >&5 +- gmp_compile="$cc $cflags $cppflags conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -6704,7 +6704,7 @@ main () + + EOF + echo "Test compile: mpn_lshift_com optimization" >&5 +- gmp_compile="$cc $cflags $cppflags conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -6813,7 +6813,7 @@ main () + + EOF + echo "Test compile: mpn_lshift_com optimization 2" >&5 +- gmp_compile="$cc $cflags $cppflags conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -7325,7 +7325,7 @@ _main: + xorl %eax, %eax + ret + EOF +- gmp_compile="$cc $cflags $cppflags conftest.s -o conftest >&5" ++ gmp_compile="$cc $cflags $cppflags $LDFLAGS conftest.s -o conftest >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -7390,7 +7390,7 @@ $as_echo_n "checking compiler $cc $cflag + cat >conftest.c <&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -7498,7 +7498,7 @@ if test "$gmp_prog_cc_works" = yes; then + int main () { return 0; } + EOF + echo "Test compile: " >&5 +- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -7552,7 +7552,7 @@ void *f() { return g(); } + int main () { return 0; } + EOF + echo "Test compile: function pointer return" >&5 +- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -7608,7 +7608,7 @@ int cmov () { return (n >= 0 ? n : 0); } + int main () { return 0; } + EOF + echo "Test compile: cmov instruction" >&5 +- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -7665,7 +7665,7 @@ unsigned long gcc303 () { return (unsign + int main () { return 0; } + EOF + echo "Test compile: double -> ulong conversion" >&5 +- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -7720,7 +7720,7 @@ unsigned long fneg () { return -fneg_dat + int main () { return 0; } + EOF + echo "Test compile: double negation" >&5 +- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -7776,7 +7776,7 @@ float ftod () { return (float) ftod_data + int main () { return 0; } + EOF + echo "Test compile: double -> float conversion" >&5 +- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -7861,7 +7861,7 @@ param_init () + int main () { return 0; } + EOF + echo "Test compile: gnupro alpha ev6 char spilling" >&5 +- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -7912,7 +7912,7 @@ if test "$gmp_prog_cc_works" = yes; then + int k; int foo () { __builtin_alloca (k); } + EOF + echo "Test compile: __builtin_alloca availability" >&5 +- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -7958,7 +7958,7 @@ int foo () + int main () { return 0; } + EOF + echo "Test compile: alloca array" >&5 +- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -8036,7 +8036,7 @@ int f () + int main () { return 0; } + EOF + echo "Test compile: abs int -> double conversion" >&5 +- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -8101,7 +8101,7 @@ int dummy; + int main () { return 0; } + EOF + echo "Test compile: long long reliability test 1" >&5 +- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -8162,7 +8162,7 @@ int dummy; + int main () { return 0; } + EOF + echo "Test compile: long long reliability test 2" >&5 +- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -8223,7 +8223,7 @@ int dummy; + int main () { return 0; } + EOF + echo "Test compile: freebsd hacked gcc" >&5 +- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -8322,7 +8322,7 @@ main () + + EOF + echo "Test compile: mpn_lshift_com optimization" >&5 +- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -8431,7 +8431,7 @@ main () + + EOF + echo "Test compile: mpn_lshift_com optimization 2" >&5 +- gmp_compile="$cc $cflags $cppflags $flag conftest.c >&5" ++ gmp_compile="$cc $cflags $cppflags $flag $LDFLAGS conftest.c >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -9987,7 +9987,7 @@ main () + return 0; + } + EOF +-gmp_compile="$CC_FOR_BUILD conftest.c" ++gmp_compile="$CC_FOR_BUILD $LDFLAGS conftest.c" + cc_for_build_works=no + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 +@@ -10019,7 +10019,7 @@ main () + return 0; + } + EOF +-gmp_compile="$HOST_CC conftest.c" ++gmp_compile="$HOST_CC $LDFLAGS conftest.c" + cc_for_build_works=no + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 +@@ -10052,7 +10052,7 @@ main () + return 0; + } + EOF +-gmp_compile="$i conftest.c" ++gmp_compile="$i $LDFLAGS conftest.c" + cc_for_build_works=no + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 +@@ -10132,7 +10132,7 @@ main () + } + EOF + for i in .exe ,ff8 ""; do +- gmp_compile="$CC_FOR_BUILD conftest.c -o conftest$i" ++ gmp_compile="$CC_FOR_BUILD $LDFLAGS conftest.c -o conftest$i" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -10168,7 +10168,7 @@ main (int argc, char **argv) + return 0; + } + EOF +-gmp_compile="$CC_FOR_BUILD conftest.c" ++gmp_compile="$CC_FOR_BUILD $LDFLAGS conftest.c" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -10210,7 +10210,7 @@ foo () + return log (d); + } + EOF +-gmp_compile="$CC_FOR_BUILD conftest.c -lm" ++gmp_compile="$CC_FOR_BUILD $LDFLAGS conftest.c -lm" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? +@@ -10543,7 +10543,7 @@ if test "$gmp_prog_cxx_works" = yes; the + int main (void) { return 0; } + EOF + echo "Test compile: " >&5 +- gmp_cxxcompile="$CXX $CPPFLAGS $CXXFLAGS conftest.cc >&5" ++ gmp_cxxcompile="$CXX $CPPFLAGS $CXXFLAGS $LDFLAGS conftest.cc >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_cxxcompile\""; } >&5 + (eval $gmp_cxxcompile) 2>&5 + ac_status=$? +@@ -10583,7 +10583,7 @@ using namespace foo; + int main (void) { return 0; } + EOF + echo "Test compile: namespace" >&5 +- gmp_cxxcompile="$CXX $CPPFLAGS $CXXFLAGS conftest.cc >&5" ++ gmp_cxxcompile="$CXX $CPPFLAGS $CXXFLAGS $LDFLAGS conftest.cc >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_cxxcompile\""; } >&5 + (eval $gmp_cxxcompile) 2>&5 + ac_status=$? +@@ -10629,7 +10629,7 @@ void someoutput (void) { std::cout << 12 + int main (void) { return 0; } + EOF + echo "Test compile: std iostream" >&5 +- gmp_cxxcompile="$CXX $CPPFLAGS $CXXFLAGS conftest.cc >&5" ++ gmp_cxxcompile="$CXX $CPPFLAGS $CXXFLAGS $LDFLAGS conftest.cc >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_cxxcompile\""; } >&5 + (eval $gmp_cxxcompile) 2>&5 + ac_status=$? +@@ -27095,7 +27095,7 @@ for tmp_underscore in "" "_"; do + ${tmp_gsym_prefix}main$gmp_cv_asm_label_suffix + addl $ ${tmp_underscore}_GLOBAL_OFFSET_TABLE_, %ebx + EOF +- gmp_compile="$CCAS $CFLAGS $CPPFLAGS $lt_prog_compiler_pic conftest.s >&5 && $CC $CFLAGS $CPPFLAGS $lt_prog_compiler_pic conftest.$OBJEXT >&5" ++ gmp_compile="$CCAS $CFLAGS $CPPFLAGS $lt_prog_compiler_pic conftest.s >&5 && $CC $CFLAGS $CPPFLAGS $LDFLAGS $lt_prog_compiler_pic conftest.$OBJEXT >&5" + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$gmp_compile\""; } >&5 + (eval $gmp_compile) 2>&5 + ac_status=$? + diff --git a/deps/patches/gmp-exception.patch b/deps/patches/gmp-exception.patch new file mode 100644 index 0000000..e4672be --- /dev/null +++ b/deps/patches/gmp-exception.patch @@ -0,0 +1,35 @@ +diff -r 842c2ba359bf errno.c +--- a/errno.c Sun Jan 24 22:06:51 2016 +0100 ++++ b/errno.c Thu Jan 28 13:37:54 2016 -0500 +@@ -33,24 +33,24 @@ + see https://www.gnu.org/licenses/. */ + + #include ++ ++#include ++ + #include "gmp.h" + #include "gmp-impl.h" + + int gmp_errno = 0; + + +-/* The deliberate divide by zero triggers an exception on most systems. On +- those where it doesn't, for example power and powerpc, use abort instead. +- +- Enhancement: Perhaps raise(SIGFPE) (or the same with kill()) would be +- better than abort. Perhaps it'd be possible to get the BSD style +- FPE_INTDIV_TRAP parameter in there too. */ +- ++/* Use SIGFPE on systems which have it. Otherwise, deliberate divide ++ by zero, which triggers an exception on most systems. On those ++ where it doesn't, for example power and powerpc, use abort instead. */ + void + __gmp_exception (int error_bit) + { + gmp_errno |= error_bit; + __gmp_junk = 10 / __gmp_0; ++ raise (SIGFPE); + abort (); + } + diff --git a/deps/patches/gmp_alloc_overflow_func.patch b/deps/patches/gmp_alloc_overflow_func.patch new file mode 100644 index 0000000..51506d7 --- /dev/null +++ b/deps/patches/gmp_alloc_overflow_func.patch @@ -0,0 +1,193 @@ +diff --git a/gmp-h.in b/gmp-h.in +--- a/gmp-h.in ++++ b/gmp-h.in +@@ -479,6 +479,13 @@ using std::FILE; + void *(**) (void *, size_t, size_t), + void (**) (void *, size_t)) __GMP_NOTHROW; + ++#define mp_set_alloc_overflow_function __gmp_set_alloc_overflow_function ++__GMP_DECLSPEC void mp_set_alloc_overflow_function (void (*) (void)) __GMP_NOTHROW; ++ ++#define mp_get_alloc_overflow_function __gmp_get_alloc_overflow_function ++__GMP_DECLSPEC void mp_get_alloc_overflow_function (void (**) (void)) __GMP_NOTHROW; ++ ++ + #define mp_bits_per_limb __gmp_bits_per_limb + __GMP_DECLSPEC extern const int mp_bits_per_limb; + +diff --git a/gmp-impl.h b/gmp-impl.h +--- a/gmp-impl.h ++++ b/gmp-impl.h +@@ -696,10 +696,12 @@ struct tmp_debug_entry_t { + __GMP_DECLSPEC extern void * (*__gmp_allocate_func) (size_t); + __GMP_DECLSPEC extern void * (*__gmp_reallocate_func) (void *, size_t, size_t); + __GMP_DECLSPEC extern void (*__gmp_free_func) (void *, size_t); ++__GMP_DECLSPEC extern void (*__gmp_alloc_overflow_func)(void); + + __GMP_DECLSPEC void *__gmp_default_allocate (size_t); + __GMP_DECLSPEC void *__gmp_default_reallocate (void *, size_t, size_t); + __GMP_DECLSPEC void __gmp_default_free (void *, size_t); ++__GMP_DECLSPEC void __gmp_default_alloc_overflow (void); + + #define __GMP_ALLOCATE_FUNC_TYPE(n,type) \ + ((type *) (*__gmp_allocate_func) ((n) * sizeof (type))) +@@ -727,6 +729,12 @@ struct tmp_debug_entry_t { + (ptr, (oldsize) * sizeof (type), (newsize) * sizeof (type)); \ + } while (0) + ++#define __GMP_ALLOC_OVERFLOW_FUNC() \ ++ do { \ ++ (*__gmp_alloc_overflow_func) (); \ ++ fprintf (stderr, "unexpected return from alloc_overflow\n"); \ ++ abort (); \ ++ } while (0) + + /* Dummy for non-gcc, code involving it will go dead. */ + #if ! defined (__GNUC__) || __GNUC__ < 2 +diff --git a/memory.c b/memory.c +--- a/memory.c ++++ b/memory.c +@@ -38,6 +38,7 @@ see https://www.gnu.org/licenses/. */ + void * (*__gmp_allocate_func) (size_t) = __gmp_default_allocate; + void * (*__gmp_reallocate_func) (void *, size_t, size_t) = __gmp_default_reallocate; + void (*__gmp_free_func) (void *, size_t) = __gmp_default_free; ++void (*__gmp_alloc_overflow_func) (void) = __gmp_default_alloc_overflow; + + + /* Default allocation functions. In case of failure to allocate/reallocate +@@ -144,3 +145,10 @@ void + #endif + free (blk_ptr); + } ++ ++void ++__gmp_default_alloc_overflow(void) ++{ ++ fprintf (stderr, "gmp: overflow in mpz type\n"); ++ abort(); ++} +diff --git a/mp_get_fns.c b/mp_get_fns.c +--- a/mp_get_fns.c ++++ b/mp_get_fns.c +@@ -46,3 +46,11 @@ mp_get_memory_functions (void *(**alloc_ + if (free_func != NULL) + *free_func = __gmp_free_func; + } ++ ++void ++mp_get_alloc_overflow_function( ++ void (**alloc_overflow_func) (void)) __GMP_NOTHROW ++{ ++ if (alloc_overflow_func != NULL) ++ *alloc_overflow_func = __gmp_alloc_overflow_func; ++} +diff --git a/mp_set_fns.c b/mp_set_fns.c +--- a/mp_set_fns.c ++++ b/mp_set_fns.c +@@ -48,3 +48,12 @@ mp_set_memory_functions (void *(*alloc_f + __gmp_reallocate_func = realloc_func; + __gmp_free_func = free_func; + } ++ ++void ++mp_set_alloc_overflow_function( ++ void (*alloc_overflow_func) (void)) __GMP_NOTHROW ++{ ++ if (alloc_overflow_func == 0) ++ alloc_overflow_func = __gmp_default_alloc_overflow; ++ __gmp_alloc_overflow_func = alloc_overflow_func; ++} +diff --git a/mpz/init2.c b/mpz/init2.c +--- a/mpz/init2.c ++++ b/mpz/init2.c +@@ -45,8 +45,7 @@ mpz_init2 (mpz_ptr x, mp_bitcnt_t bits) + { + if (UNLIKELY (new_alloc > INT_MAX)) + { +- fprintf (stderr, "gmp: overflow in mpz type\n"); +- abort (); ++ __GMP_ALLOC_OVERFLOW_FUNC (); + } + } + +diff --git a/mpz/realloc.c b/mpz/realloc.c +--- a/mpz/realloc.c ++++ b/mpz/realloc.c +@@ -45,16 +45,14 @@ void * + { + if (UNLIKELY (new_alloc > ULONG_MAX / GMP_NUMB_BITS)) + { +- fprintf (stderr, "gmp: overflow in mpz type\n"); +- abort (); ++ __GMP_ALLOC_OVERFLOW_FUNC (); + } + } + else + { + if (UNLIKELY (new_alloc > INT_MAX)) + { +- fprintf (stderr, "gmp: overflow in mpz type\n"); +- abort (); ++ __GMP_ALLOC_OVERFLOW_FUNC (); + } + } + +diff --git a/mpz/realloc2.c b/mpz/realloc2.c +--- a/mpz/realloc2.c ++++ b/mpz/realloc2.c +@@ -45,8 +45,7 @@ mpz_realloc2 (mpz_ptr m, mp_bitcnt_t bit + { + if (UNLIKELY (new_alloc > INT_MAX)) + { +- fprintf (stderr, "gmp: overflow in mpz type\n"); +- abort (); ++ __GMP_ALLOC_OVERFLOW_FUNC (); + } + } + +diff --git a/tests/mpz/t-pow.c b/tests/mpz/t-pow.c +--- a/tests/mpz/t-pow.c ++++ b/tests/mpz/t-pow.c +@@ -195,6 +195,34 @@ check_random (int reps) + mpz_clear (want); + } + ++jmp_buf env; ++ ++void ++alloc_overflow_handler (void) ++{ ++ longjmp(env, 1); ++} ++ ++void ++check_overflow (void) ++{ ++ mpz_t x; ++ mpz_init (x); ++ int overflow_intercepted = 0; ++ if (setjmp (env) == 0) { ++ mp_set_alloc_overflow_function (&alloc_overflow_handler); ++ mpz_ui_pow_ui (x, 3, 7625597484987LL); ++ } else { ++ ++overflow_intercepted; ++ } ++ if (overflow_intercepted != 1) { ++ printf ("overflow not intercepted\n"); ++ abort (); ++ } ++ mpz_clear (x); ++} ++ ++ + int + main (int argc, char **argv) + { +@@ -212,6 +240,7 @@ main (int argc, char **argv) + + check_various (); + check_random (reps); ++ check_overflow (); + + tests_end (); + exit (0); diff --git a/deps/patches/libgit2-agent-nonfatal.patch b/deps/patches/libgit2-agent-nonfatal.patch new file mode 100644 index 0000000..3ada9ec --- /dev/null +++ b/deps/patches/libgit2-agent-nonfatal.patch @@ -0,0 +1,25 @@ +commit 70020247d1903c7a1262d967cf205a44dc6f6ebe +Author: Keno Fischer +Date: Wed Jul 20 19:59:00 2016 -0400 + + Make failure to connect to ssh-agent non-fatal + + Julia issue: https://github.com/JuliaLang/julia/pull/17459 + Upstream: https://github.com/libgit2/libgit2/issues/3866 + +diff --git a/src/transports/ssh.c b/src/transports/ssh.c +index cfd5736..82d2c63 100644 +--- a/src/transports/ssh.c ++++ b/src/transports/ssh.c +@@ -296,8 +296,10 @@ static int ssh_agent_auth(LIBSSH2_SESSION *session, git_cred_ssh_key *c) { + + rc = libssh2_agent_connect(agent); + +- if (rc != LIBSSH2_ERROR_NONE) ++ if (rc != LIBSSH2_ERROR_NONE) { ++ rc = LIBSSH2_ERROR_AUTHENTICATION_FAILED; + goto shutdown; ++ } + + rc = libssh2_agent_list_identities(agent); + diff --git a/deps/patches/libunwind-prefer-extbl.patch b/deps/patches/libunwind-prefer-extbl.patch new file mode 100644 index 0000000..8d93605 --- /dev/null +++ b/deps/patches/libunwind-prefer-extbl.patch @@ -0,0 +1,144 @@ +From bc7b50355cb37cfa56f6131b2f9174b499053188 Mon Sep 17 00:00:00 2001 +From: Yichao Yu +Date: Sat, 1 Oct 2016 16:55:40 +0000 +Subject: [PATCH] Prefer EXTBL unwinding on ARM + +It is part of the C++ ABI so a EXTBL unwind info that's not `CANT_UNWIND` +should always be reliable/correct. +Ignore `ESTOPUNWIND` so that a `CANT_UNWIND` info can fallback to unwinding +using the debug info instead. +--- + include/tdep-arm/libunwind_i.h | 4 ++++ + src/arm/Gex_tables.c | 18 ++++++++++++++---- + src/arm/Gstep.c | 35 +++++++++++++++++++++-------------- + 3 files changed, 39 insertions(+), 18 deletions(-) + +diff --git a/include/tdep-arm/libunwind_i.h b/include/tdep-arm/libunwind_i.h +index 2602f41c..074fc8cb 100644 +--- a/include/tdep-arm/libunwind_i.h ++++ b/include/tdep-arm/libunwind_i.h +@@ -253,6 +253,7 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val) + #define tdep_init_done UNW_OBJ(init_done) + #define tdep_init UNW_OBJ(init) + #define arm_find_proc_info UNW_OBJ(find_proc_info) ++#define arm_find_proc_info2 UNW_OBJ(find_proc_info2) + #define arm_put_unwind_info UNW_OBJ(put_unwind_info) + /* Platforms that support UNW_INFO_FORMAT_TABLE need to define + tdep_search_unwind_table. */ +@@ -294,6 +295,9 @@ extern void tdep_init (void); + extern int arm_find_proc_info (unw_addr_space_t as, unw_word_t ip, + unw_proc_info_t *pi, int need_unwind_info, + void *arg); ++extern int arm_find_proc_info2 (unw_addr_space_t as, unw_word_t ip, ++ unw_proc_info_t *pi, int need_unwind_info, ++ void *arg, int methods); + extern void arm_put_unwind_info (unw_addr_space_t as, + unw_proc_info_t *pi, void *arg); + extern int tdep_search_unwind_table (unw_addr_space_t as, unw_word_t ip, +diff --git a/src/arm/Gex_tables.c b/src/arm/Gex_tables.c +index d6573a65..a895e0cc 100644 +--- a/src/arm/Gex_tables.c ++++ b/src/arm/Gex_tables.c +@@ -506,18 +506,20 @@ arm_phdr_cb (struct dl_phdr_info *info, size_t size, void *data) + } + + HIDDEN int +-arm_find_proc_info (unw_addr_space_t as, unw_word_t ip, +- unw_proc_info_t *pi, int need_unwind_info, void *arg) ++arm_find_proc_info2 (unw_addr_space_t as, unw_word_t ip, ++ unw_proc_info_t *pi, int need_unwind_info, void *arg, ++ int methods) + { + int ret = -1; + intrmask_t saved_mask; + + Debug (14, "looking for IP=0x%lx\n", (long) ip); + +- if (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF)) ++ if (UNW_TRY_METHOD (UNW_ARM_METHOD_DWARF) && (methods & UNW_ARM_METHOD_DWARF)) + ret = dwarf_find_proc_info (as, ip, pi, need_unwind_info, arg); + +- if (ret < 0 && UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX)) ++ if (ret < 0 && UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX) && ++ (methods & UNW_ARM_METHOD_EXIDX)) + { + struct arm_cb_data cb_data; + +@@ -540,6 +542,14 @@ arm_find_proc_info (unw_addr_space_t as, unw_word_t ip, + return ret; + } + ++HIDDEN int ++arm_find_proc_info (unw_addr_space_t as, unw_word_t ip, ++ unw_proc_info_t *pi, int need_unwind_info, void *arg) ++{ ++ return arm_find_proc_info2 (as, ip, pi, need_unwind_info, arg, ++ UNW_ARM_METHOD_ALL); ++} ++ + HIDDEN void + arm_put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg) + { +diff --git a/src/arm/Gstep.c b/src/arm/Gstep.c +index adec02e0..c43daa1c 100644 +--- a/src/arm/Gstep.c ++++ b/src/arm/Gstep.c +@@ -53,8 +53,15 @@ arm_exidx_step (struct cursor *c) + c->dwarf.as_arg); + if (ret == -UNW_ENOINFO) + { ++#ifdef UNW_LOCAL_ONLY ++ if ((ret = arm_find_proc_info2 (c->dwarf.as, ip, &c->dwarf.pi, ++ 1, c->dwarf.as_arg, ++ UNW_ARM_METHOD_EXIDX)) < 0) ++ return ret; ++#else + if ((ret = tdep_find_proc_info (&c->dwarf, ip, 1)) < 0) + return ret; ++#endif + } + + if (c->dwarf.pi.format != UNW_INFO_FORMAT_ARM_EXIDX) +@@ -94,8 +101,21 @@ unw_step (unw_cursor_t *cursor) + if (unw_is_signal_frame (cursor) > 0) + return arm_handle_signal_frame (cursor); + ++ /* First, try extbl-based unwinding. */ ++ if (UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX)) ++ { ++ Debug (13, "%s(ret=%d), trying extbl\n", ++ UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF) ? "dwarf_step() failed " : "", ++ ret); ++ ret = arm_exidx_step (c); ++ if (ret > 0) ++ return 1; ++ if (ret == 0) ++ return ret; ++ } ++ + #ifdef CONFIG_DEBUG_FRAME +- /* First, try DWARF-based unwinding. */ ++ /* Second, try DWARF-based unwinding. */ + if (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF)) + { + ret = dwarf_step (&c->dwarf); +@@ -114,16 +129,6 @@ unw_step (unw_cursor_t *cursor) + } + #endif /* CONFIG_DEBUG_FRAME */ + +- /* Next, try extbl-based unwinding. */ +- if (UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX)) +- { +- ret = arm_exidx_step (c); +- if (ret > 0) +- return 1; +- if (ret == -UNW_ESTOPUNWIND || ret == 0) +- return ret; +- } +- + /* Fall back on APCS frame parsing. + Note: This won't work in case the ARM EABI is used. */ + #ifdef __FreeBSD__ +-- +2.16.1 + diff --git a/deps/patches/libunwind-static-arm.patch b/deps/patches/libunwind-static-arm.patch new file mode 100644 index 0000000..92544a0 --- /dev/null +++ b/deps/patches/libunwind-static-arm.patch @@ -0,0 +1,13 @@ +diff --git a/src/arm/Gex_tables.c b/src/arm/Gex_tables.c +index d6573a65..1d64803e 100644 +--- a/src/arm/Gex_tables.c ++++ b/src/arm/Gex_tables.c +@@ -381,7 +381,7 @@ arm_exidx_extract (struct dwarf_cursor *c, uint8_t *buf) + return nbuf; + } + +-int ++static int + arm_search_unwind_table (unw_addr_space_t as, unw_word_t ip, + unw_dyn_info_t *di, unw_proc_info_t *pi, + int need_unwind_info, void *arg) diff --git a/deps/patches/llvm-6.0-D44650.patch b/deps/patches/llvm-6.0-D44650.patch new file mode 100644 index 0000000..353c823 --- /dev/null +++ b/deps/patches/llvm-6.0-D44650.patch @@ -0,0 +1,13 @@ +Index: tools/llvm-cfi-verify/CMakeLists.txt +=================================================================== +--- a/tools/llvm-cfi-verify/CMakeLists.txt ++++ b/tools/llvm-cfi-verify/CMakeLists.txt +@@ -11,7 +11,7 @@ + Symbolize + ) + +-add_llvm_tool(llvm-cfi-verify ++add_llvm_tool(llvm-cfi-verify DISABLE_LLVM_LINK_LLVM_DYLIB + llvm-cfi-verify.cpp) + + add_subdirectory(lib) diff --git a/deps/patches/llvm-6.0-D63688-wasm-isLocal.patch b/deps/patches/llvm-6.0-D63688-wasm-isLocal.patch new file mode 100644 index 0000000..f76376c --- /dev/null +++ b/deps/patches/llvm-6.0-D63688-wasm-isLocal.patch @@ -0,0 +1,36 @@ +commit e5f9df1ac59925e12588c6edf4a371175090d203 +Author: Keno Fischer +Date: Sat Jun 22 19:42:28 2019 -0400 + + [Support] Fix build under Emscripten + + Summary: + Emscripten's libc doesn't define MNT_LOCAL, thus causing a build + failure in the fallback path. However, to the best of my knowledge, + it also doesn't support remote file system mounts, so we may simply + return `true` here (as we do for e.g. Fuchsia). With this fix, the + core LLVM libraries build correctly under emscripten (though some + of the tools and utils do not). + + Reviewers: kripken + + Subscribers: kristina, llvm-commits + + Tags: #llvm + + Differential Revision: https://reviews.llvm.org/D63688 + +diff --git a/lib/Support/Unix/Path.inc b/lib/Support/Unix/Path.inc +index 2ecb97316c8..ea4c8b9db47 100644 +--- a/lib/Support/Unix/Path.inc ++++ b/lib/Support/Unix/Path.inc +@@ -380,6 +380,9 @@ static bool is_local_impl(struct STATVFS &Vfs) { + #elif defined(__CYGWIN__) + // Cygwin doesn't expose this information; would need to use Win32 API. + return false; ++#elif defined(__EMSCRIPTEN__) ++ // Emscripten doesn't currently support remote filesystem mounts. ++ return true; + #elif defined(__sun) + // statvfs::f_basetype contains a null-terminated FSType name of the mounted target + StringRef fstype(Vfs.f_basetype); diff --git a/deps/patches/llvm-6.0-D64032-cmake-cross.patch b/deps/patches/llvm-6.0-D64032-cmake-cross.patch new file mode 100644 index 0000000..e7dc1eb --- /dev/null +++ b/deps/patches/llvm-6.0-D64032-cmake-cross.patch @@ -0,0 +1,69 @@ +commit e83088cbec5e5798651f02e2ff6b11969994cc86 +Author: Keno Fischer +Date: Mon Jul 1 16:43:53 2019 -0400 + + With utils disabled, don't build tblgen in cross mode + + Summary: + In cross mode, we build a separate NATIVE tblgen that runs on the + host and is used during the build. Separately, we have a flag that + disables building all executables in utils/. Of course generally, + this doesn't turn off tblgen, since we need that during the build. + In cross mode, however, that tblegen is useless since we never + actually use it. Furthermore, it can be actively problematic if the + cross toolchain doesn't like building executables for whatever reason. + And even if building executables works fine, we can at least save + compile time by omitting it from the target build. There's two changes + needed to make this happen: + - Stop creating a dependency from the native tool to the target tool. + No such dependency is required for a correct build, so I'm not entirely + sure why it was there in the first place. + - If utils were disabled on the CMake command line and we're in cross mode, + respect that by excluding it from the install target (using EXCLUDE_FROM_ALL). + + Reviewers: smeenai, compnerd, aganea, pzheng + + Subscribers: mgorny, llvm-commits + + Tags: #llvm + + Differential Revision: https://reviews.llvm.org/D64032 + +diff --git a/cmake/modules/TableGen.cmake b/cmake/modules/TableGen.cmake +index d1afcb42f9d..095b316d627 100644 +--- a/cmake/modules/TableGen.cmake ++++ b/cmake/modules/TableGen.cmake +@@ -127,9 +127,6 @@ macro(add_tablegen target project) + set(LLVM_ENABLE_OBJLIB ON) + endif() + +- add_llvm_executable(${target} DISABLE_LLVM_LINK_LLVM_DYLIB ${ARGN}) +- set(LLVM_LINK_COMPONENTS ${${target}_OLD_LLVM_LINK_COMPONENTS}) +- + set(${project}_TABLEGEN "${target}" CACHE + STRING "Native TableGen executable. Saves building one when cross-compiling.") + +@@ -160,15 +157,22 @@ macro(add_tablegen target project) + CONFIGURATION Release) + add_custom_command(OUTPUT ${${project}_TABLEGEN_EXE} + COMMAND ${tblgen_build_cmd} +- DEPENDS CONFIGURE_LLVM_NATIVE ${target} ++ DEPENDS CONFIGURE_LLVM_NATIVE + WORKING_DIRECTORY ${LLVM_NATIVE_BUILD} + COMMENT "Building native TableGen..." + USES_TERMINAL) + add_custom_target(${project}-tablegen-host DEPENDS ${${project}_TABLEGEN_EXE}) + set(${project}_TABLEGEN_TARGET ${project}-tablegen-host PARENT_SCOPE) ++ ++ if ( NOT LLVM_BUILD_UTILS ) ++ set(EXCLUDE_FROM_ALL ON) ++ endif() + endif() + endif() + ++ add_llvm_executable(${target} DISABLE_LLVM_LINK_LLVM_DYLIB ${ARGN}) ++ set(LLVM_LINK_COMPONENTS ${${target}_OLD_LLVM_LINK_COMPONENTS}) ++ + if (${project} STREQUAL LLVM AND NOT LLVM_INSTALL_TOOLCHAIN_ONLY) + if(${target} IN_LIST LLVM_DISTRIBUTION_COMPONENTS OR + NOT LLVM_DISTRIBUTION_COMPONENTS) diff --git a/deps/patches/llvm-6.0-D64225-cmake-cross2.patch b/deps/patches/llvm-6.0-D64225-cmake-cross2.patch new file mode 100644 index 0000000..0010246 --- /dev/null +++ b/deps/patches/llvm-6.0-D64225-cmake-cross2.patch @@ -0,0 +1,37 @@ +commit 4e47c3ca1fd460a3000a58cd2c37ee0a91f77247 +Author: Keno Fischer +Date: Thu Jul 4 19:33:56 2019 -0400 + + [cmake] Don't set install rules for tblgen if building utils is disabled + + Summary: + This is a follow up to D64032. Afterwards if building utils is disabled + and cross compilation is attempted, CMake will complain that adding + `install()` directives to targets with EXCLUDE_FROM_ALL set is "undefined". + Indeed, it appears depending on the CMake version and the selected + Generator, the install rule will error because the underlying target isn't + built. Fix that by not adding the install rule if building utils is not + requested. Note that this doesn't prevent building tblgen as a + dependency in not cross-build, even if building tools is disabled. + + Reviewers: smeenai + + Subscribers: mgorny, llvm-commits + + Tags: #llvm + + Differential Revision: https://reviews.llvm.org/D64225 + +diff --git a/cmake/modules/TableGen.cmake b/cmake/modules/TableGen.cmake +index 095b316d627..75985ba905e 100644 +--- a/cmake/modules/TableGen.cmake ++++ b/cmake/modules/TableGen.cmake +@@ -173,7 +173,7 @@ macro(add_tablegen target project) + add_llvm_executable(${target} DISABLE_LLVM_LINK_LLVM_DYLIB ${ARGN}) + set(LLVM_LINK_COMPONENTS ${${target}_OLD_LLVM_LINK_COMPONENTS}) + +- if (${project} STREQUAL LLVM AND NOT LLVM_INSTALL_TOOLCHAIN_ONLY) ++ if (${project} STREQUAL LLVM AND NOT LLVM_INSTALL_TOOLCHAIN_ONLY AND LLVM_BUILD_UTILS) + if(${target} IN_LIST LLVM_DISTRIBUTION_COMPONENTS OR + NOT LLVM_DISTRIBUTION_COMPONENTS) + set(export_to_llvmexports EXPORT LLVMExports) diff --git a/deps/patches/llvm-6.0-DISABLE_ABI_CHECKS.patch b/deps/patches/llvm-6.0-DISABLE_ABI_CHECKS.patch new file mode 100644 index 0000000..d537c25 --- /dev/null +++ b/deps/patches/llvm-6.0-DISABLE_ABI_CHECKS.patch @@ -0,0 +1,39 @@ +From d793ba4bacae51ae25be19c1636fcf38707938fd Mon Sep 17 00:00:00 2001 +From: Valentin Churavy +Date: Fri, 1 Jun 2018 17:43:55 -0400 +Subject: [PATCH] fix LLVM_DISABLE_ABI_BREAKING_CHECKS_ENFORCING + +--- + cmake/modules/HandleLLVMOptions.cmake | 2 +- + include/llvm/Config/abi-breaking.h.cmake | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/cmake/modules/HandleLLVMOptions.cmake b/cmake/modules/HandleLLVMOptions.cmake +index 3d2dd48018c..b67ee6a896e 100644 +--- a/cmake/modules/HandleLLVMOptions.cmake ++++ b/cmake/modules/HandleLLVMOptions.cmake +@@ -572,7 +572,7 @@ if (LLVM_ENABLE_WARNINGS AND (LLVM_COMPILER_IS_GCC_COMPATIBLE OR CLANG_CL)) + + if (LLVM_ENABLE_PEDANTIC AND LLVM_COMPILER_IS_GCC_COMPATIBLE) + append("-pedantic" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) +- append("-Wno-long-long" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) ++ append("-Wno-long-long -Wundef" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) + endif() + + add_flag_if_supported("-Wcovered-switch-default" COVERED_SWITCH_DEFAULT_FLAG) +diff --git a/include/llvm/Config/abi-breaking.h.cmake b/include/llvm/Config/abi-breaking.h.cmake +index 7ae401e5b8a..d52c4609101 100644 +--- a/include/llvm/Config/abi-breaking.h.cmake ++++ b/include/llvm/Config/abi-breaking.h.cmake +@@ -20,7 +20,7 @@ + + /* Allow selectively disabling link-time mismatch checking so that header-only + ADT content from LLVM can be used without linking libSupport. */ +-#if !LLVM_DISABLE_ABI_BREAKING_CHECKS_ENFORCING ++#ifndef LLVM_DISABLE_ABI_BREAKING_CHECKS_ENFORCING + + // ABI_BREAKING_CHECKS protection: provides link-time failure when clients build + // mismatch with LLVM +-- +2.17.0 + diff --git a/deps/patches/llvm-6.0-NVPTX-addrspaces.patch b/deps/patches/llvm-6.0-NVPTX-addrspaces.patch new file mode 100644 index 0000000..d8c519e --- /dev/null +++ b/deps/patches/llvm-6.0-NVPTX-addrspaces.patch @@ -0,0 +1,32 @@ +diff --git a/lib/Target/NVPTX/NVPTXISelLowering.cpp b/lib/Target/NVPTX/NVPTXISelLowering.cpp +index f1e4251a44b..73d49f5d7e4 100644 +--- a/lib/Target/NVPTX/NVPTXISelLowering.cpp ++++ b/lib/Target/NVPTX/NVPTXISelLowering.cpp +@@ -1248,6 +1248,14 @@ SDValue NVPTXTargetLowering::getSqrtEstimate(SDValue Operand, SelectionDAG &DAG, + } + } + ++bool NVPTXTargetLowering::isNoopAddrSpaceCast(unsigned SrcAS, ++ unsigned DestAS) const { ++ assert(SrcAS != DestAS && "Expected different address spaces!"); ++ ++ return (SrcAS == ADDRESS_SPACE_GENERIC || SrcAS > ADDRESS_SPACE_LOCAL) && ++ (DestAS == ADDRESS_SPACE_GENERIC || DestAS > ADDRESS_SPACE_LOCAL); ++} ++ + SDValue + NVPTXTargetLowering::LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const { + SDLoc dl(Op); +diff --git a/lib/Target/NVPTX/NVPTXISelLowering.h b/lib/Target/NVPTX/NVPTXISelLowering.h +index ef04a8573d4..68a9a7195c4 100644 +--- a/lib/Target/NVPTX/NVPTXISelLowering.h ++++ b/lib/Target/NVPTX/NVPTXISelLowering.h +@@ -443,6 +443,8 @@ public: + const NVPTXSubtarget &STI); + SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override; + ++ bool isNoopAddrSpaceCast(unsigned SrcAS, unsigned DestAS) const override; ++ + SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const; + + const char *getTargetNodeName(unsigned Opcode) const override; diff --git a/deps/patches/llvm-6.0-r327540.patch b/deps/patches/llvm-6.0-r327540.patch new file mode 100644 index 0000000..f1d7fd7 --- /dev/null +++ b/deps/patches/llvm-6.0-r327540.patch @@ -0,0 +1,79 @@ +From 2671f80b2d151c9a87a4a93860f8a6b98db57aad Mon Sep 17 00:00:00 2001 +From: Craig Topper +Date: Wed, 14 Mar 2018 17:57:19 +0000 +Subject: [PATCH] [X86] Add back fast-isel code for handling i8 shifts. + +I removed this in r316797 because the coverage report showed no coverage and I thought it should have been handled by the auto generated table. I now see that there is code that bypasses the table if the shift amount is out of bounds. + +This adds back the code. We'll codegen out of bounds i8 shifts to effectively (amount & 0x1f). The 0x1f is a strange quirk of x86 that shift amounts are always masked to 5-bits(except 64-bits). So if the masked value is still out bounds the result will be 0. + +Fixes PR36731. + +git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@327540 91177308-0d34-0410-b5e6-96231b3b80d8 +--- + lib/Target/X86/X86FastISel.cpp | 21 ++++++++++++++------- + test/CodeGen/X86/fast-isel-shift.ll | 12 ++++++++++++ + 2 files changed, 26 insertions(+), 7 deletions(-) + +diff --git a/lib/Target/X86/X86FastISel.cpp b/lib/Target/X86/X86FastISel.cpp +index 7e88304ae19..943022585a1 100644 +--- a/lib/Target/X86/X86FastISel.cpp ++++ b/lib/Target/X86/X86FastISel.cpp +@@ -1789,9 +1789,16 @@ bool X86FastISel::X86SelectBranch(const Instruction *I) { + bool X86FastISel::X86SelectShift(const Instruction *I) { + unsigned CReg = 0, OpReg = 0; + const TargetRegisterClass *RC = nullptr; +- assert(!I->getType()->isIntegerTy(8) && +- "i8 shifts should be handled by autogenerated table"); +- if (I->getType()->isIntegerTy(16)) { ++ if (I->getType()->isIntegerTy(8)) { ++ CReg = X86::CL; ++ RC = &X86::GR8RegClass; ++ switch (I->getOpcode()) { ++ case Instruction::LShr: OpReg = X86::SHR8rCL; break; ++ case Instruction::AShr: OpReg = X86::SAR8rCL; break; ++ case Instruction::Shl: OpReg = X86::SHL8rCL; break; ++ default: return false; ++ } ++ } else if (I->getType()->isIntegerTy(16)) { + CReg = X86::CX; + RC = &X86::GR16RegClass; + switch (I->getOpcode()) { +@@ -1836,10 +1843,10 @@ bool X86FastISel::X86SelectShift(const Instruction *I) { + + // The shift instruction uses X86::CL. If we defined a super-register + // of X86::CL, emit a subreg KILL to precisely describe what we're doing here. +- assert(CReg != X86::CL && "CReg should be a super register of CL"); +- BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, +- TII.get(TargetOpcode::KILL), X86::CL) +- .addReg(CReg, RegState::Kill); ++ if (CReg != X86::CL) ++ BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, ++ TII.get(TargetOpcode::KILL), X86::CL) ++ .addReg(CReg, RegState::Kill); + + unsigned ResultReg = createResultReg(RC); + BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(OpReg), ResultReg) +diff --git a/test/CodeGen/X86/fast-isel-shift.ll b/test/CodeGen/X86/fast-isel-shift.ll +index 3699b7ba4bf..4dc56f351f5 100644 +--- a/test/CodeGen/X86/fast-isel-shift.ll ++++ b/test/CodeGen/X86/fast-isel-shift.ll +@@ -381,3 +381,15 @@ define i64 @ashr_imm4_i64(i64 %a) { + %c = ashr i64 %a, 4 + ret i64 %c + } ++ ++; Make sure we don't crash on out of bounds i8 shifts. ++define i8 @PR36731(i8 %a) { ++; CHECK-LABEL: PR36731: ++; CHECK: ## %bb.0: ++; CHECK-NEXT: movb $255, %cl ++; CHECK-NEXT: shlb %cl, %dil ++; CHECK-NEXT: movl %edi, %eax ++; CHECK-NEXT: retq ++ %b = shl i8 %a, -1 ++ ret i8 %b ++} +-- +2.16.2 + diff --git a/deps/patches/llvm-6.0.0-ifconv-D45819.patch b/deps/patches/llvm-6.0.0-ifconv-D45819.patch new file mode 100644 index 0000000..0fcd5a9 --- /dev/null +++ b/deps/patches/llvm-6.0.0-ifconv-D45819.patch @@ -0,0 +1,158 @@ +Index: lib/CodeGen/IfConversion.cpp +=================================================================== +--- a/lib/CodeGen/IfConversion.cpp ++++ b/lib/CodeGen/IfConversion.cpp +@@ -1714,20 +1714,25 @@ + } + + // Remove the duplicated instructions at the beginnings of both paths. +- // Skip dbg_value instructions ++ // Skip dbg_value instructions. + MachineBasicBlock::iterator DI1 = MBB1.getFirstNonDebugInstr(); + MachineBasicBlock::iterator DI2 = MBB2.getFirstNonDebugInstr(); + BBI1->NonPredSize -= NumDups1; + BBI2->NonPredSize -= NumDups1; + + // Skip past the dups on each side separately since there may be +- // differing dbg_value entries. ++ // differing dbg_value entries. NumDups1 can include a "return" ++ // instruction, if it's not marked as "branch". + for (unsigned i = 0; i < NumDups1; ++DI1) { ++ if (DI1 == MBB1.end()) ++ break; + if (!DI1->isDebugValue()) + ++i; + } + while (NumDups1 != 0) { + ++DI2; ++ if (DI2 == MBB2.end()) ++ break; + if (!DI2->isDebugValue()) + --NumDups1; + } +@@ -1738,11 +1743,16 @@ + Redefs.stepForward(MI, Dummy); + } + } ++ + BBI.BB->splice(BBI.BB->end(), &MBB1, MBB1.begin(), DI1); + MBB2.erase(MBB2.begin(), DI2); + +- // The branches have been checked to match, so it is safe to remove the branch +- // in BB1 and rely on the copy in BB2 ++ // The branches have been checked to match, so it is safe to remove the ++ // branch in BB1 and rely on the copy in BB2. The complication is that ++ // the blocks may end with a return instruction, which may or may not ++ // be marked as "branch". If it's not, then it could be included in ++ // "dups1", leaving the blocks potentially empty after moving the common ++ // duplicates. + #ifndef NDEBUG + // Unanalyzable branches must match exactly. Check that now. + if (!BBI1->IsBrAnalyzable) +@@ -1768,11 +1778,14 @@ + if (RemoveBranch) + BBI2->NonPredSize -= TII->removeBranch(*BBI2->BB); + else { +- do { +- assert(DI2 != MBB2.begin()); +- DI2--; +- } while (DI2->isBranch() || DI2->isDebugValue()); +- DI2++; ++ // Make DI2 point to the end of the range where the common "tail" ++ // instructions could be found. ++ while (DI2 != MBB2.begin()) { ++ MachineBasicBlock::iterator Prev = std::prev(DI2); ++ if (!Prev->isBranch() && !Prev->isDebugValue()) ++ break; ++ DI2 = Prev; ++ } + } + while (NumDups2 != 0) { + // NumDups2 only counted non-dbg_value instructions, so this won't +@@ -1833,11 +1846,15 @@ + // a non-predicated in BBI2, then we don't want to predicate the one from + // BBI2. The reason is that if we merged these blocks, we would end up with + // two predicated terminators in the same block. ++ // Also, if the branches in MBB1 and MBB2 were non-analyzable, then don't ++ // predicate them either. They were checked to be identical, and so the ++ // same branch would happen regardless of which path was taken. + if (!MBB2.empty() && (DI2 == MBB2.end())) { + MachineBasicBlock::iterator BBI1T = MBB1.getFirstTerminator(); + MachineBasicBlock::iterator BBI2T = MBB2.getFirstTerminator(); +- if (BBI1T != MBB1.end() && TII->isPredicated(*BBI1T) && +- BBI2T != MBB2.end() && !TII->isPredicated(*BBI2T)) ++ bool BB1Predicated = BBI1T != MBB1.end() && TII->isPredicated(*BBI1T); ++ bool BB2NonPredicated = BBI2T != MBB2.end() && !TII->isPredicated(*BBI2T); ++ if (BB2NonPredicated && (BB1Predicated || !BBI2->IsBrAnalyzable)) + --DI2; + } + +Index: test/CodeGen/Hexagon/ifcvt-diamond-ret.mir +=================================================================== +--- /dev/null ++++ test/CodeGen/Hexagon/ifcvt-diamond-ret.mir +@@ -0,0 +1,25 @@ ++# RUN: llc -march=hexagon -run-pass if-converter %s -o - | FileCheck %s ++ ++# Make sure this gets if-converted and it doesn't crash. ++# CHECK-LABEL: bb.0 ++# CHECK: PS_jmpret $r31 ++# CHECK-NOT: bb.{{[1-9]+}}: ++ ++--- ++name: fred ++tracksRegLiveness: true ++body: | ++ bb.0: ++ successors: %bb.1, %bb.2 ++ liveins: $r0 ++ renamable $p0 = C2_cmpeqi killed renamable $r0, 0 ++ J2_jumpf killed renamable $p0, %bb.2, implicit-def dead $pc ++ ++ bb.1: ++ S4_storeiri_io undef renamable $r0, 0, 32768 :: (store 4 into `i32* undef`) ++ PS_jmpret $r31, implicit-def dead $pc ++ ++ bb.2: ++ S4_storeiri_io undef renamable $r0, 0, 32768 :: (store 4 into `i32* undef`) ++ PS_jmpret $r31, implicit-def dead $pc ++... +Index: test/CodeGen/MIR/PowerPC/ifcvt-diamond-ret.mir +=================================================================== +--- /dev/null ++++ test/CodeGen/MIR/PowerPC/ifcvt-diamond-ret.mir +@@ -0,0 +1,34 @@ ++# RUN: llc -mtriple=powerpc64le-unknown-linux-gnu -run-pass=if-converter %s -o - | FileCheck %s ++--- ++name: foo ++body: | ++ bb.0: ++ liveins: $x0, $x3 ++ successors: %bb.1(0x40000000), %bb.2(0x40000000) ++ ++ dead renamable $x3 = ANDIo8 killed renamable $x3, 1, implicit-def dead $cr0, implicit-def $cr0gt ++ $cr2lt = CROR $cr0gt, $cr0gt ++ BCn killed renamable $cr2lt, %bb.2 ++ B %bb.1 ++ ++ bb.1: ++ renamable $x3 = LIS8 4096 ++ MTLR8 $x0, implicit-def $lr8 ++ BLR8 implicit $lr8, implicit $rm, implicit $x3 ++ ++ bb.2: ++ renamable $x3 = LIS8 4096 ++ MTLR8 $x0, implicit-def $lr8 ++ BLR8 implicit $lr8, implicit $rm, implicit $x3 ++... ++ ++# Diamond testcase with equivalent branches terminating in returns. ++ ++# CHECK: body: | ++# CHECK: bb.0: ++# CHECK: dead renamable $x3 = ANDIo8 killed renamable $x3, 1, implicit-def dead $cr0, implicit-def $cr0gt ++# CHECK: $cr2lt = CROR $cr0gt, $cr0gt ++# CHECK: renamable $x3 = LIS8 4096 ++# CHECK: MTLR8 $x0, implicit-def $lr8 ++# CHECK: BLR8 implicit $lr8, implicit $rm, implicit $x3 ++ diff --git a/deps/patches/llvm-6.0.0_D27296-libssp.patch b/deps/patches/llvm-6.0.0_D27296-libssp.patch new file mode 100644 index 0000000..dc703ad --- /dev/null +++ b/deps/patches/llvm-6.0.0_D27296-libssp.patch @@ -0,0 +1,35 @@ +Index: llvm/trunk/lib/Target/X86/X86ISelLowering.cpp +=================================================================== +--- a/lib/Target/X86/X86ISelLowering.cpp ++++ b/lib/Target/X86/X86ISelLowering.cpp +@@ -2098,7 +2098,8 @@ + + void X86TargetLowering::insertSSPDeclarations(Module &M) const { + // MSVC CRT provides functionalities for stack protection. +- if (Subtarget.getTargetTriple().isOSMSVCRT()) { ++ if (Subtarget.getTargetTriple().isWindowsMSVCEnvironment() || ++ Subtarget.getTargetTriple().isWindowsItaniumEnvironment()) { + // MSVC CRT has a global variable holding security cookie. + M.getOrInsertGlobal("__security_cookie", + Type::getInt8PtrTy(M.getContext())); +@@ -2120,15 +2121,19 @@ + + Value *X86TargetLowering::getSDagStackGuard(const Module &M) const { + // MSVC CRT has a global variable holding security cookie. +- if (Subtarget.getTargetTriple().isOSMSVCRT()) ++ if (Subtarget.getTargetTriple().isWindowsMSVCEnvironment() || ++ Subtarget.getTargetTriple().isWindowsItaniumEnvironment()) { + return M.getGlobalVariable("__security_cookie"); ++ } + return TargetLowering::getSDagStackGuard(M); + } + + Value *X86TargetLowering::getSSPStackGuardCheck(const Module &M) const { + // MSVC CRT has a function to validate security cookie. +- if (Subtarget.getTargetTriple().isOSMSVCRT()) ++ if (Subtarget.getTargetTriple().isWindowsMSVCEnvironment() || ++ Subtarget.getTargetTriple().isWindowsItaniumEnvironment()) { + return M.getFunction("__security_check_cookie"); ++ } + return TargetLowering::getSSPStackGuardCheck(M); + } diff --git a/deps/patches/llvm-7.0-D44650.patch b/deps/patches/llvm-7.0-D44650.patch new file mode 100644 index 0000000..09b5b27 --- /dev/null +++ b/deps/patches/llvm-7.0-D44650.patch @@ -0,0 +1,13 @@ +diff --git a/tools/llvm-cfi-verify/CMakeLists.txt b/tools/llvm-cfi-verify/CMakeLists.txt +index ae12bec5e80..9ffbe4e070d 100644 +--- a/tools/llvm-cfi-verify/CMakeLists.txt ++++ b/tools/llvm-cfi-verify/CMakeLists.txt +@@ -11,7 +11,7 @@ set(LLVM_LINK_COMPONENTS + Symbolize + ) + +-add_llvm_tool(llvm-cfi-verify ++add_llvm_tool(llvm-cfi-verify DISABLE_LLVM_LINK_LLVM_DYLIB + llvm-cfi-verify.cpp + ) + diff --git a/deps/patches/llvm-7.0-D50167-scev-umin.patch b/deps/patches/llvm-7.0-D50167-scev-umin.patch new file mode 100644 index 0000000..81576a1 --- /dev/null +++ b/deps/patches/llvm-7.0-D50167-scev-umin.patch @@ -0,0 +1,1861 @@ +commit d68d9140287ad41d11df3fe6038b79c2b8c96bbf +Author: Keno Fischer +Date: Sat Aug 11 05:44:38 2018 -0400 + + RFC: [SCEV] Add explicit representations of umin/smin + + Summary: + Currently we express umin as `~umax(~x, ~y)`. However, this becomes + a problem for operands in non-integral pointer spaces, because `~x` + is not something we can compute for `x` non-integral. However, since + comparisons are generally still allowed, we are actually able to + express `umin(x, y)` directly as long as we don't try to express is + as a umax. Support this by adding an explicit umin/smin representation + to SCEV. We do this by factoring the existing getUMax/getSMax functions + into a new function that does all four. The previous two functions + were largely identical, except that the SMax variant used `isKnownPredicate` + while the UMax variant used `isKnownViaNonRecursiveReasoning`. + + Trying to make the UMax variant also use `isKnownPredicate` yields to + an infinite recursion, while trying to make the `SMax` variant use + `isKnownViaNonRecursiveReasoning` causes + `Transforms/IndVarSimplify/backedge-on-min-max.ll` to fail. + + I would appreciate any insight into which predicate is correct here. + + Reviewers: reames, sanjoy, mkazantsev + + Subscribers: dmgreen, vchuravy, javed.absar, llvm-commits + + Differential Revision: https://reviews.llvm.org/D50167 + +diff --git a/include/llvm/Analysis/ScalarEvolution.h b/include/llvm/Analysis/ScalarEvolution.h +index 89918e3c205..73e6cc36254 100644 +--- a/include/llvm/Analysis/ScalarEvolution.h ++++ b/include/llvm/Analysis/ScalarEvolution.h +@@ -582,6 +582,8 @@ public: + /// \p IndexExprs The expressions for the indices. + const SCEV *getGEPExpr(GEPOperator *GEP, + const SmallVectorImpl &IndexExprs); ++ const SCEV *getUSMinMaxExpr(unsigned Kind, ++ SmallVectorImpl &Operands); + const SCEV *getSMaxExpr(const SCEV *LHS, const SCEV *RHS); + const SCEV *getSMaxExpr(SmallVectorImpl &Operands); + const SCEV *getUMaxExpr(const SCEV *LHS, const SCEV *RHS); +diff --git a/include/llvm/Analysis/ScalarEvolutionExpander.h b/include/llvm/Analysis/ScalarEvolutionExpander.h +index 58d42680d6b..57d658b157d 100644 +--- a/include/llvm/Analysis/ScalarEvolutionExpander.h ++++ b/include/llvm/Analysis/ScalarEvolutionExpander.h +@@ -368,6 +368,10 @@ namespace llvm { + + Value *visitUMaxExpr(const SCEVUMaxExpr *S); + ++ Value *visitSMinExpr(const SCEVSMinExpr *S); ++ ++ Value *visitUMinExpr(const SCEVUMinExpr *S); ++ + Value *visitUnknown(const SCEVUnknown *S) { + return S->getValue(); + } +diff --git a/include/llvm/Analysis/ScalarEvolutionExpressions.h b/include/llvm/Analysis/ScalarEvolutionExpressions.h +index 42e76094eb2..a4fc607b0c3 100644 +--- a/include/llvm/Analysis/ScalarEvolutionExpressions.h ++++ b/include/llvm/Analysis/ScalarEvolutionExpressions.h +@@ -36,33 +36,42 @@ class ConstantRange; + class Loop; + class Type; + +- enum SCEVTypes { +- // These should be ordered in terms of increasing complexity to make the +- // folders simpler. +- scConstant, scTruncate, scZeroExtend, scSignExtend, scAddExpr, scMulExpr, +- scUDivExpr, scAddRecExpr, scUMaxExpr, scSMaxExpr, +- scUnknown, scCouldNotCompute +- }; +- +- /// This class represents a constant integer value. +- class SCEVConstant : public SCEV { +- friend class ScalarEvolution; +- +- ConstantInt *V; +- +- SCEVConstant(const FoldingSetNodeIDRef ID, ConstantInt *v) : +- SCEV(ID, scConstant), V(v) {} +- +- public: +- ConstantInt *getValue() const { return V; } +- const APInt &getAPInt() const { return getValue()->getValue(); } +- +- Type *getType() const { return V->getType(); } +- +- /// Methods for support type inquiry through isa, cast, and dyn_cast: +- static bool classof(const SCEV *S) { +- return S->getSCEVType() == scConstant; +- } ++enum SCEVTypes { ++ // These should be ordered in terms of increasing complexity to make the ++ // folders simpler. ++ scConstant, ++ scTruncate, ++ scZeroExtend, ++ scSignExtend, ++ scAddExpr, ++ scMulExpr, ++ scUDivExpr, ++ scAddRecExpr, ++ scUMaxExpr, ++ scSMaxExpr, ++ scUMinExpr, ++ scSMinExpr, ++ scUnknown, ++ scCouldNotCompute ++}; ++ ++/// This class represents a constant integer value. ++class SCEVConstant : public SCEV { ++ friend class ScalarEvolution; ++ ++ ConstantInt *V; ++ ++ SCEVConstant(const FoldingSetNodeIDRef ID, ConstantInt *v) ++ : SCEV(ID, scConstant), V(v) {} ++ ++public: ++ ConstantInt *getValue() const { return V; } ++ const APInt &getAPInt() const { return getValue()->getValue(); } ++ ++ Type *getType() const { return V->getType(); } ++ ++ /// Methods for support type inquiry through isa, cast, and dyn_cast: ++ static bool classof(const SCEV *S) { return S->getSCEVType() == scConstant; } + }; + + /// This is the base class for unary cast operator classes. +@@ -183,10 +192,9 @@ class Type; + + /// Methods for support type inquiry through isa, cast, and dyn_cast: + static bool classof(const SCEV *S) { +- return S->getSCEVType() == scAddExpr || +- S->getSCEVType() == scMulExpr || +- S->getSCEVType() == scSMaxExpr || +- S->getSCEVType() == scUMaxExpr || ++ return S->getSCEVType() == scAddExpr || S->getSCEVType() == scMulExpr || ++ S->getSCEVType() == scSMaxExpr || S->getSCEVType() == scUMaxExpr || ++ S->getSCEVType() == scSMinExpr || S->getSCEVType() == scUMinExpr || + S->getSCEVType() == scAddRecExpr; + } + }; +@@ -201,10 +209,9 @@ class Type; + public: + /// Methods for support type inquiry through isa, cast, and dyn_cast: + static bool classof(const SCEV *S) { +- return S->getSCEVType() == scAddExpr || +- S->getSCEVType() == scMulExpr || +- S->getSCEVType() == scSMaxExpr || +- S->getSCEVType() == scUMaxExpr; ++ return S->getSCEVType() == scAddExpr || S->getSCEVType() == scMulExpr || ++ S->getSCEVType() == scSMaxExpr || S->getSCEVType() == scUMaxExpr || ++ S->getSCEVType() == scSMinExpr || S->getSCEVType() == scUMinExpr; + } + + /// Set flags for a non-recurrence without clearing previously set flags. +@@ -394,6 +401,40 @@ class Type; + } + }; + ++ /// This class represents a signed minimum selection. ++ class SCEVSMinExpr : public SCEVCommutativeExpr { ++ friend class ScalarEvolution; ++ ++ SCEVSMinExpr(const FoldingSetNodeIDRef ID, const SCEV *const *O, size_t N) ++ : SCEVCommutativeExpr(ID, scSMinExpr, O, N) { ++ // Min never overflows. ++ setNoWrapFlags((NoWrapFlags)(FlagNUW | FlagNSW)); ++ } ++ ++ public: ++ /// Methods for support type inquiry through isa, cast, and dyn_cast: ++ static bool classof(const SCEV *S) { ++ return S->getSCEVType() == scSMinExpr; ++ } ++ }; ++ ++ /// This class represents an unsigned minimum selection. ++ class SCEVUMinExpr : public SCEVCommutativeExpr { ++ friend class ScalarEvolution; ++ ++ SCEVUMinExpr(const FoldingSetNodeIDRef ID, const SCEV *const *O, size_t N) ++ : SCEVCommutativeExpr(ID, scUMinExpr, O, N) { ++ // Min never overflows. ++ setNoWrapFlags((NoWrapFlags)(FlagNUW | FlagNSW)); ++ } ++ ++ public: ++ /// Methods for support type inquiry through isa, cast, and dyn_cast: ++ static bool classof(const SCEV *S) { ++ return S->getSCEVType() == scUMinExpr; ++ } ++ }; ++ + /// This means that we are dealing with an entirely unknown SCEV + /// value, and only represent it as its LLVM Value. This is the + /// "bottom" value for the analysis. +@@ -466,6 +507,10 @@ class Type; + return ((SC*)this)->visitSMaxExpr((const SCEVSMaxExpr*)S); + case scUMaxExpr: + return ((SC*)this)->visitUMaxExpr((const SCEVUMaxExpr*)S); ++ case scSMinExpr: ++ return ((SC *)this)->visitSMinExpr((const SCEVSMinExpr *)S); ++ case scUMinExpr: ++ return ((SC *)this)->visitUMinExpr((const SCEVUMinExpr *)S); + case scUnknown: + return ((SC*)this)->visitUnknown((const SCEVUnknown*)S); + case scCouldNotCompute: +@@ -519,6 +564,8 @@ class Type; + case scMulExpr: + case scSMaxExpr: + case scUMaxExpr: ++ case scSMinExpr: ++ case scUMinExpr: + case scAddRecExpr: + for (const auto *Op : cast(S)->operands()) + push(Op); +@@ -681,6 +728,26 @@ class Type; + return !Changed ? Expr : SE.getUMaxExpr(Operands); + } + ++ const SCEV *visitSMinExpr(const SCEVSMinExpr *Expr) { ++ SmallVector Operands; ++ bool Changed = false; ++ for (auto *Op : Expr->operands()) { ++ Operands.push_back(((SC *)this)->visit(Op)); ++ Changed |= Op != Operands.back(); ++ } ++ return !Changed ? Expr : SE.getSMinExpr(Operands); ++ } ++ ++ const SCEV *visitUMinExpr(const SCEVUMinExpr *Expr) { ++ SmallVector Operands; ++ bool Changed = false; ++ for (auto *Op : Expr->operands()) { ++ Operands.push_back(((SC *)this)->visit(Op)); ++ Changed |= Op != Operands.back(); ++ } ++ return !Changed ? Expr : SE.getUMinExpr(Operands); ++ } ++ + const SCEV *visitUnknown(const SCEVUnknown *Expr) { + return Expr; + } +diff --git a/lib/Analysis/ScalarEvolution.cpp b/lib/Analysis/ScalarEvolution.cpp +index 0e715b8814f..19dab859a00 100644 +--- a/lib/Analysis/ScalarEvolution.cpp ++++ b/lib/Analysis/ScalarEvolution.cpp +@@ -267,7 +267,9 @@ void SCEV::print(raw_ostream &OS) const { + case scAddExpr: + case scMulExpr: + case scUMaxExpr: +- case scSMaxExpr: { ++ case scSMaxExpr: ++ case scUMinExpr: ++ case scSMinExpr: { + const SCEVNAryExpr *NAry = cast(this); + const char *OpStr = nullptr; + switch (NAry->getSCEVType()) { +@@ -275,6 +277,12 @@ void SCEV::print(raw_ostream &OS) const { + case scMulExpr: OpStr = " * "; break; + case scUMaxExpr: OpStr = " umax "; break; + case scSMaxExpr: OpStr = " smax "; break; ++ case scUMinExpr: ++ OpStr = " umin "; ++ break; ++ case scSMinExpr: ++ OpStr = " smin "; ++ break; + } + OS << "("; + for (SCEVNAryExpr::op_iterator I = NAry->op_begin(), E = NAry->op_end(); +@@ -343,6 +351,8 @@ Type *SCEV::getType() const { + case scMulExpr: + case scUMaxExpr: + case scSMaxExpr: ++ case scUMinExpr: ++ case scSMinExpr: + return cast(this)->getType(); + case scAddExpr: + return cast(this)->getType(); +@@ -711,7 +721,9 @@ static int CompareSCEVComplexity( + case scAddExpr: + case scMulExpr: + case scSMaxExpr: +- case scUMaxExpr: { ++ case scUMaxExpr: ++ case scSMinExpr: ++ case scUMinExpr: { + const SCEVNAryExpr *LC = cast(LHS); + const SCEVNAryExpr *RC = cast(RHS); + +@@ -915,6 +927,8 @@ public: + void visitUDivExpr(const SCEVUDivExpr *Numerator) {} + void visitSMaxExpr(const SCEVSMaxExpr *Numerator) {} + void visitUMaxExpr(const SCEVUMaxExpr *Numerator) {} ++ void visitSMinExpr(const SCEVSMinExpr *Numerator) {} ++ void visitUMinExpr(const SCEVUMinExpr *Numerator) {} + void visitUnknown(const SCEVUnknown *Numerator) {} + void visitCouldNotCompute(const SCEVCouldNotCompute *Numerator) {} + +@@ -3488,23 +3502,21 @@ ScalarEvolution::getGEPExpr(GEPOperator *GEP, + return getAddExpr(BaseExpr, TotalOffset, Wrap); + } + +-const SCEV *ScalarEvolution::getSMaxExpr(const SCEV *LHS, +- const SCEV *RHS) { +- SmallVector Ops = {LHS, RHS}; +- return getSMaxExpr(Ops); +-} +- + const SCEV * +-ScalarEvolution::getSMaxExpr(SmallVectorImpl &Ops) { +- assert(!Ops.empty() && "Cannot get empty smax!"); ++ScalarEvolution::getUSMinMaxExpr(unsigned Kind, ++ SmallVectorImpl &Ops) { ++ assert(!Ops.empty() && "Cannot get empty (u|s)(min|max)!"); + if (Ops.size() == 1) return Ops[0]; + #ifndef NDEBUG + Type *ETy = getEffectiveSCEVType(Ops[0]->getType()); + for (unsigned i = 1, e = Ops.size(); i != e; ++i) + assert(getEffectiveSCEVType(Ops[i]->getType()) == ETy && +- "SCEVSMaxExpr operand types don't match!"); ++ "Operand types don't match!"); + #endif + ++ bool IsSigned = Kind == scSMaxExpr || Kind == scSMinExpr; ++ bool IsMax = Kind == scSMaxExpr || Kind == scUMaxExpr; ++ + // Sort by complexity, this groups all similar expression types together. + GroupByComplexity(Ops, &LI, DT); + +@@ -3513,61 +3525,91 @@ ScalarEvolution::getSMaxExpr(SmallVectorImpl &Ops) { + if (const SCEVConstant *LHSC = dyn_cast(Ops[0])) { + ++Idx; + assert(Idx < Ops.size()); ++ auto &FoldOp = ++ Kind == scSMaxExpr ++ ? APIntOps::smax ++ : Kind == scSMinExpr ++ ? APIntOps::smin ++ : Kind == scUMaxExpr ? APIntOps::umax : APIntOps::umin; + while (const SCEVConstant *RHSC = dyn_cast(Ops[Idx])) { + // We found two constants, fold them together! + ConstantInt *Fold = ConstantInt::get( +- getContext(), APIntOps::smax(LHSC->getAPInt(), RHSC->getAPInt())); ++ getContext(), FoldOp(LHSC->getAPInt(), RHSC->getAPInt())); + Ops[0] = getConstant(Fold); + Ops.erase(Ops.begin()+1); // Erase the folded element + if (Ops.size() == 1) return Ops[0]; + LHSC = cast(Ops[0]); + } + +- // If we are left with a constant minimum-int, strip it off. +- if (cast(Ops[0])->getValue()->isMinValue(true)) { +- Ops.erase(Ops.begin()); +- --Idx; +- } else if (cast(Ops[0])->getValue()->isMaxValue(true)) { +- // If we have an smax with a constant maximum-int, it will always be +- // maximum-int. +- return Ops[0]; ++ if (IsMax) { ++ // If we are left with a constant minimum-int, strip it off. ++ if (cast(Ops[0])->getValue()->isMinValue(IsSigned)) { ++ Ops.erase(Ops.begin()); ++ --Idx; ++ } else if (cast(Ops[0])->getValue()->isMaxValue(IsSigned)) { ++ // If we have an smax with a constant maximum-int, it will always be ++ // maximum-int. ++ return Ops[0]; ++ } ++ } else { ++ // If we are left with a constant maximum-int, strip it off. ++ if (cast(Ops[0])->getValue()->isMaxValue(IsSigned)) { ++ Ops.erase(Ops.begin()); ++ --Idx; ++ } else if (cast(Ops[0])->getValue()->isMinValue(IsSigned)) { ++ // If we have an smax with a constant minimum-int, it will always be ++ // maximum-int. ++ return Ops[0]; ++ } + } + + if (Ops.size() == 1) return Ops[0]; + } + +- // Find the first SMax +- while (Idx < Ops.size() && Ops[Idx]->getSCEVType() < scSMaxExpr) ++ // Find the first operation of the same kind ++ while (Idx < Ops.size() && Ops[Idx]->getSCEVType() != Kind) + ++Idx; + + // Check to see if one of the operands is an SMax. If so, expand its operands + // onto our operand list, and recurse to simplify. + if (Idx < Ops.size()) { +- bool DeletedSMax = false; +- while (const SCEVSMaxExpr *SMax = dyn_cast(Ops[Idx])) { ++ bool DeletedAny = false; ++ while (Ops[Idx]->getSCEVType() == Kind) { ++ const SCEVCommutativeExpr *SCE = cast(Ops[Idx]); + Ops.erase(Ops.begin()+Idx); +- Ops.append(SMax->op_begin(), SMax->op_end()); +- DeletedSMax = true; ++ Ops.append(SCE->op_begin(), SCE->op_end()); ++ DeletedAny = true; + } + +- if (DeletedSMax) +- return getSMaxExpr(Ops); ++ if (DeletedAny) ++ return getUSMinMaxExpr(Kind, Ops); + } + + // Okay, check to see if the same value occurs in the operand list twice. If + // so, delete one. Since we sorted the list, these values are required to + // be adjacent. +- for (unsigned i = 0, e = Ops.size()-1; i != e; ++i) +- // X smax Y smax Y --> X smax Y +- // X smax Y --> X, if X is always greater than Y +- if (Ops[i] == Ops[i+1] || +- isKnownPredicate(ICmpInst::ICMP_SGE, Ops[i], Ops[i+1])) { +- Ops.erase(Ops.begin()+i+1, Ops.begin()+i+2); +- --i; --e; +- } else if (isKnownPredicate(ICmpInst::ICMP_SLE, Ops[i], Ops[i+1])) { +- Ops.erase(Ops.begin()+i, Ops.begin()+i+1); +- --i; --e; ++ llvm::CmpInst::Predicate GEPred = ++ IsSigned ? ICmpInst::ICMP_SGE : ICmpInst::ICMP_UGE; ++ llvm::CmpInst::Predicate LEPred = ++ IsSigned ? ICmpInst::ICMP_SLE : ICmpInst::ICMP_ULE; ++ llvm::CmpInst::Predicate FirstPred = IsMax ? GEPred : LEPred; ++ llvm::CmpInst::Predicate SecondPred = IsMax ? LEPred : GEPred; ++ for (unsigned i = 0, e = Ops.size() - 1; i != e; ++i) { ++ if (Ops[i] == Ops[i + 1] || ++ isKnownViaNonRecursiveReasoning(FirstPred, Ops[i], Ops[i + 1])) { ++ // X op Y op Y --> X op Y ++ // X op Y --> X, if we know X, Y are ordered appropriately ++ Ops.erase(Ops.begin() + i + 1, Ops.begin() + i + 2); ++ --i; ++ --e; ++ } else if (isKnownViaNonRecursiveReasoning(SecondPred, Ops[i], ++ Ops[i + 1])) { ++ // X op Y --> Y, if we know X, Y are ordered appropriately ++ Ops.erase(Ops.begin() + i, Ops.begin() + i + 1); ++ --i; ++ --e; + } ++ } + + if (Ops.size() == 1) return Ops[0]; + +@@ -3576,121 +3618,51 @@ ScalarEvolution::getSMaxExpr(SmallVectorImpl &Ops) { + // Okay, it looks like we really DO need an smax expr. Check to see if we + // already have one, otherwise create a new one. + FoldingSetNodeID ID; +- ID.AddInteger(scSMaxExpr); ++ ID.AddInteger(Kind); + for (unsigned i = 0, e = Ops.size(); i != e; ++i) + ID.AddPointer(Ops[i]); + void *IP = nullptr; + if (const SCEV *S = UniqueSCEVs.FindNodeOrInsertPos(ID, IP)) return S; + const SCEV **O = SCEVAllocator.Allocate(Ops.size()); + std::uninitialized_copy(Ops.begin(), Ops.end(), O); +- SCEV *S = new (SCEVAllocator) SCEVSMaxExpr(ID.Intern(SCEVAllocator), +- O, Ops.size()); ++ SCEV *S = nullptr; ++ ++ if (Kind == scSMaxExpr) { ++ S = new (SCEVAllocator) ++ SCEVSMaxExpr(ID.Intern(SCEVAllocator), O, Ops.size()); ++ } else if (Kind == scUMaxExpr) { ++ S = new (SCEVAllocator) ++ SCEVUMaxExpr(ID.Intern(SCEVAllocator), O, Ops.size()); ++ } else if (Kind == scSMinExpr) { ++ S = new (SCEVAllocator) ++ SCEVSMinExpr(ID.Intern(SCEVAllocator), O, Ops.size()); ++ } else { ++ assert(Kind == scUMinExpr); ++ S = new (SCEVAllocator) ++ SCEVUMinExpr(ID.Intern(SCEVAllocator), O, Ops.size()); ++ } ++ + UniqueSCEVs.InsertNode(S, IP); + addToLoopUseLists(S); + return S; + } + +-const SCEV *ScalarEvolution::getUMaxExpr(const SCEV *LHS, +- const SCEV *RHS) { ++const SCEV *ScalarEvolution::getSMaxExpr(const SCEV *LHS, const SCEV *RHS) { + SmallVector Ops = {LHS, RHS}; +- return getUMaxExpr(Ops); ++ return getSMaxExpr(Ops); + } + +-const SCEV * +-ScalarEvolution::getUMaxExpr(SmallVectorImpl &Ops) { +- assert(!Ops.empty() && "Cannot get empty umax!"); +- if (Ops.size() == 1) return Ops[0]; +-#ifndef NDEBUG +- Type *ETy = getEffectiveSCEVType(Ops[0]->getType()); +- for (unsigned i = 1, e = Ops.size(); i != e; ++i) +- assert(getEffectiveSCEVType(Ops[i]->getType()) == ETy && +- "SCEVUMaxExpr operand types don't match!"); +-#endif +- +- // Sort by complexity, this groups all similar expression types together. +- GroupByComplexity(Ops, &LI, DT); +- +- // If there are any constants, fold them together. +- unsigned Idx = 0; +- if (const SCEVConstant *LHSC = dyn_cast(Ops[0])) { +- ++Idx; +- assert(Idx < Ops.size()); +- while (const SCEVConstant *RHSC = dyn_cast(Ops[Idx])) { +- // We found two constants, fold them together! +- ConstantInt *Fold = ConstantInt::get( +- getContext(), APIntOps::umax(LHSC->getAPInt(), RHSC->getAPInt())); +- Ops[0] = getConstant(Fold); +- Ops.erase(Ops.begin()+1); // Erase the folded element +- if (Ops.size() == 1) return Ops[0]; +- LHSC = cast(Ops[0]); +- } +- +- // If we are left with a constant minimum-int, strip it off. +- if (cast(Ops[0])->getValue()->isMinValue(false)) { +- Ops.erase(Ops.begin()); +- --Idx; +- } else if (cast(Ops[0])->getValue()->isMaxValue(false)) { +- // If we have an umax with a constant maximum-int, it will always be +- // maximum-int. +- return Ops[0]; +- } +- +- if (Ops.size() == 1) return Ops[0]; +- } +- +- // Find the first UMax +- while (Idx < Ops.size() && Ops[Idx]->getSCEVType() < scUMaxExpr) +- ++Idx; +- +- // Check to see if one of the operands is a UMax. If so, expand its operands +- // onto our operand list, and recurse to simplify. +- if (Idx < Ops.size()) { +- bool DeletedUMax = false; +- while (const SCEVUMaxExpr *UMax = dyn_cast(Ops[Idx])) { +- Ops.erase(Ops.begin()+Idx); +- Ops.append(UMax->op_begin(), UMax->op_end()); +- DeletedUMax = true; +- } +- +- if (DeletedUMax) +- return getUMaxExpr(Ops); +- } +- +- // Okay, check to see if the same value occurs in the operand list twice. If +- // so, delete one. Since we sorted the list, these values are required to +- // be adjacent. +- for (unsigned i = 0, e = Ops.size()-1; i != e; ++i) +- // X umax Y umax Y --> X umax Y +- // X umax Y --> X, if X is always greater than Y +- if (Ops[i] == Ops[i + 1] || isKnownViaNonRecursiveReasoning( +- ICmpInst::ICMP_UGE, Ops[i], Ops[i + 1])) { +- Ops.erase(Ops.begin() + i + 1, Ops.begin() + i + 2); +- --i; --e; +- } else if (isKnownViaNonRecursiveReasoning(ICmpInst::ICMP_ULE, Ops[i], +- Ops[i + 1])) { +- Ops.erase(Ops.begin() + i, Ops.begin() + i + 1); +- --i; --e; +- } +- +- if (Ops.size() == 1) return Ops[0]; ++const SCEV *ScalarEvolution::getSMaxExpr(SmallVectorImpl &Ops) { ++ return getUSMinMaxExpr(scSMaxExpr, Ops); ++} + +- assert(!Ops.empty() && "Reduced umax down to nothing!"); ++const SCEV *ScalarEvolution::getUMaxExpr(const SCEV *LHS, const SCEV *RHS) { ++ SmallVector Ops = {LHS, RHS}; ++ return getUMaxExpr(Ops); ++} + +- // Okay, it looks like we really DO need a umax expr. Check to see if we +- // already have one, otherwise create a new one. +- FoldingSetNodeID ID; +- ID.AddInteger(scUMaxExpr); +- for (unsigned i = 0, e = Ops.size(); i != e; ++i) +- ID.AddPointer(Ops[i]); +- void *IP = nullptr; +- if (const SCEV *S = UniqueSCEVs.FindNodeOrInsertPos(ID, IP)) return S; +- const SCEV **O = SCEVAllocator.Allocate(Ops.size()); +- std::uninitialized_copy(Ops.begin(), Ops.end(), O); +- SCEV *S = new (SCEVAllocator) SCEVUMaxExpr(ID.Intern(SCEVAllocator), +- O, Ops.size()); +- UniqueSCEVs.InsertNode(S, IP); +- addToLoopUseLists(S); +- return S; ++const SCEV *ScalarEvolution::getUMaxExpr(SmallVectorImpl &Ops) { ++ return getUSMinMaxExpr(scUMaxExpr, Ops); + } + + const SCEV *ScalarEvolution::getSMinExpr(const SCEV *LHS, +@@ -3700,11 +3672,7 @@ const SCEV *ScalarEvolution::getSMinExpr(const SCEV *LHS, + } + + const SCEV *ScalarEvolution::getSMinExpr(SmallVectorImpl &Ops) { +- // ~smax(~x, ~y, ~z) == smin(x, y, z). +- SmallVector NotOps; +- for (auto *S : Ops) +- NotOps.push_back(getNotSCEV(S)); +- return getNotSCEV(getSMaxExpr(NotOps)); ++ return getUSMinMaxExpr(scSMinExpr, Ops); + } + + const SCEV *ScalarEvolution::getUMinExpr(const SCEV *LHS, +@@ -3714,16 +3682,7 @@ const SCEV *ScalarEvolution::getUMinExpr(const SCEV *LHS, + } + + const SCEV *ScalarEvolution::getUMinExpr(SmallVectorImpl &Ops) { +- assert(!Ops.empty() && "At least one operand must be!"); +- // Trivial case. +- if (Ops.size() == 1) +- return Ops[0]; +- +- // ~umax(~x, ~y, ~z) == umin(x, y, z). +- SmallVector NotOps; +- for (auto *S : Ops) +- NotOps.push_back(getNotSCEV(S)); +- return getNotSCEV(getUMaxExpr(NotOps)); ++ return getUSMinMaxExpr(scUMinExpr, Ops); + } + + const SCEV *ScalarEvolution::getSizeOfExpr(Type *IntTy, Type *AllocTy) { +@@ -5191,6 +5150,8 @@ static bool IsAvailableOnEntry(const Loop *L, DominatorTree &DT, const SCEV *S, + switch (S->getSCEVType()) { + case scConstant: case scTruncate: case scZeroExtend: case scSignExtend: + case scAddExpr: case scMulExpr: case scUMaxExpr: case scSMaxExpr: ++ case scUMinExpr: ++ case scSMinExpr: + // These expressions are available if their operand(s) is/are. + return true; + +@@ -8070,7 +8031,9 @@ static Constant *BuildConstantFromSCEV(const SCEV *V) { + } + case scSMaxExpr: + case scUMaxExpr: +- break; // TODO: smax, umax. ++ case scSMinExpr: ++ case scUMinExpr: ++ break; // TODO: smax, umax, smin, umax. + } + return nullptr; + } +@@ -8200,6 +8163,10 @@ const SCEV *ScalarEvolution::computeSCEVAtScope(const SCEV *V, const Loop *L) { + return getSMaxExpr(NewOps); + if (isa(Comm)) + return getUMaxExpr(NewOps); ++ if (isa(Comm)) ++ return getSMinExpr(NewOps); ++ if (isa(Comm)) ++ return getUMinExpr(NewOps); + llvm_unreachable("Unknown commutative SCEV type!"); + } + } +@@ -9859,26 +9826,28 @@ static const SCEV *MatchNotExpr(const SCEV *Expr) { + return AddRHS->getOperand(1); + } + +-/// Is MaybeMaxExpr an SMax or UMax of Candidate and some other values? +-template +-static bool IsMaxConsistingOf(const SCEV *MaybeMaxExpr, +- const SCEV *Candidate) { +- const MaxExprType *MaxExpr = dyn_cast(MaybeMaxExpr); +- if (!MaxExpr) return false; ++/// Is MaybeMinMaxExpr an (U|S)(Min|Max) of Candidate and some other values? ++template ++static bool IsMinMaxConsistingOf(const SCEV *MaybeMinMaxExpr, ++ const SCEV *Candidate) { ++ const MinMaxExprType *MinMaxExpr = dyn_cast(MaybeMinMaxExpr); ++ if (!MinMaxExpr) ++ return false; + +- return find(MaxExpr->operands(), Candidate) != MaxExpr->op_end(); ++ return find(MinMaxExpr->operands(), Candidate) != MinMaxExpr->op_end(); + } + +-/// Is MaybeMinExpr an SMin or UMin of Candidate and some other values? +-template +-static bool IsMinConsistingOf(ScalarEvolution &SE, +- const SCEV *MaybeMinExpr, +- const SCEV *Candidate) { +- const SCEV *MaybeMaxExpr = MatchNotExpr(MaybeMinExpr); +- if (!MaybeMaxExpr) ++/// Is MaybeMinMaxExpr an ~(U|S)(Min|Max) of ~Candidate and some other values? ++template ++static bool IsMinMaxConsistingOfByNegation(ScalarEvolution &SE, ++ const SCEV *MaybeMinMaxExpr, ++ const SCEV *Candidate) { ++ const SCEV *MinMaxExpr = MatchNotExpr(MaybeMinMaxExpr); ++ if (!MinMaxExpr) + return false; + +- return IsMaxConsistingOf(MaybeMaxExpr, SE.getNotSCEV(Candidate)); ++ return IsMinMaxConsistingOf(MinMaxExpr, ++ SE.getNotSCEV(Candidate)); + } + + static bool IsKnownPredicateViaAddRecStart(ScalarEvolution &SE, +@@ -9927,20 +9896,24 @@ static bool IsKnownPredicateViaMinOrMax(ScalarEvolution &SE, + LLVM_FALLTHROUGH; + case ICmpInst::ICMP_SLE: + return +- // min(A, ...) <= A +- IsMinConsistingOf(SE, LHS, RHS) || +- // A <= max(A, ...) +- IsMaxConsistingOf(RHS, LHS); ++ // min(A, ...) <= A ++ IsMinMaxConsistingOfByNegation(SE, LHS, RHS) || ++ IsMinMaxConsistingOf(LHS, RHS) || ++ // A <= max(A, ...) ++ IsMinMaxConsistingOfByNegation(SE, RHS, LHS) || ++ IsMinMaxConsistingOf(RHS, LHS); + + case ICmpInst::ICMP_UGE: + std::swap(LHS, RHS); + LLVM_FALLTHROUGH; + case ICmpInst::ICMP_ULE: + return +- // min(A, ...) <= A +- IsMinConsistingOf(SE, LHS, RHS) || +- // A <= max(A, ...) +- IsMaxConsistingOf(RHS, LHS); ++ // min(A, ...) <= A ++ IsMinMaxConsistingOfByNegation(SE, LHS, RHS) || ++ IsMinMaxConsistingOf(LHS, RHS) || ++ // A <= max(A, ...) ++ IsMinMaxConsistingOfByNegation(SE, RHS, LHS) || ++ IsMinMaxConsistingOf(RHS, LHS); + } + + llvm_unreachable("covered switch fell through?!"); +@@ -11451,7 +11424,9 @@ ScalarEvolution::computeLoopDisposition(const SCEV *S, const Loop *L) { + case scAddExpr: + case scMulExpr: + case scUMaxExpr: +- case scSMaxExpr: { ++ case scSMaxExpr: ++ case scUMinExpr: ++ case scSMinExpr: { + bool HasVarying = false; + for (auto *Op : cast(S)->operands()) { + LoopDisposition D = getLoopDisposition(Op, L); +@@ -11538,7 +11513,9 @@ ScalarEvolution::computeBlockDisposition(const SCEV *S, const BasicBlock *BB) { + case scAddExpr: + case scMulExpr: + case scUMaxExpr: +- case scSMaxExpr: { ++ case scSMaxExpr: ++ case scUMinExpr: ++ case scSMinExpr: { + const SCEVNAryExpr *NAry = cast(S); + bool Proper = true; + for (const SCEV *NAryOp : NAry->operands()) { +diff --git a/lib/Analysis/ScalarEvolutionExpander.cpp b/lib/Analysis/ScalarEvolutionExpander.cpp +index 8f89389c4b5..07b8156f61b 100644 +--- a/lib/Analysis/ScalarEvolutionExpander.cpp ++++ b/lib/Analysis/ScalarEvolutionExpander.cpp +@@ -1634,14 +1634,15 @@ Value *SCEVExpander::visitSMaxExpr(const SCEVSMaxExpr *S) { + for (int i = S->getNumOperands()-2; i >= 0; --i) { + // In the case of mixed integer and pointer types, do the + // rest of the comparisons as integer. +- if (S->getOperand(i)->getType() != Ty) { ++ Type *OpTy = S->getOperand(i)->getType(); ++ if (OpTy->isIntegerTy() != Ty->isIntegerTy()) { + Ty = SE.getEffectiveSCEVType(Ty); + LHS = InsertNoopCastOfTo(LHS, Ty); + } + Value *RHS = expandCodeFor(S->getOperand(i), Ty); + Value *ICmp = Builder.CreateICmpSGT(LHS, RHS); + rememberInstruction(ICmp); +- Value *Sel = Builder.CreateSelect(ICmp, LHS, RHS, "smax"); ++ Value *Sel = Builder.CreateSelect(ICmp, LHS, RHS, "smin"); + rememberInstruction(Sel); + LHS = Sel; + } +@@ -1658,13 +1659,64 @@ Value *SCEVExpander::visitUMaxExpr(const SCEVUMaxExpr *S) { + for (int i = S->getNumOperands()-2; i >= 0; --i) { + // In the case of mixed integer and pointer types, do the + // rest of the comparisons as integer. +- if (S->getOperand(i)->getType() != Ty) { ++ Type *OpTy = S->getOperand(i)->getType(); ++ if (OpTy->isIntegerTy() != Ty->isIntegerTy()) { + Ty = SE.getEffectiveSCEVType(Ty); + LHS = InsertNoopCastOfTo(LHS, Ty); + } + Value *RHS = expandCodeFor(S->getOperand(i), Ty); + Value *ICmp = Builder.CreateICmpUGT(LHS, RHS); + rememberInstruction(ICmp); ++ Value *Sel = Builder.CreateSelect(ICmp, LHS, RHS, "umin"); ++ rememberInstruction(Sel); ++ LHS = Sel; ++ } ++ // In the case of mixed integer and pointer types, cast the ++ // final result back to the pointer type. ++ if (LHS->getType() != S->getType()) ++ LHS = InsertNoopCastOfTo(LHS, S->getType()); ++ return LHS; ++} ++ ++Value *SCEVExpander::visitSMinExpr(const SCEVSMinExpr *S) { ++ Value *LHS = expand(S->getOperand(S->getNumOperands() - 1)); ++ Type *Ty = LHS->getType(); ++ for (int i = S->getNumOperands() - 2; i >= 0; --i) { ++ // In the case of mixed integer and pointer types, do the ++ // rest of the comparisons as integer. ++ Type *OpTy = S->getOperand(i)->getType(); ++ if (OpTy->isIntegerTy() != Ty->isIntegerTy()) { ++ Ty = SE.getEffectiveSCEVType(Ty); ++ LHS = InsertNoopCastOfTo(LHS, Ty); ++ } ++ Value *RHS = expandCodeFor(S->getOperand(i), Ty); ++ Value *ICmp = Builder.CreateICmpSLT(LHS, RHS); ++ rememberInstruction(ICmp); ++ Value *Sel = Builder.CreateSelect(ICmp, LHS, RHS, "smax"); ++ rememberInstruction(Sel); ++ LHS = Sel; ++ } ++ // In the case of mixed integer and pointer types, cast the ++ // final result back to the pointer type. ++ if (LHS->getType() != S->getType()) ++ LHS = InsertNoopCastOfTo(LHS, S->getType()); ++ return LHS; ++} ++ ++Value *SCEVExpander::visitUMinExpr(const SCEVUMinExpr *S) { ++ Value *LHS = expand(S->getOperand(S->getNumOperands() - 1)); ++ Type *Ty = LHS->getType(); ++ for (int i = S->getNumOperands() - 2; i >= 0; --i) { ++ // In the case of mixed integer and pointer types, do the ++ // rest of the comparisons as integer. ++ Type *OpTy = S->getOperand(i)->getType(); ++ if (OpTy->isIntegerTy() != Ty->isIntegerTy()) { ++ Ty = SE.getEffectiveSCEVType(Ty); ++ LHS = InsertNoopCastOfTo(LHS, Ty); ++ } ++ Value *RHS = expandCodeFor(S->getOperand(i), Ty); ++ Value *ICmp = Builder.CreateICmpULT(LHS, RHS); ++ rememberInstruction(ICmp); + Value *Sel = Builder.CreateSelect(ICmp, LHS, RHS, "umax"); + rememberInstruction(Sel); + LHS = Sel; +diff --git a/test/Analysis/LoopAccessAnalysis/memcheck-ni.ll b/test/Analysis/LoopAccessAnalysis/memcheck-ni.ll +new file mode 100644 +index 00000000000..a08632f38d1 +--- /dev/null ++++ b/test/Analysis/LoopAccessAnalysis/memcheck-ni.ll +@@ -0,0 +1,50 @@ ++; RUN: opt -loop-versioning -S < %s | FileCheck %s ++ ++; NB: addrspaces 10-13 are non-integral ++target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128-ni:10:11:12:13" ++ ++%jl_value_t = type opaque ++%jl_array_t = type { i8 addrspace(13)*, i64, i16, i16, i32 } ++ ++define void @"japi1_permutedims!_33509"(%jl_value_t addrspace(10)**) { ++; CHECK: [[CMP:%[^ ]*]] = icmp ult double addrspace(13)* [[A:%[^ ]*]], [[B:%[^ ]*]] ++; CHECK: [[SELECT:%[^ ]*]] = select i1 %18, double addrspace(13)* [[A]], double addrspace(13)* [[B]] ++top: ++ %1 = alloca [3 x i64], align 8 ++ %2 = load %jl_value_t addrspace(10)*, %jl_value_t addrspace(10)** %0, align 8 ++ %3 = getelementptr inbounds %jl_value_t addrspace(10)*, %jl_value_t addrspace(10)** %0, i64 1 ++ %4 = load %jl_value_t addrspace(10)*, %jl_value_t addrspace(10)** %3, align 8 ++ %5 = getelementptr inbounds [3 x i64], [3 x i64]* %1, i64 0, i64 0 ++ store i64 1, i64* %5, align 8 ++ %6 = getelementptr inbounds [3 x i64], [3 x i64]* %1, i64 0, i64 1 ++ %7 = load i64, i64* inttoptr (i64 24 to i64*), align 8 ++ %8 = addrspacecast %jl_value_t addrspace(10)* %4 to %jl_value_t addrspace(11)* ++ %9 = bitcast %jl_value_t addrspace(11)* %8 to double addrspace(13)* addrspace(11)* ++ %10 = load double addrspace(13)*, double addrspace(13)* addrspace(11)* %9, align 8 ++ %11 = addrspacecast %jl_value_t addrspace(10)* %2 to %jl_value_t addrspace(11)* ++ %12 = bitcast %jl_value_t addrspace(11)* %11 to double addrspace(13)* addrspace(11)* ++ %13 = load double addrspace(13)*, double addrspace(13)* addrspace(11)* %12, align 8 ++ %14 = load i64, i64* %6, align 8 ++ br label %L74 ++ ++L74: ++ %value_phi20 = phi i64 [ 1, %top ], [ %22, %L74 ] ++ %value_phi21 = phi i64 [ 1, %top ], [ %23, %L74 ] ++ %value_phi22 = phi i64 [ 1, %top ], [ %25, %L74 ] ++ %15 = add i64 %value_phi21, -1 ++ %16 = getelementptr inbounds double, double addrspace(13)* %10, i64 %15 ++ %17 = bitcast double addrspace(13)* %16 to i64 addrspace(13)* ++ %18 = load i64, i64 addrspace(13)* %17, align 8 ++ %19 = add i64 %value_phi20, -1 ++ %20 = getelementptr inbounds double, double addrspace(13)* %13, i64 %19 ++ %21 = bitcast double addrspace(13)* %20 to i64 addrspace(13)* ++ store i64 %18, i64 addrspace(13)* %21, align 8 ++ %22 = add i64 %value_phi20, 1 ++ %23 = add i64 %14, %value_phi21 ++ %24 = icmp eq i64 %value_phi22, %7 ++ %25 = add i64 %value_phi22, 1 ++ br i1 %24, label %L94, label %L74 ++ ++L94: ++ ret void ++} +diff --git a/test/Analysis/LoopAccessAnalysis/reverse-memcheck-bounds.ll b/test/Analysis/LoopAccessAnalysis/reverse-memcheck-bounds.ll +index 405a47554e4..4285ef0f117 100644 +--- a/test/Analysis/LoopAccessAnalysis/reverse-memcheck-bounds.ll ++++ b/test/Analysis/LoopAccessAnalysis/reverse-memcheck-bounds.ll +@@ -58,7 +58,7 @@ for.end: ; preds = %for.body + + ; Here it is not obvious what the limits are, since 'step' could be negative. + +-; CHECK: Low: (-1 + (-1 * ((-60001 + (-1 * %a)) umax (-60001 + (40000 * %step) + (-1 * %a))))) ++; CHECK: Low: ((60000 + %a) umin (60000 + (-40000 * %step) + %a)) + ; CHECK: High: (4 + ((60000 + %a) umax (60000 + (-40000 * %step) + %a))) + + define void @g(i64 %step) { +diff --git a/test/Analysis/ScalarEvolution/2008-07-29-SMinExpr.ll b/test/Analysis/ScalarEvolution/2008-07-29-SMinExpr.ll +index 3542ad2a41e..53e024a68fb 100644 +--- a/test/Analysis/ScalarEvolution/2008-07-29-SMinExpr.ll ++++ b/test/Analysis/ScalarEvolution/2008-07-29-SMinExpr.ll +@@ -22,5 +22,5 @@ afterfor: ; preds = %forinc, %entry + ret i32 %j.0.lcssa + } + +-; CHECK: backedge-taken count is (-2147483632 + ((-1 + (-1 * %{{[xy]}})) smax (-1 + (-1 * %{{[xy]}})))) ++; CHECK: backedge-taken count is (-2147483633 + (-1 * (%x smin %y))) + +diff --git a/test/Analysis/ScalarEvolution/min-max-exprs.ll b/test/Analysis/ScalarEvolution/min-max-exprs.ll +index e8c1e33e095..51f72c643cc 100644 +--- a/test/Analysis/ScalarEvolution/min-max-exprs.ll ++++ b/test/Analysis/ScalarEvolution/min-max-exprs.ll +@@ -33,7 +33,7 @@ bb2: ; preds = %bb1 + %tmp9 = select i1 %tmp4, i64 %tmp5, i64 %tmp6 + ; min(N, i+3) + ; CHECK: select i1 %tmp4, i64 %tmp5, i64 %tmp6 +-; CHECK-NEXT: --> (-1 + (-1 * ((-1 + (-1 * (sext i32 {3,+,1}<%bb1> to i64))) smax (-1 + (-1 * (sext i32 %N to i64)))))) ++; CHECK-NEXT: --> ((sext i32 {3,+,1}<%bb1> to i64) smin (sext i32 %N to i64)) + %tmp11 = getelementptr inbounds i32, i32* %A, i64 %tmp9 + %tmp12 = load i32, i32* %tmp11, align 4 + %tmp13 = shl nsw i32 %tmp12, 1 +diff --git a/test/Analysis/ScalarEvolution/pr28705.ll b/test/Analysis/ScalarEvolution/pr28705.ll +index 8fbc08e3ca6..7d797a15bd5 100644 +--- a/test/Analysis/ScalarEvolution/pr28705.ll ++++ b/test/Analysis/ScalarEvolution/pr28705.ll +@@ -5,7 +5,7 @@ + ; with "%.sroa.speculated + 1". + ; + ; CHECK-LABEL: @foo( +-; CHECK: %[[EXIT:.+]] = sub i32 %.sroa.speculated, -1 ++; CHECK: %[[EXIT:.+]] = add i32 %.sroa.speculated, 1 + ; CHECK: %DB.sroa.9.0.lcssa = phi i32 [ 1, %entry ], [ %[[EXIT]], %loopexit ] + ; + define void @foo(i32 %sub.ptr.div.i, i8* %ref.i1174) local_unnamed_addr { +diff --git a/test/Analysis/ScalarEvolution/predicated-trip-count.ll b/test/Analysis/ScalarEvolution/predicated-trip-count.ll +index a0afcf457d2..b07662ed95f 100644 +--- a/test/Analysis/ScalarEvolution/predicated-trip-count.ll ++++ b/test/Analysis/ScalarEvolution/predicated-trip-count.ll +@@ -80,7 +80,7 @@ return: ; preds = %bb5 + ; CHECK-NEXT: --> (sext i16 {%Start,+,-1}<%bb3> to i32) + ; CHECK: Loop %bb3: Unpredictable backedge-taken count. + ; CHECK-NEXT: Loop %bb3: Unpredictable max backedge-taken count. +-; CHECK-NEXT: Loop %bb3: Predicated backedge-taken count is (2 + (sext i16 %Start to i32) + ((-2 + (-1 * (sext i16 %Start to i32))) smax (-1 + (-1 * %M)))) ++; CHECK-NEXT: Loop %bb3: Predicated backedge-taken count is (1 + (sext i16 %Start to i32) + (-1 * ((1 + (sext i16 %Start to i32)) smin %M))) + ; CHECK-NEXT: Predicates: + ; CHECK-NEXT: {%Start,+,-1}<%bb3> Added Flags: + +diff --git a/test/Analysis/ScalarEvolution/trip-count14.ll b/test/Analysis/ScalarEvolution/trip-count14.ll +index 5e6cfe85101..15080613881 100644 +--- a/test/Analysis/ScalarEvolution/trip-count14.ll ++++ b/test/Analysis/ScalarEvolution/trip-count14.ll +@@ -81,7 +81,7 @@ if.end: + br i1 %cmp1, label %do.body, label %do.end ; taken either 0 or 2 times + + ; CHECK-LABEL: Determining loop execution counts for: @s32_max2_unpredictable_exit +-; CHECK-NEXT: Loop %do.body: backedge-taken count is (-1 + (-1 * ((-1 + (-1 * ((2 + %n) smax %n)) + %n) umax (-1 + (-1 * %x) + %n)))) ++; CHECK-NEXT: Loop %do.body: backedge-taken count is (((-1 * %n) + ((2 + %n) smax %n)) umin ((-1 * %n) + %x)) + ; CHECK-NEXT: Loop %do.body: max backedge-taken count is 2{{$}} + + do.end: +@@ -169,7 +169,7 @@ if.end: + br i1 %cmp1, label %do.body, label %do.end ; taken either 0 or 2 times + + ; CHECK-LABEL: Determining loop execution counts for: @u32_max2_unpredictable_exit +-; CHECK-NEXT: Loop %do.body: backedge-taken count is (-1 + (-1 * ((-1 + (-1 * ((2 + %n) umax %n)) + %n) umax (-1 + (-1 * %x) + %n)))) ++; CHECK-NEXT: Loop %do.body: backedge-taken count is (((-1 * %n) + ((2 + %n) umax %n)) umin ((-1 * %n) + %x)) + ; CHECK-NEXT: Loop %do.body: max backedge-taken count is 2{{$}} + + do.end: +diff --git a/test/Analysis/ScalarEvolution/trip-count3.ll b/test/Analysis/ScalarEvolution/trip-count3.ll +index df6637a4ced..e10012c0c32 100644 +--- a/test/Analysis/ScalarEvolution/trip-count3.ll ++++ b/test/Analysis/ScalarEvolution/trip-count3.ll +@@ -4,7 +4,7 @@ + ; dividing by the stride will have a remainder. This could theoretically + ; be teaching it how to use a more elaborate trip count computation. + +-; CHECK: Loop %bb3.i: backedge-taken count is ((64 + (-64 smax (-1 + (-1 * %0))) + %0) /u 64) ++; CHECK: Loop %bb3.i: backedge-taken count is ((63 + (-1 * (63 smin %0)) + %0) /u 64) + ; CHECK: Loop %bb3.i: max backedge-taken count is 33554431 + + %struct.FILE = type { i32, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, %struct._IO_marker*, %struct.FILE*, i32, i32, i64, i16, i8, [1 x i8], i8*, i64, i8*, i8*, i8*, i8*, i64, i32, [20 x i8] } +diff --git a/test/Transforms/IRCE/conjunctive-checks.ll b/test/Transforms/IRCE/conjunctive-checks.ll +index 4e3cf354125..8711c1b00e8 100644 +--- a/test/Transforms/IRCE/conjunctive-checks.ll ++++ b/test/Transforms/IRCE/conjunctive-checks.ll +@@ -5,17 +5,15 @@ define void @f_0(i32 *%arr, i32 *%a_len_ptr, i32 %n, i1* %cond_buf) { + ; CHECK-LABEL: @f_0( + + ; CHECK: loop.preheader: +-; CHECK: [[not_safe_range_end:[^ ]+]] = sub i32 3, %len +-; CHECK: [[not_n:[^ ]+]] = sub i32 -1, %n +-; CHECK: [[not_exit_main_loop_at_hiclamp_cmp:[^ ]+]] = icmp sgt i32 [[not_safe_range_end]], [[not_n]] +-; CHECK: [[not_exit_main_loop_at_hiclamp:[^ ]+]] = select i1 [[not_exit_main_loop_at_hiclamp_cmp]], i32 [[not_safe_range_end]], i32 [[not_n]] +-; CHECK: [[exit_main_loop_at_hiclamp:[^ ]+]] = sub i32 -1, [[not_exit_main_loop_at_hiclamp]] ++; CHECK: [[len_sub:[^ ]+]] = add i32 %len, -4 ++; CHECK: [[exit_main_loop_at_hiclamp_cmp:[^ ]+]] = icmp slt i32 %n, [[len_sub]] ++; CHECK: [[exit_main_loop_at_hiclamp:[^ ]+]] = select i1 [[exit_main_loop_at_hiclamp_cmp]], i32 %n, i32 [[len_sub]] + ; CHECK: [[exit_main_loop_at_loclamp_cmp:[^ ]+]] = icmp sgt i32 [[exit_main_loop_at_hiclamp]], 0 + ; CHECK: [[exit_main_loop_at_loclamp:[^ ]+]] = select i1 [[exit_main_loop_at_loclamp_cmp]], i32 [[exit_main_loop_at_hiclamp]], i32 0 + ; CHECK: [[enter_main_loop:[^ ]+]] = icmp slt i32 0, [[exit_main_loop_at_loclamp]] +-; CHECK: br i1 [[enter_main_loop]], label %loop.preheader2, label %main.pseudo.exit ++; CHECK: br i1 [[enter_main_loop]], label %[[loop_preheader2:[^ ,]+]], label %main.pseudo.exit + +-; CHECK: loop.preheader2: ++; CHECK: [[loop_preheader2]]: + ; CHECK: br label %loop + + entry: +@@ -35,9 +33,9 @@ define void @f_0(i32 *%arr, i32 *%a_len_ptr, i32 %n, i1* %cond_buf) { + ; CHECK: loop: + ; CHECK: %cond = load volatile i1, i1* %cond_buf + ; CHECK: %abc = and i1 %cond, true +-; CHECK: br i1 %abc, label %in.bounds, label %out.of.bounds.loopexit3, !prof !1 ++; CHECK: br i1 %abc, label %in.bounds, label %[[loop_exit:[^ ,]+]], !prof !1 + +-; CHECK: out.of.bounds.loopexit: ++; CHECK: [[loop_exit]]: + ; CHECK: br label %out.of.bounds + + in.bounds: +@@ -58,14 +56,10 @@ define void @f_1( + ; CHECK-LABEL: @f_1( + + ; CHECK: loop.preheader: +-; CHECK: [[not_len_b:[^ ]+]] = sub i32 -1, %len.b +-; CHECK: [[not_len_a:[^ ]+]] = sub i32 -1, %len.a +-; CHECK: [[smax_not_len_cond:[^ ]+]] = icmp sgt i32 [[not_len_b]], [[not_len_a]] +-; CHECK: [[smax_not_len:[^ ]+]] = select i1 [[smax_not_len_cond]], i32 [[not_len_b]], i32 [[not_len_a]] +-; CHECK: [[not_n:[^ ]+]] = sub i32 -1, %n +-; CHECK: [[not_upper_limit_cond_loclamp:[^ ]+]] = icmp sgt i32 [[smax_not_len]], [[not_n]] +-; CHECK: [[not_upper_limit_loclamp:[^ ]+]] = select i1 [[not_upper_limit_cond_loclamp]], i32 [[smax_not_len]], i32 [[not_n]] +-; CHECK: [[upper_limit_loclamp:[^ ]+]] = sub i32 -1, [[not_upper_limit_loclamp]] ++; CHECK: [[smax_len_cond:[^ ]+]] = icmp slt i32 %len.b, %len.a ++; CHECK: [[smax_len:[^ ]+]] = select i1 [[smax_len_cond]], i32 %len.b, i32 %len.a ++; CHECK: [[upper_limit_cond_loclamp:[^ ]+]] = icmp slt i32 [[smax_len]], %n ++; CHECK: [[upper_limit_loclamp:[^ ]+]] = select i1 [[upper_limit_cond_loclamp]], i32 [[smax_len]], i32 %n + ; CHECK: [[upper_limit_cmp:[^ ]+]] = icmp sgt i32 [[upper_limit_loclamp]], 0 + ; CHECK: [[upper_limit:[^ ]+]] = select i1 [[upper_limit_cmp]], i32 [[upper_limit_loclamp]], i32 0 + +@@ -85,9 +79,9 @@ define void @f_1( + + ; CHECK: loop: + ; CHECK: %abc = and i1 true, true +-; CHECK: br i1 %abc, label %in.bounds, label %out.of.bounds.loopexit4, !prof !1 ++; CHECK: br i1 %abc, label %in.bounds, label %[[oob_loopexit:[^ ,]+]], !prof !1 + +-; CHECK: out.of.bounds.loopexit: ++; CHECK: [[oob_loopexit]]: + ; CHECK-NEXT: br label %out.of.bounds + + +diff --git a/test/Transforms/IRCE/decrementing-loop.ll b/test/Transforms/IRCE/decrementing-loop.ll +index 4c82cd3e341..2994a432a71 100644 +--- a/test/Transforms/IRCE/decrementing-loop.ll ++++ b/test/Transforms/IRCE/decrementing-loop.ll +@@ -29,11 +29,8 @@ define void @decrementing_loop(i32 *%arr, i32 *%a_len_ptr, i32 %n) { + ret void + + ; CHECK: loop.preheader: +-; CHECK: [[not_len:[^ ]+]] = sub i32 -1, %len +-; CHECK: [[not_n:[^ ]+]] = sub i32 -1, %n +-; CHECK: [[not_len_hiclamp_cmp:[^ ]+]] = icmp sgt i32 [[not_len]], [[not_n]] +-; CHECK: [[not_len_hiclamp:[^ ]+]] = select i1 [[not_len_hiclamp_cmp]], i32 [[not_len]], i32 [[not_n]] +-; CHECK: [[len_hiclamp:[^ ]+]] = sub i32 -1, [[not_len_hiclamp]] ++; CHECK: [[len_hiclamp_cmp:[^ ]+]] = icmp slt i32 %len, %n ++; CHECK: [[len_hiclamp:[^ ]+]] = select i1 [[len_hiclamp_cmp]], i32 %len, i32 %n + ; CHECK: [[not_exit_preloop_at_cmp:[^ ]+]] = icmp sgt i32 [[len_hiclamp]], 0 + ; CHECK: [[not_exit_preloop_at:[^ ]+]] = select i1 [[not_exit_preloop_at_cmp]], i32 [[len_hiclamp]], i32 0 + ; CHECK: %exit.preloop.at = add i32 [[not_exit_preloop_at]], -1 +diff --git a/test/Transforms/IRCE/multiple-access-no-preloop.ll b/test/Transforms/IRCE/multiple-access-no-preloop.ll +index 000d1ab36f2..3bde9bd8668 100644 +--- a/test/Transforms/IRCE/multiple-access-no-preloop.ll ++++ b/test/Transforms/IRCE/multiple-access-no-preloop.ll +@@ -38,14 +38,10 @@ define void @multiple_access_no_preloop( + ; CHECK-LABEL: @multiple_access_no_preloop( + + ; CHECK: loop.preheader: +-; CHECK: [[not_len_b:[^ ]+]] = sub i32 -1, %len.b +-; CHECK: [[not_len_a:[^ ]+]] = sub i32 -1, %len.a +-; CHECK: [[smax_not_len_cond:[^ ]+]] = icmp sgt i32 [[not_len_b]], [[not_len_a]] +-; CHECK: [[smax_not_len:[^ ]+]] = select i1 [[smax_not_len_cond]], i32 [[not_len_b]], i32 [[not_len_a]] +-; CHECK: [[not_n:[^ ]+]] = sub i32 -1, %n +-; CHECK: [[not_upper_limit_cond_loclamp:[^ ]+]] = icmp sgt i32 [[smax_not_len]], [[not_n]] +-; CHECK: [[not_upper_limit_loclamp:[^ ]+]] = select i1 [[not_upper_limit_cond_loclamp]], i32 [[smax_not_len]], i32 [[not_n]] +-; CHECK: [[upper_limit_loclamp:[^ ]+]] = sub i32 -1, [[not_upper_limit_loclamp]] ++; CHECK: [[smax_len_cond:[^ ]+]] = icmp slt i32 %len.b, %len.a ++; CHECK: [[smax_len:[^ ]+]] = select i1 [[smax_len_cond]], i32 %len.b, i32 %len.a ++; CHECK: [[upper_limit_cond_loclamp:[^ ]+]] = icmp slt i32 [[smax_len]], %n ++; CHECK: [[upper_limit_loclamp:[^ ]+]] = select i1 [[upper_limit_cond_loclamp]], i32 [[smax_len]], i32 %n + ; CHECK: [[upper_limit_cmp:[^ ]+]] = icmp sgt i32 [[upper_limit_loclamp]], 0 + ; CHECK: [[upper_limit:[^ ]+]] = select i1 [[upper_limit_cmp]], i32 [[upper_limit_loclamp]], i32 0 + +diff --git a/test/Transforms/IRCE/ranges_of_different_types.ll b/test/Transforms/IRCE/ranges_of_different_types.ll +index 5c8161369f2..46bd94ce687 100644 +--- a/test/Transforms/IRCE/ranges_of_different_types.ll ++++ b/test/Transforms/IRCE/ranges_of_different_types.ll +@@ -23,12 +23,11 @@ define void @test_01(i32* %arr, i32* %a_len_ptr) #0 { + ; CHECK-NOT: preloop + ; CHECK: entry: + ; CHECK-NEXT: %len = load i32, i32* %a_len_ptr, !range !0 +-; CHECK-NEXT: [[SUB1:%[^ ]+]] = sub i32 12, %len +-; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp sgt i32 [[SUB1]], -102 +-; CHECK-NEXT: [[SMAX:%[^ ]+]] = select i1 [[CMP1]], i32 [[SUB1]], i32 -102 +-; CHECK-NEXT: [[SUB2:%[^ ]+]] = sub i32 -1, [[SMAX]] +-; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp sgt i32 [[SUB2]], 0 +-; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP2]], i32 [[SUB2]], i32 0 ++; CHECK-NEXT: [[SUB1:%[^ ]+]] = add i32 %len, -13 ++; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp slt i32 [[SUB1]], 101 ++; CHECK-NEXT: [[SMAX:%[^ ]+]] = select i1 [[CMP1]], i32 [[SUB1]], i32 101 ++; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp sgt i32 [[SMAX]], 0 ++; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP2]], i32 [[SMAX]], i32 0 + ; CHECK-NEXT: [[GOTO_LOOP:%[^ ]+]] = icmp slt i32 0, %exit.mainloop.at + ; CHECK-NEXT: br i1 [[GOTO_LOOP]], label %loop.preheader, label %main.pseudo.exit + ; CHECK: loop +@@ -83,13 +82,11 @@ define void @test_02(i32* %arr, i32* %a_len_ptr) #0 { + ; CHECK-NEXT: [[LEN_MINUS_SMAX:%[^ ]+]] = add i32 %len, -2147483647 + ; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp sgt i32 [[LEN_MINUS_SMAX]], -13 + ; CHECK-NEXT: [[SMAX1:%[^ ]+]] = select i1 [[CMP1]], i32 [[LEN_MINUS_SMAX]], i32 -13 +-; CHECK-NEXT: [[ADD1:%[^ ]+]] = add i32 [[SMAX1]], -1 +-; CHECK-NEXT: [[SUB1:%[^ ]+]] = sub i32 [[ADD1]], %len +-; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp sgt i32 [[SUB1]], -102 +-; CHECK-NEXT: [[SMAX2:%[^ ]+]] = select i1 [[CMP2]], i32 [[SUB1]], i32 -102 +-; CHECK-NEXT: [[SUB2:%[^ ]+]] = sub i32 -1, [[SMAX2]] +-; CHECK-NEXT: [[CMP3:%[^ ]+]] = icmp sgt i32 [[SUB2]], 0 +-; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP3]], i32 [[SUB2]], i32 0 ++; CHECK-NEXT: [[SUB1:%[^ ]+]] = sub i32 %len, [[SMAX1]] ++; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp slt i32 [[SUB1]], 101 ++; CHECK-NEXT: [[SMAX2:%[^ ]+]] = select i1 [[CMP2]], i32 [[SUB1]], i32 101 ++; CHECK-NEXT: [[CMP3:%[^ ]+]] = icmp sgt i32 [[SMAX2]], 0 ++; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP3]], i32 [[SMAX2]], i32 0 + ; CHECK-NEXT: br i1 true, label %loop.preloop.preheader + ; CHECK: loop.preloop: + ; CHECK-NEXT: %idx.preloop = phi i32 [ %idx.next.preloop, %in.bounds.preloop ], [ 0, %loop.preloop.preheader ] +@@ -151,14 +148,11 @@ define void @test_03(i32* %arr, i32* %a_len_ptr) #0 { + ; CHECK-NOT: preloop + ; CHECK: entry: + ; CHECK-NEXT: %len = load i32, i32* %a_len_ptr, !range !0 +-; CHECK-NEXT: [[SUB1:%[^ ]+]] = sub i32 -2, %len +-; CHECK-NEXT: [[SUB2:%[^ ]+]] = sub i32 -1, %len +-; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp sgt i32 [[SUB2]], -14 +-; CHECK-NEXT: [[SMAX1:%[^ ]+]] = select i1 [[CMP1]], i32 [[SUB2]], i32 -14 +-; CHECK-NEXT: [[SUB3:%[^ ]+]] = sub i32 [[SUB1]], [[SMAX1]] +-; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp ugt i32 [[SUB3]], -102 +-; CHECK-NEXT: [[UMAX1:%[^ ]+]] = select i1 [[CMP2]], i32 [[SUB3]], i32 -102 +-; CHECK-NEXT: %exit.mainloop.at = sub i32 -1, [[UMAX1]] ++; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp slt i32 %len, 13 ++; CHECK-NEXT: [[SMAX1:%[^ ]+]] = select i1 [[CMP1]], i32 %len, i32 13 ++; CHECK-NEXT: [[SUB3:%[^ ]+]] = sub i32 %len, [[SMAX1]] ++; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp ult i32 [[SUB3]], 101 ++; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP2]], i32 [[SUB3]], i32 101 + ; CHECK-NEXT: [[CMP3:%[^ ]+]] = icmp ult i32 0, %exit.mainloop.at + ; CHECK-NEXT: br i1 [[CMP3]], label %loop.preheader, label %main.pseudo.exit + ; CHECK: postloop: +@@ -208,10 +202,9 @@ define void @test_04(i32* %arr, i32* %a_len_ptr) #0 { + ; CHECK-LABEL: test_04( + ; CHECK: entry: + ; CHECK-NEXT: %len = load i32, i32* %a_len_ptr, !range !0 +-; CHECK-NEXT: [[SUB1:%[^ ]+]] = sub i32 -14, %len +-; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp ugt i32 [[SUB1]], -102 +-; CHECK-NEXT: [[UMAX1:%[^ ]+]] = select i1 [[CMP1]], i32 [[SUB1]], i32 -102 +-; CHECK-NEXT: %exit.mainloop.at = sub i32 -1, [[UMAX1]] ++; CHECK-NEXT: [[SUB1:%[^ ]+]] = add i32 %len, 13 ++; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp ult i32 [[SUB1]], 101 ++; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP1]], i32 [[SUB1]], i32 101 + ; CHECK-NEXT: br i1 true, label %loop.preloop.preheader + ; CHECK: in.bounds.preloop: + ; CHECK-NEXT: %addr.preloop = getelementptr i32, i32* %arr, i32 %idx.preloop +@@ -252,12 +245,11 @@ define void @test_05(i32* %arr, i32* %a_len_ptr) #0 { + ; CHECK-NOT: preloop + ; CHECK: entry: + ; CHECK-NEXT: %len = load i32, i32* %a_len_ptr, !range !0 +-; CHECK-NEXT: [[SUB1:%[^ ]+]] = sub i32 12, %len +-; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp sgt i32 [[SUB1]], -102 +-; CHECK-NEXT: [[SMAX:%[^ ]+]] = select i1 [[CMP1]], i32 [[SUB1]], i32 -102 +-; CHECK-NEXT: [[SUB2:%[^ ]+]] = sub i32 -1, [[SMAX]] +-; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp sgt i32 [[SUB2]], 0 +-; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP2]], i32 [[SUB2]], i32 0 ++; CHECK-NEXT: [[SUB1:%[^ ]+]] = add i32 %len, -13 ++; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp slt i32 [[SUB1]], 101 ++; CHECK-NEXT: [[SMAX:%[^ ]+]] = select i1 [[CMP1]], i32 [[SUB1]], i32 101 ++; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp sgt i32 [[SMAX]], 0 ++; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP2]], i32 [[SMAX]], i32 0 + ; CHECK-NEXT: [[GOTO_LOOP:%[^ ]+]] = icmp slt i32 0, %exit.mainloop.at + ; CHECK-NEXT: br i1 [[GOTO_LOOP]], label %loop.preheader, label %main.pseudo.exit + ; CHECK: loop +@@ -297,13 +289,11 @@ define void @test_06(i32* %arr, i32* %a_len_ptr) #0 { + ; CHECK-NEXT: [[LEN_MINUS_SMAX:%[^ ]+]] = add i32 %len, -2147483647 + ; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp sgt i32 [[LEN_MINUS_SMAX]], -13 + ; CHECK-NEXT: [[SMAX1:%[^ ]+]] = select i1 [[CMP1]], i32 [[LEN_MINUS_SMAX]], i32 -13 +-; CHECK-NEXT: [[ADD1:%[^ ]+]] = add i32 [[SMAX1]], -1 +-; CHECK-NEXT: [[SUB1:%[^ ]+]] = sub i32 [[ADD1]], %len +-; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp sgt i32 [[SUB1]], -102 +-; CHECK-NEXT: [[SMAX2:%[^ ]+]] = select i1 [[CMP2]], i32 [[SUB1]], i32 -102 +-; CHECK-NEXT: [[SUB2:%[^ ]+]] = sub i32 -1, [[SMAX2]] +-; CHECK-NEXT: [[CMP3:%[^ ]+]] = icmp sgt i32 [[SUB2]], 0 +-; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP3]], i32 [[SUB2]], i32 0 ++; CHECK-NEXT: [[SUB1:%[^ ]+]] = sub i32 %len, [[SMAX1]] ++; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp slt i32 [[SUB1]], 101 ++; CHECK-NEXT: [[SMAX2:%[^ ]+]] = select i1 [[CMP2]], i32 [[SUB1]], i32 101 ++; CHECK-NEXT: [[CMP3:%[^ ]+]] = icmp sgt i32 [[SMAX2]], 0 ++; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP3]], i32 [[SMAX2]], i32 0 + ; CHECK-NEXT: br i1 true, label %loop.preloop.preheader + ; CHECK: in.bounds.preloop: + ; CHECK-NEXT: %addr.preloop = getelementptr i32, i32* %arr, i32 %idx.preloop +@@ -344,14 +334,11 @@ define void @test_07(i32* %arr, i32* %a_len_ptr) #0 { + ; CHECK-NOT: preloop + ; CHECK: entry: + ; CHECK-NEXT: %len = load i32, i32* %a_len_ptr, !range !0 +-; CHECK-NEXT: [[SUB1:%[^ ]+]] = sub i32 -2, %len +-; CHECK-NEXT: [[SUB2:%[^ ]+]] = sub i32 -1, %len +-; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp sgt i32 [[SUB2]], -14 +-; CHECK-NEXT: [[SMAX1:%[^ ]+]] = select i1 [[CMP1]], i32 [[SUB2]], i32 -14 +-; CHECK-NEXT: [[SUB3:%[^ ]+]] = sub i32 [[SUB1]], [[SMAX1]] +-; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp ugt i32 [[SUB3]], -102 +-; CHECK-NEXT: [[UMAX1:%[^ ]+]] = select i1 [[CMP2]], i32 [[SUB3]], i32 -102 +-; CHECK-NEXT: %exit.mainloop.at = sub i32 -1, [[UMAX1]] ++; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp slt i32 %len, 13 ++; CHECK-NEXT: [[SMAX1:%[^ ]+]] = select i1 [[CMP1]], i32 %len, i32 13 ++; CHECK-NEXT: [[SUB3:%[^ ]+]] = sub i32 %len, [[SMAX1]] ++; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp ult i32 [[SUB3]], 101 ++; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP2]], i32 [[SUB3]], i32 101 + ; CHECK-NEXT: [[CMP3:%[^ ]+]] = icmp ult i32 0, %exit.mainloop.at + ; CHECK-NEXT: br i1 [[CMP3]], label %loop.preheader, label %main.pseudo.exit + ; CHECK: loop +@@ -388,10 +375,9 @@ define void @test_08(i32* %arr, i32* %a_len_ptr) #0 { + ; CHECK-LABEL: test_08( + ; CHECK: entry: + ; CHECK-NEXT: %len = load i32, i32* %a_len_ptr, !range !0 +-; CHECK-NEXT: [[SUB1:%[^ ]+]] = sub i32 -14, %len +-; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp ugt i32 [[SUB1]], -102 +-; CHECK-NEXT: [[UMAX1:%[^ ]+]] = select i1 [[CMP1]], i32 [[SUB1]], i32 -102 +-; CHECK-NEXT: %exit.mainloop.at = sub i32 -1, [[UMAX1]] ++; CHECK-NEXT: [[SUB1:%[^ ]+]] = add i32 %len, 13 ++; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp ult i32 [[SUB1]], 101 ++; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP1]], i32 [[SUB1]], i32 101 + ; CHECK-NEXT: br i1 true, label %loop.preloop.preheader + ; CHECK: in.bounds.preloop: + ; CHECK-NEXT: %addr.preloop = getelementptr i32, i32* %arr, i32 %idx.preloop +diff --git a/test/Transforms/IRCE/rc-negative-bound.ll b/test/Transforms/IRCE/rc-negative-bound.ll +index bfc0cd14778..d226bffeaae 100644 +--- a/test/Transforms/IRCE/rc-negative-bound.ll ++++ b/test/Transforms/IRCE/rc-negative-bound.ll +@@ -114,49 +114,44 @@ define void @test_03(i32 *%arr, i32 %n, i32 %bound) { + ; CHECK: loop.preheader: + ; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[BOUND:%.*]], -2147483647 + ; CHECK-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[TMP0]], 0 +-; CHECK-NEXT: [[SMAX:%.*]] = select i1 [[TMP1]], i32 [[TMP0]], i32 0 +-; CHECK-NEXT: [[TMP2:%.*]] = sub i32 [[BOUND]], [[SMAX]] +-; CHECK-NEXT: [[TMP3:%.*]] = sub i32 -1, [[BOUND]] +-; CHECK-NEXT: [[TMP4:%.*]] = icmp sgt i32 [[TMP3]], -1 +-; CHECK-NEXT: [[SMAX1:%.*]] = select i1 [[TMP4]], i32 [[TMP3]], i32 -1 +-; CHECK-NEXT: [[TMP5:%.*]] = sub i32 -1, [[SMAX1]] +-; CHECK-NEXT: [[TMP6:%.*]] = icmp sgt i32 [[TMP5]], -1 +-; CHECK-NEXT: [[SMAX2:%.*]] = select i1 [[TMP6]], i32 [[TMP5]], i32 -1 +-; CHECK-NEXT: [[TMP7:%.*]] = add i32 [[SMAX2]], 1 +-; CHECK-NEXT: [[TMP8:%.*]] = mul i32 [[TMP2]], [[TMP7]] +-; CHECK-NEXT: [[TMP9:%.*]] = sub i32 -1, [[TMP8]] +-; CHECK-NEXT: [[TMP10:%.*]] = sub i32 -1, [[N]] +-; CHECK-NEXT: [[TMP11:%.*]] = icmp sgt i32 [[TMP9]], [[TMP10]] +-; CHECK-NEXT: [[SMAX3:%.*]] = select i1 [[TMP11]], i32 [[TMP9]], i32 [[TMP10]] +-; CHECK-NEXT: [[TMP12:%.*]] = sub i32 -1, [[SMAX3]] +-; CHECK-NEXT: [[TMP13:%.*]] = icmp sgt i32 [[TMP12]], 0 +-; CHECK-NEXT: [[EXIT_MAINLOOP_AT:%.*]] = select i1 [[TMP13]], i32 [[TMP12]], i32 0 +-; CHECK-NEXT: [[TMP14:%.*]] = icmp slt i32 0, [[EXIT_MAINLOOP_AT]] +-; CHECK-NEXT: br i1 [[TMP14]], label [[LOOP_PREHEADER5:%.*]], label [[MAIN_PSEUDO_EXIT:%.*]] +-; CHECK: loop.preheader5: ++; CHECK-NEXT: [[SMIN:%.*]] = select i1 [[TMP1]], i32 [[TMP0]], i32 0 ++; CHECK-NEXT: [[TMP2:%.*]] = sub i32 [[BOUND]], [[SMIN]] ++; CHECK-NEXT: [[TMP3:%.*]] = icmp slt i32 [[BOUND]], 0 ++; CHECK-NEXT: [[SMAX:%.*]] = select i1 [[TMP3]], i32 [[BOUND]], i32 0 ++; CHECK-NEXT: [[TMP4:%.*]] = icmp sgt i32 [[SMAX]], -1 ++; CHECK-NEXT: [[SMIN1:%.*]] = select i1 [[TMP4]], i32 [[SMAX]], i32 -1 ++; CHECK-NEXT: [[TMP5:%.*]] = add i32 [[SMIN1]], 1 ++; CHECK-NEXT: [[TMP6:%.*]] = mul i32 [[TMP2]], [[TMP5]] ++; CHECK-NEXT: [[TMP7:%.*]] = icmp slt i32 [[N]], [[TMP6]] ++; CHECK-NEXT: [[SMAX2:%.*]] = select i1 [[TMP7]], i32 [[N]], i32 [[TMP6]] ++; CHECK-NEXT: [[TMP8:%.*]] = icmp sgt i32 [[SMAX2]], 0 ++; CHECK-NEXT: [[EXIT_MAINLOOP_AT:%.*]] = select i1 [[TMP8]], i32 [[SMAX2]], i32 0 ++; CHECK-NEXT: [[TMP9:%.*]] = icmp slt i32 0, [[EXIT_MAINLOOP_AT]] ++; CHECK-NEXT: br i1 [[TMP9]], label [[LOOP_PREHEADER4:%.*]], label [[MAIN_PSEUDO_EXIT:%.*]] ++; CHECK: loop.preheader4: + ; CHECK-NEXT: br label [[LOOP:%.*]] + ; CHECK: loop: +-; CHECK-NEXT: [[IDX:%.*]] = phi i32 [ [[IDX_NEXT:%.*]], [[IN_BOUNDS:%.*]] ], [ 0, [[LOOP_PREHEADER5]] ] ++; CHECK-NEXT: [[IDX:%.*]] = phi i32 [ [[IDX_NEXT:%.*]], [[IN_BOUNDS:%.*]] ], [ 0, [[LOOP_PREHEADER4]] ] + ; CHECK-NEXT: [[IDX_NEXT]] = add i32 [[IDX]], 1 + ; CHECK-NEXT: [[ABC:%.*]] = icmp slt i32 [[IDX]], [[BOUND]] +-; CHECK-NEXT: br i1 true, label [[IN_BOUNDS]], label [[OUT_OF_BOUNDS_LOOPEXIT6:%.*]], !prof !0 ++; CHECK-NEXT: br i1 true, label [[IN_BOUNDS]], label [[OUT_OF_BOUNDS_LOOPEXIT5:%.*]], !prof !0 + ; CHECK: in.bounds: + ; CHECK-NEXT: [[ADDR:%.*]] = getelementptr i32, i32* [[ARR:%.*]], i32 [[IDX]] + ; CHECK-NEXT: store i32 0, i32* [[ADDR]] + ; CHECK-NEXT: [[NEXT:%.*]] = icmp slt i32 [[IDX_NEXT]], [[N]] +-; CHECK-NEXT: [[TMP15:%.*]] = icmp slt i32 [[IDX_NEXT]], [[EXIT_MAINLOOP_AT]] +-; CHECK-NEXT: br i1 [[TMP15]], label [[LOOP]], label [[MAIN_EXIT_SELECTOR:%.*]] ++; CHECK-NEXT: [[TMP10:%.*]] = icmp slt i32 [[IDX_NEXT]], [[EXIT_MAINLOOP_AT]] ++; CHECK-NEXT: br i1 [[TMP10]], label [[LOOP]], label [[MAIN_EXIT_SELECTOR:%.*]] + ; CHECK: main.exit.selector: + ; CHECK-NEXT: [[IDX_NEXT_LCSSA:%.*]] = phi i32 [ [[IDX_NEXT]], [[IN_BOUNDS]] ] +-; CHECK-NEXT: [[TMP16:%.*]] = icmp slt i32 [[IDX_NEXT_LCSSA]], [[N]] +-; CHECK-NEXT: br i1 [[TMP16]], label [[MAIN_PSEUDO_EXIT]], label [[EXIT_LOOPEXIT:%.*]] ++; CHECK-NEXT: [[TMP11:%.*]] = icmp slt i32 [[IDX_NEXT_LCSSA]], [[N]] ++; CHECK-NEXT: br i1 [[TMP11]], label [[MAIN_PSEUDO_EXIT]], label [[EXIT_LOOPEXIT:%.*]] + ; CHECK: main.pseudo.exit: + ; CHECK-NEXT: [[IDX_COPY:%.*]] = phi i32 [ 0, [[LOOP_PREHEADER]] ], [ [[IDX_NEXT_LCSSA]], [[MAIN_EXIT_SELECTOR]] ] + ; CHECK-NEXT: [[INDVAR_END:%.*]] = phi i32 [ 0, [[LOOP_PREHEADER]] ], [ [[IDX_NEXT_LCSSA]], [[MAIN_EXIT_SELECTOR]] ] + ; CHECK-NEXT: br label [[POSTLOOP:%.*]] + ; CHECK: out.of.bounds.loopexit: + ; CHECK-NEXT: br label [[OUT_OF_BOUNDS:%.*]] +-; CHECK: out.of.bounds.loopexit6: ++; CHECK: out.of.bounds.loopexit5: + ; CHECK-NEXT: br label [[OUT_OF_BOUNDS]] + ; CHECK: out.of.bounds: + ; CHECK-NEXT: ret void +@@ -211,47 +206,41 @@ define void @test_04(i32 *%arr, i32 %n, i32 %bound) { + ; CHECK-NEXT: [[FIRST_ITR_CHECK:%.*]] = icmp sgt i32 [[N:%.*]], 0 + ; CHECK-NEXT: br i1 [[FIRST_ITR_CHECK]], label [[LOOP_PREHEADER:%.*]], label [[EXIT:%.*]] + ; CHECK: loop.preheader: +-; CHECK-NEXT: [[TMP0:%.*]] = sub i32 -1, [[BOUND:%.*]] +-; CHECK-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[TMP0]], -1 +-; CHECK-NEXT: [[SMAX:%.*]] = select i1 [[TMP1]], i32 [[TMP0]], i32 -1 +-; CHECK-NEXT: [[TMP2:%.*]] = add i32 [[BOUND]], [[SMAX]] +-; CHECK-NEXT: [[TMP3:%.*]] = add i32 [[TMP2]], 1 +-; CHECK-NEXT: [[TMP4:%.*]] = sub i32 -1, [[SMAX]] +-; CHECK-NEXT: [[TMP5:%.*]] = icmp sgt i32 [[TMP4]], -1 +-; CHECK-NEXT: [[SMAX1:%.*]] = select i1 [[TMP5]], i32 [[TMP4]], i32 -1 +-; CHECK-NEXT: [[TMP6:%.*]] = add i32 [[SMAX1]], 1 +-; CHECK-NEXT: [[TMP7:%.*]] = mul i32 [[TMP3]], [[TMP6]] +-; CHECK-NEXT: [[TMP8:%.*]] = sub i32 -1, [[TMP7]] +-; CHECK-NEXT: [[TMP9:%.*]] = sub i32 -1, [[N]] +-; CHECK-NEXT: [[TMP10:%.*]] = icmp ugt i32 [[TMP8]], [[TMP9]] +-; CHECK-NEXT: [[UMAX:%.*]] = select i1 [[TMP10]], i32 [[TMP8]], i32 [[TMP9]] +-; CHECK-NEXT: [[EXIT_MAINLOOP_AT:%.*]] = sub i32 -1, [[UMAX]] +-; CHECK-NEXT: [[TMP11:%.*]] = icmp ult i32 0, [[EXIT_MAINLOOP_AT]] +-; CHECK-NEXT: br i1 [[TMP11]], label [[LOOP_PREHEADER2:%.*]], label [[MAIN_PSEUDO_EXIT:%.*]] +-; CHECK: loop.preheader2: ++; CHECK-NEXT: [[TMP0:%.*]] = icmp slt i32 [[BOUND:%.*]], 0 ++; CHECK-NEXT: [[SMAX:%.*]] = select i1 [[TMP0]], i32 [[BOUND]], i32 0 ++; CHECK-NEXT: [[TMP1:%.*]] = sub i32 [[BOUND]], [[SMAX]] ++; CHECK-NEXT: [[TMP2:%.*]] = icmp sgt i32 [[SMAX]], -1 ++; CHECK-NEXT: [[SMIN:%.*]] = select i1 [[TMP2]], i32 [[SMAX]], i32 -1 ++; CHECK-NEXT: [[TMP3:%.*]] = add i32 [[SMIN]], 1 ++; CHECK-NEXT: [[TMP4:%.*]] = mul i32 [[TMP1]], [[TMP3]] ++; CHECK-NEXT: [[TMP5:%.*]] = icmp ult i32 [[N]], [[TMP4]] ++; CHECK-NEXT: [[EXIT_MAINLOOP_AT:%.*]] = select i1 [[TMP5]], i32 [[N]], i32 [[TMP4]] ++; CHECK-NEXT: [[TMP6:%.*]] = icmp ult i32 0, [[EXIT_MAINLOOP_AT]] ++; CHECK-NEXT: br i1 [[TMP6]], label [[LOOP_PREHEADER1:%.*]], label [[MAIN_PSEUDO_EXIT:%.*]] ++; CHECK: loop.preheader1: + ; CHECK-NEXT: br label [[LOOP:%.*]] + ; CHECK: loop: +-; CHECK-NEXT: [[IDX:%.*]] = phi i32 [ [[IDX_NEXT:%.*]], [[IN_BOUNDS:%.*]] ], [ 0, [[LOOP_PREHEADER2]] ] ++; CHECK-NEXT: [[IDX:%.*]] = phi i32 [ [[IDX_NEXT:%.*]], [[IN_BOUNDS:%.*]] ], [ 0, [[LOOP_PREHEADER1]] ] + ; CHECK-NEXT: [[IDX_NEXT]] = add i32 [[IDX]], 1 + ; CHECK-NEXT: [[ABC:%.*]] = icmp slt i32 [[IDX]], [[BOUND]] +-; CHECK-NEXT: br i1 true, label [[IN_BOUNDS]], label [[OUT_OF_BOUNDS_LOOPEXIT3:%.*]], !prof !0 ++; CHECK-NEXT: br i1 true, label [[IN_BOUNDS]], label [[OUT_OF_BOUNDS_LOOPEXIT2:%.*]], !prof !0 + ; CHECK: in.bounds: + ; CHECK-NEXT: [[ADDR:%.*]] = getelementptr i32, i32* [[ARR:%.*]], i32 [[IDX]] + ; CHECK-NEXT: store i32 0, i32* [[ADDR]] + ; CHECK-NEXT: [[NEXT:%.*]] = icmp ult i32 [[IDX_NEXT]], [[N]] +-; CHECK-NEXT: [[TMP12:%.*]] = icmp ult i32 [[IDX_NEXT]], [[EXIT_MAINLOOP_AT]] +-; CHECK-NEXT: br i1 [[TMP12]], label [[LOOP]], label [[MAIN_EXIT_SELECTOR:%.*]] ++; CHECK-NEXT: [[TMP7:%.*]] = icmp ult i32 [[IDX_NEXT]], [[EXIT_MAINLOOP_AT]] ++; CHECK-NEXT: br i1 [[TMP7]], label [[LOOP]], label [[MAIN_EXIT_SELECTOR:%.*]] + ; CHECK: main.exit.selector: + ; CHECK-NEXT: [[IDX_NEXT_LCSSA:%.*]] = phi i32 [ [[IDX_NEXT]], [[IN_BOUNDS]] ] +-; CHECK-NEXT: [[TMP13:%.*]] = icmp ult i32 [[IDX_NEXT_LCSSA]], [[N]] +-; CHECK-NEXT: br i1 [[TMP13]], label [[MAIN_PSEUDO_EXIT]], label [[EXIT_LOOPEXIT:%.*]] ++; CHECK-NEXT: [[TMP8:%.*]] = icmp ult i32 [[IDX_NEXT_LCSSA]], [[N]] ++; CHECK-NEXT: br i1 [[TMP8]], label [[MAIN_PSEUDO_EXIT]], label [[EXIT_LOOPEXIT:%.*]] + ; CHECK: main.pseudo.exit: + ; CHECK-NEXT: [[IDX_COPY:%.*]] = phi i32 [ 0, [[LOOP_PREHEADER]] ], [ [[IDX_NEXT_LCSSA]], [[MAIN_EXIT_SELECTOR]] ] + ; CHECK-NEXT: [[INDVAR_END:%.*]] = phi i32 [ 0, [[LOOP_PREHEADER]] ], [ [[IDX_NEXT_LCSSA]], [[MAIN_EXIT_SELECTOR]] ] + ; CHECK-NEXT: br label [[POSTLOOP:%.*]] + ; CHECK: out.of.bounds.loopexit: + ; CHECK-NEXT: br label [[OUT_OF_BOUNDS:%.*]] +-; CHECK: out.of.bounds.loopexit3: ++; CHECK: out.of.bounds.loopexit2: + ; CHECK-NEXT: br label [[OUT_OF_BOUNDS]] + ; CHECK: out.of.bounds: + ; CHECK-NEXT: ret void +@@ -413,49 +402,44 @@ define void @test_07(i32 *%arr, i32 %n, i32 %bound) { + ; CHECK: loop.preheader: + ; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[BOUND:%.*]], -2147483647 + ; CHECK-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[TMP0]], 0 +-; CHECK-NEXT: [[SMAX:%.*]] = select i1 [[TMP1]], i32 [[TMP0]], i32 0 +-; CHECK-NEXT: [[TMP2:%.*]] = sub i32 [[BOUND]], [[SMAX]] +-; CHECK-NEXT: [[TMP3:%.*]] = sub i32 -1, [[BOUND]] +-; CHECK-NEXT: [[TMP4:%.*]] = icmp sgt i32 [[TMP3]], -1 +-; CHECK-NEXT: [[SMAX1:%.*]] = select i1 [[TMP4]], i32 [[TMP3]], i32 -1 +-; CHECK-NEXT: [[TMP5:%.*]] = sub i32 -1, [[SMAX1]] +-; CHECK-NEXT: [[TMP6:%.*]] = icmp sgt i32 [[TMP5]], -1 +-; CHECK-NEXT: [[SMAX2:%.*]] = select i1 [[TMP6]], i32 [[TMP5]], i32 -1 +-; CHECK-NEXT: [[TMP7:%.*]] = add i32 [[SMAX2]], 1 +-; CHECK-NEXT: [[TMP8:%.*]] = mul i32 [[TMP2]], [[TMP7]] +-; CHECK-NEXT: [[TMP9:%.*]] = sub i32 -1, [[TMP8]] +-; CHECK-NEXT: [[TMP10:%.*]] = sub i32 -1, [[N]] +-; CHECK-NEXT: [[TMP11:%.*]] = icmp sgt i32 [[TMP9]], [[TMP10]] +-; CHECK-NEXT: [[SMAX3:%.*]] = select i1 [[TMP11]], i32 [[TMP9]], i32 [[TMP10]] +-; CHECK-NEXT: [[TMP12:%.*]] = sub i32 -1, [[SMAX3]] +-; CHECK-NEXT: [[TMP13:%.*]] = icmp sgt i32 [[TMP12]], 0 +-; CHECK-NEXT: [[EXIT_MAINLOOP_AT:%.*]] = select i1 [[TMP13]], i32 [[TMP12]], i32 0 +-; CHECK-NEXT: [[TMP14:%.*]] = icmp slt i32 0, [[EXIT_MAINLOOP_AT]] +-; CHECK-NEXT: br i1 [[TMP14]], label [[LOOP_PREHEADER5:%.*]], label [[MAIN_PSEUDO_EXIT:%.*]] +-; CHECK: loop.preheader5: ++; CHECK-NEXT: [[SMIN:%.*]] = select i1 [[TMP1]], i32 [[TMP0]], i32 0 ++; CHECK-NEXT: [[TMP2:%.*]] = sub i32 [[BOUND]], [[SMIN]] ++; CHECK-NEXT: [[TMP3:%.*]] = icmp slt i32 [[BOUND]], 0 ++; CHECK-NEXT: [[SMAX:%.*]] = select i1 [[TMP3]], i32 [[BOUND]], i32 0 ++; CHECK-NEXT: [[TMP4:%.*]] = icmp sgt i32 [[SMAX]], -1 ++; CHECK-NEXT: [[SMIN1:%.*]] = select i1 [[TMP4]], i32 [[SMAX]], i32 -1 ++; CHECK-NEXT: [[TMP5:%.*]] = add i32 [[SMIN1]], 1 ++; CHECK-NEXT: [[TMP6:%.*]] = mul i32 [[TMP2]], [[TMP5]] ++; CHECK-NEXT: [[TMP7:%.*]] = icmp slt i32 [[N]], [[TMP6]] ++; CHECK-NEXT: [[SMAX2:%.*]] = select i1 [[TMP7]], i32 [[N]], i32 [[TMP6]] ++; CHECK-NEXT: [[TMP8:%.*]] = icmp sgt i32 [[SMAX2]], 0 ++; CHECK-NEXT: [[EXIT_MAINLOOP_AT:%.*]] = select i1 [[TMP8]], i32 [[SMAX2]], i32 0 ++; CHECK-NEXT: [[TMP9:%.*]] = icmp slt i32 0, [[EXIT_MAINLOOP_AT]] ++; CHECK-NEXT: br i1 [[TMP9]], label [[LOOP_PREHEADER4:%.*]], label [[MAIN_PSEUDO_EXIT:%.*]] ++; CHECK: loop.preheader4: + ; CHECK-NEXT: br label [[LOOP:%.*]] + ; CHECK: loop: +-; CHECK-NEXT: [[IDX:%.*]] = phi i32 [ [[IDX_NEXT:%.*]], [[IN_BOUNDS:%.*]] ], [ 0, [[LOOP_PREHEADER5]] ] ++; CHECK-NEXT: [[IDX:%.*]] = phi i32 [ [[IDX_NEXT:%.*]], [[IN_BOUNDS:%.*]] ], [ 0, [[LOOP_PREHEADER4]] ] + ; CHECK-NEXT: [[IDX_NEXT]] = add i32 [[IDX]], 1 + ; CHECK-NEXT: [[ABC:%.*]] = icmp ult i32 [[IDX]], [[BOUND]] +-; CHECK-NEXT: br i1 true, label [[IN_BOUNDS]], label [[OUT_OF_BOUNDS_LOOPEXIT6:%.*]], !prof !0 ++; CHECK-NEXT: br i1 true, label [[IN_BOUNDS]], label [[OUT_OF_BOUNDS_LOOPEXIT5:%.*]], !prof !0 + ; CHECK: in.bounds: + ; CHECK-NEXT: [[ADDR:%.*]] = getelementptr i32, i32* [[ARR:%.*]], i32 [[IDX]] + ; CHECK-NEXT: store i32 0, i32* [[ADDR]] + ; CHECK-NEXT: [[NEXT:%.*]] = icmp slt i32 [[IDX_NEXT]], [[N]] +-; CHECK-NEXT: [[TMP15:%.*]] = icmp slt i32 [[IDX_NEXT]], [[EXIT_MAINLOOP_AT]] +-; CHECK-NEXT: br i1 [[TMP15]], label [[LOOP]], label [[MAIN_EXIT_SELECTOR:%.*]] ++; CHECK-NEXT: [[TMP10:%.*]] = icmp slt i32 [[IDX_NEXT]], [[EXIT_MAINLOOP_AT]] ++; CHECK-NEXT: br i1 [[TMP10]], label [[LOOP]], label [[MAIN_EXIT_SELECTOR:%.*]] + ; CHECK: main.exit.selector: + ; CHECK-NEXT: [[IDX_NEXT_LCSSA:%.*]] = phi i32 [ [[IDX_NEXT]], [[IN_BOUNDS]] ] +-; CHECK-NEXT: [[TMP16:%.*]] = icmp slt i32 [[IDX_NEXT_LCSSA]], [[N]] +-; CHECK-NEXT: br i1 [[TMP16]], label [[MAIN_PSEUDO_EXIT]], label [[EXIT_LOOPEXIT:%.*]] ++; CHECK-NEXT: [[TMP11:%.*]] = icmp slt i32 [[IDX_NEXT_LCSSA]], [[N]] ++; CHECK-NEXT: br i1 [[TMP11]], label [[MAIN_PSEUDO_EXIT]], label [[EXIT_LOOPEXIT:%.*]] + ; CHECK: main.pseudo.exit: + ; CHECK-NEXT: [[IDX_COPY:%.*]] = phi i32 [ 0, [[LOOP_PREHEADER]] ], [ [[IDX_NEXT_LCSSA]], [[MAIN_EXIT_SELECTOR]] ] + ; CHECK-NEXT: [[INDVAR_END:%.*]] = phi i32 [ 0, [[LOOP_PREHEADER]] ], [ [[IDX_NEXT_LCSSA]], [[MAIN_EXIT_SELECTOR]] ] + ; CHECK-NEXT: br label [[POSTLOOP:%.*]] + ; CHECK: out.of.bounds.loopexit: + ; CHECK-NEXT: br label [[OUT_OF_BOUNDS:%.*]] +-; CHECK: out.of.bounds.loopexit6: ++; CHECK: out.of.bounds.loopexit5: + ; CHECK-NEXT: br label [[OUT_OF_BOUNDS]] + ; CHECK: out.of.bounds: + ; CHECK-NEXT: ret void +@@ -512,47 +496,41 @@ define void @test_08(i32 *%arr, i32 %n, i32 %bound) { + ; CHECK-NEXT: [[FIRST_ITR_CHECK:%.*]] = icmp sgt i32 [[N:%.*]], 0 + ; CHECK-NEXT: br i1 [[FIRST_ITR_CHECK]], label [[LOOP_PREHEADER:%.*]], label [[EXIT:%.*]] + ; CHECK: loop.preheader: +-; CHECK-NEXT: [[TMP0:%.*]] = sub i32 -1, [[BOUND:%.*]] +-; CHECK-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[TMP0]], -1 +-; CHECK-NEXT: [[SMAX:%.*]] = select i1 [[TMP1]], i32 [[TMP0]], i32 -1 +-; CHECK-NEXT: [[TMP2:%.*]] = add i32 [[BOUND]], [[SMAX]] +-; CHECK-NEXT: [[TMP3:%.*]] = add i32 [[TMP2]], 1 +-; CHECK-NEXT: [[TMP4:%.*]] = sub i32 -1, [[SMAX]] +-; CHECK-NEXT: [[TMP5:%.*]] = icmp sgt i32 [[TMP4]], -1 +-; CHECK-NEXT: [[SMAX1:%.*]] = select i1 [[TMP5]], i32 [[TMP4]], i32 -1 +-; CHECK-NEXT: [[TMP6:%.*]] = add i32 [[SMAX1]], 1 +-; CHECK-NEXT: [[TMP7:%.*]] = mul i32 [[TMP3]], [[TMP6]] +-; CHECK-NEXT: [[TMP8:%.*]] = sub i32 -1, [[TMP7]] +-; CHECK-NEXT: [[TMP9:%.*]] = sub i32 -1, [[N]] +-; CHECK-NEXT: [[TMP10:%.*]] = icmp ugt i32 [[TMP8]], [[TMP9]] +-; CHECK-NEXT: [[UMAX:%.*]] = select i1 [[TMP10]], i32 [[TMP8]], i32 [[TMP9]] +-; CHECK-NEXT: [[EXIT_MAINLOOP_AT:%.*]] = sub i32 -1, [[UMAX]] +-; CHECK-NEXT: [[TMP11:%.*]] = icmp ult i32 0, [[EXIT_MAINLOOP_AT]] +-; CHECK-NEXT: br i1 [[TMP11]], label [[LOOP_PREHEADER2:%.*]], label [[MAIN_PSEUDO_EXIT:%.*]] +-; CHECK: loop.preheader2: ++; CHECK-NEXT: [[TMP0:%.*]] = icmp slt i32 [[BOUND:%.*]], 0 ++; CHECK-NEXT: [[SMAX:%.*]] = select i1 [[TMP0]], i32 [[BOUND]], i32 0 ++; CHECK-NEXT: [[TMP1:%.*]] = sub i32 [[BOUND]], [[SMAX]] ++; CHECK-NEXT: [[TMP2:%.*]] = icmp sgt i32 [[SMAX]], -1 ++; CHECK-NEXT: [[SMIN:%.*]] = select i1 [[TMP2]], i32 [[SMAX]], i32 -1 ++; CHECK-NEXT: [[TMP3:%.*]] = add i32 [[SMIN]], 1 ++; CHECK-NEXT: [[TMP4:%.*]] = mul i32 [[TMP1]], [[TMP3]] ++; CHECK-NEXT: [[TMP5:%.*]] = icmp ult i32 [[N]], [[TMP4]] ++; CHECK-NEXT: [[EXIT_MAINLOOP_AT:%.*]] = select i1 [[TMP5]], i32 [[N]], i32 [[TMP4]] ++; CHECK-NEXT: [[TMP6:%.*]] = icmp ult i32 0, [[EXIT_MAINLOOP_AT]] ++; CHECK-NEXT: br i1 [[TMP6]], label [[LOOP_PREHEADER1:%.*]], label [[MAIN_PSEUDO_EXIT:%.*]] ++; CHECK: loop.preheader1: + ; CHECK-NEXT: br label [[LOOP:%.*]] + ; CHECK: loop: +-; CHECK-NEXT: [[IDX:%.*]] = phi i32 [ [[IDX_NEXT:%.*]], [[IN_BOUNDS:%.*]] ], [ 0, [[LOOP_PREHEADER2]] ] ++; CHECK-NEXT: [[IDX:%.*]] = phi i32 [ [[IDX_NEXT:%.*]], [[IN_BOUNDS:%.*]] ], [ 0, [[LOOP_PREHEADER1]] ] + ; CHECK-NEXT: [[IDX_NEXT]] = add i32 [[IDX]], 1 + ; CHECK-NEXT: [[ABC:%.*]] = icmp ult i32 [[IDX]], [[BOUND]] +-; CHECK-NEXT: br i1 true, label [[IN_BOUNDS]], label [[OUT_OF_BOUNDS_LOOPEXIT3:%.*]], !prof !0 ++; CHECK-NEXT: br i1 true, label [[IN_BOUNDS]], label [[OUT_OF_BOUNDS_LOOPEXIT2:%.*]], !prof !0 + ; CHECK: in.bounds: + ; CHECK-NEXT: [[ADDR:%.*]] = getelementptr i32, i32* [[ARR:%.*]], i32 [[IDX]] + ; CHECK-NEXT: store i32 0, i32* [[ADDR]] + ; CHECK-NEXT: [[NEXT:%.*]] = icmp ult i32 [[IDX_NEXT]], [[N]] +-; CHECK-NEXT: [[TMP12:%.*]] = icmp ult i32 [[IDX_NEXT]], [[EXIT_MAINLOOP_AT]] +-; CHECK-NEXT: br i1 [[TMP12]], label [[LOOP]], label [[MAIN_EXIT_SELECTOR:%.*]] ++; CHECK-NEXT: [[TMP7:%.*]] = icmp ult i32 [[IDX_NEXT]], [[EXIT_MAINLOOP_AT]] ++; CHECK-NEXT: br i1 [[TMP7]], label [[LOOP]], label [[MAIN_EXIT_SELECTOR:%.*]] + ; CHECK: main.exit.selector: + ; CHECK-NEXT: [[IDX_NEXT_LCSSA:%.*]] = phi i32 [ [[IDX_NEXT]], [[IN_BOUNDS]] ] +-; CHECK-NEXT: [[TMP13:%.*]] = icmp ult i32 [[IDX_NEXT_LCSSA]], [[N]] +-; CHECK-NEXT: br i1 [[TMP13]], label [[MAIN_PSEUDO_EXIT]], label [[EXIT_LOOPEXIT:%.*]] ++; CHECK-NEXT: [[TMP8:%.*]] = icmp ult i32 [[IDX_NEXT_LCSSA]], [[N]] ++; CHECK-NEXT: br i1 [[TMP8]], label [[MAIN_PSEUDO_EXIT]], label [[EXIT_LOOPEXIT:%.*]] + ; CHECK: main.pseudo.exit: + ; CHECK-NEXT: [[IDX_COPY:%.*]] = phi i32 [ 0, [[LOOP_PREHEADER]] ], [ [[IDX_NEXT_LCSSA]], [[MAIN_EXIT_SELECTOR]] ] + ; CHECK-NEXT: [[INDVAR_END:%.*]] = phi i32 [ 0, [[LOOP_PREHEADER]] ], [ [[IDX_NEXT_LCSSA]], [[MAIN_EXIT_SELECTOR]] ] + ; CHECK-NEXT: br label [[POSTLOOP:%.*]] + ; CHECK: out.of.bounds.loopexit: + ; CHECK-NEXT: br label [[OUT_OF_BOUNDS:%.*]] +-; CHECK: out.of.bounds.loopexit3: ++; CHECK: out.of.bounds.loopexit2: + ; CHECK-NEXT: br label [[OUT_OF_BOUNDS]] + ; CHECK: out.of.bounds: + ; CHECK-NEXT: ret void +diff --git a/test/Transforms/IRCE/single-access-no-preloop.ll b/test/Transforms/IRCE/single-access-no-preloop.ll +index acca948a7ab..7bf36f7c254 100644 +--- a/test/Transforms/IRCE/single-access-no-preloop.ll ++++ b/test/Transforms/IRCE/single-access-no-preloop.ll +@@ -86,15 +86,13 @@ define void @single_access_no_preloop_with_offset(i32 *%arr, i32 *%a_len_ptr, i3 + ; CHECK-LABEL: @single_access_no_preloop_with_offset( + + ; CHECK: loop.preheader: +-; CHECK: [[not_safe_range_end:[^ ]+]] = sub i32 3, %len +-; CHECK: [[not_n:[^ ]+]] = sub i32 -1, %n +-; CHECK: [[not_exit_main_loop_at_hiclamp_cmp:[^ ]+]] = icmp sgt i32 [[not_safe_range_end]], [[not_n]] +-; CHECK: [[not_exit_main_loop_at_hiclamp:[^ ]+]] = select i1 [[not_exit_main_loop_at_hiclamp_cmp]], i32 [[not_safe_range_end]], i32 [[not_n]] +-; CHECK: [[exit_main_loop_at_hiclamp:[^ ]+]] = sub i32 -1, [[not_exit_main_loop_at_hiclamp]] ++; CHECK: [[safe_range_end:[^ ]+]] = add i32 %len, -4 ++; CHECK: [[exit_main_loop_at_hiclamp_cmp:[^ ]+]] = icmp slt i32 %n, [[safe_range_end]] ++; CHECK: [[exit_main_loop_at_hiclamp:[^ ]+]] = select i1 [[exit_main_loop_at_hiclamp_cmp]], i32 %n, i32 [[safe_range_end]] + ; CHECK: [[exit_main_loop_at_loclamp_cmp:[^ ]+]] = icmp sgt i32 [[exit_main_loop_at_hiclamp]], 0 + ; CHECK: [[exit_main_loop_at_loclamp:[^ ]+]] = select i1 [[exit_main_loop_at_loclamp_cmp]], i32 [[exit_main_loop_at_hiclamp]], i32 0 + ; CHECK: [[enter_main_loop:[^ ]+]] = icmp slt i32 0, [[exit_main_loop_at_loclamp]] +-; CHECK: br i1 [[enter_main_loop]], label %loop.preheader2, label %main.pseudo.exit ++; CHECK: br i1 [[enter_main_loop]], label %[[loop_preheader:[^ ,]+]], label %main.pseudo.exit + + ; CHECK: loop: + ; CHECK: br i1 true, label %in.bounds, label %out.of.bounds +diff --git a/test/Transforms/IRCE/single-access-with-preloop.ll b/test/Transforms/IRCE/single-access-with-preloop.ll +index 6f3b0324e39..bd235aa4a73 100644 +--- a/test/Transforms/IRCE/single-access-with-preloop.ll ++++ b/test/Transforms/IRCE/single-access-with-preloop.ll +@@ -34,11 +34,9 @@ define void @single_access_with_preloop(i32 *%arr, i32 *%a_len_ptr, i32 %n, i32 + ; CHECK: [[check_min_sint_offset:[^ ]+]] = icmp sgt i32 %offset, -2147483647 + ; CHECK: [[safe_offset_preloop:[^ ]+]] = select i1 [[check_min_sint_offset]], i32 %offset, i32 -2147483647 + ; If Offset was a SINT_MIN, we could have an overflow here. That is why we calculated its safe version. +-; CHECK: [[not_safe_start:[^ ]+]] = add i32 [[safe_offset_preloop]], -1 +-; CHECK: [[not_n:[^ ]+]] = sub i32 -1, %n +-; CHECK: [[not_exit_preloop_at_cond_loclamp:[^ ]+]] = icmp sgt i32 [[not_safe_start]], [[not_n]] +-; CHECK: [[not_exit_preloop_at_loclamp:[^ ]+]] = select i1 [[not_exit_preloop_at_cond_loclamp]], i32 [[not_safe_start]], i32 [[not_n]] +-; CHECK: [[exit_preloop_at_loclamp:[^ ]+]] = sub i32 -1, [[not_exit_preloop_at_loclamp]] ++; CHECK: [[safe_start:[^ ]+]] = sub i32 0, [[safe_offset_preloop]] ++; CHECK: [[exit_preloop_at_cond_loclamp:[^ ]+]] = icmp slt i32 %n, [[safe_start]] ++; CHECK: [[exit_preloop_at_loclamp:[^ ]+]] = select i1 [[exit_preloop_at_cond_loclamp]], i32 %n, i32 [[safe_start]] + ; CHECK: [[exit_preloop_at_cond:[^ ]+]] = icmp sgt i32 [[exit_preloop_at_loclamp]], 0 + ; CHECK: [[exit_preloop_at:[^ ]+]] = select i1 [[exit_preloop_at_cond]], i32 [[exit_preloop_at_loclamp]], i32 0 + +@@ -46,17 +44,15 @@ define void @single_access_with_preloop(i32 *%arr, i32 *%a_len_ptr, i32 %n, i32 + ; CHECK: [[len_minus_sint_max:[^ ]+]] = add i32 %len, -2147483647 + ; CHECK: [[check_len_min_sint_offset:[^ ]+]] = icmp sgt i32 %offset, [[len_minus_sint_max]] + ; CHECK: [[safe_offset_mainloop:[^ ]+]] = select i1 [[check_len_min_sint_offset]], i32 %offset, i32 [[len_minus_sint_max]] +-; CHECK: [[not_safe_start_2:[^ ]+]] = add i32 [[safe_offset_mainloop]], -1 + ; If Offset was a SINT_MIN, we could have an overflow here. That is why we calculated its safe version. +-; CHECK: [[not_safe_upper_end:[^ ]+]] = sub i32 [[not_safe_start_2]], %len +-; CHECK: [[not_exit_mainloop_at_cond_loclamp:[^ ]+]] = icmp sgt i32 [[not_safe_upper_end]], [[not_n]] +-; CHECK: [[not_exit_mainloop_at_loclamp:[^ ]+]] = select i1 [[not_exit_mainloop_at_cond_loclamp]], i32 [[not_safe_upper_end]], i32 [[not_n]] ++; CHECK: [[safe_upper_end:[^ ]+]] = sub i32 %len, [[safe_offset_mainloop]] ++; CHECK: [[exit_mainloop_at_cond_loclamp:[^ ]+]] = icmp slt i32 %n, [[safe_upper_end]] ++; CHECK: [[exit_mainloop_at_loclamp:[^ ]+]] = select i1 [[exit_mainloop_at_cond_loclamp]], i32 %n, i32 [[safe_upper_end]] + ; CHECK: [[check_offset_mainloop_2:[^ ]+]] = icmp sgt i32 %offset, 0 + ; CHECK: [[safe_offset_mainloop_2:[^ ]+]] = select i1 [[check_offset_mainloop_2]], i32 %offset, i32 0 +-; CHECK: [[not_safe_lower_end:[^ ]+]] = add i32 [[safe_offset_mainloop_2]], -2147483648 +-; CHECK: [[not_exit_mainloop_at_cond_hiclamp:[^ ]+]] = icmp sgt i32 [[not_exit_mainloop_at_loclamp]], [[not_safe_lower_end]] +-; CHECK: [[not_exit_mainloop_at_hiclamp:[^ ]+]] = select i1 [[not_exit_mainloop_at_cond_hiclamp]], i32 [[not_exit_mainloop_at_loclamp]], i32 [[not_safe_lower_end]] +-; CHECK: [[exit_mainloop_at_hiclamp:[^ ]+]] = sub i32 -1, [[not_exit_mainloop_at_hiclamp]] ++; CHECK: [[safe_lower_end:[^ ]+]] = sub i32 2147483647, [[safe_offset_mainloop_2]] ++; CHECK: [[exit_mainloop_at_cond_hiclamp:[^ ]+]] = icmp slt i32 [[exit_mainloop_at_loclamp]], [[safe_lower_end]] ++; CHECK: [[exit_mainloop_at_hiclamp:[^ ]+]] = select i1 [[exit_mainloop_at_cond_hiclamp]], i32 [[exit_mainloop_at_loclamp]], i32 [[safe_lower_end]] + ; CHECK: [[exit_mainloop_at_cmp:[^ ]+]] = icmp sgt i32 [[exit_mainloop_at_hiclamp]], 0 + ; CHECK: [[exit_mainloop_at:[^ ]+]] = select i1 [[exit_mainloop_at_cmp]], i32 [[exit_mainloop_at_hiclamp]], i32 0 + +@@ -67,7 +63,7 @@ define void @single_access_with_preloop(i32 *%arr, i32 *%a_len_ptr, i32 %n, i32 + ; CHECK: %abc.high = icmp slt i32 %array.idx, %len + ; CHECK: %abc.low = icmp sge i32 %array.idx, 0 + ; CHECK: %abc = and i1 true, true +-; CHECK: br i1 %abc, label %in.bounds, label %out.of.bounds.loopexit11 ++; CHECK: br i1 %abc, label %in.bounds, label %[[loopexit:[^ ,]+]] + + ; CHECK: in.bounds: + ; CHECK: [[continue_mainloop_cond:[^ ]+]] = icmp slt i32 %idx.next, [[exit_mainloop_at]] +diff --git a/test/Transforms/IRCE/unsigned_comparisons_ugt.ll b/test/Transforms/IRCE/unsigned_comparisons_ugt.ll +index 8f00c733569..3451d65c7bb 100644 +--- a/test/Transforms/IRCE/unsigned_comparisons_ugt.ll ++++ b/test/Transforms/IRCE/unsigned_comparisons_ugt.ll +@@ -58,8 +58,8 @@ define void @test_02(i32* %arr, i32* %a_len_ptr) #0 { + ; CHECK: entry: + ; CHECK-NEXT: %len = load i32, i32* %a_len_ptr, !range !0 + ; CHECK-NEXT: [[COND1:%[^ ]+]] = icmp ugt i32 %len, 1 +-; CHECK-NEXT: %umax = select i1 [[COND1]], i32 %len, i32 1 +-; CHECK-NEXT: %exit.preloop.at = add i32 %umax, -1 ++; CHECK-NEXT: [[UMIN:%[^ ]+]] = select i1 [[COND1]], i32 %len, i32 1 ++; CHECK-NEXT: %exit.preloop.at = add i32 [[UMIN]], -1 + ; CHECK-NEXT: [[COND2:%[^ ]+]] = icmp ugt i32 100, %exit.preloop.at + ; CHECK-NEXT: br i1 [[COND2]], label %loop.preloop.preheader, label %preloop.pseudo.exit + ; CHECK: mainloop: +@@ -149,8 +149,8 @@ define void @test_04(i32* %arr, i32* %a_len_ptr) #0 { + ; CHECK: entry: + ; CHECK-NEXT: %len = load i32, i32* %a_len_ptr, !range !0 + ; CHECK-NEXT: [[COND1:%[^ ]+]] = icmp ugt i32 %len, 1 +-; CHECK-NEXT: %umax = select i1 [[COND1]], i32 %len, i32 1 +-; CHECK-NEXT: %exit.preloop.at = add i32 %umax, -1 ++; CHECK-NEXT: [[UMIN:%[^ ]+]] = select i1 [[COND1]], i32 %len, i32 1 ++; CHECK-NEXT: %exit.preloop.at = add i32 [[UMIN]], -1 + ; CHECK-NEXT: [[COND2:%[^ ]+]] = icmp ugt i32 -2147483648, %exit.preloop.at + ; CHECK-NEXT: br i1 [[COND2]], label %loop.preloop.preheader, label %preloop.pseudo.exit + ; CHECK: mainloop: +diff --git a/test/Transforms/IRCE/unsigned_comparisons_ult.ll b/test/Transforms/IRCE/unsigned_comparisons_ult.ll +index dc59c11df1b..aca3c3d192e 100644 +--- a/test/Transforms/IRCE/unsigned_comparisons_ult.ll ++++ b/test/Transforms/IRCE/unsigned_comparisons_ult.ll +@@ -61,8 +61,8 @@ define void @test_02(i32* %arr, i32* %a_len_ptr) #0 { + ; CHECK: entry: + ; CHECK-NEXT: %len = load i32, i32* %a_len_ptr, !range !0 + ; CHECK-NEXT: [[COND1:%[^ ]+]] = icmp ugt i32 %len, 1 +-; CHECK-NEXT: %umax = select i1 [[COND1]], i32 %len, i32 1 +-; CHECK-NEXT: %exit.preloop.at = add i32 %umax, -1 ++; CHECK-NEXT: [[UMIN:%[^ ]+]] = select i1 [[COND1]], i32 %len, i32 1 ++; CHECK-NEXT: %exit.preloop.at = add i32 [[UMIN]], -1 + ; CHECK-NEXT: [[COND2:%[^ ]+]] = icmp ugt i32 100, %exit.preloop.at + ; CHECK-NEXT: br i1 [[COND2]], label %loop.preloop.preheader, label %preloop.pseudo.exit + ; CHECK: mainloop: +@@ -194,8 +194,8 @@ define void @test_05(i32* %arr, i32* %a_len_ptr) #0 { + ; CHECK: entry: + ; CHECK-NEXT: %len = load i32, i32* %a_len_ptr, !range !0 + ; CHECK-NEXT: [[COND1:%[^ ]+]] = icmp ugt i32 %len, 1 +-; CHECK-NEXT: %umax = select i1 [[COND1]], i32 %len, i32 1 +-; CHECK-NEXT: %exit.preloop.at = add i32 %umax, -1 ++; CHECK-NEXT: [[UMIN:%[^ ]+]] = select i1 [[COND1]], i32 %len, i32 1 ++; CHECK-NEXT: %exit.preloop.at = add i32 [[UMIN]], -1 + ; CHECK-NEXT: [[COND2:%[^ ]+]] = icmp ugt i32 -2147483648, %exit.preloop.at + ; CHECK-NEXT: br i1 [[COND2]], label %loop.preloop.preheader, label %preloop.pseudo.exit + ; CHECK: mainloop: +diff --git a/test/Transforms/IndVarSimplify/eliminate-trunc.ll b/test/Transforms/IndVarSimplify/eliminate-trunc.ll +index 7e0971f9f31..c83a48723ca 100644 +--- a/test/Transforms/IndVarSimplify/eliminate-trunc.ll ++++ b/test/Transforms/IndVarSimplify/eliminate-trunc.ll +@@ -459,15 +459,17 @@ exit: + define void @test_10(i32 %n) { + ; CHECK-LABEL: @test_10( + ; CHECK-NEXT: entry: +-; CHECK-NEXT: [[SEXT:%.*]] = sext i32 [[N:%.*]] to i64 ++; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[N:%.*]], 100 ++; CHECK-NEXT: [[TMP1:%.*]] = zext i32 [[TMP0]] to i64 ++; CHECK-NEXT: [[TMP2:%.*]] = icmp ult i64 [[TMP1]], 90 ++; CHECK-NEXT: [[UMAX:%.*]] = select i1 [[TMP2]], i64 [[TMP1]], i64 90 ++; CHECK-NEXT: [[TMP3:%.*]] = add i64 [[UMAX]], -99 + ; CHECK-NEXT: br label [[LOOP:%.*]] + ; CHECK: loop: + ; CHECK-NEXT: [[IV:%.*]] = phi i64 [ -100, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ] + ; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1 +-; CHECK-NEXT: [[TMP0:%.*]] = icmp ne i64 [[IV]], [[SEXT]] +-; CHECK-NEXT: [[NEGCMP:%.*]] = icmp slt i64 [[IV]], -10 +-; CHECK-NEXT: [[CMP:%.*]] = and i1 [[TMP0]], [[NEGCMP]] +-; CHECK-NEXT: br i1 [[CMP]], label [[LOOP]], label [[EXIT:%.*]] ++; CHECK-NEXT: [[EXITCOND:%.*]] = icmp ne i64 [[IV_NEXT]], [[TMP3]] ++; CHECK-NEXT: br i1 [[EXITCOND]], label [[LOOP]], label [[EXIT:%.*]] + ; CHECK: exit: + ; CHECK-NEXT: ret void + ; +diff --git a/test/Transforms/LoopStrengthReduce/2013-01-14-ReuseCast.ll b/test/Transforms/LoopStrengthReduce/2013-01-14-ReuseCast.ll +index ea3f6077231..d5232e1874c 100644 +--- a/test/Transforms/LoopStrengthReduce/2013-01-14-ReuseCast.ll ++++ b/test/Transforms/LoopStrengthReduce/2013-01-14-ReuseCast.ll +@@ -14,8 +14,6 @@ target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f3 + ; current LSR cost model. + ; CHECK-NOT: = ptrtoint i8* undef to i64 + ; CHECK: .lr.ph +-; CHECK: [[TMP:%[^ ]+]] = add i64 %tmp{{[0-9]+}}, -1 +-; CHECK: sub i64 [[TMP]], %tmp{{[0-9]+}} + ; CHECK: ret void + define void @VerifyDiagnosticConsumerTest() unnamed_addr nounwind uwtable align 2 { + bb: +diff --git a/test/Transforms/LoopVectorize/X86/pr35432.ll b/test/Transforms/LoopVectorize/X86/pr35432.ll +index b8760cb8d50..6aaa13c183a 100644 +--- a/test/Transforms/LoopVectorize/X86/pr35432.ll ++++ b/test/Transforms/LoopVectorize/X86/pr35432.ll +@@ -27,7 +27,6 @@ define i32 @main() local_unnamed_addr #0 { + ; CHECK-NEXT: [[CMP8:%.*]] = icmp eq i32 [[CONV17]], 0 + ; CHECK-NEXT: br i1 [[CMP8]], label [[FOR_BODY_LR_PH:%.*]], label [[FOR_END12:%.*]] + ; CHECK: for.body.lr.ph: +-; CHECK-NEXT: [[TMP3:%.*]] = sub i32 -1, [[TMP2]] + ; CHECK-NEXT: br label [[FOR_BODY:%.*]] + ; CHECK: for.body: + ; CHECK-NEXT: [[STOREMERGE_IN9:%.*]] = phi i32 [ [[TMP2]], [[FOR_BODY_LR_PH]] ], [ [[ADD:%.*]], [[FOR_INC9:%.*]] ] +@@ -37,77 +36,74 @@ define i32 @main() local_unnamed_addr #0 { + ; CHECK: for.body8.lr.ph: + ; CHECK-NEXT: [[CONV3:%.*]] = trunc i32 [[STOREMERGE_IN9]] to i8 + ; CHECK-NEXT: [[DOTPROMOTED:%.*]] = load i32, i32* getelementptr inbounds ([192 x [192 x i32]], [192 x [192 x i32]]* @a, i64 0, i64 0, i64 0), align 16 +-; CHECK-NEXT: [[TMP4:%.*]] = add i8 [[CONV3]], -1 +-; CHECK-NEXT: [[TMP5:%.*]] = zext i8 [[TMP4]] to i32 +-; CHECK-NEXT: [[TMP6:%.*]] = sub i32 -1, [[TMP5]] +-; CHECK-NEXT: [[TMP7:%.*]] = icmp ugt i32 [[TMP6]], [[TMP3]] +-; CHECK-NEXT: [[UMAX:%.*]] = select i1 [[TMP7]], i32 [[TMP6]], i32 [[TMP3]] +-; CHECK-NEXT: [[TMP8:%.*]] = add i32 [[UMAX]], 2 +-; CHECK-NEXT: [[TMP9:%.*]] = add i32 [[TMP8]], [[TMP5]] +-; CHECK-NEXT: [[MIN_ITERS_CHECK:%.*]] = icmp ult i32 [[TMP9]], 8 ++; CHECK-NEXT: [[TMP3:%.*]] = add i8 [[CONV3]], -1 ++; CHECK-NEXT: [[TMP4:%.*]] = zext i8 [[TMP3]] to i32 ++; CHECK-NEXT: [[TMP5:%.*]] = add i32 [[TMP4]], 1 ++; CHECK-NEXT: [[TMP6:%.*]] = icmp ult i32 [[TMP2]], [[TMP4]] ++; CHECK-NEXT: [[UMAX:%.*]] = select i1 [[TMP6]], i32 [[TMP2]], i32 [[TMP4]] ++; CHECK-NEXT: [[TMP7:%.*]] = sub i32 [[TMP5]], [[UMAX]] ++; CHECK-NEXT: [[MIN_ITERS_CHECK:%.*]] = icmp ult i32 [[TMP7]], 8 + ; CHECK-NEXT: br i1 [[MIN_ITERS_CHECK]], label [[SCALAR_PH:%.*]], label [[VECTOR_SCEVCHECK:%.*]] + ; CHECK: vector.scevcheck: +-; CHECK-NEXT: [[TMP10:%.*]] = add i8 [[CONV3]], -1 +-; CHECK-NEXT: [[TMP11:%.*]] = zext i8 [[TMP10]] to i32 +-; CHECK-NEXT: [[TMP12:%.*]] = sub i32 -1, [[TMP11]] +-; CHECK-NEXT: [[TMP13:%.*]] = icmp ugt i32 [[TMP12]], [[TMP3]] +-; CHECK-NEXT: [[UMAX1:%.*]] = select i1 [[TMP13]], i32 [[TMP12]], i32 [[TMP3]] +-; CHECK-NEXT: [[TMP14:%.*]] = add i32 [[UMAX1]], 1 +-; CHECK-NEXT: [[TMP15:%.*]] = add i32 [[TMP14]], [[TMP11]] +-; CHECK-NEXT: [[TMP16:%.*]] = trunc i32 [[TMP15]] to i8 +-; CHECK-NEXT: [[MUL:%.*]] = call { i8, i1 } @llvm.umul.with.overflow.i8(i8 1, i8 [[TMP16]]) ++; CHECK-NEXT: [[TMP8:%.*]] = add i8 [[CONV3]], -1 ++; CHECK-NEXT: [[TMP9:%.*]] = zext i8 [[TMP8]] to i32 ++; CHECK-NEXT: [[TMP10:%.*]] = icmp ult i32 [[TMP2]], [[TMP9]] ++; CHECK-NEXT: [[UMAX1:%.*]] = select i1 [[TMP10]], i32 [[TMP2]], i32 [[TMP9]] ++; CHECK-NEXT: [[TMP11:%.*]] = sub i32 [[TMP9]], [[UMAX1]] ++; CHECK-NEXT: [[TMP12:%.*]] = trunc i32 [[TMP11]] to i8 ++; CHECK-NEXT: [[MUL:%.*]] = call { i8, i1 } @llvm.umul.with.overflow.i8(i8 1, i8 [[TMP12]]) + ; CHECK-NEXT: [[MUL_RESULT:%.*]] = extractvalue { i8, i1 } [[MUL]], 0 + ; CHECK-NEXT: [[MUL_OVERFLOW:%.*]] = extractvalue { i8, i1 } [[MUL]], 1 +-; CHECK-NEXT: [[TMP17:%.*]] = add i8 [[TMP10]], [[MUL_RESULT]] +-; CHECK-NEXT: [[TMP18:%.*]] = sub i8 [[TMP10]], [[MUL_RESULT]] +-; CHECK-NEXT: [[TMP19:%.*]] = icmp ugt i8 [[TMP18]], [[TMP10]] +-; CHECK-NEXT: [[TMP20:%.*]] = icmp ult i8 [[TMP17]], [[TMP10]] +-; CHECK-NEXT: [[TMP21:%.*]] = select i1 true, i1 [[TMP19]], i1 [[TMP20]] +-; CHECK-NEXT: [[TMP22:%.*]] = icmp ugt i32 [[TMP15]], 255 +-; CHECK-NEXT: [[TMP23:%.*]] = or i1 [[TMP21]], [[TMP22]] +-; CHECK-NEXT: [[TMP24:%.*]] = or i1 [[TMP23]], [[MUL_OVERFLOW]] +-; CHECK-NEXT: [[TMP25:%.*]] = or i1 false, [[TMP24]] +-; CHECK-NEXT: br i1 [[TMP25]], label [[SCALAR_PH]], label [[VECTOR_PH:%.*]] ++; CHECK-NEXT: [[TMP13:%.*]] = add i8 [[TMP8]], [[MUL_RESULT]] ++; CHECK-NEXT: [[TMP14:%.*]] = sub i8 [[TMP8]], [[MUL_RESULT]] ++; CHECK-NEXT: [[TMP15:%.*]] = icmp ugt i8 [[TMP14]], [[TMP8]] ++; CHECK-NEXT: [[TMP16:%.*]] = icmp ult i8 [[TMP13]], [[TMP8]] ++; CHECK-NEXT: [[TMP17:%.*]] = select i1 true, i1 [[TMP15]], i1 [[TMP16]] ++; CHECK-NEXT: [[TMP18:%.*]] = icmp ugt i32 [[TMP11]], 255 ++; CHECK-NEXT: [[TMP19:%.*]] = or i1 [[TMP17]], [[TMP18]] ++; CHECK-NEXT: [[TMP20:%.*]] = or i1 [[TMP19]], [[MUL_OVERFLOW]] ++; CHECK-NEXT: [[TMP21:%.*]] = or i1 false, [[TMP20]] ++; CHECK-NEXT: br i1 [[TMP21]], label [[SCALAR_PH]], label [[VECTOR_PH:%.*]] + ; CHECK: vector.ph: +-; CHECK-NEXT: [[N_MOD_VF:%.*]] = urem i32 [[TMP9]], 8 +-; CHECK-NEXT: [[N_VEC:%.*]] = sub i32 [[TMP9]], [[N_MOD_VF]] ++; CHECK-NEXT: [[N_MOD_VF:%.*]] = urem i32 [[TMP7]], 8 ++; CHECK-NEXT: [[N_VEC:%.*]] = sub i32 [[TMP7]], [[N_MOD_VF]] + ; CHECK-NEXT: [[CAST_CRD:%.*]] = trunc i32 [[N_VEC]] to i8 + ; CHECK-NEXT: [[IND_END:%.*]] = sub i8 [[CONV3]], [[CAST_CRD]] +-; CHECK-NEXT: [[TMP26:%.*]] = insertelement <4 x i32> zeroinitializer, i32 [[DOTPROMOTED]], i32 0 ++; CHECK-NEXT: [[TMP22:%.*]] = insertelement <4 x i32> zeroinitializer, i32 [[DOTPROMOTED]], i32 0 + ; CHECK-NEXT: br label [[VECTOR_BODY:%.*]] + ; CHECK: vector.body: + ; CHECK-NEXT: [[INDEX:%.*]] = phi i32 [ 0, [[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], [[VECTOR_BODY]] ] +-; CHECK-NEXT: [[VEC_PHI:%.*]] = phi <4 x i32> [ [[TMP26]], [[VECTOR_PH]] ], [ [[TMP30:%.*]], [[VECTOR_BODY]] ] +-; CHECK-NEXT: [[VEC_PHI2:%.*]] = phi <4 x i32> [ zeroinitializer, [[VECTOR_PH]] ], [ [[TMP31:%.*]], [[VECTOR_BODY]] ] +-; CHECK-NEXT: [[TMP27:%.*]] = trunc i32 [[INDEX]] to i8 +-; CHECK-NEXT: [[OFFSET_IDX:%.*]] = sub i8 [[CONV3]], [[TMP27]] ++; CHECK-NEXT: [[VEC_PHI:%.*]] = phi <4 x i32> [ [[TMP22]], [[VECTOR_PH]] ], [ [[TMP26:%.*]], [[VECTOR_BODY]] ] ++; CHECK-NEXT: [[VEC_PHI2:%.*]] = phi <4 x i32> [ zeroinitializer, [[VECTOR_PH]] ], [ [[TMP27:%.*]], [[VECTOR_BODY]] ] ++; CHECK-NEXT: [[TMP23:%.*]] = trunc i32 [[INDEX]] to i8 ++; CHECK-NEXT: [[OFFSET_IDX:%.*]] = sub i8 [[CONV3]], [[TMP23]] + ; CHECK-NEXT: [[BROADCAST_SPLATINSERT:%.*]] = insertelement <4 x i8> undef, i8 [[OFFSET_IDX]], i32 0 + ; CHECK-NEXT: [[BROADCAST_SPLAT:%.*]] = shufflevector <4 x i8> [[BROADCAST_SPLATINSERT]], <4 x i8> undef, <4 x i32> zeroinitializer + ; CHECK-NEXT: [[INDUCTION:%.*]] = add <4 x i8> [[BROADCAST_SPLAT]], + ; CHECK-NEXT: [[INDUCTION3:%.*]] = add <4 x i8> [[BROADCAST_SPLAT]], +-; CHECK-NEXT: [[TMP28:%.*]] = add i8 [[OFFSET_IDX]], 0 +-; CHECK-NEXT: [[TMP29:%.*]] = add i8 [[OFFSET_IDX]], -4 +-; CHECK-NEXT: [[TMP30]] = add <4 x i32> [[VEC_PHI]], +-; CHECK-NEXT: [[TMP31]] = add <4 x i32> [[VEC_PHI2]], +-; CHECK-NEXT: [[TMP32:%.*]] = add i8 [[TMP28]], -1 +-; CHECK-NEXT: [[TMP33:%.*]] = add i8 [[TMP29]], -1 +-; CHECK-NEXT: [[TMP34:%.*]] = zext i8 [[TMP32]] to i32 +-; CHECK-NEXT: [[TMP35:%.*]] = zext i8 [[TMP33]] to i32 ++; CHECK-NEXT: [[TMP24:%.*]] = add i8 [[OFFSET_IDX]], 0 ++; CHECK-NEXT: [[TMP25:%.*]] = add i8 [[OFFSET_IDX]], -4 ++; CHECK-NEXT: [[TMP26]] = add <4 x i32> [[VEC_PHI]], ++; CHECK-NEXT: [[TMP27]] = add <4 x i32> [[VEC_PHI2]], ++; CHECK-NEXT: [[TMP28:%.*]] = add i8 [[TMP24]], -1 ++; CHECK-NEXT: [[TMP29:%.*]] = add i8 [[TMP25]], -1 ++; CHECK-NEXT: [[TMP30:%.*]] = zext i8 [[TMP28]] to i32 ++; CHECK-NEXT: [[TMP31:%.*]] = zext i8 [[TMP29]] to i32 + ; CHECK-NEXT: [[INDEX_NEXT]] = add i32 [[INDEX]], 8 +-; CHECK-NEXT: [[TMP36:%.*]] = icmp eq i32 [[INDEX_NEXT]], [[N_VEC]] +-; CHECK-NEXT: br i1 [[TMP36]], label [[MIDDLE_BLOCK:%.*]], label [[VECTOR_BODY]], !llvm.loop !0 ++; CHECK-NEXT: [[TMP32:%.*]] = icmp eq i32 [[INDEX_NEXT]], [[N_VEC]] ++; CHECK-NEXT: br i1 [[TMP32]], label [[MIDDLE_BLOCK:%.*]], label [[VECTOR_BODY]], !llvm.loop !0 + ; CHECK: middle.block: +-; CHECK-NEXT: [[BIN_RDX:%.*]] = add <4 x i32> [[TMP31]], [[TMP30]] ++; CHECK-NEXT: [[BIN_RDX:%.*]] = add <4 x i32> [[TMP27]], [[TMP26]] + ; CHECK-NEXT: [[RDX_SHUF:%.*]] = shufflevector <4 x i32> [[BIN_RDX]], <4 x i32> undef, <4 x i32> + ; CHECK-NEXT: [[BIN_RDX4:%.*]] = add <4 x i32> [[BIN_RDX]], [[RDX_SHUF]] + ; CHECK-NEXT: [[RDX_SHUF5:%.*]] = shufflevector <4 x i32> [[BIN_RDX4]], <4 x i32> undef, <4 x i32> + ; CHECK-NEXT: [[BIN_RDX6:%.*]] = add <4 x i32> [[BIN_RDX4]], [[RDX_SHUF5]] +-; CHECK-NEXT: [[TMP37:%.*]] = extractelement <4 x i32> [[BIN_RDX6]], i32 0 +-; CHECK-NEXT: [[CMP_N:%.*]] = icmp eq i32 [[TMP9]], [[N_VEC]] ++; CHECK-NEXT: [[TMP33:%.*]] = extractelement <4 x i32> [[BIN_RDX6]], i32 0 ++; CHECK-NEXT: [[CMP_N:%.*]] = icmp eq i32 [[TMP7]], [[N_VEC]] + ; CHECK-NEXT: br i1 [[CMP_N]], label [[FOR_COND4_FOR_INC9_CRIT_EDGE:%.*]], label [[SCALAR_PH]] + ; CHECK: scalar.ph: + ; CHECK-NEXT: [[BC_RESUME_VAL:%.*]] = phi i8 [ [[IND_END]], [[MIDDLE_BLOCK]] ], [ [[CONV3]], [[FOR_BODY8_LR_PH]] ], [ [[CONV3]], [[VECTOR_SCEVCHECK]] ] +-; CHECK-NEXT: [[BC_MERGE_RDX:%.*]] = phi i32 [ [[DOTPROMOTED]], [[FOR_BODY8_LR_PH]] ], [ [[DOTPROMOTED]], [[VECTOR_SCEVCHECK]] ], [ [[TMP37]], [[MIDDLE_BLOCK]] ] ++; CHECK-NEXT: [[BC_MERGE_RDX:%.*]] = phi i32 [ [[DOTPROMOTED]], [[FOR_BODY8_LR_PH]] ], [ [[DOTPROMOTED]], [[VECTOR_SCEVCHECK]] ], [ [[TMP33]], [[MIDDLE_BLOCK]] ] + ; CHECK-NEXT: br label [[FOR_BODY8:%.*]] + ; CHECK: for.body8: + ; CHECK-NEXT: [[INC5:%.*]] = phi i32 [ [[BC_MERGE_RDX]], [[SCALAR_PH]] ], [ [[INC:%.*]], [[FOR_BODY8]] ] +@@ -118,7 +114,7 @@ define i32 @main() local_unnamed_addr #0 { + ; CHECK-NEXT: [[CMP6:%.*]] = icmp ult i32 [[TMP2]], [[CONV5]] + ; CHECK-NEXT: br i1 [[CMP6]], label [[FOR_BODY8]], label [[FOR_COND4_FOR_INC9_CRIT_EDGE]], !llvm.loop !2 + ; CHECK: for.cond4.for.inc9_crit_edge: +-; CHECK-NEXT: [[INC_LCSSA:%.*]] = phi i32 [ [[INC]], [[FOR_BODY8]] ], [ [[TMP37]], [[MIDDLE_BLOCK]] ] ++; CHECK-NEXT: [[INC_LCSSA:%.*]] = phi i32 [ [[INC]], [[FOR_BODY8]] ], [ [[TMP33]], [[MIDDLE_BLOCK]] ] + ; CHECK-NEXT: store i32 [[INC_LCSSA]], i32* getelementptr inbounds ([192 x [192 x i32]], [192 x [192 x i32]]* @a, i64 0, i64 0, i64 0), align 16 + ; CHECK-NEXT: br label [[FOR_INC9]] + ; CHECK: for.inc9: diff --git a/deps/patches/llvm-8.0-D50167-scev-umin.patch b/deps/patches/llvm-8.0-D50167-scev-umin.patch new file mode 100644 index 0000000..f11fd54 --- /dev/null +++ b/deps/patches/llvm-8.0-D50167-scev-umin.patch @@ -0,0 +1,1870 @@ +commit 18e563f695dd561c32393512fbdb8ce8771d7e5f +Author: Keno Fischer +Date: Thu May 2 08:35:22 2019 -0400 + + [SCEV] Add explicit representations of umin/smin + + Summary: + Currently we express umin as `~umax(~x, ~y)`. However, this becomes + a problem for operands in non-integral pointer spaces, because `~x` + is not something we can compute for `x` non-integral. However, since + comparisons are generally still allowed, we are actually able to + express `umin(x, y)` directly as long as we don't try to express is + as a umax. Support this by adding an explicit umin/smin representation + to SCEV. We do this by factoring the existing getUMax/getSMax functions + into a new function that does all four. The previous two functions were + largely identical. + + Reviewers: reames, sanjoy, mkazantsev + + Reviewed By: sanjoy + + Subscribers: tvvikram, dmgreen, vchuravy, javed.absar, llvm-commits + + Tags: #llvm + + Differential Revision: https://reviews.llvm.org/D50167 + +diff --git a/include/llvm/Analysis/ScalarEvolution.h b/include/llvm/Analysis/ScalarEvolution.h +index 8f4200b07e5..6b76a16a2b4 100644 +--- a/include/llvm/Analysis/ScalarEvolution.h ++++ b/include/llvm/Analysis/ScalarEvolution.h +@@ -582,6 +582,8 @@ public: + /// \p IndexExprs The expressions for the indices. + const SCEV *getGEPExpr(GEPOperator *GEP, + const SmallVectorImpl &IndexExprs); ++ const SCEV *getMinMaxExpr(unsigned Kind, ++ SmallVectorImpl &Operands); + const SCEV *getSMaxExpr(const SCEV *LHS, const SCEV *RHS); + const SCEV *getSMaxExpr(SmallVectorImpl &Operands); + const SCEV *getUMaxExpr(const SCEV *LHS, const SCEV *RHS); +diff --git a/include/llvm/Analysis/ScalarEvolutionExpander.h b/include/llvm/Analysis/ScalarEvolutionExpander.h +index 58d42680d6b..57d658b157d 100644 +--- a/include/llvm/Analysis/ScalarEvolutionExpander.h ++++ b/include/llvm/Analysis/ScalarEvolutionExpander.h +@@ -368,6 +368,10 @@ namespace llvm { + + Value *visitUMaxExpr(const SCEVUMaxExpr *S); + ++ Value *visitSMinExpr(const SCEVSMinExpr *S); ++ ++ Value *visitUMinExpr(const SCEVUMinExpr *S); ++ + Value *visitUnknown(const SCEVUnknown *S) { + return S->getValue(); + } +diff --git a/include/llvm/Analysis/ScalarEvolutionExpressions.h b/include/llvm/Analysis/ScalarEvolutionExpressions.h +index 42e76094eb2..99e39d484c5 100644 +--- a/include/llvm/Analysis/ScalarEvolutionExpressions.h ++++ b/include/llvm/Analysis/ScalarEvolutionExpressions.h +@@ -40,7 +40,7 @@ class Type; + // These should be ordered in terms of increasing complexity to make the + // folders simpler. + scConstant, scTruncate, scZeroExtend, scSignExtend, scAddExpr, scMulExpr, +- scUDivExpr, scAddRecExpr, scUMaxExpr, scSMaxExpr, ++ scUDivExpr, scAddRecExpr, scUMaxExpr, scSMaxExpr, scUMinExpr, scSMinExpr, + scUnknown, scCouldNotCompute + }; + +@@ -183,10 +183,9 @@ class Type; + + /// Methods for support type inquiry through isa, cast, and dyn_cast: + static bool classof(const SCEV *S) { +- return S->getSCEVType() == scAddExpr || +- S->getSCEVType() == scMulExpr || +- S->getSCEVType() == scSMaxExpr || +- S->getSCEVType() == scUMaxExpr || ++ return S->getSCEVType() == scAddExpr || S->getSCEVType() == scMulExpr || ++ S->getSCEVType() == scSMaxExpr || S->getSCEVType() == scUMaxExpr || ++ S->getSCEVType() == scSMinExpr || S->getSCEVType() == scUMinExpr || + S->getSCEVType() == scAddRecExpr; + } + }; +@@ -201,10 +200,9 @@ class Type; + public: + /// Methods for support type inquiry through isa, cast, and dyn_cast: + static bool classof(const SCEV *S) { +- return S->getSCEVType() == scAddExpr || +- S->getSCEVType() == scMulExpr || +- S->getSCEVType() == scSMaxExpr || +- S->getSCEVType() == scUMaxExpr; ++ return S->getSCEVType() == scAddExpr || S->getSCEVType() == scMulExpr || ++ S->getSCEVType() == scSMaxExpr || S->getSCEVType() == scUMaxExpr || ++ S->getSCEVType() == scSMinExpr || S->getSCEVType() == scUMinExpr; + } + + /// Set flags for a non-recurrence without clearing previously set flags. +@@ -358,17 +356,53 @@ class Type; + } + }; + +- /// This class represents a signed maximum selection. +- class SCEVSMaxExpr : public SCEVCommutativeExpr { ++ /// This node is the base class min/max selections. ++ class SCEVMinMaxExpr : public SCEVCommutativeExpr { + friend class ScalarEvolution; + +- SCEVSMaxExpr(const FoldingSetNodeIDRef ID, +- const SCEV *const *O, size_t N) +- : SCEVCommutativeExpr(ID, scSMaxExpr, O, N) { +- // Max never overflows. ++ static bool isMinMaxType(enum SCEVTypes T) { ++ return T == scSMaxExpr || T == scUMaxExpr || T == scSMinExpr || ++ T == scUMinExpr; ++ } ++ ++ protected: ++ /// Note: Constructing subclasses via this constructor is allowed ++ SCEVMinMaxExpr(const FoldingSetNodeIDRef ID, enum SCEVTypes T, ++ const SCEV *const *O, size_t N) ++ : SCEVCommutativeExpr(ID, T, O, N) { ++ assert(isMinMaxType(T)); ++ // Min and max nenver overflow + setNoWrapFlags((NoWrapFlags)(FlagNUW | FlagNSW)); + } + ++ public: ++ static bool classof(const SCEV *S) { ++ return isMinMaxType(static_cast(S->getSCEVType())); ++ } ++ ++ static enum SCEVTypes negate(enum SCEVTypes T) { ++ switch (T) { ++ case scSMaxExpr: ++ return scSMinExpr; ++ case scSMinExpr: ++ return scSMaxExpr; ++ case scUMaxExpr: ++ return scUMaxExpr; ++ case scUMinExpr: ++ return scUMinExpr; ++ default: ++ llvm_unreachable("Not a min or max SCEV type!"); ++ } ++ } ++ }; ++ ++ /// This class represents a signed maximum selection. ++ class SCEVSMaxExpr : public SCEVMinMaxExpr { ++ friend class ScalarEvolution; ++ ++ SCEVSMaxExpr(const FoldingSetNodeIDRef ID, const SCEV *const *O, size_t N) ++ : SCEVMinMaxExpr(ID, scSMaxExpr, O, N) {} ++ + public: + /// Methods for support type inquiry through isa, cast, and dyn_cast: + static bool classof(const SCEV *S) { +@@ -377,15 +411,11 @@ class Type; + }; + + /// This class represents an unsigned maximum selection. +- class SCEVUMaxExpr : public SCEVCommutativeExpr { ++ class SCEVUMaxExpr : public SCEVMinMaxExpr { + friend class ScalarEvolution; + +- SCEVUMaxExpr(const FoldingSetNodeIDRef ID, +- const SCEV *const *O, size_t N) +- : SCEVCommutativeExpr(ID, scUMaxExpr, O, N) { +- // Max never overflows. +- setNoWrapFlags((NoWrapFlags)(FlagNUW | FlagNSW)); +- } ++ SCEVUMaxExpr(const FoldingSetNodeIDRef ID, const SCEV *const *O, size_t N) ++ : SCEVMinMaxExpr(ID, scUMaxExpr, O, N) {} + + public: + /// Methods for support type inquiry through isa, cast, and dyn_cast: +@@ -394,6 +424,34 @@ class Type; + } + }; + ++ /// This class represents a signed minimum selection. ++ class SCEVSMinExpr : public SCEVMinMaxExpr { ++ friend class ScalarEvolution; ++ ++ SCEVSMinExpr(const FoldingSetNodeIDRef ID, const SCEV *const *O, size_t N) ++ : SCEVMinMaxExpr(ID, scSMinExpr, O, N) {} ++ ++ public: ++ /// Methods for support type inquiry through isa, cast, and dyn_cast: ++ static bool classof(const SCEV *S) { ++ return S->getSCEVType() == scSMinExpr; ++ } ++ }; ++ ++ /// This class represents an unsigned minimum selection. ++ class SCEVUMinExpr : public SCEVMinMaxExpr { ++ friend class ScalarEvolution; ++ ++ SCEVUMinExpr(const FoldingSetNodeIDRef ID, const SCEV *const *O, size_t N) ++ : SCEVMinMaxExpr(ID, scUMinExpr, O, N) {} ++ ++ public: ++ /// Methods for support type inquiry through isa, cast, and dyn_cast: ++ static bool classof(const SCEV *S) { ++ return S->getSCEVType() == scUMinExpr; ++ } ++ }; ++ + /// This means that we are dealing with an entirely unknown SCEV + /// value, and only represent it as its LLVM Value. This is the + /// "bottom" value for the analysis. +@@ -466,6 +524,10 @@ class Type; + return ((SC*)this)->visitSMaxExpr((const SCEVSMaxExpr*)S); + case scUMaxExpr: + return ((SC*)this)->visitUMaxExpr((const SCEVUMaxExpr*)S); ++ case scSMinExpr: ++ return ((SC *)this)->visitSMinExpr((const SCEVSMinExpr *)S); ++ case scUMinExpr: ++ return ((SC *)this)->visitUMinExpr((const SCEVUMinExpr *)S); + case scUnknown: + return ((SC*)this)->visitUnknown((const SCEVUnknown*)S); + case scCouldNotCompute: +@@ -519,6 +581,8 @@ class Type; + case scMulExpr: + case scSMaxExpr: + case scUMaxExpr: ++ case scSMinExpr: ++ case scUMinExpr: + case scAddRecExpr: + for (const auto *Op : cast(S)->operands()) + push(Op); +@@ -681,6 +745,26 @@ class Type; + return !Changed ? Expr : SE.getUMaxExpr(Operands); + } + ++ const SCEV *visitSMinExpr(const SCEVSMinExpr *Expr) { ++ SmallVector Operands; ++ bool Changed = false; ++ for (auto *Op : Expr->operands()) { ++ Operands.push_back(((SC *)this)->visit(Op)); ++ Changed |= Op != Operands.back(); ++ } ++ return !Changed ? Expr : SE.getSMinExpr(Operands); ++ } ++ ++ const SCEV *visitUMinExpr(const SCEVUMinExpr *Expr) { ++ SmallVector Operands; ++ bool Changed = false; ++ for (auto *Op : Expr->operands()) { ++ Operands.push_back(((SC *)this)->visit(Op)); ++ Changed |= Op != Operands.back(); ++ } ++ return !Changed ? Expr : SE.getUMinExpr(Operands); ++ } ++ + const SCEV *visitUnknown(const SCEVUnknown *Expr) { + return Expr; + } +diff --git a/lib/Analysis/ScalarEvolution.cpp b/lib/Analysis/ScalarEvolution.cpp +index e5134f2eeda..f2553de0af1 100644 +--- a/lib/Analysis/ScalarEvolution.cpp ++++ b/lib/Analysis/ScalarEvolution.cpp +@@ -273,7 +273,9 @@ void SCEV::print(raw_ostream &OS) const { + case scAddExpr: + case scMulExpr: + case scUMaxExpr: +- case scSMaxExpr: { ++ case scSMaxExpr: ++ case scUMinExpr: ++ case scSMinExpr: { + const SCEVNAryExpr *NAry = cast(this); + const char *OpStr = nullptr; + switch (NAry->getSCEVType()) { +@@ -281,6 +283,12 @@ void SCEV::print(raw_ostream &OS) const { + case scMulExpr: OpStr = " * "; break; + case scUMaxExpr: OpStr = " umax "; break; + case scSMaxExpr: OpStr = " smax "; break; ++ case scUMinExpr: ++ OpStr = " umin "; ++ break; ++ case scSMinExpr: ++ OpStr = " smin "; ++ break; + } + OS << "("; + for (SCEVNAryExpr::op_iterator I = NAry->op_begin(), E = NAry->op_end(); +@@ -349,6 +357,8 @@ Type *SCEV::getType() const { + case scMulExpr: + case scUMaxExpr: + case scSMaxExpr: ++ case scUMinExpr: ++ case scSMinExpr: + return cast(this)->getType(); + case scAddExpr: + return cast(this)->getType(); +@@ -713,7 +723,9 @@ static int CompareSCEVComplexity( + case scAddExpr: + case scMulExpr: + case scSMaxExpr: +- case scUMaxExpr: { ++ case scUMaxExpr: ++ case scSMinExpr: ++ case scUMinExpr: { + const SCEVNAryExpr *LC = cast(LHS); + const SCEVNAryExpr *RC = cast(RHS); + +@@ -913,6 +925,8 @@ public: + void visitUDivExpr(const SCEVUDivExpr *Numerator) {} + void visitSMaxExpr(const SCEVSMaxExpr *Numerator) {} + void visitUMaxExpr(const SCEVUMaxExpr *Numerator) {} ++ void visitSMinExpr(const SCEVSMinExpr *Numerator) {} ++ void visitUMinExpr(const SCEVUMinExpr *Numerator) {} + void visitUnknown(const SCEVUnknown *Numerator) {} + void visitCouldNotCompute(const SCEVCouldNotCompute *Numerator) {} + +@@ -3493,209 +3507,153 @@ ScalarEvolution::getGEPExpr(GEPOperator *GEP, + return getAddExpr(BaseExpr, TotalOffset, Wrap); + } + +-const SCEV *ScalarEvolution::getSMaxExpr(const SCEV *LHS, +- const SCEV *RHS) { +- SmallVector Ops = {LHS, RHS}; +- return getSMaxExpr(Ops); +-} +- +-const SCEV * +-ScalarEvolution::getSMaxExpr(SmallVectorImpl &Ops) { +- assert(!Ops.empty() && "Cannot get empty smax!"); ++const SCEV *ScalarEvolution::getMinMaxExpr(unsigned Kind, ++ SmallVectorImpl &Ops) { ++ assert(!Ops.empty() && "Cannot get empty (u|s)(min|max)!"); + if (Ops.size() == 1) return Ops[0]; + #ifndef NDEBUG + Type *ETy = getEffectiveSCEVType(Ops[0]->getType()); + for (unsigned i = 1, e = Ops.size(); i != e; ++i) + assert(getEffectiveSCEVType(Ops[i]->getType()) == ETy && +- "SCEVSMaxExpr operand types don't match!"); ++ "Operand types don't match!"); + #endif + ++ bool IsSigned = Kind == scSMaxExpr || Kind == scSMinExpr; ++ bool IsMax = Kind == scSMaxExpr || Kind == scUMaxExpr; ++ + // Sort by complexity, this groups all similar expression types together. + GroupByComplexity(Ops, &LI, DT); + ++ ++ ++ ++ ++ + // If there are any constants, fold them together. + unsigned Idx = 0; + if (const SCEVConstant *LHSC = dyn_cast(Ops[0])) { + ++Idx; + assert(Idx < Ops.size()); ++ auto FoldOp = [&](const APInt &LHS, const APInt &RHS) { ++ if (Kind == scSMaxExpr) ++ return APIntOps::smax(LHS, RHS); ++ else if (Kind == scSMinExpr) ++ return APIntOps::smin(LHS, RHS); ++ else if (Kind == scUMaxExpr) ++ return APIntOps::umax(LHS, RHS); ++ else if (Kind == scUMinExpr) ++ return APIntOps::umin(LHS, RHS); ++ llvm_unreachable("Unknown SCEV min/max opcode"); ++ }; ++ + while (const SCEVConstant *RHSC = dyn_cast(Ops[Idx])) { + // We found two constants, fold them together! + ConstantInt *Fold = ConstantInt::get( +- getContext(), APIntOps::smax(LHSC->getAPInt(), RHSC->getAPInt())); ++ getContext(), FoldOp(LHSC->getAPInt(), RHSC->getAPInt())); + Ops[0] = getConstant(Fold); + Ops.erase(Ops.begin()+1); // Erase the folded element + if (Ops.size() == 1) return Ops[0]; + LHSC = cast(Ops[0]); + } + +- // If we are left with a constant minimum-int, strip it off. +- if (cast(Ops[0])->getValue()->isMinValue(true)) { ++ bool IsMinV = LHSC->getValue()->isMinValue(IsSigned); ++ bool IsMaxV = LHSC->getValue()->isMaxValue(IsSigned); ++ ++ if (IsMax ? IsMinV : IsMaxV) { ++ // If we are left with a constant minimum(/maximum)-int, strip it off. + Ops.erase(Ops.begin()); + --Idx; +- } else if (cast(Ops[0])->getValue()->isMaxValue(true)) { +- // If we have an smax with a constant maximum-int, it will always be +- // maximum-int. +- return Ops[0]; ++ } else if (IsMax ? IsMaxV : IsMinV) { ++ // If we have a max(/min) with a constant maximum(/minimum)-int, ++ // it will always be the extremum. ++ return LHSC; + } + + if (Ops.size() == 1) return Ops[0]; + } + +- // Find the first SMax +- while (Idx < Ops.size() && Ops[Idx]->getSCEVType() < scSMaxExpr) ++ // Find the first operation of the same kind ++ while (Idx < Ops.size() && Ops[Idx]->getSCEVType() < Kind) + ++Idx; + +- // Check to see if one of the operands is an SMax. If so, expand its operands +- // onto our operand list, and recurse to simplify. ++ // Check to see if one of the operands is of the same kind. If so, expand its ++ // operands onto our operand list, and recurse to simplify. + if (Idx < Ops.size()) { +- bool DeletedSMax = false; +- while (const SCEVSMaxExpr *SMax = dyn_cast(Ops[Idx])) { ++ bool DeletedAny = false; ++ while (Ops[Idx]->getSCEVType() == Kind) { ++ const SCEVMinMaxExpr *SMME = cast(Ops[Idx]); + Ops.erase(Ops.begin()+Idx); +- Ops.append(SMax->op_begin(), SMax->op_end()); +- DeletedSMax = true; ++ Ops.append(SMME->op_begin(), SMME->op_end()); ++ DeletedAny = true; + } + +- if (DeletedSMax) +- return getSMaxExpr(Ops); ++ if (DeletedAny) ++ return getMinMaxExpr(Kind, Ops); + } + + // Okay, check to see if the same value occurs in the operand list twice. If + // so, delete one. Since we sorted the list, these values are required to + // be adjacent. +- for (unsigned i = 0, e = Ops.size()-1; i != e; ++i) +- // X smax Y smax Y --> X smax Y +- // X smax Y --> X, if X is always greater than Y +- if (Ops[i] == Ops[i+1] || +- isKnownPredicate(ICmpInst::ICMP_SGE, Ops[i], Ops[i+1])) { +- Ops.erase(Ops.begin()+i+1, Ops.begin()+i+2); +- --i; --e; +- } else if (isKnownPredicate(ICmpInst::ICMP_SLE, Ops[i], Ops[i+1])) { +- Ops.erase(Ops.begin()+i, Ops.begin()+i+1); +- --i; --e; ++ llvm::CmpInst::Predicate GEPred = ++ IsSigned ? ICmpInst::ICMP_SGE : ICmpInst::ICMP_UGE; ++ llvm::CmpInst::Predicate LEPred = ++ IsSigned ? ICmpInst::ICMP_SLE : ICmpInst::ICMP_ULE; ++ llvm::CmpInst::Predicate FirstPred = IsMax ? GEPred : LEPred; ++ llvm::CmpInst::Predicate SecondPred = IsMax ? LEPred : GEPred; ++ for (unsigned i = 0, e = Ops.size() - 1; i != e; ++i) { ++ if (Ops[i] == Ops[i + 1] || ++ isKnownViaNonRecursiveReasoning(FirstPred, Ops[i], Ops[i + 1])) { ++ // X op Y op Y --> X op Y ++ // X op Y --> X, if we know X, Y are ordered appropriately ++ Ops.erase(Ops.begin() + i + 1, Ops.begin() + i + 2); ++ --i; ++ --e; ++ } else if (isKnownViaNonRecursiveReasoning(SecondPred, Ops[i], ++ Ops[i + 1])) { ++ // X op Y --> Y, if we know X, Y are ordered appropriately ++ Ops.erase(Ops.begin() + i, Ops.begin() + i + 1); ++ --i; ++ --e; + } ++ } + + if (Ops.size() == 1) return Ops[0]; + + assert(!Ops.empty() && "Reduced smax down to nothing!"); + +- // Okay, it looks like we really DO need an smax expr. Check to see if we ++ // Okay, it looks like we really DO need an expr. Check to see if we + // already have one, otherwise create a new one. + FoldingSetNodeID ID; +- ID.AddInteger(scSMaxExpr); ++ ID.AddInteger(Kind); + for (unsigned i = 0, e = Ops.size(); i != e; ++i) + ID.AddPointer(Ops[i]); + void *IP = nullptr; + if (const SCEV *S = UniqueSCEVs.FindNodeOrInsertPos(ID, IP)) return S; + const SCEV **O = SCEVAllocator.Allocate(Ops.size()); + std::uninitialized_copy(Ops.begin(), Ops.end(), O); +- SCEV *S = new (SCEVAllocator) SCEVSMaxExpr(ID.Intern(SCEVAllocator), +- O, Ops.size()); ++ SCEV *S = new (SCEVAllocator) SCEVMinMaxExpr( ++ ID.Intern(SCEVAllocator), static_cast(Kind), O, Ops.size()); + UniqueSCEVs.InsertNode(S, IP); + addToLoopUseLists(S); + return S; + } + +-const SCEV *ScalarEvolution::getUMaxExpr(const SCEV *LHS, +- const SCEV *RHS) { ++const SCEV *ScalarEvolution::getSMaxExpr(const SCEV *LHS, const SCEV *RHS) { + SmallVector Ops = {LHS, RHS}; +- return getUMaxExpr(Ops); ++ return getSMaxExpr(Ops); + } + +-const SCEV * +-ScalarEvolution::getUMaxExpr(SmallVectorImpl &Ops) { +- assert(!Ops.empty() && "Cannot get empty umax!"); +- if (Ops.size() == 1) return Ops[0]; +-#ifndef NDEBUG +- Type *ETy = getEffectiveSCEVType(Ops[0]->getType()); +- for (unsigned i = 1, e = Ops.size(); i != e; ++i) +- assert(getEffectiveSCEVType(Ops[i]->getType()) == ETy && +- "SCEVUMaxExpr operand types don't match!"); +-#endif +- +- // Sort by complexity, this groups all similar expression types together. +- GroupByComplexity(Ops, &LI, DT); +- +- // If there are any constants, fold them together. +- unsigned Idx = 0; +- if (const SCEVConstant *LHSC = dyn_cast(Ops[0])) { +- ++Idx; +- assert(Idx < Ops.size()); +- while (const SCEVConstant *RHSC = dyn_cast(Ops[Idx])) { +- // We found two constants, fold them together! +- ConstantInt *Fold = ConstantInt::get( +- getContext(), APIntOps::umax(LHSC->getAPInt(), RHSC->getAPInt())); +- Ops[0] = getConstant(Fold); +- Ops.erase(Ops.begin()+1); // Erase the folded element +- if (Ops.size() == 1) return Ops[0]; +- LHSC = cast(Ops[0]); +- } +- +- // If we are left with a constant minimum-int, strip it off. +- if (cast(Ops[0])->getValue()->isMinValue(false)) { +- Ops.erase(Ops.begin()); +- --Idx; +- } else if (cast(Ops[0])->getValue()->isMaxValue(false)) { +- // If we have an umax with a constant maximum-int, it will always be +- // maximum-int. +- return Ops[0]; +- } +- +- if (Ops.size() == 1) return Ops[0]; +- } +- +- // Find the first UMax +- while (Idx < Ops.size() && Ops[Idx]->getSCEVType() < scUMaxExpr) +- ++Idx; +- +- // Check to see if one of the operands is a UMax. If so, expand its operands +- // onto our operand list, and recurse to simplify. +- if (Idx < Ops.size()) { +- bool DeletedUMax = false; +- while (const SCEVUMaxExpr *UMax = dyn_cast(Ops[Idx])) { +- Ops.erase(Ops.begin()+Idx); +- Ops.append(UMax->op_begin(), UMax->op_end()); +- DeletedUMax = true; +- } +- +- if (DeletedUMax) +- return getUMaxExpr(Ops); +- } +- +- // Okay, check to see if the same value occurs in the operand list twice. If +- // so, delete one. Since we sorted the list, these values are required to +- // be adjacent. +- for (unsigned i = 0, e = Ops.size()-1; i != e; ++i) +- // X umax Y umax Y --> X umax Y +- // X umax Y --> X, if X is always greater than Y +- if (Ops[i] == Ops[i + 1] || isKnownViaNonRecursiveReasoning( +- ICmpInst::ICMP_UGE, Ops[i], Ops[i + 1])) { +- Ops.erase(Ops.begin() + i + 1, Ops.begin() + i + 2); +- --i; --e; +- } else if (isKnownViaNonRecursiveReasoning(ICmpInst::ICMP_ULE, Ops[i], +- Ops[i + 1])) { +- Ops.erase(Ops.begin() + i, Ops.begin() + i + 1); +- --i; --e; +- } +- +- if (Ops.size() == 1) return Ops[0]; ++const SCEV *ScalarEvolution::getSMaxExpr(SmallVectorImpl &Ops) { ++ return getMinMaxExpr(scSMaxExpr, Ops); ++} + +- assert(!Ops.empty() && "Reduced umax down to nothing!"); ++const SCEV *ScalarEvolution::getUMaxExpr(const SCEV *LHS, const SCEV *RHS) { ++ SmallVector Ops = {LHS, RHS}; ++ return getUMaxExpr(Ops); ++} + +- // Okay, it looks like we really DO need a umax expr. Check to see if we +- // already have one, otherwise create a new one. +- FoldingSetNodeID ID; +- ID.AddInteger(scUMaxExpr); +- for (unsigned i = 0, e = Ops.size(); i != e; ++i) +- ID.AddPointer(Ops[i]); +- void *IP = nullptr; +- if (const SCEV *S = UniqueSCEVs.FindNodeOrInsertPos(ID, IP)) return S; +- const SCEV **O = SCEVAllocator.Allocate(Ops.size()); +- std::uninitialized_copy(Ops.begin(), Ops.end(), O); +- SCEV *S = new (SCEVAllocator) SCEVUMaxExpr(ID.Intern(SCEVAllocator), +- O, Ops.size()); +- UniqueSCEVs.InsertNode(S, IP); +- addToLoopUseLists(S); +- return S; ++const SCEV *ScalarEvolution::getUMaxExpr(SmallVectorImpl &Ops) { ++ return getMinMaxExpr(scUMaxExpr, Ops); + } + + const SCEV *ScalarEvolution::getSMinExpr(const SCEV *LHS, +@@ -3705,11 +3663,7 @@ const SCEV *ScalarEvolution::getSMinExpr(const SCEV *LHS, + } + + const SCEV *ScalarEvolution::getSMinExpr(SmallVectorImpl &Ops) { +- // ~smax(~x, ~y, ~z) == smin(x, y, z). +- SmallVector NotOps; +- for (auto *S : Ops) +- NotOps.push_back(getNotSCEV(S)); +- return getNotSCEV(getSMaxExpr(NotOps)); ++ return getMinMaxExpr(scSMinExpr, Ops); + } + + const SCEV *ScalarEvolution::getUMinExpr(const SCEV *LHS, +@@ -3719,16 +3673,7 @@ const SCEV *ScalarEvolution::getUMinExpr(const SCEV *LHS, + } + + const SCEV *ScalarEvolution::getUMinExpr(SmallVectorImpl &Ops) { +- assert(!Ops.empty() && "At least one operand must be!"); +- // Trivial case. +- if (Ops.size() == 1) +- return Ops[0]; +- +- // ~umax(~x, ~y, ~z) == umin(x, y, z). +- SmallVector NotOps; +- for (auto *S : Ops) +- NotOps.push_back(getNotSCEV(S)); +- return getNotSCEV(getUMaxExpr(NotOps)); ++ return getMinMaxExpr(scUMinExpr, Ops); + } + + const SCEV *ScalarEvolution::getSizeOfExpr(Type *IntTy, Type *AllocTy) { +@@ -3970,12 +3915,45 @@ const SCEV *ScalarEvolution::getNegativeSCEV(const SCEV *V, + V, getConstant(cast(Constant::getAllOnesValue(Ty))), Flags); + } + ++/// If Expr computes ~A, return A else return nullptr ++static const SCEV *MatchNotExpr(const SCEV *Expr) { ++ const SCEVAddExpr *Add = dyn_cast(Expr); ++ if (!Add || Add->getNumOperands() != 2 || ++ !Add->getOperand(0)->isAllOnesValue()) ++ return nullptr; ++ ++ const SCEVMulExpr *AddRHS = dyn_cast(Add->getOperand(1)); ++ if (!AddRHS || AddRHS->getNumOperands() != 2 || ++ !AddRHS->getOperand(0)->isAllOnesValue()) ++ return nullptr; ++ ++ return AddRHS->getOperand(1); ++} ++ + /// Return a SCEV corresponding to ~V = -1-V + const SCEV *ScalarEvolution::getNotSCEV(const SCEV *V) { + if (const SCEVConstant *VC = dyn_cast(V)) + return getConstant( + cast(ConstantExpr::getNot(VC->getValue()))); + ++ // Fold ~(u|s)(min|max)(~x, ~y) to (u|s)(max|min)(x, y) ++ if (const SCEVMinMaxExpr *MME = dyn_cast(V)) { ++ auto MatchMinMaxNegation = [&](const SCEVMinMaxExpr *MME) { ++ SmallVector MatchedOperands; ++ for (const SCEV *Operand : MME->operands()) { ++ const SCEV *Matched = MatchNotExpr(Operand); ++ if (!Matched) ++ return (const SCEV *)nullptr; ++ MatchedOperands.push_back(Matched); ++ } ++ return getMinMaxExpr( ++ SCEVMinMaxExpr::negate(static_cast(MME->getSCEVType())), ++ MatchedOperands); ++ }; ++ if (const SCEV *Replaced = MatchMinMaxNegation(MME)) ++ return Replaced; ++ } ++ + Type *Ty = V->getType(); + Ty = getEffectiveSCEVType(Ty); + const SCEV *AllOnes = +@@ -5196,6 +5174,8 @@ static bool IsAvailableOnEntry(const Loop *L, DominatorTree &DT, const SCEV *S, + switch (S->getSCEVType()) { + case scConstant: case scTruncate: case scZeroExtend: case scSignExtend: + case scAddExpr: case scMulExpr: case scUMaxExpr: case scSMaxExpr: ++ case scUMinExpr: ++ case scSMinExpr: + // These expressions are available if their operand(s) is/are. + return true; + +@@ -8075,7 +8055,9 @@ static Constant *BuildConstantFromSCEV(const SCEV *V) { + } + case scSMaxExpr: + case scUMaxExpr: +- break; // TODO: smax, umax. ++ case scSMinExpr: ++ case scUMinExpr: ++ break; // TODO: smax, umax, smin, umax. + } + return nullptr; + } +@@ -8201,10 +8183,8 @@ const SCEV *ScalarEvolution::computeSCEVAtScope(const SCEV *V, const Loop *L) { + return getAddExpr(NewOps); + if (isa(Comm)) + return getMulExpr(NewOps); +- if (isa(Comm)) +- return getSMaxExpr(NewOps); +- if (isa(Comm)) +- return getUMaxExpr(NewOps); ++ if (isa(Comm)) ++ return getMinMaxExpr(Comm->getSCEVType(), NewOps); + llvm_unreachable("Unknown commutative SCEV type!"); + } + } +@@ -10045,41 +10025,15 @@ bool ScalarEvolution::isImpliedCondOperands(ICmpInst::Predicate Pred, + getNotSCEV(FoundLHS)); + } + +-/// If Expr computes ~A, return A else return nullptr +-static const SCEV *MatchNotExpr(const SCEV *Expr) { +- const SCEVAddExpr *Add = dyn_cast(Expr); +- if (!Add || Add->getNumOperands() != 2 || +- !Add->getOperand(0)->isAllOnesValue()) +- return nullptr; +- +- const SCEVMulExpr *AddRHS = dyn_cast(Add->getOperand(1)); +- if (!AddRHS || AddRHS->getNumOperands() != 2 || +- !AddRHS->getOperand(0)->isAllOnesValue()) +- return nullptr; +- +- return AddRHS->getOperand(1); +-} +- +-/// Is MaybeMaxExpr an SMax or UMax of Candidate and some other values? +-template +-static bool IsMaxConsistingOf(const SCEV *MaybeMaxExpr, +- const SCEV *Candidate) { +- const MaxExprType *MaxExpr = dyn_cast(MaybeMaxExpr); +- if (!MaxExpr) return false; +- +- return find(MaxExpr->operands(), Candidate) != MaxExpr->op_end(); +-} +- +-/// Is MaybeMinExpr an SMin or UMin of Candidate and some other values? +-template +-static bool IsMinConsistingOf(ScalarEvolution &SE, +- const SCEV *MaybeMinExpr, +- const SCEV *Candidate) { +- const SCEV *MaybeMaxExpr = MatchNotExpr(MaybeMinExpr); +- if (!MaybeMaxExpr) ++/// Is MaybeMinMaxExpr an (U|S)(Min|Max) of Candidate and some other values? ++template ++static bool IsMinMaxConsistingOf(const SCEV *MaybeMinMaxExpr, ++ const SCEV *Candidate) { ++ const MinMaxExprType *MinMaxExpr = dyn_cast(MaybeMinMaxExpr); ++ if (!MinMaxExpr) + return false; + +- return IsMaxConsistingOf(MaybeMaxExpr, SE.getNotSCEV(Candidate)); ++ return find(MinMaxExpr->operands(), Candidate) != MinMaxExpr->op_end(); + } + + static bool IsKnownPredicateViaAddRecStart(ScalarEvolution &SE, +@@ -10128,20 +10082,20 @@ static bool IsKnownPredicateViaMinOrMax(ScalarEvolution &SE, + LLVM_FALLTHROUGH; + case ICmpInst::ICMP_SLE: + return +- // min(A, ...) <= A +- IsMinConsistingOf(SE, LHS, RHS) || +- // A <= max(A, ...) +- IsMaxConsistingOf(RHS, LHS); ++ // min(A, ...) <= A ++ IsMinMaxConsistingOf(LHS, RHS) || ++ // A <= max(A, ...) ++ IsMinMaxConsistingOf(RHS, LHS); + + case ICmpInst::ICMP_UGE: + std::swap(LHS, RHS); + LLVM_FALLTHROUGH; + case ICmpInst::ICMP_ULE: + return +- // min(A, ...) <= A +- IsMinConsistingOf(SE, LHS, RHS) || +- // A <= max(A, ...) +- IsMaxConsistingOf(RHS, LHS); ++ // min(A, ...) <= A ++ IsMinMaxConsistingOf(LHS, RHS) || ++ // A <= max(A, ...) ++ IsMinMaxConsistingOf(RHS, LHS); + } + + llvm_unreachable("covered switch fell through?!"); +@@ -11611,7 +11565,9 @@ ScalarEvolution::computeLoopDisposition(const SCEV *S, const Loop *L) { + case scAddExpr: + case scMulExpr: + case scUMaxExpr: +- case scSMaxExpr: { ++ case scSMaxExpr: ++ case scUMinExpr: ++ case scSMinExpr: { + bool HasVarying = false; + for (auto *Op : cast(S)->operands()) { + LoopDisposition D = getLoopDisposition(Op, L); +@@ -11698,7 +11654,9 @@ ScalarEvolution::computeBlockDisposition(const SCEV *S, const BasicBlock *BB) { + case scAddExpr: + case scMulExpr: + case scUMaxExpr: +- case scSMaxExpr: { ++ case scSMaxExpr: ++ case scUMinExpr: ++ case scSMinExpr: { + const SCEVNAryExpr *NAry = cast(S); + bool Proper = true; + for (const SCEV *NAryOp : NAry->operands()) { +diff --git a/lib/Analysis/ScalarEvolutionExpander.cpp b/lib/Analysis/ScalarEvolutionExpander.cpp +index ca5cf1663b8..b56ec40ab75 100644 +--- a/lib/Analysis/ScalarEvolutionExpander.cpp ++++ b/lib/Analysis/ScalarEvolutionExpander.cpp +@@ -1634,7 +1634,8 @@ Value *SCEVExpander::visitSMaxExpr(const SCEVSMaxExpr *S) { + for (int i = S->getNumOperands()-2; i >= 0; --i) { + // In the case of mixed integer and pointer types, do the + // rest of the comparisons as integer. +- if (S->getOperand(i)->getType() != Ty) { ++ Type *OpTy = S->getOperand(i)->getType(); ++ if (OpTy->isIntegerTy() != Ty->isIntegerTy()) { + Ty = SE.getEffectiveSCEVType(Ty); + LHS = InsertNoopCastOfTo(LHS, Ty); + } +@@ -1658,7 +1659,8 @@ Value *SCEVExpander::visitUMaxExpr(const SCEVUMaxExpr *S) { + for (int i = S->getNumOperands()-2; i >= 0; --i) { + // In the case of mixed integer and pointer types, do the + // rest of the comparisons as integer. +- if (S->getOperand(i)->getType() != Ty) { ++ Type *OpTy = S->getOperand(i)->getType(); ++ if (OpTy->isIntegerTy() != Ty->isIntegerTy()) { + Ty = SE.getEffectiveSCEVType(Ty); + LHS = InsertNoopCastOfTo(LHS, Ty); + } +@@ -1676,6 +1678,56 @@ Value *SCEVExpander::visitUMaxExpr(const SCEVUMaxExpr *S) { + return LHS; + } + ++Value *SCEVExpander::visitSMinExpr(const SCEVSMinExpr *S) { ++ Value *LHS = expand(S->getOperand(S->getNumOperands() - 1)); ++ Type *Ty = LHS->getType(); ++ for (int i = S->getNumOperands() - 2; i >= 0; --i) { ++ // In the case of mixed integer and pointer types, do the ++ // rest of the comparisons as integer. ++ Type *OpTy = S->getOperand(i)->getType(); ++ if (OpTy->isIntegerTy() != Ty->isIntegerTy()) { ++ Ty = SE.getEffectiveSCEVType(Ty); ++ LHS = InsertNoopCastOfTo(LHS, Ty); ++ } ++ Value *RHS = expandCodeFor(S->getOperand(i), Ty); ++ Value *ICmp = Builder.CreateICmpSLT(LHS, RHS); ++ rememberInstruction(ICmp); ++ Value *Sel = Builder.CreateSelect(ICmp, LHS, RHS, "smin"); ++ rememberInstruction(Sel); ++ LHS = Sel; ++ } ++ // In the case of mixed integer and pointer types, cast the ++ // final result back to the pointer type. ++ if (LHS->getType() != S->getType()) ++ LHS = InsertNoopCastOfTo(LHS, S->getType()); ++ return LHS; ++} ++ ++Value *SCEVExpander::visitUMinExpr(const SCEVUMinExpr *S) { ++ Value *LHS = expand(S->getOperand(S->getNumOperands() - 1)); ++ Type *Ty = LHS->getType(); ++ for (int i = S->getNumOperands() - 2; i >= 0; --i) { ++ // In the case of mixed integer and pointer types, do the ++ // rest of the comparisons as integer. ++ Type *OpTy = S->getOperand(i)->getType(); ++ if (OpTy->isIntegerTy() != Ty->isIntegerTy()) { ++ Ty = SE.getEffectiveSCEVType(Ty); ++ LHS = InsertNoopCastOfTo(LHS, Ty); ++ } ++ Value *RHS = expandCodeFor(S->getOperand(i), Ty); ++ Value *ICmp = Builder.CreateICmpULT(LHS, RHS); ++ rememberInstruction(ICmp); ++ Value *Sel = Builder.CreateSelect(ICmp, LHS, RHS, "umin"); ++ rememberInstruction(Sel); ++ LHS = Sel; ++ } ++ // In the case of mixed integer and pointer types, cast the ++ // final result back to the pointer type. ++ if (LHS->getType() != S->getType()) ++ LHS = InsertNoopCastOfTo(LHS, S->getType()); ++ return LHS; ++} ++ + Value *SCEVExpander::expandCodeFor(const SCEV *SH, Type *Ty, + Instruction *IP) { + setInsertPoint(IP); +@@ -2102,7 +2154,7 @@ bool SCEVExpander::isHighCostExpansionHelper( + + // HowManyLessThans uses a Max expression whenever the loop is not guarded by + // the exit condition. +- if (isa(S) || isa(S)) ++ if (isa(S)) + return true; + + // Recurse past nary expressions, which commonly occur in the +diff --git a/test/Analysis/LoopAccessAnalysis/memcheck-ni.ll b/test/Analysis/LoopAccessAnalysis/memcheck-ni.ll +new file mode 100644 +index 00000000000..a08632f38d1 +--- /dev/null ++++ b/test/Analysis/LoopAccessAnalysis/memcheck-ni.ll +@@ -0,0 +1,50 @@ ++; RUN: opt -loop-versioning -S < %s | FileCheck %s ++ ++; NB: addrspaces 10-13 are non-integral ++target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128-ni:10:11:12:13" ++ ++%jl_value_t = type opaque ++%jl_array_t = type { i8 addrspace(13)*, i64, i16, i16, i32 } ++ ++define void @"japi1_permutedims!_33509"(%jl_value_t addrspace(10)**) { ++; CHECK: [[CMP:%[^ ]*]] = icmp ult double addrspace(13)* [[A:%[^ ]*]], [[B:%[^ ]*]] ++; CHECK: [[SELECT:%[^ ]*]] = select i1 %18, double addrspace(13)* [[A]], double addrspace(13)* [[B]] ++top: ++ %1 = alloca [3 x i64], align 8 ++ %2 = load %jl_value_t addrspace(10)*, %jl_value_t addrspace(10)** %0, align 8 ++ %3 = getelementptr inbounds %jl_value_t addrspace(10)*, %jl_value_t addrspace(10)** %0, i64 1 ++ %4 = load %jl_value_t addrspace(10)*, %jl_value_t addrspace(10)** %3, align 8 ++ %5 = getelementptr inbounds [3 x i64], [3 x i64]* %1, i64 0, i64 0 ++ store i64 1, i64* %5, align 8 ++ %6 = getelementptr inbounds [3 x i64], [3 x i64]* %1, i64 0, i64 1 ++ %7 = load i64, i64* inttoptr (i64 24 to i64*), align 8 ++ %8 = addrspacecast %jl_value_t addrspace(10)* %4 to %jl_value_t addrspace(11)* ++ %9 = bitcast %jl_value_t addrspace(11)* %8 to double addrspace(13)* addrspace(11)* ++ %10 = load double addrspace(13)*, double addrspace(13)* addrspace(11)* %9, align 8 ++ %11 = addrspacecast %jl_value_t addrspace(10)* %2 to %jl_value_t addrspace(11)* ++ %12 = bitcast %jl_value_t addrspace(11)* %11 to double addrspace(13)* addrspace(11)* ++ %13 = load double addrspace(13)*, double addrspace(13)* addrspace(11)* %12, align 8 ++ %14 = load i64, i64* %6, align 8 ++ br label %L74 ++ ++L74: ++ %value_phi20 = phi i64 [ 1, %top ], [ %22, %L74 ] ++ %value_phi21 = phi i64 [ 1, %top ], [ %23, %L74 ] ++ %value_phi22 = phi i64 [ 1, %top ], [ %25, %L74 ] ++ %15 = add i64 %value_phi21, -1 ++ %16 = getelementptr inbounds double, double addrspace(13)* %10, i64 %15 ++ %17 = bitcast double addrspace(13)* %16 to i64 addrspace(13)* ++ %18 = load i64, i64 addrspace(13)* %17, align 8 ++ %19 = add i64 %value_phi20, -1 ++ %20 = getelementptr inbounds double, double addrspace(13)* %13, i64 %19 ++ %21 = bitcast double addrspace(13)* %20 to i64 addrspace(13)* ++ store i64 %18, i64 addrspace(13)* %21, align 8 ++ %22 = add i64 %value_phi20, 1 ++ %23 = add i64 %14, %value_phi21 ++ %24 = icmp eq i64 %value_phi22, %7 ++ %25 = add i64 %value_phi22, 1 ++ br i1 %24, label %L94, label %L74 ++ ++L94: ++ ret void ++} +diff --git a/test/Analysis/LoopAccessAnalysis/reverse-memcheck-bounds.ll b/test/Analysis/LoopAccessAnalysis/reverse-memcheck-bounds.ll +index 405a47554e4..4285ef0f117 100644 +--- a/test/Analysis/LoopAccessAnalysis/reverse-memcheck-bounds.ll ++++ b/test/Analysis/LoopAccessAnalysis/reverse-memcheck-bounds.ll +@@ -58,7 +58,7 @@ for.end: ; preds = %for.body + + ; Here it is not obvious what the limits are, since 'step' could be negative. + +-; CHECK: Low: (-1 + (-1 * ((-60001 + (-1 * %a)) umax (-60001 + (40000 * %step) + (-1 * %a))))) ++; CHECK: Low: ((60000 + %a) umin (60000 + (-40000 * %step) + %a)) + ; CHECK: High: (4 + ((60000 + %a) umax (60000 + (-40000 * %step) + %a))) + + define void @g(i64 %step) { +diff --git a/test/Analysis/ScalarEvolution/2008-07-29-SMinExpr.ll b/test/Analysis/ScalarEvolution/2008-07-29-SMinExpr.ll +index 3542ad2a41e..d930706d7d2 100644 +--- a/test/Analysis/ScalarEvolution/2008-07-29-SMinExpr.ll ++++ b/test/Analysis/ScalarEvolution/2008-07-29-SMinExpr.ll +@@ -22,5 +22,5 @@ afterfor: ; preds = %forinc, %entry + ret i32 %j.0.lcssa + } + +-; CHECK: backedge-taken count is (-2147483632 + ((-1 + (-1 * %{{[xy]}})) smax (-1 + (-1 * %{{[xy]}})))) ++; CHECK: backedge-taken count is (-2147483633 + (-1 * (%{{[xy]}} smin %{{[xy]}}))) + +diff --git a/test/Analysis/ScalarEvolution/min-max-exprs.ll b/test/Analysis/ScalarEvolution/min-max-exprs.ll +index e8c1e33e095..51f72c643cc 100644 +--- a/test/Analysis/ScalarEvolution/min-max-exprs.ll ++++ b/test/Analysis/ScalarEvolution/min-max-exprs.ll +@@ -33,7 +33,7 @@ bb2: ; preds = %bb1 + %tmp9 = select i1 %tmp4, i64 %tmp5, i64 %tmp6 + ; min(N, i+3) + ; CHECK: select i1 %tmp4, i64 %tmp5, i64 %tmp6 +-; CHECK-NEXT: --> (-1 + (-1 * ((-1 + (-1 * (sext i32 {3,+,1}<%bb1> to i64))) smax (-1 + (-1 * (sext i32 %N to i64)))))) ++; CHECK-NEXT: --> ((sext i32 {3,+,1}<%bb1> to i64) smin (sext i32 %N to i64)) + %tmp11 = getelementptr inbounds i32, i32* %A, i64 %tmp9 + %tmp12 = load i32, i32* %tmp11, align 4 + %tmp13 = shl nsw i32 %tmp12, 1 +diff --git a/test/Analysis/ScalarEvolution/predicated-trip-count.ll b/test/Analysis/ScalarEvolution/predicated-trip-count.ll +index a0afcf457d2..b07662ed95f 100644 +--- a/test/Analysis/ScalarEvolution/predicated-trip-count.ll ++++ b/test/Analysis/ScalarEvolution/predicated-trip-count.ll +@@ -80,7 +80,7 @@ return: ; preds = %bb5 + ; CHECK-NEXT: --> (sext i16 {%Start,+,-1}<%bb3> to i32) + ; CHECK: Loop %bb3: Unpredictable backedge-taken count. + ; CHECK-NEXT: Loop %bb3: Unpredictable max backedge-taken count. +-; CHECK-NEXT: Loop %bb3: Predicated backedge-taken count is (2 + (sext i16 %Start to i32) + ((-2 + (-1 * (sext i16 %Start to i32))) smax (-1 + (-1 * %M)))) ++; CHECK-NEXT: Loop %bb3: Predicated backedge-taken count is (1 + (sext i16 %Start to i32) + (-1 * ((1 + (sext i16 %Start to i32)) smin %M))) + ; CHECK-NEXT: Predicates: + ; CHECK-NEXT: {%Start,+,-1}<%bb3> Added Flags: + +diff --git a/test/Analysis/ScalarEvolution/trip-count14.ll b/test/Analysis/ScalarEvolution/trip-count14.ll +index 5e6cfe85101..15080613881 100644 +--- a/test/Analysis/ScalarEvolution/trip-count14.ll ++++ b/test/Analysis/ScalarEvolution/trip-count14.ll +@@ -81,7 +81,7 @@ if.end: + br i1 %cmp1, label %do.body, label %do.end ; taken either 0 or 2 times + + ; CHECK-LABEL: Determining loop execution counts for: @s32_max2_unpredictable_exit +-; CHECK-NEXT: Loop %do.body: backedge-taken count is (-1 + (-1 * ((-1 + (-1 * ((2 + %n) smax %n)) + %n) umax (-1 + (-1 * %x) + %n)))) ++; CHECK-NEXT: Loop %do.body: backedge-taken count is (((-1 * %n) + ((2 + %n) smax %n)) umin ((-1 * %n) + %x)) + ; CHECK-NEXT: Loop %do.body: max backedge-taken count is 2{{$}} + + do.end: +@@ -169,7 +169,7 @@ if.end: + br i1 %cmp1, label %do.body, label %do.end ; taken either 0 or 2 times + + ; CHECK-LABEL: Determining loop execution counts for: @u32_max2_unpredictable_exit +-; CHECK-NEXT: Loop %do.body: backedge-taken count is (-1 + (-1 * ((-1 + (-1 * ((2 + %n) umax %n)) + %n) umax (-1 + (-1 * %x) + %n)))) ++; CHECK-NEXT: Loop %do.body: backedge-taken count is (((-1 * %n) + ((2 + %n) umax %n)) umin ((-1 * %n) + %x)) + ; CHECK-NEXT: Loop %do.body: max backedge-taken count is 2{{$}} + + do.end: +diff --git a/test/Analysis/ScalarEvolution/trip-count3.ll b/test/Analysis/ScalarEvolution/trip-count3.ll +index df6637a4ced..e10012c0c32 100644 +--- a/test/Analysis/ScalarEvolution/trip-count3.ll ++++ b/test/Analysis/ScalarEvolution/trip-count3.ll +@@ -4,7 +4,7 @@ + ; dividing by the stride will have a remainder. This could theoretically + ; be teaching it how to use a more elaborate trip count computation. + +-; CHECK: Loop %bb3.i: backedge-taken count is ((64 + (-64 smax (-1 + (-1 * %0))) + %0) /u 64) ++; CHECK: Loop %bb3.i: backedge-taken count is ((63 + (-1 * (63 smin %0)) + %0) /u 64) + ; CHECK: Loop %bb3.i: max backedge-taken count is 33554431 + + %struct.FILE = type { i32, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, %struct._IO_marker*, %struct.FILE*, i32, i32, i64, i16, i8, [1 x i8], i8*, i64, i8*, i8*, i8*, i8*, i64, i32, [20 x i8] } +diff --git a/test/Transforms/IRCE/conjunctive-checks.ll b/test/Transforms/IRCE/conjunctive-checks.ll +index 60a0af83174..8711c1b00e8 100644 +--- a/test/Transforms/IRCE/conjunctive-checks.ll ++++ b/test/Transforms/IRCE/conjunctive-checks.ll +@@ -5,17 +5,15 @@ define void @f_0(i32 *%arr, i32 *%a_len_ptr, i32 %n, i1* %cond_buf) { + ; CHECK-LABEL: @f_0( + + ; CHECK: loop.preheader: +-; CHECK: [[not_n:[^ ]+]] = sub i32 -1, %n +-; CHECK: [[not_safe_range_end:[^ ]+]] = sub i32 3, %len +-; CHECK: [[not_exit_main_loop_at_hiclamp_cmp:[^ ]+]] = icmp sgt i32 [[not_n]], [[not_safe_range_end]] +-; CHECK: [[not_exit_main_loop_at_hiclamp:[^ ]+]] = select i1 [[not_exit_main_loop_at_hiclamp_cmp]], i32 [[not_n]], i32 [[not_safe_range_end]] +-; CHECK: [[exit_main_loop_at_hiclamp:[^ ]+]] = sub i32 -1, [[not_exit_main_loop_at_hiclamp]] ++; CHECK: [[len_sub:[^ ]+]] = add i32 %len, -4 ++; CHECK: [[exit_main_loop_at_hiclamp_cmp:[^ ]+]] = icmp slt i32 %n, [[len_sub]] ++; CHECK: [[exit_main_loop_at_hiclamp:[^ ]+]] = select i1 [[exit_main_loop_at_hiclamp_cmp]], i32 %n, i32 [[len_sub]] + ; CHECK: [[exit_main_loop_at_loclamp_cmp:[^ ]+]] = icmp sgt i32 [[exit_main_loop_at_hiclamp]], 0 + ; CHECK: [[exit_main_loop_at_loclamp:[^ ]+]] = select i1 [[exit_main_loop_at_loclamp_cmp]], i32 [[exit_main_loop_at_hiclamp]], i32 0 + ; CHECK: [[enter_main_loop:[^ ]+]] = icmp slt i32 0, [[exit_main_loop_at_loclamp]] +-; CHECK: br i1 [[enter_main_loop]], label %loop.preheader2, label %main.pseudo.exit ++; CHECK: br i1 [[enter_main_loop]], label %[[loop_preheader2:[^ ,]+]], label %main.pseudo.exit + +-; CHECK: loop.preheader2: ++; CHECK: [[loop_preheader2]]: + ; CHECK: br label %loop + + entry: +@@ -35,9 +33,9 @@ define void @f_0(i32 *%arr, i32 *%a_len_ptr, i32 %n, i1* %cond_buf) { + ; CHECK: loop: + ; CHECK: %cond = load volatile i1, i1* %cond_buf + ; CHECK: %abc = and i1 %cond, true +-; CHECK: br i1 %abc, label %in.bounds, label %out.of.bounds.loopexit3, !prof !1 ++; CHECK: br i1 %abc, label %in.bounds, label %[[loop_exit:[^ ,]+]], !prof !1 + +-; CHECK: out.of.bounds.loopexit: ++; CHECK: [[loop_exit]]: + ; CHECK: br label %out.of.bounds + + in.bounds: +@@ -58,14 +56,10 @@ define void @f_1( + ; CHECK-LABEL: @f_1( + + ; CHECK: loop.preheader: +-; CHECK: [[not_len_b:[^ ]+]] = sub i32 -1, %len.b +-; CHECK: [[not_len_a:[^ ]+]] = sub i32 -1, %len.a +-; CHECK: [[smax_not_len_cond:[^ ]+]] = icmp sgt i32 [[not_len_b]], [[not_len_a]] +-; CHECK: [[smax_not_len:[^ ]+]] = select i1 [[smax_not_len_cond]], i32 [[not_len_b]], i32 [[not_len_a]] +-; CHECK: [[not_n:[^ ]+]] = sub i32 -1, %n +-; CHECK: [[not_upper_limit_cond_loclamp:[^ ]+]] = icmp sgt i32 [[smax_not_len]], [[not_n]] +-; CHECK: [[not_upper_limit_loclamp:[^ ]+]] = select i1 [[not_upper_limit_cond_loclamp]], i32 [[smax_not_len]], i32 [[not_n]] +-; CHECK: [[upper_limit_loclamp:[^ ]+]] = sub i32 -1, [[not_upper_limit_loclamp]] ++; CHECK: [[smax_len_cond:[^ ]+]] = icmp slt i32 %len.b, %len.a ++; CHECK: [[smax_len:[^ ]+]] = select i1 [[smax_len_cond]], i32 %len.b, i32 %len.a ++; CHECK: [[upper_limit_cond_loclamp:[^ ]+]] = icmp slt i32 [[smax_len]], %n ++; CHECK: [[upper_limit_loclamp:[^ ]+]] = select i1 [[upper_limit_cond_loclamp]], i32 [[smax_len]], i32 %n + ; CHECK: [[upper_limit_cmp:[^ ]+]] = icmp sgt i32 [[upper_limit_loclamp]], 0 + ; CHECK: [[upper_limit:[^ ]+]] = select i1 [[upper_limit_cmp]], i32 [[upper_limit_loclamp]], i32 0 + +@@ -85,9 +79,9 @@ define void @f_1( + + ; CHECK: loop: + ; CHECK: %abc = and i1 true, true +-; CHECK: br i1 %abc, label %in.bounds, label %out.of.bounds.loopexit4, !prof !1 ++; CHECK: br i1 %abc, label %in.bounds, label %[[oob_loopexit:[^ ,]+]], !prof !1 + +-; CHECK: out.of.bounds.loopexit: ++; CHECK: [[oob_loopexit]]: + ; CHECK-NEXT: br label %out.of.bounds + + +diff --git a/test/Transforms/IRCE/decrementing-loop.ll b/test/Transforms/IRCE/decrementing-loop.ll +index 4c82cd3e341..2994a432a71 100644 +--- a/test/Transforms/IRCE/decrementing-loop.ll ++++ b/test/Transforms/IRCE/decrementing-loop.ll +@@ -29,11 +29,8 @@ define void @decrementing_loop(i32 *%arr, i32 *%a_len_ptr, i32 %n) { + ret void + + ; CHECK: loop.preheader: +-; CHECK: [[not_len:[^ ]+]] = sub i32 -1, %len +-; CHECK: [[not_n:[^ ]+]] = sub i32 -1, %n +-; CHECK: [[not_len_hiclamp_cmp:[^ ]+]] = icmp sgt i32 [[not_len]], [[not_n]] +-; CHECK: [[not_len_hiclamp:[^ ]+]] = select i1 [[not_len_hiclamp_cmp]], i32 [[not_len]], i32 [[not_n]] +-; CHECK: [[len_hiclamp:[^ ]+]] = sub i32 -1, [[not_len_hiclamp]] ++; CHECK: [[len_hiclamp_cmp:[^ ]+]] = icmp slt i32 %len, %n ++; CHECK: [[len_hiclamp:[^ ]+]] = select i1 [[len_hiclamp_cmp]], i32 %len, i32 %n + ; CHECK: [[not_exit_preloop_at_cmp:[^ ]+]] = icmp sgt i32 [[len_hiclamp]], 0 + ; CHECK: [[not_exit_preloop_at:[^ ]+]] = select i1 [[not_exit_preloop_at_cmp]], i32 [[len_hiclamp]], i32 0 + ; CHECK: %exit.preloop.at = add i32 [[not_exit_preloop_at]], -1 +diff --git a/test/Transforms/IRCE/multiple-access-no-preloop.ll b/test/Transforms/IRCE/multiple-access-no-preloop.ll +index 000d1ab36f2..3bde9bd8668 100644 +--- a/test/Transforms/IRCE/multiple-access-no-preloop.ll ++++ b/test/Transforms/IRCE/multiple-access-no-preloop.ll +@@ -38,14 +38,10 @@ define void @multiple_access_no_preloop( + ; CHECK-LABEL: @multiple_access_no_preloop( + + ; CHECK: loop.preheader: +-; CHECK: [[not_len_b:[^ ]+]] = sub i32 -1, %len.b +-; CHECK: [[not_len_a:[^ ]+]] = sub i32 -1, %len.a +-; CHECK: [[smax_not_len_cond:[^ ]+]] = icmp sgt i32 [[not_len_b]], [[not_len_a]] +-; CHECK: [[smax_not_len:[^ ]+]] = select i1 [[smax_not_len_cond]], i32 [[not_len_b]], i32 [[not_len_a]] +-; CHECK: [[not_n:[^ ]+]] = sub i32 -1, %n +-; CHECK: [[not_upper_limit_cond_loclamp:[^ ]+]] = icmp sgt i32 [[smax_not_len]], [[not_n]] +-; CHECK: [[not_upper_limit_loclamp:[^ ]+]] = select i1 [[not_upper_limit_cond_loclamp]], i32 [[smax_not_len]], i32 [[not_n]] +-; CHECK: [[upper_limit_loclamp:[^ ]+]] = sub i32 -1, [[not_upper_limit_loclamp]] ++; CHECK: [[smax_len_cond:[^ ]+]] = icmp slt i32 %len.b, %len.a ++; CHECK: [[smax_len:[^ ]+]] = select i1 [[smax_len_cond]], i32 %len.b, i32 %len.a ++; CHECK: [[upper_limit_cond_loclamp:[^ ]+]] = icmp slt i32 [[smax_len]], %n ++; CHECK: [[upper_limit_loclamp:[^ ]+]] = select i1 [[upper_limit_cond_loclamp]], i32 [[smax_len]], i32 %n + ; CHECK: [[upper_limit_cmp:[^ ]+]] = icmp sgt i32 [[upper_limit_loclamp]], 0 + ; CHECK: [[upper_limit:[^ ]+]] = select i1 [[upper_limit_cmp]], i32 [[upper_limit_loclamp]], i32 0 + +diff --git a/test/Transforms/IRCE/ranges_of_different_types.ll b/test/Transforms/IRCE/ranges_of_different_types.ll +index 5c8161369f2..46bd94ce687 100644 +--- a/test/Transforms/IRCE/ranges_of_different_types.ll ++++ b/test/Transforms/IRCE/ranges_of_different_types.ll +@@ -23,12 +23,11 @@ define void @test_01(i32* %arr, i32* %a_len_ptr) #0 { + ; CHECK-NOT: preloop + ; CHECK: entry: + ; CHECK-NEXT: %len = load i32, i32* %a_len_ptr, !range !0 +-; CHECK-NEXT: [[SUB1:%[^ ]+]] = sub i32 12, %len +-; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp sgt i32 [[SUB1]], -102 +-; CHECK-NEXT: [[SMAX:%[^ ]+]] = select i1 [[CMP1]], i32 [[SUB1]], i32 -102 +-; CHECK-NEXT: [[SUB2:%[^ ]+]] = sub i32 -1, [[SMAX]] +-; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp sgt i32 [[SUB2]], 0 +-; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP2]], i32 [[SUB2]], i32 0 ++; CHECK-NEXT: [[SUB1:%[^ ]+]] = add i32 %len, -13 ++; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp slt i32 [[SUB1]], 101 ++; CHECK-NEXT: [[SMAX:%[^ ]+]] = select i1 [[CMP1]], i32 [[SUB1]], i32 101 ++; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp sgt i32 [[SMAX]], 0 ++; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP2]], i32 [[SMAX]], i32 0 + ; CHECK-NEXT: [[GOTO_LOOP:%[^ ]+]] = icmp slt i32 0, %exit.mainloop.at + ; CHECK-NEXT: br i1 [[GOTO_LOOP]], label %loop.preheader, label %main.pseudo.exit + ; CHECK: loop +@@ -83,13 +82,11 @@ define void @test_02(i32* %arr, i32* %a_len_ptr) #0 { + ; CHECK-NEXT: [[LEN_MINUS_SMAX:%[^ ]+]] = add i32 %len, -2147483647 + ; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp sgt i32 [[LEN_MINUS_SMAX]], -13 + ; CHECK-NEXT: [[SMAX1:%[^ ]+]] = select i1 [[CMP1]], i32 [[LEN_MINUS_SMAX]], i32 -13 +-; CHECK-NEXT: [[ADD1:%[^ ]+]] = add i32 [[SMAX1]], -1 +-; CHECK-NEXT: [[SUB1:%[^ ]+]] = sub i32 [[ADD1]], %len +-; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp sgt i32 [[SUB1]], -102 +-; CHECK-NEXT: [[SMAX2:%[^ ]+]] = select i1 [[CMP2]], i32 [[SUB1]], i32 -102 +-; CHECK-NEXT: [[SUB2:%[^ ]+]] = sub i32 -1, [[SMAX2]] +-; CHECK-NEXT: [[CMP3:%[^ ]+]] = icmp sgt i32 [[SUB2]], 0 +-; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP3]], i32 [[SUB2]], i32 0 ++; CHECK-NEXT: [[SUB1:%[^ ]+]] = sub i32 %len, [[SMAX1]] ++; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp slt i32 [[SUB1]], 101 ++; CHECK-NEXT: [[SMAX2:%[^ ]+]] = select i1 [[CMP2]], i32 [[SUB1]], i32 101 ++; CHECK-NEXT: [[CMP3:%[^ ]+]] = icmp sgt i32 [[SMAX2]], 0 ++; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP3]], i32 [[SMAX2]], i32 0 + ; CHECK-NEXT: br i1 true, label %loop.preloop.preheader + ; CHECK: loop.preloop: + ; CHECK-NEXT: %idx.preloop = phi i32 [ %idx.next.preloop, %in.bounds.preloop ], [ 0, %loop.preloop.preheader ] +@@ -151,14 +148,11 @@ define void @test_03(i32* %arr, i32* %a_len_ptr) #0 { + ; CHECK-NOT: preloop + ; CHECK: entry: + ; CHECK-NEXT: %len = load i32, i32* %a_len_ptr, !range !0 +-; CHECK-NEXT: [[SUB1:%[^ ]+]] = sub i32 -2, %len +-; CHECK-NEXT: [[SUB2:%[^ ]+]] = sub i32 -1, %len +-; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp sgt i32 [[SUB2]], -14 +-; CHECK-NEXT: [[SMAX1:%[^ ]+]] = select i1 [[CMP1]], i32 [[SUB2]], i32 -14 +-; CHECK-NEXT: [[SUB3:%[^ ]+]] = sub i32 [[SUB1]], [[SMAX1]] +-; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp ugt i32 [[SUB3]], -102 +-; CHECK-NEXT: [[UMAX1:%[^ ]+]] = select i1 [[CMP2]], i32 [[SUB3]], i32 -102 +-; CHECK-NEXT: %exit.mainloop.at = sub i32 -1, [[UMAX1]] ++; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp slt i32 %len, 13 ++; CHECK-NEXT: [[SMAX1:%[^ ]+]] = select i1 [[CMP1]], i32 %len, i32 13 ++; CHECK-NEXT: [[SUB3:%[^ ]+]] = sub i32 %len, [[SMAX1]] ++; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp ult i32 [[SUB3]], 101 ++; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP2]], i32 [[SUB3]], i32 101 + ; CHECK-NEXT: [[CMP3:%[^ ]+]] = icmp ult i32 0, %exit.mainloop.at + ; CHECK-NEXT: br i1 [[CMP3]], label %loop.preheader, label %main.pseudo.exit + ; CHECK: postloop: +@@ -208,10 +202,9 @@ define void @test_04(i32* %arr, i32* %a_len_ptr) #0 { + ; CHECK-LABEL: test_04( + ; CHECK: entry: + ; CHECK-NEXT: %len = load i32, i32* %a_len_ptr, !range !0 +-; CHECK-NEXT: [[SUB1:%[^ ]+]] = sub i32 -14, %len +-; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp ugt i32 [[SUB1]], -102 +-; CHECK-NEXT: [[UMAX1:%[^ ]+]] = select i1 [[CMP1]], i32 [[SUB1]], i32 -102 +-; CHECK-NEXT: %exit.mainloop.at = sub i32 -1, [[UMAX1]] ++; CHECK-NEXT: [[SUB1:%[^ ]+]] = add i32 %len, 13 ++; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp ult i32 [[SUB1]], 101 ++; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP1]], i32 [[SUB1]], i32 101 + ; CHECK-NEXT: br i1 true, label %loop.preloop.preheader + ; CHECK: in.bounds.preloop: + ; CHECK-NEXT: %addr.preloop = getelementptr i32, i32* %arr, i32 %idx.preloop +@@ -252,12 +245,11 @@ define void @test_05(i32* %arr, i32* %a_len_ptr) #0 { + ; CHECK-NOT: preloop + ; CHECK: entry: + ; CHECK-NEXT: %len = load i32, i32* %a_len_ptr, !range !0 +-; CHECK-NEXT: [[SUB1:%[^ ]+]] = sub i32 12, %len +-; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp sgt i32 [[SUB1]], -102 +-; CHECK-NEXT: [[SMAX:%[^ ]+]] = select i1 [[CMP1]], i32 [[SUB1]], i32 -102 +-; CHECK-NEXT: [[SUB2:%[^ ]+]] = sub i32 -1, [[SMAX]] +-; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp sgt i32 [[SUB2]], 0 +-; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP2]], i32 [[SUB2]], i32 0 ++; CHECK-NEXT: [[SUB1:%[^ ]+]] = add i32 %len, -13 ++; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp slt i32 [[SUB1]], 101 ++; CHECK-NEXT: [[SMAX:%[^ ]+]] = select i1 [[CMP1]], i32 [[SUB1]], i32 101 ++; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp sgt i32 [[SMAX]], 0 ++; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP2]], i32 [[SMAX]], i32 0 + ; CHECK-NEXT: [[GOTO_LOOP:%[^ ]+]] = icmp slt i32 0, %exit.mainloop.at + ; CHECK-NEXT: br i1 [[GOTO_LOOP]], label %loop.preheader, label %main.pseudo.exit + ; CHECK: loop +@@ -297,13 +289,11 @@ define void @test_06(i32* %arr, i32* %a_len_ptr) #0 { + ; CHECK-NEXT: [[LEN_MINUS_SMAX:%[^ ]+]] = add i32 %len, -2147483647 + ; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp sgt i32 [[LEN_MINUS_SMAX]], -13 + ; CHECK-NEXT: [[SMAX1:%[^ ]+]] = select i1 [[CMP1]], i32 [[LEN_MINUS_SMAX]], i32 -13 +-; CHECK-NEXT: [[ADD1:%[^ ]+]] = add i32 [[SMAX1]], -1 +-; CHECK-NEXT: [[SUB1:%[^ ]+]] = sub i32 [[ADD1]], %len +-; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp sgt i32 [[SUB1]], -102 +-; CHECK-NEXT: [[SMAX2:%[^ ]+]] = select i1 [[CMP2]], i32 [[SUB1]], i32 -102 +-; CHECK-NEXT: [[SUB2:%[^ ]+]] = sub i32 -1, [[SMAX2]] +-; CHECK-NEXT: [[CMP3:%[^ ]+]] = icmp sgt i32 [[SUB2]], 0 +-; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP3]], i32 [[SUB2]], i32 0 ++; CHECK-NEXT: [[SUB1:%[^ ]+]] = sub i32 %len, [[SMAX1]] ++; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp slt i32 [[SUB1]], 101 ++; CHECK-NEXT: [[SMAX2:%[^ ]+]] = select i1 [[CMP2]], i32 [[SUB1]], i32 101 ++; CHECK-NEXT: [[CMP3:%[^ ]+]] = icmp sgt i32 [[SMAX2]], 0 ++; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP3]], i32 [[SMAX2]], i32 0 + ; CHECK-NEXT: br i1 true, label %loop.preloop.preheader + ; CHECK: in.bounds.preloop: + ; CHECK-NEXT: %addr.preloop = getelementptr i32, i32* %arr, i32 %idx.preloop +@@ -344,14 +334,11 @@ define void @test_07(i32* %arr, i32* %a_len_ptr) #0 { + ; CHECK-NOT: preloop + ; CHECK: entry: + ; CHECK-NEXT: %len = load i32, i32* %a_len_ptr, !range !0 +-; CHECK-NEXT: [[SUB1:%[^ ]+]] = sub i32 -2, %len +-; CHECK-NEXT: [[SUB2:%[^ ]+]] = sub i32 -1, %len +-; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp sgt i32 [[SUB2]], -14 +-; CHECK-NEXT: [[SMAX1:%[^ ]+]] = select i1 [[CMP1]], i32 [[SUB2]], i32 -14 +-; CHECK-NEXT: [[SUB3:%[^ ]+]] = sub i32 [[SUB1]], [[SMAX1]] +-; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp ugt i32 [[SUB3]], -102 +-; CHECK-NEXT: [[UMAX1:%[^ ]+]] = select i1 [[CMP2]], i32 [[SUB3]], i32 -102 +-; CHECK-NEXT: %exit.mainloop.at = sub i32 -1, [[UMAX1]] ++; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp slt i32 %len, 13 ++; CHECK-NEXT: [[SMAX1:%[^ ]+]] = select i1 [[CMP1]], i32 %len, i32 13 ++; CHECK-NEXT: [[SUB3:%[^ ]+]] = sub i32 %len, [[SMAX1]] ++; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp ult i32 [[SUB3]], 101 ++; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP2]], i32 [[SUB3]], i32 101 + ; CHECK-NEXT: [[CMP3:%[^ ]+]] = icmp ult i32 0, %exit.mainloop.at + ; CHECK-NEXT: br i1 [[CMP3]], label %loop.preheader, label %main.pseudo.exit + ; CHECK: loop +@@ -388,10 +375,9 @@ define void @test_08(i32* %arr, i32* %a_len_ptr) #0 { + ; CHECK-LABEL: test_08( + ; CHECK: entry: + ; CHECK-NEXT: %len = load i32, i32* %a_len_ptr, !range !0 +-; CHECK-NEXT: [[SUB1:%[^ ]+]] = sub i32 -14, %len +-; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp ugt i32 [[SUB1]], -102 +-; CHECK-NEXT: [[UMAX1:%[^ ]+]] = select i1 [[CMP1]], i32 [[SUB1]], i32 -102 +-; CHECK-NEXT: %exit.mainloop.at = sub i32 -1, [[UMAX1]] ++; CHECK-NEXT: [[SUB1:%[^ ]+]] = add i32 %len, 13 ++; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp ult i32 [[SUB1]], 101 ++; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP1]], i32 [[SUB1]], i32 101 + ; CHECK-NEXT: br i1 true, label %loop.preloop.preheader + ; CHECK: in.bounds.preloop: + ; CHECK-NEXT: %addr.preloop = getelementptr i32, i32* %arr, i32 %idx.preloop +diff --git a/test/Transforms/IRCE/rc-negative-bound.ll b/test/Transforms/IRCE/rc-negative-bound.ll +index bfc0cd14778..d226bffeaae 100644 +--- a/test/Transforms/IRCE/rc-negative-bound.ll ++++ b/test/Transforms/IRCE/rc-negative-bound.ll +@@ -114,49 +114,44 @@ define void @test_03(i32 *%arr, i32 %n, i32 %bound) { + ; CHECK: loop.preheader: + ; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[BOUND:%.*]], -2147483647 + ; CHECK-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[TMP0]], 0 +-; CHECK-NEXT: [[SMAX:%.*]] = select i1 [[TMP1]], i32 [[TMP0]], i32 0 +-; CHECK-NEXT: [[TMP2:%.*]] = sub i32 [[BOUND]], [[SMAX]] +-; CHECK-NEXT: [[TMP3:%.*]] = sub i32 -1, [[BOUND]] +-; CHECK-NEXT: [[TMP4:%.*]] = icmp sgt i32 [[TMP3]], -1 +-; CHECK-NEXT: [[SMAX1:%.*]] = select i1 [[TMP4]], i32 [[TMP3]], i32 -1 +-; CHECK-NEXT: [[TMP5:%.*]] = sub i32 -1, [[SMAX1]] +-; CHECK-NEXT: [[TMP6:%.*]] = icmp sgt i32 [[TMP5]], -1 +-; CHECK-NEXT: [[SMAX2:%.*]] = select i1 [[TMP6]], i32 [[TMP5]], i32 -1 +-; CHECK-NEXT: [[TMP7:%.*]] = add i32 [[SMAX2]], 1 +-; CHECK-NEXT: [[TMP8:%.*]] = mul i32 [[TMP2]], [[TMP7]] +-; CHECK-NEXT: [[TMP9:%.*]] = sub i32 -1, [[TMP8]] +-; CHECK-NEXT: [[TMP10:%.*]] = sub i32 -1, [[N]] +-; CHECK-NEXT: [[TMP11:%.*]] = icmp sgt i32 [[TMP9]], [[TMP10]] +-; CHECK-NEXT: [[SMAX3:%.*]] = select i1 [[TMP11]], i32 [[TMP9]], i32 [[TMP10]] +-; CHECK-NEXT: [[TMP12:%.*]] = sub i32 -1, [[SMAX3]] +-; CHECK-NEXT: [[TMP13:%.*]] = icmp sgt i32 [[TMP12]], 0 +-; CHECK-NEXT: [[EXIT_MAINLOOP_AT:%.*]] = select i1 [[TMP13]], i32 [[TMP12]], i32 0 +-; CHECK-NEXT: [[TMP14:%.*]] = icmp slt i32 0, [[EXIT_MAINLOOP_AT]] +-; CHECK-NEXT: br i1 [[TMP14]], label [[LOOP_PREHEADER5:%.*]], label [[MAIN_PSEUDO_EXIT:%.*]] +-; CHECK: loop.preheader5: ++; CHECK-NEXT: [[SMIN:%.*]] = select i1 [[TMP1]], i32 [[TMP0]], i32 0 ++; CHECK-NEXT: [[TMP2:%.*]] = sub i32 [[BOUND]], [[SMIN]] ++; CHECK-NEXT: [[TMP3:%.*]] = icmp slt i32 [[BOUND]], 0 ++; CHECK-NEXT: [[SMAX:%.*]] = select i1 [[TMP3]], i32 [[BOUND]], i32 0 ++; CHECK-NEXT: [[TMP4:%.*]] = icmp sgt i32 [[SMAX]], -1 ++; CHECK-NEXT: [[SMIN1:%.*]] = select i1 [[TMP4]], i32 [[SMAX]], i32 -1 ++; CHECK-NEXT: [[TMP5:%.*]] = add i32 [[SMIN1]], 1 ++; CHECK-NEXT: [[TMP6:%.*]] = mul i32 [[TMP2]], [[TMP5]] ++; CHECK-NEXT: [[TMP7:%.*]] = icmp slt i32 [[N]], [[TMP6]] ++; CHECK-NEXT: [[SMAX2:%.*]] = select i1 [[TMP7]], i32 [[N]], i32 [[TMP6]] ++; CHECK-NEXT: [[TMP8:%.*]] = icmp sgt i32 [[SMAX2]], 0 ++; CHECK-NEXT: [[EXIT_MAINLOOP_AT:%.*]] = select i1 [[TMP8]], i32 [[SMAX2]], i32 0 ++; CHECK-NEXT: [[TMP9:%.*]] = icmp slt i32 0, [[EXIT_MAINLOOP_AT]] ++; CHECK-NEXT: br i1 [[TMP9]], label [[LOOP_PREHEADER4:%.*]], label [[MAIN_PSEUDO_EXIT:%.*]] ++; CHECK: loop.preheader4: + ; CHECK-NEXT: br label [[LOOP:%.*]] + ; CHECK: loop: +-; CHECK-NEXT: [[IDX:%.*]] = phi i32 [ [[IDX_NEXT:%.*]], [[IN_BOUNDS:%.*]] ], [ 0, [[LOOP_PREHEADER5]] ] ++; CHECK-NEXT: [[IDX:%.*]] = phi i32 [ [[IDX_NEXT:%.*]], [[IN_BOUNDS:%.*]] ], [ 0, [[LOOP_PREHEADER4]] ] + ; CHECK-NEXT: [[IDX_NEXT]] = add i32 [[IDX]], 1 + ; CHECK-NEXT: [[ABC:%.*]] = icmp slt i32 [[IDX]], [[BOUND]] +-; CHECK-NEXT: br i1 true, label [[IN_BOUNDS]], label [[OUT_OF_BOUNDS_LOOPEXIT6:%.*]], !prof !0 ++; CHECK-NEXT: br i1 true, label [[IN_BOUNDS]], label [[OUT_OF_BOUNDS_LOOPEXIT5:%.*]], !prof !0 + ; CHECK: in.bounds: + ; CHECK-NEXT: [[ADDR:%.*]] = getelementptr i32, i32* [[ARR:%.*]], i32 [[IDX]] + ; CHECK-NEXT: store i32 0, i32* [[ADDR]] + ; CHECK-NEXT: [[NEXT:%.*]] = icmp slt i32 [[IDX_NEXT]], [[N]] +-; CHECK-NEXT: [[TMP15:%.*]] = icmp slt i32 [[IDX_NEXT]], [[EXIT_MAINLOOP_AT]] +-; CHECK-NEXT: br i1 [[TMP15]], label [[LOOP]], label [[MAIN_EXIT_SELECTOR:%.*]] ++; CHECK-NEXT: [[TMP10:%.*]] = icmp slt i32 [[IDX_NEXT]], [[EXIT_MAINLOOP_AT]] ++; CHECK-NEXT: br i1 [[TMP10]], label [[LOOP]], label [[MAIN_EXIT_SELECTOR:%.*]] + ; CHECK: main.exit.selector: + ; CHECK-NEXT: [[IDX_NEXT_LCSSA:%.*]] = phi i32 [ [[IDX_NEXT]], [[IN_BOUNDS]] ] +-; CHECK-NEXT: [[TMP16:%.*]] = icmp slt i32 [[IDX_NEXT_LCSSA]], [[N]] +-; CHECK-NEXT: br i1 [[TMP16]], label [[MAIN_PSEUDO_EXIT]], label [[EXIT_LOOPEXIT:%.*]] ++; CHECK-NEXT: [[TMP11:%.*]] = icmp slt i32 [[IDX_NEXT_LCSSA]], [[N]] ++; CHECK-NEXT: br i1 [[TMP11]], label [[MAIN_PSEUDO_EXIT]], label [[EXIT_LOOPEXIT:%.*]] + ; CHECK: main.pseudo.exit: + ; CHECK-NEXT: [[IDX_COPY:%.*]] = phi i32 [ 0, [[LOOP_PREHEADER]] ], [ [[IDX_NEXT_LCSSA]], [[MAIN_EXIT_SELECTOR]] ] + ; CHECK-NEXT: [[INDVAR_END:%.*]] = phi i32 [ 0, [[LOOP_PREHEADER]] ], [ [[IDX_NEXT_LCSSA]], [[MAIN_EXIT_SELECTOR]] ] + ; CHECK-NEXT: br label [[POSTLOOP:%.*]] + ; CHECK: out.of.bounds.loopexit: + ; CHECK-NEXT: br label [[OUT_OF_BOUNDS:%.*]] +-; CHECK: out.of.bounds.loopexit6: ++; CHECK: out.of.bounds.loopexit5: + ; CHECK-NEXT: br label [[OUT_OF_BOUNDS]] + ; CHECK: out.of.bounds: + ; CHECK-NEXT: ret void +@@ -211,47 +206,41 @@ define void @test_04(i32 *%arr, i32 %n, i32 %bound) { + ; CHECK-NEXT: [[FIRST_ITR_CHECK:%.*]] = icmp sgt i32 [[N:%.*]], 0 + ; CHECK-NEXT: br i1 [[FIRST_ITR_CHECK]], label [[LOOP_PREHEADER:%.*]], label [[EXIT:%.*]] + ; CHECK: loop.preheader: +-; CHECK-NEXT: [[TMP0:%.*]] = sub i32 -1, [[BOUND:%.*]] +-; CHECK-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[TMP0]], -1 +-; CHECK-NEXT: [[SMAX:%.*]] = select i1 [[TMP1]], i32 [[TMP0]], i32 -1 +-; CHECK-NEXT: [[TMP2:%.*]] = add i32 [[BOUND]], [[SMAX]] +-; CHECK-NEXT: [[TMP3:%.*]] = add i32 [[TMP2]], 1 +-; CHECK-NEXT: [[TMP4:%.*]] = sub i32 -1, [[SMAX]] +-; CHECK-NEXT: [[TMP5:%.*]] = icmp sgt i32 [[TMP4]], -1 +-; CHECK-NEXT: [[SMAX1:%.*]] = select i1 [[TMP5]], i32 [[TMP4]], i32 -1 +-; CHECK-NEXT: [[TMP6:%.*]] = add i32 [[SMAX1]], 1 +-; CHECK-NEXT: [[TMP7:%.*]] = mul i32 [[TMP3]], [[TMP6]] +-; CHECK-NEXT: [[TMP8:%.*]] = sub i32 -1, [[TMP7]] +-; CHECK-NEXT: [[TMP9:%.*]] = sub i32 -1, [[N]] +-; CHECK-NEXT: [[TMP10:%.*]] = icmp ugt i32 [[TMP8]], [[TMP9]] +-; CHECK-NEXT: [[UMAX:%.*]] = select i1 [[TMP10]], i32 [[TMP8]], i32 [[TMP9]] +-; CHECK-NEXT: [[EXIT_MAINLOOP_AT:%.*]] = sub i32 -1, [[UMAX]] +-; CHECK-NEXT: [[TMP11:%.*]] = icmp ult i32 0, [[EXIT_MAINLOOP_AT]] +-; CHECK-NEXT: br i1 [[TMP11]], label [[LOOP_PREHEADER2:%.*]], label [[MAIN_PSEUDO_EXIT:%.*]] +-; CHECK: loop.preheader2: ++; CHECK-NEXT: [[TMP0:%.*]] = icmp slt i32 [[BOUND:%.*]], 0 ++; CHECK-NEXT: [[SMAX:%.*]] = select i1 [[TMP0]], i32 [[BOUND]], i32 0 ++; CHECK-NEXT: [[TMP1:%.*]] = sub i32 [[BOUND]], [[SMAX]] ++; CHECK-NEXT: [[TMP2:%.*]] = icmp sgt i32 [[SMAX]], -1 ++; CHECK-NEXT: [[SMIN:%.*]] = select i1 [[TMP2]], i32 [[SMAX]], i32 -1 ++; CHECK-NEXT: [[TMP3:%.*]] = add i32 [[SMIN]], 1 ++; CHECK-NEXT: [[TMP4:%.*]] = mul i32 [[TMP1]], [[TMP3]] ++; CHECK-NEXT: [[TMP5:%.*]] = icmp ult i32 [[N]], [[TMP4]] ++; CHECK-NEXT: [[EXIT_MAINLOOP_AT:%.*]] = select i1 [[TMP5]], i32 [[N]], i32 [[TMP4]] ++; CHECK-NEXT: [[TMP6:%.*]] = icmp ult i32 0, [[EXIT_MAINLOOP_AT]] ++; CHECK-NEXT: br i1 [[TMP6]], label [[LOOP_PREHEADER1:%.*]], label [[MAIN_PSEUDO_EXIT:%.*]] ++; CHECK: loop.preheader1: + ; CHECK-NEXT: br label [[LOOP:%.*]] + ; CHECK: loop: +-; CHECK-NEXT: [[IDX:%.*]] = phi i32 [ [[IDX_NEXT:%.*]], [[IN_BOUNDS:%.*]] ], [ 0, [[LOOP_PREHEADER2]] ] ++; CHECK-NEXT: [[IDX:%.*]] = phi i32 [ [[IDX_NEXT:%.*]], [[IN_BOUNDS:%.*]] ], [ 0, [[LOOP_PREHEADER1]] ] + ; CHECK-NEXT: [[IDX_NEXT]] = add i32 [[IDX]], 1 + ; CHECK-NEXT: [[ABC:%.*]] = icmp slt i32 [[IDX]], [[BOUND]] +-; CHECK-NEXT: br i1 true, label [[IN_BOUNDS]], label [[OUT_OF_BOUNDS_LOOPEXIT3:%.*]], !prof !0 ++; CHECK-NEXT: br i1 true, label [[IN_BOUNDS]], label [[OUT_OF_BOUNDS_LOOPEXIT2:%.*]], !prof !0 + ; CHECK: in.bounds: + ; CHECK-NEXT: [[ADDR:%.*]] = getelementptr i32, i32* [[ARR:%.*]], i32 [[IDX]] + ; CHECK-NEXT: store i32 0, i32* [[ADDR]] + ; CHECK-NEXT: [[NEXT:%.*]] = icmp ult i32 [[IDX_NEXT]], [[N]] +-; CHECK-NEXT: [[TMP12:%.*]] = icmp ult i32 [[IDX_NEXT]], [[EXIT_MAINLOOP_AT]] +-; CHECK-NEXT: br i1 [[TMP12]], label [[LOOP]], label [[MAIN_EXIT_SELECTOR:%.*]] ++; CHECK-NEXT: [[TMP7:%.*]] = icmp ult i32 [[IDX_NEXT]], [[EXIT_MAINLOOP_AT]] ++; CHECK-NEXT: br i1 [[TMP7]], label [[LOOP]], label [[MAIN_EXIT_SELECTOR:%.*]] + ; CHECK: main.exit.selector: + ; CHECK-NEXT: [[IDX_NEXT_LCSSA:%.*]] = phi i32 [ [[IDX_NEXT]], [[IN_BOUNDS]] ] +-; CHECK-NEXT: [[TMP13:%.*]] = icmp ult i32 [[IDX_NEXT_LCSSA]], [[N]] +-; CHECK-NEXT: br i1 [[TMP13]], label [[MAIN_PSEUDO_EXIT]], label [[EXIT_LOOPEXIT:%.*]] ++; CHECK-NEXT: [[TMP8:%.*]] = icmp ult i32 [[IDX_NEXT_LCSSA]], [[N]] ++; CHECK-NEXT: br i1 [[TMP8]], label [[MAIN_PSEUDO_EXIT]], label [[EXIT_LOOPEXIT:%.*]] + ; CHECK: main.pseudo.exit: + ; CHECK-NEXT: [[IDX_COPY:%.*]] = phi i32 [ 0, [[LOOP_PREHEADER]] ], [ [[IDX_NEXT_LCSSA]], [[MAIN_EXIT_SELECTOR]] ] + ; CHECK-NEXT: [[INDVAR_END:%.*]] = phi i32 [ 0, [[LOOP_PREHEADER]] ], [ [[IDX_NEXT_LCSSA]], [[MAIN_EXIT_SELECTOR]] ] + ; CHECK-NEXT: br label [[POSTLOOP:%.*]] + ; CHECK: out.of.bounds.loopexit: + ; CHECK-NEXT: br label [[OUT_OF_BOUNDS:%.*]] +-; CHECK: out.of.bounds.loopexit3: ++; CHECK: out.of.bounds.loopexit2: + ; CHECK-NEXT: br label [[OUT_OF_BOUNDS]] + ; CHECK: out.of.bounds: + ; CHECK-NEXT: ret void +@@ -413,49 +402,44 @@ define void @test_07(i32 *%arr, i32 %n, i32 %bound) { + ; CHECK: loop.preheader: + ; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[BOUND:%.*]], -2147483647 + ; CHECK-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[TMP0]], 0 +-; CHECK-NEXT: [[SMAX:%.*]] = select i1 [[TMP1]], i32 [[TMP0]], i32 0 +-; CHECK-NEXT: [[TMP2:%.*]] = sub i32 [[BOUND]], [[SMAX]] +-; CHECK-NEXT: [[TMP3:%.*]] = sub i32 -1, [[BOUND]] +-; CHECK-NEXT: [[TMP4:%.*]] = icmp sgt i32 [[TMP3]], -1 +-; CHECK-NEXT: [[SMAX1:%.*]] = select i1 [[TMP4]], i32 [[TMP3]], i32 -1 +-; CHECK-NEXT: [[TMP5:%.*]] = sub i32 -1, [[SMAX1]] +-; CHECK-NEXT: [[TMP6:%.*]] = icmp sgt i32 [[TMP5]], -1 +-; CHECK-NEXT: [[SMAX2:%.*]] = select i1 [[TMP6]], i32 [[TMP5]], i32 -1 +-; CHECK-NEXT: [[TMP7:%.*]] = add i32 [[SMAX2]], 1 +-; CHECK-NEXT: [[TMP8:%.*]] = mul i32 [[TMP2]], [[TMP7]] +-; CHECK-NEXT: [[TMP9:%.*]] = sub i32 -1, [[TMP8]] +-; CHECK-NEXT: [[TMP10:%.*]] = sub i32 -1, [[N]] +-; CHECK-NEXT: [[TMP11:%.*]] = icmp sgt i32 [[TMP9]], [[TMP10]] +-; CHECK-NEXT: [[SMAX3:%.*]] = select i1 [[TMP11]], i32 [[TMP9]], i32 [[TMP10]] +-; CHECK-NEXT: [[TMP12:%.*]] = sub i32 -1, [[SMAX3]] +-; CHECK-NEXT: [[TMP13:%.*]] = icmp sgt i32 [[TMP12]], 0 +-; CHECK-NEXT: [[EXIT_MAINLOOP_AT:%.*]] = select i1 [[TMP13]], i32 [[TMP12]], i32 0 +-; CHECK-NEXT: [[TMP14:%.*]] = icmp slt i32 0, [[EXIT_MAINLOOP_AT]] +-; CHECK-NEXT: br i1 [[TMP14]], label [[LOOP_PREHEADER5:%.*]], label [[MAIN_PSEUDO_EXIT:%.*]] +-; CHECK: loop.preheader5: ++; CHECK-NEXT: [[SMIN:%.*]] = select i1 [[TMP1]], i32 [[TMP0]], i32 0 ++; CHECK-NEXT: [[TMP2:%.*]] = sub i32 [[BOUND]], [[SMIN]] ++; CHECK-NEXT: [[TMP3:%.*]] = icmp slt i32 [[BOUND]], 0 ++; CHECK-NEXT: [[SMAX:%.*]] = select i1 [[TMP3]], i32 [[BOUND]], i32 0 ++; CHECK-NEXT: [[TMP4:%.*]] = icmp sgt i32 [[SMAX]], -1 ++; CHECK-NEXT: [[SMIN1:%.*]] = select i1 [[TMP4]], i32 [[SMAX]], i32 -1 ++; CHECK-NEXT: [[TMP5:%.*]] = add i32 [[SMIN1]], 1 ++; CHECK-NEXT: [[TMP6:%.*]] = mul i32 [[TMP2]], [[TMP5]] ++; CHECK-NEXT: [[TMP7:%.*]] = icmp slt i32 [[N]], [[TMP6]] ++; CHECK-NEXT: [[SMAX2:%.*]] = select i1 [[TMP7]], i32 [[N]], i32 [[TMP6]] ++; CHECK-NEXT: [[TMP8:%.*]] = icmp sgt i32 [[SMAX2]], 0 ++; CHECK-NEXT: [[EXIT_MAINLOOP_AT:%.*]] = select i1 [[TMP8]], i32 [[SMAX2]], i32 0 ++; CHECK-NEXT: [[TMP9:%.*]] = icmp slt i32 0, [[EXIT_MAINLOOP_AT]] ++; CHECK-NEXT: br i1 [[TMP9]], label [[LOOP_PREHEADER4:%.*]], label [[MAIN_PSEUDO_EXIT:%.*]] ++; CHECK: loop.preheader4: + ; CHECK-NEXT: br label [[LOOP:%.*]] + ; CHECK: loop: +-; CHECK-NEXT: [[IDX:%.*]] = phi i32 [ [[IDX_NEXT:%.*]], [[IN_BOUNDS:%.*]] ], [ 0, [[LOOP_PREHEADER5]] ] ++; CHECK-NEXT: [[IDX:%.*]] = phi i32 [ [[IDX_NEXT:%.*]], [[IN_BOUNDS:%.*]] ], [ 0, [[LOOP_PREHEADER4]] ] + ; CHECK-NEXT: [[IDX_NEXT]] = add i32 [[IDX]], 1 + ; CHECK-NEXT: [[ABC:%.*]] = icmp ult i32 [[IDX]], [[BOUND]] +-; CHECK-NEXT: br i1 true, label [[IN_BOUNDS]], label [[OUT_OF_BOUNDS_LOOPEXIT6:%.*]], !prof !0 ++; CHECK-NEXT: br i1 true, label [[IN_BOUNDS]], label [[OUT_OF_BOUNDS_LOOPEXIT5:%.*]], !prof !0 + ; CHECK: in.bounds: + ; CHECK-NEXT: [[ADDR:%.*]] = getelementptr i32, i32* [[ARR:%.*]], i32 [[IDX]] + ; CHECK-NEXT: store i32 0, i32* [[ADDR]] + ; CHECK-NEXT: [[NEXT:%.*]] = icmp slt i32 [[IDX_NEXT]], [[N]] +-; CHECK-NEXT: [[TMP15:%.*]] = icmp slt i32 [[IDX_NEXT]], [[EXIT_MAINLOOP_AT]] +-; CHECK-NEXT: br i1 [[TMP15]], label [[LOOP]], label [[MAIN_EXIT_SELECTOR:%.*]] ++; CHECK-NEXT: [[TMP10:%.*]] = icmp slt i32 [[IDX_NEXT]], [[EXIT_MAINLOOP_AT]] ++; CHECK-NEXT: br i1 [[TMP10]], label [[LOOP]], label [[MAIN_EXIT_SELECTOR:%.*]] + ; CHECK: main.exit.selector: + ; CHECK-NEXT: [[IDX_NEXT_LCSSA:%.*]] = phi i32 [ [[IDX_NEXT]], [[IN_BOUNDS]] ] +-; CHECK-NEXT: [[TMP16:%.*]] = icmp slt i32 [[IDX_NEXT_LCSSA]], [[N]] +-; CHECK-NEXT: br i1 [[TMP16]], label [[MAIN_PSEUDO_EXIT]], label [[EXIT_LOOPEXIT:%.*]] ++; CHECK-NEXT: [[TMP11:%.*]] = icmp slt i32 [[IDX_NEXT_LCSSA]], [[N]] ++; CHECK-NEXT: br i1 [[TMP11]], label [[MAIN_PSEUDO_EXIT]], label [[EXIT_LOOPEXIT:%.*]] + ; CHECK: main.pseudo.exit: + ; CHECK-NEXT: [[IDX_COPY:%.*]] = phi i32 [ 0, [[LOOP_PREHEADER]] ], [ [[IDX_NEXT_LCSSA]], [[MAIN_EXIT_SELECTOR]] ] + ; CHECK-NEXT: [[INDVAR_END:%.*]] = phi i32 [ 0, [[LOOP_PREHEADER]] ], [ [[IDX_NEXT_LCSSA]], [[MAIN_EXIT_SELECTOR]] ] + ; CHECK-NEXT: br label [[POSTLOOP:%.*]] + ; CHECK: out.of.bounds.loopexit: + ; CHECK-NEXT: br label [[OUT_OF_BOUNDS:%.*]] +-; CHECK: out.of.bounds.loopexit6: ++; CHECK: out.of.bounds.loopexit5: + ; CHECK-NEXT: br label [[OUT_OF_BOUNDS]] + ; CHECK: out.of.bounds: + ; CHECK-NEXT: ret void +@@ -512,47 +496,41 @@ define void @test_08(i32 *%arr, i32 %n, i32 %bound) { + ; CHECK-NEXT: [[FIRST_ITR_CHECK:%.*]] = icmp sgt i32 [[N:%.*]], 0 + ; CHECK-NEXT: br i1 [[FIRST_ITR_CHECK]], label [[LOOP_PREHEADER:%.*]], label [[EXIT:%.*]] + ; CHECK: loop.preheader: +-; CHECK-NEXT: [[TMP0:%.*]] = sub i32 -1, [[BOUND:%.*]] +-; CHECK-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[TMP0]], -1 +-; CHECK-NEXT: [[SMAX:%.*]] = select i1 [[TMP1]], i32 [[TMP0]], i32 -1 +-; CHECK-NEXT: [[TMP2:%.*]] = add i32 [[BOUND]], [[SMAX]] +-; CHECK-NEXT: [[TMP3:%.*]] = add i32 [[TMP2]], 1 +-; CHECK-NEXT: [[TMP4:%.*]] = sub i32 -1, [[SMAX]] +-; CHECK-NEXT: [[TMP5:%.*]] = icmp sgt i32 [[TMP4]], -1 +-; CHECK-NEXT: [[SMAX1:%.*]] = select i1 [[TMP5]], i32 [[TMP4]], i32 -1 +-; CHECK-NEXT: [[TMP6:%.*]] = add i32 [[SMAX1]], 1 +-; CHECK-NEXT: [[TMP7:%.*]] = mul i32 [[TMP3]], [[TMP6]] +-; CHECK-NEXT: [[TMP8:%.*]] = sub i32 -1, [[TMP7]] +-; CHECK-NEXT: [[TMP9:%.*]] = sub i32 -1, [[N]] +-; CHECK-NEXT: [[TMP10:%.*]] = icmp ugt i32 [[TMP8]], [[TMP9]] +-; CHECK-NEXT: [[UMAX:%.*]] = select i1 [[TMP10]], i32 [[TMP8]], i32 [[TMP9]] +-; CHECK-NEXT: [[EXIT_MAINLOOP_AT:%.*]] = sub i32 -1, [[UMAX]] +-; CHECK-NEXT: [[TMP11:%.*]] = icmp ult i32 0, [[EXIT_MAINLOOP_AT]] +-; CHECK-NEXT: br i1 [[TMP11]], label [[LOOP_PREHEADER2:%.*]], label [[MAIN_PSEUDO_EXIT:%.*]] +-; CHECK: loop.preheader2: ++; CHECK-NEXT: [[TMP0:%.*]] = icmp slt i32 [[BOUND:%.*]], 0 ++; CHECK-NEXT: [[SMAX:%.*]] = select i1 [[TMP0]], i32 [[BOUND]], i32 0 ++; CHECK-NEXT: [[TMP1:%.*]] = sub i32 [[BOUND]], [[SMAX]] ++; CHECK-NEXT: [[TMP2:%.*]] = icmp sgt i32 [[SMAX]], -1 ++; CHECK-NEXT: [[SMIN:%.*]] = select i1 [[TMP2]], i32 [[SMAX]], i32 -1 ++; CHECK-NEXT: [[TMP3:%.*]] = add i32 [[SMIN]], 1 ++; CHECK-NEXT: [[TMP4:%.*]] = mul i32 [[TMP1]], [[TMP3]] ++; CHECK-NEXT: [[TMP5:%.*]] = icmp ult i32 [[N]], [[TMP4]] ++; CHECK-NEXT: [[EXIT_MAINLOOP_AT:%.*]] = select i1 [[TMP5]], i32 [[N]], i32 [[TMP4]] ++; CHECK-NEXT: [[TMP6:%.*]] = icmp ult i32 0, [[EXIT_MAINLOOP_AT]] ++; CHECK-NEXT: br i1 [[TMP6]], label [[LOOP_PREHEADER1:%.*]], label [[MAIN_PSEUDO_EXIT:%.*]] ++; CHECK: loop.preheader1: + ; CHECK-NEXT: br label [[LOOP:%.*]] + ; CHECK: loop: +-; CHECK-NEXT: [[IDX:%.*]] = phi i32 [ [[IDX_NEXT:%.*]], [[IN_BOUNDS:%.*]] ], [ 0, [[LOOP_PREHEADER2]] ] ++; CHECK-NEXT: [[IDX:%.*]] = phi i32 [ [[IDX_NEXT:%.*]], [[IN_BOUNDS:%.*]] ], [ 0, [[LOOP_PREHEADER1]] ] + ; CHECK-NEXT: [[IDX_NEXT]] = add i32 [[IDX]], 1 + ; CHECK-NEXT: [[ABC:%.*]] = icmp ult i32 [[IDX]], [[BOUND]] +-; CHECK-NEXT: br i1 true, label [[IN_BOUNDS]], label [[OUT_OF_BOUNDS_LOOPEXIT3:%.*]], !prof !0 ++; CHECK-NEXT: br i1 true, label [[IN_BOUNDS]], label [[OUT_OF_BOUNDS_LOOPEXIT2:%.*]], !prof !0 + ; CHECK: in.bounds: + ; CHECK-NEXT: [[ADDR:%.*]] = getelementptr i32, i32* [[ARR:%.*]], i32 [[IDX]] + ; CHECK-NEXT: store i32 0, i32* [[ADDR]] + ; CHECK-NEXT: [[NEXT:%.*]] = icmp ult i32 [[IDX_NEXT]], [[N]] +-; CHECK-NEXT: [[TMP12:%.*]] = icmp ult i32 [[IDX_NEXT]], [[EXIT_MAINLOOP_AT]] +-; CHECK-NEXT: br i1 [[TMP12]], label [[LOOP]], label [[MAIN_EXIT_SELECTOR:%.*]] ++; CHECK-NEXT: [[TMP7:%.*]] = icmp ult i32 [[IDX_NEXT]], [[EXIT_MAINLOOP_AT]] ++; CHECK-NEXT: br i1 [[TMP7]], label [[LOOP]], label [[MAIN_EXIT_SELECTOR:%.*]] + ; CHECK: main.exit.selector: + ; CHECK-NEXT: [[IDX_NEXT_LCSSA:%.*]] = phi i32 [ [[IDX_NEXT]], [[IN_BOUNDS]] ] +-; CHECK-NEXT: [[TMP13:%.*]] = icmp ult i32 [[IDX_NEXT_LCSSA]], [[N]] +-; CHECK-NEXT: br i1 [[TMP13]], label [[MAIN_PSEUDO_EXIT]], label [[EXIT_LOOPEXIT:%.*]] ++; CHECK-NEXT: [[TMP8:%.*]] = icmp ult i32 [[IDX_NEXT_LCSSA]], [[N]] ++; CHECK-NEXT: br i1 [[TMP8]], label [[MAIN_PSEUDO_EXIT]], label [[EXIT_LOOPEXIT:%.*]] + ; CHECK: main.pseudo.exit: + ; CHECK-NEXT: [[IDX_COPY:%.*]] = phi i32 [ 0, [[LOOP_PREHEADER]] ], [ [[IDX_NEXT_LCSSA]], [[MAIN_EXIT_SELECTOR]] ] + ; CHECK-NEXT: [[INDVAR_END:%.*]] = phi i32 [ 0, [[LOOP_PREHEADER]] ], [ [[IDX_NEXT_LCSSA]], [[MAIN_EXIT_SELECTOR]] ] + ; CHECK-NEXT: br label [[POSTLOOP:%.*]] + ; CHECK: out.of.bounds.loopexit: + ; CHECK-NEXT: br label [[OUT_OF_BOUNDS:%.*]] +-; CHECK: out.of.bounds.loopexit3: ++; CHECK: out.of.bounds.loopexit2: + ; CHECK-NEXT: br label [[OUT_OF_BOUNDS]] + ; CHECK: out.of.bounds: + ; CHECK-NEXT: ret void +diff --git a/test/Transforms/IRCE/single-access-no-preloop.ll b/test/Transforms/IRCE/single-access-no-preloop.ll +index fb643139c6d..7bf36f7c254 100644 +--- a/test/Transforms/IRCE/single-access-no-preloop.ll ++++ b/test/Transforms/IRCE/single-access-no-preloop.ll +@@ -86,15 +86,13 @@ define void @single_access_no_preloop_with_offset(i32 *%arr, i32 *%a_len_ptr, i3 + ; CHECK-LABEL: @single_access_no_preloop_with_offset( + + ; CHECK: loop.preheader: +-; CHECK: [[not_n:[^ ]+]] = sub i32 -1, %n +-; CHECK: [[not_safe_range_end:[^ ]+]] = sub i32 3, %len +-; CHECK: [[not_exit_main_loop_at_hiclamp_cmp:[^ ]+]] = icmp sgt i32 [[not_n]], [[not_safe_range_end]] +-; CHECK: [[not_exit_main_loop_at_hiclamp:[^ ]+]] = select i1 [[not_exit_main_loop_at_hiclamp_cmp]], i32 [[not_n]], i32 [[not_safe_range_end]] +-; CHECK: [[exit_main_loop_at_hiclamp:[^ ]+]] = sub i32 -1, [[not_exit_main_loop_at_hiclamp]] ++; CHECK: [[safe_range_end:[^ ]+]] = add i32 %len, -4 ++; CHECK: [[exit_main_loop_at_hiclamp_cmp:[^ ]+]] = icmp slt i32 %n, [[safe_range_end]] ++; CHECK: [[exit_main_loop_at_hiclamp:[^ ]+]] = select i1 [[exit_main_loop_at_hiclamp_cmp]], i32 %n, i32 [[safe_range_end]] + ; CHECK: [[exit_main_loop_at_loclamp_cmp:[^ ]+]] = icmp sgt i32 [[exit_main_loop_at_hiclamp]], 0 + ; CHECK: [[exit_main_loop_at_loclamp:[^ ]+]] = select i1 [[exit_main_loop_at_loclamp_cmp]], i32 [[exit_main_loop_at_hiclamp]], i32 0 + ; CHECK: [[enter_main_loop:[^ ]+]] = icmp slt i32 0, [[exit_main_loop_at_loclamp]] +-; CHECK: br i1 [[enter_main_loop]], label %loop.preheader2, label %main.pseudo.exit ++; CHECK: br i1 [[enter_main_loop]], label %[[loop_preheader:[^ ,]+]], label %main.pseudo.exit + + ; CHECK: loop: + ; CHECK: br i1 true, label %in.bounds, label %out.of.bounds +diff --git a/test/Transforms/IRCE/single-access-with-preloop.ll b/test/Transforms/IRCE/single-access-with-preloop.ll +index 6f3b0324e39..bd235aa4a73 100644 +--- a/test/Transforms/IRCE/single-access-with-preloop.ll ++++ b/test/Transforms/IRCE/single-access-with-preloop.ll +@@ -34,11 +34,9 @@ define void @single_access_with_preloop(i32 *%arr, i32 *%a_len_ptr, i32 %n, i32 + ; CHECK: [[check_min_sint_offset:[^ ]+]] = icmp sgt i32 %offset, -2147483647 + ; CHECK: [[safe_offset_preloop:[^ ]+]] = select i1 [[check_min_sint_offset]], i32 %offset, i32 -2147483647 + ; If Offset was a SINT_MIN, we could have an overflow here. That is why we calculated its safe version. +-; CHECK: [[not_safe_start:[^ ]+]] = add i32 [[safe_offset_preloop]], -1 +-; CHECK: [[not_n:[^ ]+]] = sub i32 -1, %n +-; CHECK: [[not_exit_preloop_at_cond_loclamp:[^ ]+]] = icmp sgt i32 [[not_safe_start]], [[not_n]] +-; CHECK: [[not_exit_preloop_at_loclamp:[^ ]+]] = select i1 [[not_exit_preloop_at_cond_loclamp]], i32 [[not_safe_start]], i32 [[not_n]] +-; CHECK: [[exit_preloop_at_loclamp:[^ ]+]] = sub i32 -1, [[not_exit_preloop_at_loclamp]] ++; CHECK: [[safe_start:[^ ]+]] = sub i32 0, [[safe_offset_preloop]] ++; CHECK: [[exit_preloop_at_cond_loclamp:[^ ]+]] = icmp slt i32 %n, [[safe_start]] ++; CHECK: [[exit_preloop_at_loclamp:[^ ]+]] = select i1 [[exit_preloop_at_cond_loclamp]], i32 %n, i32 [[safe_start]] + ; CHECK: [[exit_preloop_at_cond:[^ ]+]] = icmp sgt i32 [[exit_preloop_at_loclamp]], 0 + ; CHECK: [[exit_preloop_at:[^ ]+]] = select i1 [[exit_preloop_at_cond]], i32 [[exit_preloop_at_loclamp]], i32 0 + +@@ -46,17 +44,15 @@ define void @single_access_with_preloop(i32 *%arr, i32 *%a_len_ptr, i32 %n, i32 + ; CHECK: [[len_minus_sint_max:[^ ]+]] = add i32 %len, -2147483647 + ; CHECK: [[check_len_min_sint_offset:[^ ]+]] = icmp sgt i32 %offset, [[len_minus_sint_max]] + ; CHECK: [[safe_offset_mainloop:[^ ]+]] = select i1 [[check_len_min_sint_offset]], i32 %offset, i32 [[len_minus_sint_max]] +-; CHECK: [[not_safe_start_2:[^ ]+]] = add i32 [[safe_offset_mainloop]], -1 + ; If Offset was a SINT_MIN, we could have an overflow here. That is why we calculated its safe version. +-; CHECK: [[not_safe_upper_end:[^ ]+]] = sub i32 [[not_safe_start_2]], %len +-; CHECK: [[not_exit_mainloop_at_cond_loclamp:[^ ]+]] = icmp sgt i32 [[not_safe_upper_end]], [[not_n]] +-; CHECK: [[not_exit_mainloop_at_loclamp:[^ ]+]] = select i1 [[not_exit_mainloop_at_cond_loclamp]], i32 [[not_safe_upper_end]], i32 [[not_n]] ++; CHECK: [[safe_upper_end:[^ ]+]] = sub i32 %len, [[safe_offset_mainloop]] ++; CHECK: [[exit_mainloop_at_cond_loclamp:[^ ]+]] = icmp slt i32 %n, [[safe_upper_end]] ++; CHECK: [[exit_mainloop_at_loclamp:[^ ]+]] = select i1 [[exit_mainloop_at_cond_loclamp]], i32 %n, i32 [[safe_upper_end]] + ; CHECK: [[check_offset_mainloop_2:[^ ]+]] = icmp sgt i32 %offset, 0 + ; CHECK: [[safe_offset_mainloop_2:[^ ]+]] = select i1 [[check_offset_mainloop_2]], i32 %offset, i32 0 +-; CHECK: [[not_safe_lower_end:[^ ]+]] = add i32 [[safe_offset_mainloop_2]], -2147483648 +-; CHECK: [[not_exit_mainloop_at_cond_hiclamp:[^ ]+]] = icmp sgt i32 [[not_exit_mainloop_at_loclamp]], [[not_safe_lower_end]] +-; CHECK: [[not_exit_mainloop_at_hiclamp:[^ ]+]] = select i1 [[not_exit_mainloop_at_cond_hiclamp]], i32 [[not_exit_mainloop_at_loclamp]], i32 [[not_safe_lower_end]] +-; CHECK: [[exit_mainloop_at_hiclamp:[^ ]+]] = sub i32 -1, [[not_exit_mainloop_at_hiclamp]] ++; CHECK: [[safe_lower_end:[^ ]+]] = sub i32 2147483647, [[safe_offset_mainloop_2]] ++; CHECK: [[exit_mainloop_at_cond_hiclamp:[^ ]+]] = icmp slt i32 [[exit_mainloop_at_loclamp]], [[safe_lower_end]] ++; CHECK: [[exit_mainloop_at_hiclamp:[^ ]+]] = select i1 [[exit_mainloop_at_cond_hiclamp]], i32 [[exit_mainloop_at_loclamp]], i32 [[safe_lower_end]] + ; CHECK: [[exit_mainloop_at_cmp:[^ ]+]] = icmp sgt i32 [[exit_mainloop_at_hiclamp]], 0 + ; CHECK: [[exit_mainloop_at:[^ ]+]] = select i1 [[exit_mainloop_at_cmp]], i32 [[exit_mainloop_at_hiclamp]], i32 0 + +@@ -67,7 +63,7 @@ define void @single_access_with_preloop(i32 *%arr, i32 *%a_len_ptr, i32 %n, i32 + ; CHECK: %abc.high = icmp slt i32 %array.idx, %len + ; CHECK: %abc.low = icmp sge i32 %array.idx, 0 + ; CHECK: %abc = and i1 true, true +-; CHECK: br i1 %abc, label %in.bounds, label %out.of.bounds.loopexit11 ++; CHECK: br i1 %abc, label %in.bounds, label %[[loopexit:[^ ,]+]] + + ; CHECK: in.bounds: + ; CHECK: [[continue_mainloop_cond:[^ ]+]] = icmp slt i32 %idx.next, [[exit_mainloop_at]] +diff --git a/test/Transforms/IRCE/unsigned_comparisons_ugt.ll b/test/Transforms/IRCE/unsigned_comparisons_ugt.ll +index 8f00c733569..3451d65c7bb 100644 +--- a/test/Transforms/IRCE/unsigned_comparisons_ugt.ll ++++ b/test/Transforms/IRCE/unsigned_comparisons_ugt.ll +@@ -58,8 +58,8 @@ define void @test_02(i32* %arr, i32* %a_len_ptr) #0 { + ; CHECK: entry: + ; CHECK-NEXT: %len = load i32, i32* %a_len_ptr, !range !0 + ; CHECK-NEXT: [[COND1:%[^ ]+]] = icmp ugt i32 %len, 1 +-; CHECK-NEXT: %umax = select i1 [[COND1]], i32 %len, i32 1 +-; CHECK-NEXT: %exit.preloop.at = add i32 %umax, -1 ++; CHECK-NEXT: [[UMIN:%[^ ]+]] = select i1 [[COND1]], i32 %len, i32 1 ++; CHECK-NEXT: %exit.preloop.at = add i32 [[UMIN]], -1 + ; CHECK-NEXT: [[COND2:%[^ ]+]] = icmp ugt i32 100, %exit.preloop.at + ; CHECK-NEXT: br i1 [[COND2]], label %loop.preloop.preheader, label %preloop.pseudo.exit + ; CHECK: mainloop: +@@ -149,8 +149,8 @@ define void @test_04(i32* %arr, i32* %a_len_ptr) #0 { + ; CHECK: entry: + ; CHECK-NEXT: %len = load i32, i32* %a_len_ptr, !range !0 + ; CHECK-NEXT: [[COND1:%[^ ]+]] = icmp ugt i32 %len, 1 +-; CHECK-NEXT: %umax = select i1 [[COND1]], i32 %len, i32 1 +-; CHECK-NEXT: %exit.preloop.at = add i32 %umax, -1 ++; CHECK-NEXT: [[UMIN:%[^ ]+]] = select i1 [[COND1]], i32 %len, i32 1 ++; CHECK-NEXT: %exit.preloop.at = add i32 [[UMIN]], -1 + ; CHECK-NEXT: [[COND2:%[^ ]+]] = icmp ugt i32 -2147483648, %exit.preloop.at + ; CHECK-NEXT: br i1 [[COND2]], label %loop.preloop.preheader, label %preloop.pseudo.exit + ; CHECK: mainloop: +diff --git a/test/Transforms/IRCE/unsigned_comparisons_ult.ll b/test/Transforms/IRCE/unsigned_comparisons_ult.ll +index dc59c11df1b..aca3c3d192e 100644 +--- a/test/Transforms/IRCE/unsigned_comparisons_ult.ll ++++ b/test/Transforms/IRCE/unsigned_comparisons_ult.ll +@@ -61,8 +61,8 @@ define void @test_02(i32* %arr, i32* %a_len_ptr) #0 { + ; CHECK: entry: + ; CHECK-NEXT: %len = load i32, i32* %a_len_ptr, !range !0 + ; CHECK-NEXT: [[COND1:%[^ ]+]] = icmp ugt i32 %len, 1 +-; CHECK-NEXT: %umax = select i1 [[COND1]], i32 %len, i32 1 +-; CHECK-NEXT: %exit.preloop.at = add i32 %umax, -1 ++; CHECK-NEXT: [[UMIN:%[^ ]+]] = select i1 [[COND1]], i32 %len, i32 1 ++; CHECK-NEXT: %exit.preloop.at = add i32 [[UMIN]], -1 + ; CHECK-NEXT: [[COND2:%[^ ]+]] = icmp ugt i32 100, %exit.preloop.at + ; CHECK-NEXT: br i1 [[COND2]], label %loop.preloop.preheader, label %preloop.pseudo.exit + ; CHECK: mainloop: +@@ -194,8 +194,8 @@ define void @test_05(i32* %arr, i32* %a_len_ptr) #0 { + ; CHECK: entry: + ; CHECK-NEXT: %len = load i32, i32* %a_len_ptr, !range !0 + ; CHECK-NEXT: [[COND1:%[^ ]+]] = icmp ugt i32 %len, 1 +-; CHECK-NEXT: %umax = select i1 [[COND1]], i32 %len, i32 1 +-; CHECK-NEXT: %exit.preloop.at = add i32 %umax, -1 ++; CHECK-NEXT: [[UMIN:%[^ ]+]] = select i1 [[COND1]], i32 %len, i32 1 ++; CHECK-NEXT: %exit.preloop.at = add i32 [[UMIN]], -1 + ; CHECK-NEXT: [[COND2:%[^ ]+]] = icmp ugt i32 -2147483648, %exit.preloop.at + ; CHECK-NEXT: br i1 [[COND2]], label %loop.preloop.preheader, label %preloop.pseudo.exit + ; CHECK: mainloop: +diff --git a/test/Transforms/LoopStrengthReduce/2013-01-14-ReuseCast.ll b/test/Transforms/LoopStrengthReduce/2013-01-14-ReuseCast.ll +index ea3f6077231..d5232e1874c 100644 +--- a/test/Transforms/LoopStrengthReduce/2013-01-14-ReuseCast.ll ++++ b/test/Transforms/LoopStrengthReduce/2013-01-14-ReuseCast.ll +@@ -14,8 +14,6 @@ target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f3 + ; current LSR cost model. + ; CHECK-NOT: = ptrtoint i8* undef to i64 + ; CHECK: .lr.ph +-; CHECK: [[TMP:%[^ ]+]] = add i64 %tmp{{[0-9]+}}, -1 +-; CHECK: sub i64 [[TMP]], %tmp{{[0-9]+}} + ; CHECK: ret void + define void @VerifyDiagnosticConsumerTest() unnamed_addr nounwind uwtable align 2 { + bb: +diff --git a/test/Transforms/LoopVectorize/X86/pr35432.ll b/test/Transforms/LoopVectorize/X86/pr35432.ll +index 1f2a2061586..6aaa13c183a 100644 +--- a/test/Transforms/LoopVectorize/X86/pr35432.ll ++++ b/test/Transforms/LoopVectorize/X86/pr35432.ll +@@ -27,7 +27,6 @@ define i32 @main() local_unnamed_addr #0 { + ; CHECK-NEXT: [[CMP8:%.*]] = icmp eq i32 [[CONV17]], 0 + ; CHECK-NEXT: br i1 [[CMP8]], label [[FOR_BODY_LR_PH:%.*]], label [[FOR_END12:%.*]] + ; CHECK: for.body.lr.ph: +-; CHECK-NEXT: [[TMP3:%.*]] = sub i32 -1, [[TMP2]] + ; CHECK-NEXT: br label [[FOR_BODY:%.*]] + ; CHECK: for.body: + ; CHECK-NEXT: [[STOREMERGE_IN9:%.*]] = phi i32 [ [[TMP2]], [[FOR_BODY_LR_PH]] ], [ [[ADD:%.*]], [[FOR_INC9:%.*]] ] +@@ -37,77 +36,74 @@ define i32 @main() local_unnamed_addr #0 { + ; CHECK: for.body8.lr.ph: + ; CHECK-NEXT: [[CONV3:%.*]] = trunc i32 [[STOREMERGE_IN9]] to i8 + ; CHECK-NEXT: [[DOTPROMOTED:%.*]] = load i32, i32* getelementptr inbounds ([192 x [192 x i32]], [192 x [192 x i32]]* @a, i64 0, i64 0, i64 0), align 16 +-; CHECK-NEXT: [[TMP4:%.*]] = add i8 [[CONV3]], -1 +-; CHECK-NEXT: [[TMP5:%.*]] = zext i8 [[TMP4]] to i32 +-; CHECK-NEXT: [[TMP6:%.*]] = sub i32 -1, [[TMP5]] +-; CHECK-NEXT: [[TMP7:%.*]] = icmp ugt i32 [[TMP3]], [[TMP6]] +-; CHECK-NEXT: [[UMAX:%.*]] = select i1 [[TMP7]], i32 [[TMP3]], i32 [[TMP6]] +-; CHECK-NEXT: [[TMP8:%.*]] = add i32 [[UMAX]], 2 +-; CHECK-NEXT: [[TMP9:%.*]] = add i32 [[TMP8]], [[TMP5]] +-; CHECK-NEXT: [[MIN_ITERS_CHECK:%.*]] = icmp ult i32 [[TMP9]], 8 ++; CHECK-NEXT: [[TMP3:%.*]] = add i8 [[CONV3]], -1 ++; CHECK-NEXT: [[TMP4:%.*]] = zext i8 [[TMP3]] to i32 ++; CHECK-NEXT: [[TMP5:%.*]] = add i32 [[TMP4]], 1 ++; CHECK-NEXT: [[TMP6:%.*]] = icmp ult i32 [[TMP2]], [[TMP4]] ++; CHECK-NEXT: [[UMAX:%.*]] = select i1 [[TMP6]], i32 [[TMP2]], i32 [[TMP4]] ++; CHECK-NEXT: [[TMP7:%.*]] = sub i32 [[TMP5]], [[UMAX]] ++; CHECK-NEXT: [[MIN_ITERS_CHECK:%.*]] = icmp ult i32 [[TMP7]], 8 + ; CHECK-NEXT: br i1 [[MIN_ITERS_CHECK]], label [[SCALAR_PH:%.*]], label [[VECTOR_SCEVCHECK:%.*]] + ; CHECK: vector.scevcheck: +-; CHECK-NEXT: [[TMP10:%.*]] = add i8 [[CONV3]], -1 +-; CHECK-NEXT: [[TMP11:%.*]] = zext i8 [[TMP10]] to i32 +-; CHECK-NEXT: [[TMP12:%.*]] = sub i32 -1, [[TMP11]] +-; CHECK-NEXT: [[TMP13:%.*]] = icmp ugt i32 [[TMP3]], [[TMP12]] +-; CHECK-NEXT: [[UMAX1:%.*]] = select i1 [[TMP13]], i32 [[TMP3]], i32 [[TMP12]] +-; CHECK-NEXT: [[TMP14:%.*]] = add i32 [[UMAX1]], 1 +-; CHECK-NEXT: [[TMP15:%.*]] = add i32 [[TMP14]], [[TMP11]] +-; CHECK-NEXT: [[TMP16:%.*]] = trunc i32 [[TMP15]] to i8 +-; CHECK-NEXT: [[MUL:%.*]] = call { i8, i1 } @llvm.umul.with.overflow.i8(i8 1, i8 [[TMP16]]) ++; CHECK-NEXT: [[TMP8:%.*]] = add i8 [[CONV3]], -1 ++; CHECK-NEXT: [[TMP9:%.*]] = zext i8 [[TMP8]] to i32 ++; CHECK-NEXT: [[TMP10:%.*]] = icmp ult i32 [[TMP2]], [[TMP9]] ++; CHECK-NEXT: [[UMAX1:%.*]] = select i1 [[TMP10]], i32 [[TMP2]], i32 [[TMP9]] ++; CHECK-NEXT: [[TMP11:%.*]] = sub i32 [[TMP9]], [[UMAX1]] ++; CHECK-NEXT: [[TMP12:%.*]] = trunc i32 [[TMP11]] to i8 ++; CHECK-NEXT: [[MUL:%.*]] = call { i8, i1 } @llvm.umul.with.overflow.i8(i8 1, i8 [[TMP12]]) + ; CHECK-NEXT: [[MUL_RESULT:%.*]] = extractvalue { i8, i1 } [[MUL]], 0 + ; CHECK-NEXT: [[MUL_OVERFLOW:%.*]] = extractvalue { i8, i1 } [[MUL]], 1 +-; CHECK-NEXT: [[TMP17:%.*]] = add i8 [[TMP10]], [[MUL_RESULT]] +-; CHECK-NEXT: [[TMP18:%.*]] = sub i8 [[TMP10]], [[MUL_RESULT]] +-; CHECK-NEXT: [[TMP19:%.*]] = icmp ugt i8 [[TMP18]], [[TMP10]] +-; CHECK-NEXT: [[TMP20:%.*]] = icmp ult i8 [[TMP17]], [[TMP10]] +-; CHECK-NEXT: [[TMP21:%.*]] = select i1 true, i1 [[TMP19]], i1 [[TMP20]] +-; CHECK-NEXT: [[TMP22:%.*]] = icmp ugt i32 [[TMP15]], 255 +-; CHECK-NEXT: [[TMP23:%.*]] = or i1 [[TMP21]], [[TMP22]] +-; CHECK-NEXT: [[TMP24:%.*]] = or i1 [[TMP23]], [[MUL_OVERFLOW]] +-; CHECK-NEXT: [[TMP25:%.*]] = or i1 false, [[TMP24]] +-; CHECK-NEXT: br i1 [[TMP25]], label [[SCALAR_PH]], label [[VECTOR_PH:%.*]] ++; CHECK-NEXT: [[TMP13:%.*]] = add i8 [[TMP8]], [[MUL_RESULT]] ++; CHECK-NEXT: [[TMP14:%.*]] = sub i8 [[TMP8]], [[MUL_RESULT]] ++; CHECK-NEXT: [[TMP15:%.*]] = icmp ugt i8 [[TMP14]], [[TMP8]] ++; CHECK-NEXT: [[TMP16:%.*]] = icmp ult i8 [[TMP13]], [[TMP8]] ++; CHECK-NEXT: [[TMP17:%.*]] = select i1 true, i1 [[TMP15]], i1 [[TMP16]] ++; CHECK-NEXT: [[TMP18:%.*]] = icmp ugt i32 [[TMP11]], 255 ++; CHECK-NEXT: [[TMP19:%.*]] = or i1 [[TMP17]], [[TMP18]] ++; CHECK-NEXT: [[TMP20:%.*]] = or i1 [[TMP19]], [[MUL_OVERFLOW]] ++; CHECK-NEXT: [[TMP21:%.*]] = or i1 false, [[TMP20]] ++; CHECK-NEXT: br i1 [[TMP21]], label [[SCALAR_PH]], label [[VECTOR_PH:%.*]] + ; CHECK: vector.ph: +-; CHECK-NEXT: [[N_MOD_VF:%.*]] = urem i32 [[TMP9]], 8 +-; CHECK-NEXT: [[N_VEC:%.*]] = sub i32 [[TMP9]], [[N_MOD_VF]] ++; CHECK-NEXT: [[N_MOD_VF:%.*]] = urem i32 [[TMP7]], 8 ++; CHECK-NEXT: [[N_VEC:%.*]] = sub i32 [[TMP7]], [[N_MOD_VF]] + ; CHECK-NEXT: [[CAST_CRD:%.*]] = trunc i32 [[N_VEC]] to i8 + ; CHECK-NEXT: [[IND_END:%.*]] = sub i8 [[CONV3]], [[CAST_CRD]] +-; CHECK-NEXT: [[TMP26:%.*]] = insertelement <4 x i32> zeroinitializer, i32 [[DOTPROMOTED]], i32 0 ++; CHECK-NEXT: [[TMP22:%.*]] = insertelement <4 x i32> zeroinitializer, i32 [[DOTPROMOTED]], i32 0 + ; CHECK-NEXT: br label [[VECTOR_BODY:%.*]] + ; CHECK: vector.body: + ; CHECK-NEXT: [[INDEX:%.*]] = phi i32 [ 0, [[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], [[VECTOR_BODY]] ] +-; CHECK-NEXT: [[VEC_PHI:%.*]] = phi <4 x i32> [ [[TMP26]], [[VECTOR_PH]] ], [ [[TMP30:%.*]], [[VECTOR_BODY]] ] +-; CHECK-NEXT: [[VEC_PHI2:%.*]] = phi <4 x i32> [ zeroinitializer, [[VECTOR_PH]] ], [ [[TMP31:%.*]], [[VECTOR_BODY]] ] +-; CHECK-NEXT: [[TMP27:%.*]] = trunc i32 [[INDEX]] to i8 +-; CHECK-NEXT: [[OFFSET_IDX:%.*]] = sub i8 [[CONV3]], [[TMP27]] ++; CHECK-NEXT: [[VEC_PHI:%.*]] = phi <4 x i32> [ [[TMP22]], [[VECTOR_PH]] ], [ [[TMP26:%.*]], [[VECTOR_BODY]] ] ++; CHECK-NEXT: [[VEC_PHI2:%.*]] = phi <4 x i32> [ zeroinitializer, [[VECTOR_PH]] ], [ [[TMP27:%.*]], [[VECTOR_BODY]] ] ++; CHECK-NEXT: [[TMP23:%.*]] = trunc i32 [[INDEX]] to i8 ++; CHECK-NEXT: [[OFFSET_IDX:%.*]] = sub i8 [[CONV3]], [[TMP23]] + ; CHECK-NEXT: [[BROADCAST_SPLATINSERT:%.*]] = insertelement <4 x i8> undef, i8 [[OFFSET_IDX]], i32 0 + ; CHECK-NEXT: [[BROADCAST_SPLAT:%.*]] = shufflevector <4 x i8> [[BROADCAST_SPLATINSERT]], <4 x i8> undef, <4 x i32> zeroinitializer + ; CHECK-NEXT: [[INDUCTION:%.*]] = add <4 x i8> [[BROADCAST_SPLAT]], + ; CHECK-NEXT: [[INDUCTION3:%.*]] = add <4 x i8> [[BROADCAST_SPLAT]], +-; CHECK-NEXT: [[TMP28:%.*]] = add i8 [[OFFSET_IDX]], 0 +-; CHECK-NEXT: [[TMP29:%.*]] = add i8 [[OFFSET_IDX]], -4 +-; CHECK-NEXT: [[TMP30]] = add <4 x i32> [[VEC_PHI]], +-; CHECK-NEXT: [[TMP31]] = add <4 x i32> [[VEC_PHI2]], +-; CHECK-NEXT: [[TMP32:%.*]] = add i8 [[TMP28]], -1 +-; CHECK-NEXT: [[TMP33:%.*]] = add i8 [[TMP29]], -1 +-; CHECK-NEXT: [[TMP34:%.*]] = zext i8 [[TMP32]] to i32 +-; CHECK-NEXT: [[TMP35:%.*]] = zext i8 [[TMP33]] to i32 ++; CHECK-NEXT: [[TMP24:%.*]] = add i8 [[OFFSET_IDX]], 0 ++; CHECK-NEXT: [[TMP25:%.*]] = add i8 [[OFFSET_IDX]], -4 ++; CHECK-NEXT: [[TMP26]] = add <4 x i32> [[VEC_PHI]], ++; CHECK-NEXT: [[TMP27]] = add <4 x i32> [[VEC_PHI2]], ++; CHECK-NEXT: [[TMP28:%.*]] = add i8 [[TMP24]], -1 ++; CHECK-NEXT: [[TMP29:%.*]] = add i8 [[TMP25]], -1 ++; CHECK-NEXT: [[TMP30:%.*]] = zext i8 [[TMP28]] to i32 ++; CHECK-NEXT: [[TMP31:%.*]] = zext i8 [[TMP29]] to i32 + ; CHECK-NEXT: [[INDEX_NEXT]] = add i32 [[INDEX]], 8 +-; CHECK-NEXT: [[TMP36:%.*]] = icmp eq i32 [[INDEX_NEXT]], [[N_VEC]] +-; CHECK-NEXT: br i1 [[TMP36]], label [[MIDDLE_BLOCK:%.*]], label [[VECTOR_BODY]], !llvm.loop !0 ++; CHECK-NEXT: [[TMP32:%.*]] = icmp eq i32 [[INDEX_NEXT]], [[N_VEC]] ++; CHECK-NEXT: br i1 [[TMP32]], label [[MIDDLE_BLOCK:%.*]], label [[VECTOR_BODY]], !llvm.loop !0 + ; CHECK: middle.block: +-; CHECK-NEXT: [[BIN_RDX:%.*]] = add <4 x i32> [[TMP31]], [[TMP30]] ++; CHECK-NEXT: [[BIN_RDX:%.*]] = add <4 x i32> [[TMP27]], [[TMP26]] + ; CHECK-NEXT: [[RDX_SHUF:%.*]] = shufflevector <4 x i32> [[BIN_RDX]], <4 x i32> undef, <4 x i32> + ; CHECK-NEXT: [[BIN_RDX4:%.*]] = add <4 x i32> [[BIN_RDX]], [[RDX_SHUF]] + ; CHECK-NEXT: [[RDX_SHUF5:%.*]] = shufflevector <4 x i32> [[BIN_RDX4]], <4 x i32> undef, <4 x i32> + ; CHECK-NEXT: [[BIN_RDX6:%.*]] = add <4 x i32> [[BIN_RDX4]], [[RDX_SHUF5]] +-; CHECK-NEXT: [[TMP37:%.*]] = extractelement <4 x i32> [[BIN_RDX6]], i32 0 +-; CHECK-NEXT: [[CMP_N:%.*]] = icmp eq i32 [[TMP9]], [[N_VEC]] ++; CHECK-NEXT: [[TMP33:%.*]] = extractelement <4 x i32> [[BIN_RDX6]], i32 0 ++; CHECK-NEXT: [[CMP_N:%.*]] = icmp eq i32 [[TMP7]], [[N_VEC]] + ; CHECK-NEXT: br i1 [[CMP_N]], label [[FOR_COND4_FOR_INC9_CRIT_EDGE:%.*]], label [[SCALAR_PH]] + ; CHECK: scalar.ph: + ; CHECK-NEXT: [[BC_RESUME_VAL:%.*]] = phi i8 [ [[IND_END]], [[MIDDLE_BLOCK]] ], [ [[CONV3]], [[FOR_BODY8_LR_PH]] ], [ [[CONV3]], [[VECTOR_SCEVCHECK]] ] +-; CHECK-NEXT: [[BC_MERGE_RDX:%.*]] = phi i32 [ [[DOTPROMOTED]], [[FOR_BODY8_LR_PH]] ], [ [[DOTPROMOTED]], [[VECTOR_SCEVCHECK]] ], [ [[TMP37]], [[MIDDLE_BLOCK]] ] ++; CHECK-NEXT: [[BC_MERGE_RDX:%.*]] = phi i32 [ [[DOTPROMOTED]], [[FOR_BODY8_LR_PH]] ], [ [[DOTPROMOTED]], [[VECTOR_SCEVCHECK]] ], [ [[TMP33]], [[MIDDLE_BLOCK]] ] + ; CHECK-NEXT: br label [[FOR_BODY8:%.*]] + ; CHECK: for.body8: + ; CHECK-NEXT: [[INC5:%.*]] = phi i32 [ [[BC_MERGE_RDX]], [[SCALAR_PH]] ], [ [[INC:%.*]], [[FOR_BODY8]] ] +@@ -118,7 +114,7 @@ define i32 @main() local_unnamed_addr #0 { + ; CHECK-NEXT: [[CMP6:%.*]] = icmp ult i32 [[TMP2]], [[CONV5]] + ; CHECK-NEXT: br i1 [[CMP6]], label [[FOR_BODY8]], label [[FOR_COND4_FOR_INC9_CRIT_EDGE]], !llvm.loop !2 + ; CHECK: for.cond4.for.inc9_crit_edge: +-; CHECK-NEXT: [[INC_LCSSA:%.*]] = phi i32 [ [[INC]], [[FOR_BODY8]] ], [ [[TMP37]], [[MIDDLE_BLOCK]] ] ++; CHECK-NEXT: [[INC_LCSSA:%.*]] = phi i32 [ [[INC]], [[FOR_BODY8]] ], [ [[TMP33]], [[MIDDLE_BLOCK]] ] + ; CHECK-NEXT: store i32 [[INC_LCSSA]], i32* getelementptr inbounds ([192 x [192 x i32]], [192 x [192 x i32]]* @a, i64 0, i64 0, i64 0), align 16 + ; CHECK-NEXT: br label [[FOR_INC9]] + ; CHECK: for.inc9: diff --git a/deps/patches/llvm-D27629-AArch64-large_model_4.0.patch b/deps/patches/llvm-D27629-AArch64-large_model_4.0.patch new file mode 100644 index 0000000..3e4ae2e --- /dev/null +++ b/deps/patches/llvm-D27629-AArch64-large_model_4.0.patch @@ -0,0 +1,77 @@ +From 6e7b660ee185445640110c80d80aafd436682fca Mon Sep 17 00:00:00 2001 +From: Yichao Yu +Date: Fri, 9 Dec 2016 15:59:46 -0500 +Subject: [PATCH] Fix unwind info relocation with large code model on AArch64 + +--- + lib/MC/MCObjectFileInfo.cpp | 2 ++ + .../AArch64/ELF_ARM64_BE-large-relocations.s | 18 ++++++++++++++++++ + .../RuntimeDyld/AArch64/ELF_ARM64_large-relocations.s | 18 ++++++++++++++++++ + 3 files changed, 38 insertions(+) + create mode 100644 test/ExecutionEngine/RuntimeDyld/AArch64/ELF_ARM64_BE-large-relocations.s + create mode 100644 test/ExecutionEngine/RuntimeDyld/AArch64/ELF_ARM64_large-relocations.s + +diff --git a/lib/MC/MCObjectFileInfo.cpp b/lib/MC/MCObjectFileInfo.cpp +index 687ad3dc83a..da51f4e26d9 100644 +--- a/lib/MC/MCObjectFileInfo.cpp ++++ b/lib/MC/MCObjectFileInfo.cpp +@@ -281,6 +281,8 @@ void MCObjectFileInfo::initELFMCObjectFileInfo(const Triple &T) { + case Triple::mips64el: + FDECFIEncoding = dwarf::DW_EH_PE_sdata8; + break; ++ case Triple::aarch64: ++ case Triple::aarch64_be: + case Triple::x86_64: + FDECFIEncoding = dwarf::DW_EH_PE_pcrel | + ((CMModel == CodeModel::Large) ? dwarf::DW_EH_PE_sdata8 +diff --git a/test/ExecutionEngine/RuntimeDyld/AArch64/ELF_ARM64_BE-large-relocations.s b/test/ExecutionEngine/RuntimeDyld/AArch64/ELF_ARM64_BE-large-relocations.s +new file mode 100644 +index 00000000000..e3eeb02ff01 +--- /dev/null ++++ b/test/ExecutionEngine/RuntimeDyld/AArch64/ELF_ARM64_BE-large-relocations.s +@@ -0,0 +1,18 @@ ++# RUN: llvm-mc -triple=aarch64_be-none-linux-gnu -code-model=large -filetype=obj -o %T/be-large-reloc.o %s ++# RUN: llvm-rtdyld -triple=aarch64_be-none-linux-gnu -verify -map-section be-large-reloc.o,.eh_frame=0x10000 -map-section be-large-reloc.o,.text=0xffff000000000000 -check=%s %T/be-large-reloc.o ++ ++ .text ++ .globl g ++ .p2align 2 ++ .type g,@function ++g: ++ .cfi_startproc ++ mov x0, xzr ++ ret ++ .Lfunc_end0: ++ .size g, .Lfunc_end0-g ++ .cfi_endproc ++ ++# Skip the CIE and load the 8 bytes PC begin pointer. ++# Assuming the CIE and the FDE length are both 4 bytes. ++# rtdyld-check: *{8}(section_addr(be-large-reloc.o, .eh_frame) + (*{4}(section_addr(be-large-reloc.o, .eh_frame))) + 0xc) = g - (section_addr(be-large-reloc.o, .eh_frame) + (*{4}(section_addr(be-large-reloc.o, .eh_frame))) + 0xc) +diff --git a/test/ExecutionEngine/RuntimeDyld/AArch64/ELF_ARM64_large-relocations.s b/test/ExecutionEngine/RuntimeDyld/AArch64/ELF_ARM64_large-relocations.s +new file mode 100644 +index 00000000000..ec30f19c476 +--- /dev/null ++++ b/test/ExecutionEngine/RuntimeDyld/AArch64/ELF_ARM64_large-relocations.s +@@ -0,0 +1,18 @@ ++# RUN: llvm-mc -triple=arm64-none-linux-gnu -code-model=large -filetype=obj -o %T/large-reloc.o %s ++# RUN: llvm-rtdyld -triple=arm64-none-linux-gnu -verify -map-section large-reloc.o,.eh_frame=0x10000 -map-section large-reloc.o,.text=0xffff000000000000 -check=%s %T/large-reloc.o ++ ++ .text ++ .globl g ++ .p2align 2 ++ .type g,@function ++g: ++ .cfi_startproc ++ mov x0, xzr ++ ret ++ .Lfunc_end0: ++ .size g, .Lfunc_end0-g ++ .cfi_endproc ++ ++# Skip the CIE and load the 8 bytes PC begin pointer. ++# Assuming the CIE and the FDE length are both 4 bytes. ++# rtdyld-check: *{8}(section_addr(large-reloc.o, .eh_frame) + (*{4}(section_addr(large-reloc.o, .eh_frame))) + 0xc) = g - (section_addr(large-reloc.o, .eh_frame) + (*{4}(section_addr(large-reloc.o, .eh_frame))) + 0xc) +-- +2.12.2 + diff --git a/deps/patches/llvm-D27629-AArch64-large_model_6.0.1.patch b/deps/patches/llvm-D27629-AArch64-large_model_6.0.1.patch new file mode 100644 index 0000000..89beefd --- /dev/null +++ b/deps/patches/llvm-D27629-AArch64-large_model_6.0.1.patch @@ -0,0 +1,53 @@ +From f76abe65e6d07fea5e838c4f8c9a9421c16debb0 Mon Sep 17 00:00:00 2001 +From: Valentin Churavy +Date: Thu, 5 Jul 2018 12:37:50 -0400 +Subject: [PATCH] Fix unwind info relocation with large code model on AArch64 + +--- + lib/MC/MCObjectFileInfo.cpp | 2 ++ + .../AArch64/ELF_ARM64_large-relocations.s | 20 +++++++++++++++++++ + 2 files changed, 22 insertions(+) + create mode 100644 test/ExecutionEngine/RuntimeDyld/AArch64/ELF_ARM64_large-relocations.s + +diff --git a/lib/MC/MCObjectFileInfo.cpp b/lib/MC/MCObjectFileInfo.cpp +index 328f000f37c..938b35f20d1 100644 +--- a/lib/MC/MCObjectFileInfo.cpp ++++ b/lib/MC/MCObjectFileInfo.cpp +@@ -291,6 +291,8 @@ void MCObjectFileInfo::initELFMCObjectFileInfo(const Triple &T, bool Large) { + break; + case Triple::ppc64: + case Triple::ppc64le: ++ case Triple::aarch64: ++ case Triple::aarch64_be: + case Triple::x86_64: + FDECFIEncoding = dwarf::DW_EH_PE_pcrel | + (Large ? dwarf::DW_EH_PE_sdata8 : dwarf::DW_EH_PE_sdata4); +diff --git a/test/ExecutionEngine/RuntimeDyld/AArch64/ELF_ARM64_large-relocations.s b/test/ExecutionEngine/RuntimeDyld/AArch64/ELF_ARM64_large-relocations.s +new file mode 100644 +index 00000000000..66f28dabd79 +--- /dev/null ++++ b/test/ExecutionEngine/RuntimeDyld/AArch64/ELF_ARM64_large-relocations.s +@@ -0,0 +1,20 @@ ++# RUN: llvm-mc -triple=arm64-none-linux-gnu -large-code-model -filetype=obj -o %T/large-reloc.o %s ++# RUN: llvm-rtdyld -triple=arm64-none-linux-gnu -verify -map-section large-reloc.o,.eh_frame=0x10000 -map-section large-reloc.o,.text=0xffff000000000000 -check=%s %T/large-reloc.o ++# RUN-BE: llvm-mc -triple=aarch64_be-none-linux-gnu -large-code-model -filetype=obj -o %T/be-large-reloc.o %s ++# RUN-BE: llvm-rtdyld -triple=aarch64_be-none-linux-gnu -verify -map-section be-large-reloc.o,.eh_frame=0x10000 -map-section be-large-reloc.o,.text=0xffff000000000000 -check=%s %T/be-large-reloc.o ++ ++ .text ++ .globl g ++ .p2align 2 ++ .type g,@function ++g: ++ .cfi_startproc ++ mov x0, xzr ++ ret ++ .Lfunc_end0: ++ .size g, .Lfunc_end0-g ++ .cfi_endproc ++ ++# Skip the CIE and load the 8 bytes PC begin pointer. ++# Assuming the CIE and the FDE length are both 4 bytes. ++# rtdyld-check: *{8}(section_addr(large-reloc.o, .eh_frame) + (*{4}(section_addr(large-reloc.o, .eh_frame))) + 0xc) = g - (section_addr(large-reloc.o, .eh_frame) + (*{4}(section_addr(large-reloc.o, .eh_frame))) + 0xc) +-- +2.18.0 + diff --git a/deps/patches/llvm-D34078-vectorize-fdiv.patch b/deps/patches/llvm-D34078-vectorize-fdiv.patch new file mode 100644 index 0000000..a6df7d1 --- /dev/null +++ b/deps/patches/llvm-D34078-vectorize-fdiv.patch @@ -0,0 +1,56 @@ +From f94d12b6108b944199b715f31f25a022f75d2feb Mon Sep 17 00:00:00 2001 +From: Yichao Yu +Date: Sat, 10 Jun 2017 08:45:13 -0400 +Subject: [PATCH 4/4] Enable support for floating-point division reductions + +Similar to fsub, fdiv can also be vectorized using fmul. +--- + lib/Transforms/Utils/LoopUtils.cpp | 1 + + test/Transforms/LoopVectorize/float-reduction.ll | 22 ++++++++++++++++++++++ + 2 files changed, 23 insertions(+) + +diff --git a/lib/Transforms/Utils/LoopUtils.cpp b/lib/Transforms/Utils/LoopUtils.cpp +index 3c522786641..a4aced53a95 100644 +--- a/lib/Transforms/Utils/LoopUtils.cpp ++++ b/lib/Transforms/Utils/LoopUtils.cpp +@@ -451,6 +451,7 @@ RecurrenceDescriptor::isRecurrenceInstr(Instruction *I, RecurrenceKind Kind, + return InstDesc(Kind == RK_IntegerOr, I); + case Instruction::Xor: + return InstDesc(Kind == RK_IntegerXor, I); ++ case Instruction::FDiv: + case Instruction::FMul: + return InstDesc(Kind == RK_FloatMult, I, UAI); + case Instruction::FSub: +diff --git a/test/Transforms/LoopVectorize/float-reduction.ll b/test/Transforms/LoopVectorize/float-reduction.ll +index f3b95d0ead7..669c54d55a2 100644 +--- a/test/Transforms/LoopVectorize/float-reduction.ll ++++ b/test/Transforms/LoopVectorize/float-reduction.ll +@@ -44,3 +44,25 @@ for.body: ; preds = %for.body, %entry + for.end: ; preds = %for.body + ret float %sub + } ++ ++;CHECK-LABEL: @foodiv( ++;CHECK: fdiv fast <4 x float> ++;CHECK: ret ++define float @foodiv(float* nocapture %A, i32* nocapture %n) nounwind uwtable readonly ssp { ++entry: ++ br label %for.body ++ ++for.body: ; preds = %for.body, %entry ++ %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ] ++ %sum.04 = phi float [ 1.000000e+00, %entry ], [ %sub, %for.body ] ++ %arrayidx = getelementptr inbounds float, float* %A, i64 %indvars.iv ++ %0 = load float, float* %arrayidx, align 4 ++ %sub = fdiv fast float %sum.04, %0 ++ %indvars.iv.next = add i64 %indvars.iv, 1 ++ %lftr.wideiv = trunc i64 %indvars.iv.next to i32 ++ %exitcond = icmp eq i32 %lftr.wideiv, 200 ++ br i1 %exitcond, label %for.end, label %for.body ++ ++for.end: ; preds = %for.body ++ ret float %sub ++} +-- +2.14.1 + diff --git a/deps/patches/llvm-D42260.patch b/deps/patches/llvm-D42260.patch new file mode 100644 index 0000000..9273101 --- /dev/null +++ b/deps/patches/llvm-D42260.patch @@ -0,0 +1,140 @@ +Index: test/Transforms/JumpThreading/header-succ.ll +=================================================================== +--- a/test/Transforms/JumpThreading/header-succ.ll ++++ b/test/Transforms/JumpThreading/header-succ.ll +@@ -0,0 +1,99 @@ ++; RUN: opt -S -jump-threading < %s | FileCheck %s ++ ++; Check that the heuristic for avoiding accidental introduction of irreducible ++; loops doesn't also prevent us from threading simple constructs where this ++; isn't a problem. ++ ++declare void @opaque_body() ++ ++define void @jump_threading_loopheader() { ++; CHECK-LABEL: @jump_threading_loopheader ++top: ++ br label %entry ++ ++entry: ++ %ind = phi i32 [0, %top], [%nextind, %latch] ++ %nextind = add i32 %ind, 1 ++ %cmp = icmp ule i32 %ind, 10 ++; CHECK: br i1 %cmp, label %latch, label %exit ++ br i1 %cmp, label %body, label %latch ++ ++body: ++ call void @opaque_body() ++; CHECK: br label %entry ++ br label %latch ++ ++latch: ++ %cond = phi i2 [1, %entry], [2, %body] ++ switch i2 %cond, label %unreach [ ++ i2 2, label %entry ++ i2 1, label %exit ++ ] ++ ++unreach: ++ unreachable ++ ++exit: ++ ret void ++} ++ ++; We also need to check the opposite order of the branches, in the switch ++; instruction because jump-threading relies on that to decide which edge to ++; try to thread first. ++define void @jump_threading_loopheader2() { ++; CHECK-LABEL: @jump_threading_loopheader2 ++top: ++ br label %entry ++ ++entry: ++ %ind = phi i32 [0, %top], [%nextind, %latch] ++ %nextind = add i32 %ind, 1 ++ %cmp = icmp ule i32 %ind, 10 ++; CHECK: br i1 %cmp, label %exit, label %latch ++ br i1 %cmp, label %body, label %latch ++ ++body: ++ call void @opaque_body() ++; CHECK: br label %entry ++ br label %latch ++ ++latch: ++ %cond = phi i2 [1, %entry], [2, %body] ++ switch i2 %cond, label %unreach [ ++ i2 1, label %entry ++ i2 2, label %exit ++ ] ++ ++unreach: ++ unreachable ++ ++exit: ++ ret void ++} ++ ++; Check if we can handle undef branch condition. ++define void @jump_threading_loopheader3() { ++; CHECK-LABEL: @jump_threading_loopheader3 ++top: ++ br label %entry ++ ++entry: ++ %ind = phi i32 [0, %top], [%nextind, %latch] ++ %nextind = add i32 %ind, 1 ++ %cmp = icmp ule i32 %ind, 10 ++; CHECK: br i1 %cmp, label %latch, label %exit ++ br i1 %cmp, label %body, label %latch ++ ++body: ++ call void @opaque_body() ++; CHECK: br label %entry ++ br label %latch ++ ++latch: ++ %phi = phi i32 [undef, %entry], [0, %body] ++ %cmp1 = icmp eq i32 %phi, 0 ++ br i1 %cmp1, label %entry, label %exit ++ ++exit: ++ ret void ++} +Index: lib/Transforms/Scalar/JumpThreading.cpp +=================================================================== +--- a/lib/Transforms/Scalar/JumpThreading.cpp ++++ b/lib/Transforms/Scalar/JumpThreading.cpp +@@ -1499,6 +1499,9 @@ + if (PredToDest.second) + DestPopularity[PredToDest.second]++; + ++ if (DestPopularity.empty()) ++ return nullptr; ++ + // Find the most popular dest. + DenseMap::iterator DPI = DestPopularity.begin(); + BasicBlock *MostPopularDest = DPI->first; +@@ -1679,8 +1682,20 @@ + // threadable destination (the common case) we can avoid this. + BasicBlock *MostPopularDest = OnlyDest; + +- if (MostPopularDest == MultipleDestSentinel) ++ if (MostPopularDest == MultipleDestSentinel) { ++ // Remove any loop headers from the Dest list, ThreadEdge conservatively ++ // won't process them, but we might have other destination that are eligible ++ // and we still want to process. ++ erase_if(PredToDestList, ++ [&](const std::pair &PredToDest) { ++ return LoopHeaders.count(PredToDest.second) != 0; ++ }); ++ ++ if (PredToDestList.empty()) ++ return false; ++ + MostPopularDest = FindMostPopularDest(BB, PredToDestList); ++ } + + // Now that we know what the most popular destination is, factor all + // predecessors that will jump to it into a single predecessor. diff --git a/deps/patches/llvm-D42262-jumpthreading-not-i1.patch b/deps/patches/llvm-D42262-jumpthreading-not-i1.patch new file mode 100644 index 0000000..4aec2cb --- /dev/null +++ b/deps/patches/llvm-D42262-jumpthreading-not-i1.patch @@ -0,0 +1,82 @@ +commit 6a311a7a804831fea43cfb2f61322adcb407a1af +Author: Keno Fischer +Date: Thu Jan 18 15:57:05 2018 -0500 + + [JumpThreading] Don't restrict cast-traversal to i1 + + Summary: + In D17663, JumpThreading learned to look trough simple cast instructions, + but only if the source of those cast instructions was a phi/cmp i1 + (in an effort to limit compile time effects). I think this condition + is too restrictive. For switches with limited value range, InstCombine + will readily introduce an extra `trunc` instruction to a smaller + integer type (e.g. from i8 to i2), leaving us in the somewhat perverse + situation that jump-threading would work before running instcombine, + but not after. Since instcombine produces this pattern, I think we + need to consider it canonical and support it in JumpThreading. + In general, for limiting recursion, I think the existing restriction + to phi and cmp nodes should be sufficient to avoid looking through + unprofitable chains of instructions. + + Reviewers: haicheng, gberry, bmakam, mcrosier + + Subscribers: llvm-commits + + Differential Revision: https://reviews.llvm.org/D42262 + +diff --git a/lib/Transforms/Scalar/JumpThreading.cpp b/lib/Transforms/Scalar/JumpThreading.cpp +index 95c4650..1155e18 100644 +--- a/lib/Transforms/Scalar/JumpThreading.cpp ++++ b/lib/Transforms/Scalar/JumpThreading.cpp +@@ -647,11 +647,9 @@ bool JumpThreadingPass::ComputeValueKnownInPredecessors( + } + + // Handle Cast instructions. Only see through Cast when the source operand is +- // PHI or Cmp and the source type is i1 to save the compilation time. ++ // PHI or Cmp to save the compilation time. + if (CastInst *CI = dyn_cast(I)) { + Value *Source = CI->getOperand(0); +- if (!Source->getType()->isIntegerTy(1)) +- return false; + if (!isa(Source) && !isa(Source)) + return false; + ComputeValueKnownInPredecessors(Source, BB, Result, Preference, CxtI); +diff --git a/test/Transforms/JumpThreading/basic.ll b/test/Transforms/JumpThreading/basic.ll +index ce86cba..16e7549 100644 +--- a/test/Transforms/JumpThreading/basic.ll ++++ b/test/Transforms/JumpThreading/basic.ll +@@ -547,6 +547,34 @@ l5: + ; CHECK: } + } + ++define i1 @trunc_switch(i1 %arg) { ++; CHECK-LABEL: @trunc_switch ++top: ++; CHECK: br i1 %arg, label %exitA, label %exitB ++ br i1 %arg, label %common, label %B ++ ++B: ++ br label %common ++ ++common: ++ %phi = phi i8 [ 2, %B ], [ 1, %top ] ++ %trunc = trunc i8 %phi to i2 ++; CHECK-NOT: switch ++ switch i2 %trunc, label %unreach [ ++ i2 1, label %exitA ++ i2 -2, label %exitB ++ ] ++ ++unreach: ++ unreachable ++ ++exitA: ++ ret i1 true ++ ++exitB: ++ ret i1 false ++} ++ + ; CHECK-LABEL: define void @h_con(i32 %p) { + define void @h_con(i32 %p) { + %x = icmp ult i32 %p, 5 diff --git a/deps/patches/llvm-D44892-Perf-integration.patch b/deps/patches/llvm-D44892-Perf-integration.patch new file mode 100644 index 0000000..e849bcd --- /dev/null +++ b/deps/patches/llvm-D44892-Perf-integration.patch @@ -0,0 +1,677 @@ +From 45bc0f0badbdbabaed7d204757c2aad7ab49a3fe Mon Sep 17 00:00:00 2001 +From: DokFaust +Date: Mon, 11 Jun 2018 12:59:42 +0200 +Subject: [PATCH] PerfJITEventListener integration, requires compile flag + LLVM_USE_PERF + +--- + CMakeLists.txt | 13 + + include/llvm/Config/config.h.cmake | 3 + + include/llvm/Config/llvm-config.h.cmake | 3 + + .../llvm/ExecutionEngine/JITEventListener.h | 9 + + lib/ExecutionEngine/CMakeLists.txt | 4 + + lib/ExecutionEngine/LLVMBuild.txt | 2 +- + lib/ExecutionEngine/Orc/LLVMBuild.txt | 2 +- + .../PerfJITEvents/CMakeLists.txt | 5 + + .../PerfJITEvents/LLVMBuild.txt | 23 + + .../PerfJITEvents/PerfJITEventListener.cpp | 492 ++++++++++++++++++ + 10 files changed, 554 insertions(+), 2 deletions(-) + create mode 100644 lib/ExecutionEngine/PerfJITEvents/CMakeLists.txt + create mode 100644 lib/ExecutionEngine/PerfJITEvents/LLVMBuild.txt + create mode 100644 lib/ExecutionEngine/PerfJITEvents/PerfJITEventListener.cpp + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index f8da6cf9211..fb92c825a46 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -426,6 +426,16 @@ if( LLVM_USE_OPROFILE ) + endif( NOT CMAKE_SYSTEM_NAME MATCHES "Linux" ) + endif( LLVM_USE_OPROFILE ) + ++option(LLVM_USE_PERF ++ "Use perf JIT interface to inform perf about JIT code" OFF) ++ ++# If enabled, verify we are on a platform that supports perf. ++if( LLVM_USE_PERF ) ++ if( NOT CMAKE_SYSTEM_NAME MATCHES "Linux" ) ++ message(FATAL_ERROR "perf support is available on Linux only.") ++ endif( NOT CMAKE_SYSTEM_NAME MATCHES "Linux" ) ++endif( LLVM_USE_PERF ) ++ + set(LLVM_USE_SANITIZER "" CACHE STRING + "Define the sanitizer used to build binaries and tests.") + set(LLVM_LIB_FUZZING_ENGINE "" CACHE PATH +@@ -634,6 +644,9 @@ endif (LLVM_USE_INTEL_JITEVENTS) + if (LLVM_USE_OPROFILE) + set(LLVMOPTIONALCOMPONENTS ${LLVMOPTIONALCOMPONENTS} OProfileJIT) + endif (LLVM_USE_OPROFILE) ++if (LLVM_USE_PERF) ++ set(LLVMOPTIONALCOMPONENTS ${LLVMOPTIONALCOMPONENTS} PerfJITEvents) ++endif (LLVM_USE_PERF) + + message(STATUS "Constructing LLVMBuild project information") + execute_process( +diff --git a/include/llvm/Config/config.h.cmake b/include/llvm/Config/config.h.cmake +index 940f8420304..17787ed779b 100644 +--- a/include/llvm/Config/config.h.cmake ++++ b/include/llvm/Config/config.h.cmake +@@ -377,6 +377,9 @@ + /* Define if we have the oprofile JIT-support library */ + #cmakedefine01 LLVM_USE_OPROFILE + ++/* Define if we have the perf JIT-support library */ ++#cmakedefine01 LLVM_USE_PERF ++ + /* LLVM version information */ + #cmakedefine LLVM_VERSION_INFO "${LLVM_VERSION_INFO}" + +diff --git a/include/llvm/Config/llvm-config.h.cmake b/include/llvm/Config/llvm-config.h.cmake +index 4daa00f3bc4..8d9c3b24d52 100644 +--- a/include/llvm/Config/llvm-config.h.cmake ++++ b/include/llvm/Config/llvm-config.h.cmake +@@ -65,6 +65,9 @@ + /* Define if we have the oprofile JIT-support library */ + #cmakedefine01 LLVM_USE_OPROFILE + ++/* Define if we have the perf JIT-support library */ ++#cmakedefine01 LLVM_USE_PERF ++ + /* Major version of the LLVM API */ + #define LLVM_VERSION_MAJOR ${LLVM_VERSION_MAJOR} + +diff --git a/include/llvm/ExecutionEngine/JITEventListener.h b/include/llvm/ExecutionEngine/JITEventListener.h +index ff7840f00a4..1cc2c423a8b 100644 +--- a/include/llvm/ExecutionEngine/JITEventListener.h ++++ b/include/llvm/ExecutionEngine/JITEventListener.h +@@ -115,6 +115,15 @@ public: + } + #endif // USE_OPROFILE + ++#ifdef LLVM_USE_PERF ++ static JITEventListener *createPerfJITEventListener(); ++#else ++ static JITEventListener *createPerfJITEventListener() ++ { ++ return nullptr; ++ } ++#endif //USE_PERF ++ + private: + virtual void anchor(); + }; +diff --git a/lib/ExecutionEngine/CMakeLists.txt b/lib/ExecutionEngine/CMakeLists.txt +index 84b34919e44..893d113a685 100644 +--- a/lib/ExecutionEngine/CMakeLists.txt ++++ b/lib/ExecutionEngine/CMakeLists.txt +@@ -30,3 +30,7 @@ endif( LLVM_USE_OPROFILE ) + if( LLVM_USE_INTEL_JITEVENTS ) + add_subdirectory(IntelJITEvents) + endif( LLVM_USE_INTEL_JITEVENTS ) ++ ++if( LLVM_USE_PERF ) ++ add_subdirectory(PerfJITEvents) ++endif( LLVM_USE_PERF ) +diff --git a/lib/ExecutionEngine/LLVMBuild.txt b/lib/ExecutionEngine/LLVMBuild.txt +index 9d29a41f504..b6e1bda6a51 100644 +--- a/lib/ExecutionEngine/LLVMBuild.txt ++++ b/lib/ExecutionEngine/LLVMBuild.txt +@@ -16,7 +16,7 @@ + ;===------------------------------------------------------------------------===; + + [common] +-subdirectories = Interpreter MCJIT RuntimeDyld IntelJITEvents OProfileJIT Orc ++subdirectories = Interpreter MCJIT RuntimeDyld IntelJITEvents OProfileJIT Orc PerfJITEvents + + [component_0] + type = Library +diff --git a/lib/ExecutionEngine/Orc/LLVMBuild.txt b/lib/ExecutionEngine/Orc/LLVMBuild.txt +index 8f05172e77a..ef4ae64e823 100644 +--- a/lib/ExecutionEngine/Orc/LLVMBuild.txt ++++ b/lib/ExecutionEngine/Orc/LLVMBuild.txt +@@ -19,4 +19,4 @@ + type = Library + name = OrcJIT + parent = ExecutionEngine +-required_libraries = Core ExecutionEngine Object RuntimeDyld Support TransformUtils ++required_libraries = Core ExecutionEngine Object RuntimeDyld Support TransformUtils +diff --git a/lib/ExecutionEngine/PerfJITEvents/CMakeLists.txt b/lib/ExecutionEngine/PerfJITEvents/CMakeLists.txt +new file mode 100644 +index 00000000000..136cc429d02 +--- /dev/null ++++ b/lib/ExecutionEngine/PerfJITEvents/CMakeLists.txt +@@ -0,0 +1,5 @@ ++add_llvm_library(LLVMPerfJITEvents ++ PerfJITEventListener.cpp ++ ) ++ ++add_dependencies(LLVMPerfJITEvents LLVMCodeGen) +diff --git a/lib/ExecutionEngine/PerfJITEvents/LLVMBuild.txt b/lib/ExecutionEngine/PerfJITEvents/LLVMBuild.txt +new file mode 100644 +index 00000000000..b1958a69260 +--- /dev/null ++++ b/lib/ExecutionEngine/PerfJITEvents/LLVMBuild.txt +@@ -0,0 +1,23 @@ ++;===- ./lib/ExecutionEngine/PerfJITEvents/LLVMBuild.txt ----------------*- Conf -*--===; ++; ++; The LLVM Compiler Infrastructure ++; ++; This file is distributed under the University of Illinois Open Source ++; License. See LICENSE.TXT for details. ++; ++;===------------------------------------------------------------------------===; ++; ++; This is an LLVMBuild description file for the components in this subdirectory. ++; ++; For more information on the LLVMBuild system, please see: ++; ++; http://llvm.org/docs/LLVMBuild.html ++; ++;===------------------------------------------------------------------------===; ++ ++[component_0] ++type = OptionalLibrary ++name = PerfJITEvents ++parent = ExecutionEngine ++required_libraries = CodeGen Core DebugInfoDWARF ExecutionEngine Object Support TransformUtils ++ +diff --git a/lib/ExecutionEngine/PerfJITEvents/PerfJITEventListener.cpp b/lib/ExecutionEngine/PerfJITEvents/PerfJITEventListener.cpp +new file mode 100644 +index 00000000000..c2b97dd59f3 +--- /dev/null ++++ b/lib/ExecutionEngine/PerfJITEvents/PerfJITEventListener.cpp +@@ -0,0 +1,492 @@ ++//===-- PerfJITEventListener.cpp - Tell Linux's perf about JITted code ----===// ++// ++// The LLVM Compiler Infrastructure ++// ++// This file is distributed under the University of Illinois Open Source ++// License. See LICENSE.TXT for details. ++// ++//===----------------------------------------------------------------------===// ++// ++// This file defines a JITEventListener object that tells perf about JITted ++// functions, including source line information. ++// ++// Documentation for perf jit integration is available at: ++// https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/tools/perf/Documentation/jitdump-specification.txt ++// https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/tools/perf/Documentation/jit-interface.txt ++// ++//===----------------------------------------------------------------------===// ++ ++#include "llvm/ADT/Twine.h" ++#include "llvm/Config/config.h" ++#include "llvm/DebugInfo/DWARF/DWARFContext.h" ++#include "llvm/ExecutionEngine/JITEventListener.h" ++#include "llvm/Object/ObjectFile.h" ++#include "llvm/Object/SymbolSize.h" ++#include "llvm/Support/Debug.h" ++#include "llvm/Support/Errno.h" ++#include "llvm/Support/FileSystem.h" ++#include "llvm/Support/MemoryBuffer.h" ++#include "llvm/Support/Mutex.h" ++#include "llvm/Support/MutexGuard.h" ++#include "llvm/Support/Path.h" ++#include "llvm/Support/Process.h" ++#include "llvm/Support/Threading.h" ++#include "llvm/Support/raw_ostream.h" ++ ++#include // mmap() ++#include // getpid() ++#include // clock_gettime(), time(), localtime_r() */ ++#include // for getpid(), read(), close() ++ ++using namespace llvm; ++using namespace llvm::object; ++typedef DILineInfoSpecifier::FileLineInfoKind FileLineInfoKind; ++ ++namespace { ++ ++// language identifier (XXX: should we generate something better from debug ++// info?) ++#define JIT_LANG "llvm-IR" ++#define LLVM_PERF_JIT_MAGIC \ ++ ((uint32_t)'J' << 24 | (uint32_t)'i' << 16 | (uint32_t)'T' << 8 | \ ++ (uint32_t)'D') ++#define LLVM_PERF_JIT_VERSION 1 ++ ++// bit 0: set if the jitdump file is using an architecture-specific timestamp ++// clock source ++#define JITDUMP_FLAGS_ARCH_TIMESTAMP (1ULL << 0) ++ ++struct LLVMPerfJitHeader; ++ ++class PerfJITEventListener : public JITEventListener { ++public: ++ PerfJITEventListener(); ++ ~PerfJITEventListener() { ++ if (MarkerAddr) ++ CloseMarker(); ++ } ++ ++ void NotifyObjectEmitted(const ObjectFile &Obj, ++ const RuntimeDyld::LoadedObjectInfo &L) override; ++ void NotifyFreeingObject(const ObjectFile &Obj) override; ++ ++private: ++ bool InitDebuggingDir(); ++ bool OpenMarker(); ++ void CloseMarker(); ++ static bool FillMachine(LLVMPerfJitHeader &hdr); ++ ++ void NotifyCode(Expected &Symbol, uint64_t CodeAddr, ++ uint64_t CodeSize); ++ void NotifyDebug(uint64_t CodeAddr, DILineInfoTable Lines); ++ ++ // cache lookups ++ pid_t Pid; ++ ++ // base directory for output data ++ std::string JitPath; ++ ++ // output data stream, closed via Dumpstream ++ int DumpFd = -1; ++ ++ // output data stream ++ std::unique_ptr Dumpstream; ++ ++ // prevent concurrent dumps from messing up the output file ++ sys::Mutex Mutex; ++ ++ // perf mmap marker ++ void *MarkerAddr = NULL; ++ ++ // perf support ready ++ bool SuccessfullyInitialized = false; ++ ++ // identifier for functions, primarily to identify when moving them around ++ uint64_t CodeGeneration = 1; ++}; ++ ++// The following are POD struct definitions from the perf jit specification ++ ++enum LLVMPerfJitRecordType { ++ JIT_CODE_LOAD = 0, ++ JIT_CODE_MOVE = 1, // not emitted, code isn't moved ++ JIT_CODE_DEBUG_INFO = 2, ++ JIT_CODE_CLOSE = 3, // not emitted, unnecessary ++ JIT_CODE_UNWINDING_INFO = 4, // not emitted ++ ++ JIT_CODE_MAX ++}; ++ ++struct LLVMPerfJitHeader { ++ uint32_t Magic; // characters "JiTD" ++ uint32_t Version; // header version ++ uint32_t TotalSize; // total size of header ++ uint32_t ElfMach; // elf mach target ++ uint32_t Pad1; // reserved ++ uint32_t Pid; ++ uint64_t Timestamp; // timestamp ++ uint64_t Flags; // flags ++}; ++ ++// record prefix (mandatory in each record) ++struct LLVMPerfJitRecordPrefix { ++ uint32_t Id; // record type identifier ++ uint32_t TotalSize; ++ uint64_t Timestamp; ++}; ++ ++struct LLVMPerfJitRecordCodeLoad { ++ LLVMPerfJitRecordPrefix Prefix; ++ ++ uint32_t Pid; ++ uint32_t Tid; ++ uint64_t Vma; ++ uint64_t CodeAddr; ++ uint64_t CodeSize; ++ uint64_t CodeIndex; ++}; ++ ++struct LLVMPerfJitDebugEntry { ++ uint64_t Addr; ++ int Lineno; // source line number starting at 1 ++ int Discrim; // column discriminator, 0 is default ++ // followed by null terminated filename, \xff\0 if same as previous entry ++}; ++ ++struct LLVMPerfJitRecordDebugInfo { ++ LLVMPerfJitRecordPrefix Prefix; ++ ++ uint64_t CodeAddr; ++ uint64_t NrEntry; ++ // followed by NrEntry LLVMPerfJitDebugEntry records ++}; ++ ++static inline uint64_t timespec_to_ns(const struct timespec *ts) { ++ const uint64_t NanoSecPerSec = 1000000000; ++ return ((uint64_t)ts->tv_sec * NanoSecPerSec) + ts->tv_nsec; ++} ++ ++static inline uint64_t perf_get_timestamp(void) { ++ struct timespec ts; ++ int ret; ++ ++ ret = clock_gettime(CLOCK_MONOTONIC, &ts); ++ if (ret) ++ return 0; ++ ++ return timespec_to_ns(&ts); ++} ++ ++PerfJITEventListener::PerfJITEventListener() : Pid(::getpid()) { ++ // check if clock-source is supported ++ if (!perf_get_timestamp()) { ++ errs() << "kernel does not support CLOCK_MONOTONIC\n"; ++ return; ++ } ++ ++ if (!InitDebuggingDir()) { ++ errs() << "could not initialize debugging directory\n"; ++ return; ++ } ++ ++ std::string Filename; ++ raw_string_ostream FilenameBuf(Filename); ++ FilenameBuf << JitPath << "/jit-" << Pid << ".dump"; ++ ++ // Need to open ourselves, because we need to hand the FD to OpenMarker() and ++ // raw_fd_ostream doesn't expose the FD. ++ using sys::fs::openFileForWrite; ++ if (auto EC = ++ openFileForWrite(FilenameBuf.str(), DumpFd, sys::fs::F_RW, 0666)) { ++ errs() << "could not open JIT dump file " << FilenameBuf.str() << ": " ++ << EC.message() << "\n"; ++ return; ++ } ++ ++ Dumpstream = make_unique(DumpFd, true); ++ ++ LLVMPerfJitHeader Header = {0}; ++ if (!FillMachine(Header)) ++ return; ++ ++ // signal this process emits JIT information ++ if (!OpenMarker()) ++ return; ++ ++ // emit dumpstream header ++ Header.Magic = LLVM_PERF_JIT_MAGIC; ++ Header.Version = LLVM_PERF_JIT_VERSION; ++ Header.TotalSize = sizeof(Header); ++ Header.Pid = Pid; ++ Header.Timestamp = perf_get_timestamp(); ++ Dumpstream->write(reinterpret_cast(&Header), sizeof(Header)); ++ ++ // Everything initialized, can do profiling now. ++ if (!Dumpstream->has_error()) ++ SuccessfullyInitialized = true; ++} ++ ++void PerfJITEventListener::NotifyObjectEmitted( ++ const ObjectFile &Obj, const RuntimeDyld::LoadedObjectInfo &L) { ++ ++ if (!SuccessfullyInitialized) ++ return; ++ ++ OwningBinary DebugObjOwner = L.getObjectForDebug(Obj); ++ const ObjectFile &DebugObj = *DebugObjOwner.getBinary(); ++ ++ // Get the address of the object image for use as a unique identifier ++ std::unique_ptr Context = DWARFContext::create(DebugObj); ++ ++ // Use symbol info to iterate over functions in the object. ++ for (const std::pair &P : computeSymbolSizes(DebugObj)) { ++ SymbolRef Sym = P.first; ++ std::string SourceFileName; ++ ++ Expected SymTypeOrErr = Sym.getType(); ++ if (!SymTypeOrErr) { ++ // There's not much we can with errors here ++ consumeError(SymTypeOrErr.takeError()); ++ continue; ++ } ++ SymbolRef::Type SymType = *SymTypeOrErr; ++ if (SymType != SymbolRef::ST_Function) ++ continue; ++ ++ Expected Name = Sym.getName(); ++ if (!Name) { ++ consumeError(Name.takeError()); ++ continue; ++ } ++ ++ Expected AddrOrErr = Sym.getAddress(); ++ if (!AddrOrErr) { ++ consumeError(AddrOrErr.takeError()); ++ continue; ++ } ++ uint64_t Addr = *AddrOrErr; ++ uint64_t Size = P.second; ++ ++ // According to spec debugging info has to come before loading the ++ // corresonding code load. ++ DILineInfoTable Lines = Context->getLineInfoForAddressRange( ++ Addr, Size, FileLineInfoKind::AbsoluteFilePath); ++ ++ NotifyDebug(Addr, Lines); ++ NotifyCode(Name, Addr, Size); ++ } ++ ++ Dumpstream->flush(); ++} ++ ++void PerfJITEventListener::NotifyFreeingObject(const ObjectFile &Obj) { ++ // perf currently doesn't have an interface for unloading. But munmap()ing the ++ // code section does, so that's ok. ++} ++ ++bool PerfJITEventListener::InitDebuggingDir() { ++ time_t Time; ++ struct tm LocalTime; ++ char TimeBuffer[sizeof("YYYYMMDD")]; ++ SmallString<64> Path; ++ ++ // search for location to dump data to ++ if (const char *BaseDir = getenv("JITDUMPDIR")) ++ Path.append(BaseDir); ++ else if (!sys::path::home_directory(Path)) ++ Path = "."; ++ ++ // create debug directory ++ Path += "/.debug/jit/"; ++ if (auto EC = sys::fs::create_directories(Path)) { ++ errs() << "could not create jit cache directory " << Path << ": " ++ << EC.message() << "\n"; ++ return false; ++ } ++ ++ // create unique directory for dump data related to this process ++ time(&Time); ++ localtime_r(&Time, &LocalTime); ++ strftime(TimeBuffer, sizeof(TimeBuffer), "%Y%m%d", &LocalTime); ++ Path += JIT_LANG "-jit-"; ++ Path += TimeBuffer; ++ ++ SmallString<128> UniqueDebugDir; ++ ++ using sys::fs::createUniqueDirectory; ++ if (auto EC = createUniqueDirectory(Path, UniqueDebugDir)) { ++ errs() << "could not create unique jit cache directory " << UniqueDebugDir ++ << ": " << EC.message() << "\n"; ++ return false; ++ } ++ ++ JitPath = UniqueDebugDir.str(); ++ ++ return true; ++} ++ ++bool PerfJITEventListener::OpenMarker() { ++ // We mmap the jitdump to create an MMAP RECORD in perf.data file. The mmap ++ // is captured either live (perf record running when we mmap) or in deferred ++ // mode, via /proc/PID/maps. The MMAP record is used as a marker of a jitdump ++ // file for more meta data info about the jitted code. Perf report/annotate ++ // detect this special filename and process the jitdump file. ++ // ++ // Mapping must be PROT_EXEC to ensure it is captured by perf record ++ // even when not using -d option. ++ MarkerAddr = ::mmap(NULL, sys::Process::getPageSize(), PROT_READ | PROT_EXEC, ++ MAP_PRIVATE, DumpFd, 0); ++ ++ if (MarkerAddr == MAP_FAILED) { ++ errs() << "could not mmap JIT marker\n"; ++ return false; ++ } ++ return true; ++} ++ ++void PerfJITEventListener::CloseMarker() { ++ if (!MarkerAddr) ++ return; ++ ++ munmap(MarkerAddr, sys::Process::getPageSize()); ++ MarkerAddr = nullptr; ++} ++ ++bool PerfJITEventListener::FillMachine(LLVMPerfJitHeader &hdr) { ++ char id[16]; ++ struct { ++ uint16_t e_type; ++ uint16_t e_machine; ++ } info; ++ ++ size_t RequiredMemory = sizeof(id) + sizeof(info); ++ ++ ErrorOr> MB = ++ MemoryBuffer::getFileSlice("/proc/self/exe", ++ RequiredMemory, ++ 0); ++ ++ // This'll not guarantee that enough data was actually read from the ++ // underlying file. Instead the trailing part of the buffer would be ++ // zeroed. Given the ELF signature check below that seems ok though, ++ // it's unlikely that the file ends just after that, and the ++ // consequence would just be that perf wouldn't recognize the ++ // signature. ++ if (auto EC = MB.getError()) { ++ errs() << "could not open /proc/self/exe: " << EC.message() << "\n"; ++ return false; ++ } ++ ++ memcpy(&id, (*MB)->getBufferStart(), sizeof(id)); ++ memcpy(&info, (*MB)->getBufferStart() + sizeof(id), sizeof(info)); ++ ++ // check ELF signature ++ if (id[0] != 0x7f || id[1] != 'E' || id[2] != 'L' || id[3] != 'F') { ++ errs() << "invalid elf signature\n"; ++ return false; ++ } ++ ++ hdr.ElfMach = info.e_machine; ++ ++ return true; ++} ++ ++void PerfJITEventListener::NotifyCode(Expected &Symbol, ++ uint64_t CodeAddr, uint64_t CodeSize) { ++ assert(SuccessfullyInitialized); ++ ++ // 0 length functions can't have samples. ++ if (CodeSize == 0) ++ return; ++ ++ LLVMPerfJitRecordCodeLoad rec; ++ rec.Prefix.Id = JIT_CODE_LOAD; ++ rec.Prefix.TotalSize = sizeof(rec) + // debug record itself ++ Symbol->size() + 1 + // symbol name ++ CodeSize; // and code ++ rec.Prefix.Timestamp = perf_get_timestamp(); ++ ++ rec.CodeSize = CodeSize; ++ rec.Vma = 0; ++ rec.CodeAddr = CodeAddr; ++ rec.Pid = Pid; ++ rec.Tid = get_threadid(); ++ ++ // avoid interspersing output ++ MutexGuard Guard(Mutex); ++ ++ rec.CodeIndex = CodeGeneration++; // under lock! ++ ++ Dumpstream->write(reinterpret_cast(&rec), sizeof(rec)); ++ Dumpstream->write(Symbol->data(), Symbol->size() + 1); ++ Dumpstream->write(reinterpret_cast(CodeAddr), CodeSize); ++} ++ ++void PerfJITEventListener::NotifyDebug(uint64_t CodeAddr, ++ DILineInfoTable Lines) { ++ assert(SuccessfullyInitialized); ++ ++ // Didn't get useful debug info. ++ if (Lines.empty()) ++ return; ++ ++ LLVMPerfJitRecordDebugInfo rec; ++ rec.Prefix.Id = JIT_CODE_DEBUG_INFO; ++ rec.Prefix.TotalSize = sizeof(rec); // will be increased further ++ rec.Prefix.Timestamp = perf_get_timestamp(); ++ rec.CodeAddr = CodeAddr; ++ rec.NrEntry = Lines.size(); ++ ++ // compute total size size of record (variable due to filenames) ++ DILineInfoTable::iterator Begin = Lines.begin(); ++ DILineInfoTable::iterator End = Lines.end(); ++ for (DILineInfoTable::iterator It = Begin; It != End; ++It) { ++ DILineInfo &line = It->second; ++ rec.Prefix.TotalSize += sizeof(LLVMPerfJitDebugEntry); ++ rec.Prefix.TotalSize += line.FileName.size() + 1; ++ } ++ ++ // The debug_entry describes the source line information. It is defined as ++ // follows in order: ++ // * uint64_t code_addr: address of function for which the debug information ++ // is generated ++ // * uint32_t line : source file line number (starting at 1) ++ // * uint32_t discrim : column discriminator, 0 is default ++ // * char name[n] : source file name in ASCII, including null termination ++ ++ // avoid interspersing output ++ MutexGuard Guard(Mutex); ++ ++ Dumpstream->write(reinterpret_cast(&rec), sizeof(rec)); ++ ++ for (DILineInfoTable::iterator It = Begin; It != End; ++It) { ++ LLVMPerfJitDebugEntry LineInfo; ++ DILineInfo &Line = It->second; ++ ++ LineInfo.Addr = It->first; ++ // The function re-created by perf is preceded by a elf ++ // header. Need to adjust for that, otherwise the results are ++ // wrong. ++ LineInfo.Addr += 0x40; ++ LineInfo.Lineno = Line.Line; ++ LineInfo.Discrim = Line.Discriminator; ++ ++ Dumpstream->write(reinterpret_cast(&LineInfo), ++ sizeof(LineInfo)); ++ Dumpstream->write(Line.FileName.c_str(), Line.FileName.size() + 1); ++ } ++} ++ ++// There should be only a single event listener per process, otherwise perf gets ++// confused. ++llvm::ManagedStatic PerfListener; ++ ++} // end anonymous namespace ++ ++namespace llvm { ++JITEventListener *JITEventListener::createPerfJITEventListener() { ++ return &*PerfListener; ++} ++ ++} // namespace llvm ++ +-- +2.17.1 + diff --git a/deps/patches/llvm-D45008.patch b/deps/patches/llvm-D45008.patch new file mode 100644 index 0000000..c943360 --- /dev/null +++ b/deps/patches/llvm-D45008.patch @@ -0,0 +1,70 @@ +Index: lib/CodeGen/MachineBlockPlacement.cpp +=================================================================== +--- a/lib/CodeGen/MachineBlockPlacement.cpp ++++ b/lib/CodeGen/MachineBlockPlacement.cpp +@@ -513,6 +513,11 @@ + + bool runOnMachineFunction(MachineFunction &F) override; + ++ bool allowTailDupPlacement() const { ++ assert(F); ++ return TailDupPlacement && !F->getTarget().requiresStructuredCFG(); ++ } ++ + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + AU.addRequired(); +@@ -1018,7 +1023,7 @@ + MachineBasicBlock *Succ1 = BestA.Dest; + MachineBasicBlock *Succ2 = BestB.Dest; + // Check to see if tail-duplication would be profitable. +- if (TailDupPlacement && shouldTailDuplicate(Succ2) && ++ if (allowTailDupPlacement() && shouldTailDuplicate(Succ2) && + canTailDuplicateUnplacedPreds(BB, Succ2, Chain, BlockFilter) && + isProfitableToTailDup(BB, Succ2, MBPI->getEdgeProbability(BB, Succ1), + Chain, BlockFilter)) { +@@ -1044,7 +1049,7 @@ + return Result; + } + +-/// When the option TailDupPlacement is on, this method checks if the ++/// When the option allowTailDupPlacement() is on, this method checks if the + /// fallthrough candidate block \p Succ (of block \p BB) can be tail-duplicated + /// into all of its unplaced, unfiltered predecessors, that are not BB. + bool MachineBlockPlacement::canTailDuplicateUnplacedPreds( +@@ -1493,7 +1498,7 @@ + if (hasBetterLayoutPredecessor(BB, Succ, SuccChain, SuccProb, RealSuccProb, + Chain, BlockFilter)) { + // If tail duplication would make Succ profitable, place it. +- if (TailDupPlacement && shouldTailDuplicate(Succ)) ++ if (allowTailDupPlacement() && shouldTailDuplicate(Succ)) + DupCandidates.push_back(std::make_tuple(SuccProb, Succ)); + continue; + } +@@ -1702,7 +1707,7 @@ + auto Result = selectBestSuccessor(BB, Chain, BlockFilter); + MachineBasicBlock* BestSucc = Result.BB; + bool ShouldTailDup = Result.ShouldTailDup; +- if (TailDupPlacement) ++ if (allowTailDupPlacement()) + ShouldTailDup |= (BestSucc && shouldTailDuplicate(BestSucc)); + + // If an immediate successor isn't available, look for the best viable +@@ -1724,7 +1729,7 @@ + + // Placement may have changed tail duplication opportunities. + // Check for that now. +- if (TailDupPlacement && BestSucc && ShouldTailDup) { ++ if (allowTailDupPlacement() && BestSucc && ShouldTailDup) { + // If the chosen successor was duplicated into all its predecessors, + // don't bother laying it out, just go round the loop again with BB as + // the chain end. +@@ -2758,7 +2763,7 @@ + TailDupSize = TailDupPlacementAggressiveThreshold; + } + +- if (TailDupPlacement) { ++ if (allowTailDupPlacement()) { + MPDT = &getAnalysis(); + if (MF.getFunction().optForSize()) + TailDupSize = 1; diff --git a/deps/patches/llvm-D45070.patch b/deps/patches/llvm-D45070.patch new file mode 100644 index 0000000..061528a --- /dev/null +++ b/deps/patches/llvm-D45070.patch @@ -0,0 +1,28 @@ +Index: lib/Target/NVPTX/NVPTXTargetMachine.cpp +=================================================================== +--- a/lib/Target/NVPTX/NVPTXTargetMachine.cpp ++++ b/lib/Target/NVPTX/NVPTXTargetMachine.cpp +@@ -44,6 +44,14 @@ + cl::desc("Disable load/store vectorizer"), + cl::init(false), cl::Hidden); + ++// TODO: Remove this flag when we are confident with no regressions. ++static cl::opt DisableRequireStructuredCFG( ++ "disable-nvptx-require-structured-cfg", ++ cl::desc("Transitional flag to turn off NVPTX's requirement on preserving " ++ "structured CFG. The requirement should be disabled only when " ++ "unexpected regressions happen."), ++ cl::init(false), cl::Hidden); ++ + namespace llvm { + + void initializeNVVMIntrRangePass(PassRegistry&); +@@ -108,6 +116,8 @@ + drvInterface = NVPTX::NVCL; + else + drvInterface = NVPTX::CUDA; ++ if (!DisableRequireStructuredCFG) ++ setRequiresStructuredCFG(true); + initAsmInfo(); + } + diff --git a/deps/patches/llvm-D46460.patch b/deps/patches/llvm-D46460.patch new file mode 100644 index 0000000..ec0a823 --- /dev/null +++ b/deps/patches/llvm-D46460.patch @@ -0,0 +1,26 @@ +Index: lib/Analysis/LoopInfo.cpp +=================================================================== +--- a/lib/Analysis/LoopInfo.cpp ++++ b/lib/Analysis/LoopInfo.cpp +@@ -223,15 +223,14 @@ + BasicBlock *H = getHeader(); + for (BasicBlock *BB : this->blocks()) { + TerminatorInst *TI = BB->getTerminator(); +- MDNode *MD = nullptr; + + // Check if this terminator branches to the loop header. +- for (BasicBlock *Successor : TI->successors()) { +- if (Successor == H) { +- MD = TI->getMetadata(LLVMContext::MD_loop); +- break; +- } +- } ++ bool IsPredecessor = any_of(TI->successors(), ++ [=](BasicBlock *Successor) { return Successor == H; }); ++ if (!IsPredecessor) ++ continue; ++ ++ MDNode *MD = TI->getMetadata(LLVMContext::MD_loop); + if (!MD) + return nullptr; + diff --git a/deps/patches/llvm-D49832-SCEVPred.patch b/deps/patches/llvm-D49832-SCEVPred.patch new file mode 100644 index 0000000..47be214 --- /dev/null +++ b/deps/patches/llvm-D49832-SCEVPred.patch @@ -0,0 +1,187 @@ +commit 98592fcc61307968f7df1362771534595a1e1c21 +Author: Keno Fischer +Date: Wed Jul 25 19:29:02 2018 -0400 + + [SCEV] Don't expand Wrap predicate using inttoptr in ni addrspaces + + Summary: + In non-integral address spaces, we're not allowed to introduce inttoptr/ptrtoint + intrinsics. Instead, we need to expand any pointer arithmetic as geps on the + base pointer. Luckily this is a common task for SCEV, so all we have to do here + is hook up the corresponding helper function and add test case. + + Fixes PR38290 + + Reviewers: reames, sanjoy + + Subscribers: javed.absar, llvm-commits + + Differential Revision: https://reviews.llvm.org/D49832 + +diff --git a/lib/Analysis/ScalarEvolutionExpander.cpp b/lib/Analysis/ScalarEvolutionExpander.cpp +index 7f76f057216..f441a3647fb 100644 +--- a/lib/Analysis/ScalarEvolutionExpander.cpp ++++ b/lib/Analysis/ScalarEvolutionExpander.cpp +@@ -2157,8 +2157,9 @@ Value *SCEVExpander::generateOverflowCheck(const SCEVAddRecExpr *AR, + const SCEV *Step = AR->getStepRecurrence(SE); + const SCEV *Start = AR->getStart(); + ++ Type *ARTy = AR->getType(); + unsigned SrcBits = SE.getTypeSizeInBits(ExitCount->getType()); +- unsigned DstBits = SE.getTypeSizeInBits(AR->getType()); ++ unsigned DstBits = SE.getTypeSizeInBits(ARTy); + + // The expression {Start,+,Step} has nusw/nssw if + // Step < 0, Start - |Step| * Backedge <= Start +@@ -2170,11 +2171,12 @@ Value *SCEVExpander::generateOverflowCheck(const SCEVAddRecExpr *AR, + Value *TripCountVal = expandCodeFor(ExitCount, CountTy, Loc); + + IntegerType *Ty = +- IntegerType::get(Loc->getContext(), SE.getTypeSizeInBits(AR->getType())); ++ IntegerType::get(Loc->getContext(), SE.getTypeSizeInBits(ARTy)); ++ Type *ARExpandTy = DL.isNonIntegralPointerType(ARTy) ? ARTy : Ty; + + Value *StepValue = expandCodeFor(Step, Ty, Loc); + Value *NegStepValue = expandCodeFor(SE.getNegativeSCEV(Step), Ty, Loc); +- Value *StartValue = expandCodeFor(Start, Ty, Loc); ++ Value *StartValue = expandCodeFor(Start, ARExpandTy, Loc); + + ConstantInt *Zero = + ConstantInt::get(Loc->getContext(), APInt::getNullValue(DstBits)); +@@ -2197,8 +2199,21 @@ Value *SCEVExpander::generateOverflowCheck(const SCEVAddRecExpr *AR, + // Compute: + // Start + |Step| * Backedge < Start + // Start - |Step| * Backedge > Start +- Value *Add = Builder.CreateAdd(StartValue, MulV); +- Value *Sub = Builder.CreateSub(StartValue, MulV); ++ Value *Add = nullptr, *Sub = nullptr; ++ if (ARExpandTy->isPointerTy()) { ++ PointerType *ARPtrTy = cast(ARExpandTy); ++ const SCEV *MulS = SE.getSCEV(MulV); ++ const SCEV *const StepArray[2] = {MulS, SE.getNegativeSCEV(MulS)}; ++ Add = Builder.CreateBitCast( ++ expandAddToGEP(&StepArray[0], &StepArray[1], ARPtrTy, Ty, StartValue), ++ ARPtrTy); ++ Sub = Builder.CreateBitCast( ++ expandAddToGEP(&StepArray[1], &StepArray[2], ARPtrTy, Ty, StartValue), ++ ARPtrTy); ++ } else { ++ Add = Builder.CreateAdd(StartValue, MulV); ++ Sub = Builder.CreateSub(StartValue, MulV); ++ } + + Value *EndCompareGT = Builder.CreateICmp( + Signed ? ICmpInst::ICMP_SGT : ICmpInst::ICMP_UGT, Sub, StartValue); +diff --git a/test/Analysis/LoopAccessAnalysis/wrapping-pointer-ni.ll b/test/Analysis/LoopAccessAnalysis/wrapping-pointer-ni.ll +new file mode 100644 +index 00000000000..ddcf5e1a195 +--- /dev/null ++++ b/test/Analysis/LoopAccessAnalysis/wrapping-pointer-ni.ll +@@ -0,0 +1,73 @@ ++; RUN: opt -loop-versioning -S < %s | FileCheck %s -check-prefix=LV ++ ++; NB: addrspaces 10-13 are non-integral ++target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128-ni:10:11:12:13" ++ ++; This matches the test case from PR38290 ++; Check that we expand the SCEV predicate check using GEP, rather ++; than ptrtoint. ++ ++%jl_value_t = type opaque ++%jl_array_t = type { i8 addrspace(13)*, i64, i16, i16, i32 } ++ ++declare i64 @julia_steprange_last_4949() ++ ++define void @"japi1_align!_9477"(%jl_value_t addrspace(10)**) #0 { ++; LV-LAVEL: L26.lver.check ++; LV: [[OFMul:%[^ ]*]] = call { i64, i1 } @llvm.umul.with.overflow.i64(i64 4, i64 [[Step:%[^ ]*]]) ++; LV-NEXT: [[OFMulResult:%[^ ]*]] = extractvalue { i64, i1 } [[OFMul]], 0 ++; LV-NEXT: [[OFMulOverflow:%[^ ]*]] = extractvalue { i64, i1 } [[OFMul]], 1 ++; LV-NEXT: [[PosGEP:%[^ ]*]] = getelementptr i32, i32 addrspace(13)* [[Base:%[^ ]*]], i64 [[Step]] ++; LV-NEXT: [[NegGEP:%[^ ]*]] = getelementptr i32, i32 addrspace(13)* [[Base]], i64 [[NegStep:%[^ ]*]] ++; LV-NEXT: icmp ugt i32 addrspace(13)* [[NegGEP]], [[Base]] ++; LV-NEXT: icmp ult i32 addrspace(13)* [[PosGEP]], [[Base]] ++; LV-NOT: inttoptr ++; LV-NOT: ptrtoint ++top: ++ %1 = load %jl_value_t addrspace(10)*, %jl_value_t addrspace(10)** %0, align 8, !nonnull !1, !dereferenceable !2, !align !3 ++ %2 = load i32, i32* inttoptr (i64 12 to i32*), align 4, !tbaa !4 ++ %3 = sub i32 0, %2 ++ %4 = call i64 @julia_steprange_last_4949() ++ %5 = addrspacecast %jl_value_t addrspace(10)* %1 to %jl_value_t addrspace(11)* ++ %6 = bitcast %jl_value_t addrspace(11)* %5 to %jl_value_t addrspace(10)* addrspace(11)* ++ %7 = load %jl_value_t addrspace(10)*, %jl_value_t addrspace(10)* addrspace(11)* %6, align 8, !tbaa !4, !nonnull !1, !dereferenceable !9, !align !2 ++ %8 = addrspacecast %jl_value_t addrspace(10)* %7 to %jl_value_t addrspace(11)* ++ %9 = bitcast %jl_value_t addrspace(11)* %8 to i32 addrspace(13)* addrspace(11)* ++ %10 = load i32 addrspace(13)*, i32 addrspace(13)* addrspace(11)* %9, align 8, !tbaa !10, !nonnull !1 ++ %11 = sext i32 %3 to i64 ++ br label %L26 ++ ++L26: ; preds = %L26, %top ++ %value_phi3 = phi i64 [ 0, %top ], [ %12, %L26 ] ++ %12 = add i64 %value_phi3, -1 ++ %13 = getelementptr inbounds i32, i32 addrspace(13)* %10, i64 %12 ++ %14 = load i32, i32 addrspace(13)* %13, align 4, !tbaa !13 ++ %15 = add i64 %12, %11 ++ %16 = getelementptr inbounds i32, i32 addrspace(13)* %10, i64 %15 ++ store i32 %14, i32 addrspace(13)* %16, align 4, !tbaa !13 ++ %17 = icmp eq i64 %value_phi3, %4 ++ br i1 %17, label %L45, label %L26 ++ ++L45: ; preds = %L26 ++ ret void ++} ++ ++attributes #0 = { "thunk" } ++ ++!llvm.module.flags = !{!0} ++ ++!0 = !{i32 1, !"Debug Info Version", i32 3} ++!1 = !{} ++!2 = !{i64 16} ++!3 = !{i64 8} ++!4 = !{!5, !5, i64 0} ++!5 = !{!"jtbaa_mutab", !6, i64 0} ++!6 = !{!"jtbaa_value", !7, i64 0} ++!7 = !{!"jtbaa_data", !8, i64 0} ++!8 = !{!"jtbaa"} ++!9 = !{i64 40} ++!10 = !{!11, !11, i64 0} ++!11 = !{!"jtbaa_arrayptr", !12, i64 0} ++!12 = !{!"jtbaa_array", !8, i64 0} ++!13 = !{!14, !14, i64 0} ++!14 = !{!"jtbaa_arraybuf", !7, i64 0} +diff --git a/test/Analysis/LoopAccessAnalysis/wrapping-pointer-versioning.ll b/test/Analysis/LoopAccessAnalysis/wrapping-pointer-versioning.ll +index a7e5bce7445..fa6fccecbf1 100644 +--- a/test/Analysis/LoopAccessAnalysis/wrapping-pointer-versioning.ll ++++ b/test/Analysis/LoopAccessAnalysis/wrapping-pointer-versioning.ll +@@ -58,10 +58,10 @@ target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" + ; LV-NEXT: [[OFMul1:%[^ ]*]] = call { i64, i1 } @llvm.umul.with.overflow.i64(i64 4, i64 [[BE]]) + ; LV-NEXT: [[OFMulResult1:%[^ ]*]] = extractvalue { i64, i1 } [[OFMul1]], 0 + ; LV-NEXT: [[OFMulOverflow1:%[^ ]*]] = extractvalue { i64, i1 } [[OFMul1]], 1 +-; LV-NEXT: [[AddEnd1:%[^ ]*]] = add i64 %a2, [[OFMulResult1]] +-; LV-NEXT: [[SubEnd1:%[^ ]*]] = sub i64 %a2, [[OFMulResult1]] +-; LV-NEXT: [[CmpNeg1:%[^ ]*]] = icmp ugt i64 [[SubEnd1]], %a2 +-; LV-NEXT: [[CmpPos1:%[^ ]*]] = icmp ult i64 [[AddEnd1]], %a2 ++; LV-NEXT: [[AddEnd1:%[^ ]*]] = add i64 [[A0:%[^ ]*]], [[OFMulResult1]] ++; LV-NEXT: [[SubEnd1:%[^ ]*]] = sub i64 [[A0]], [[OFMulResult1]] ++; LV-NEXT: [[CmpNeg1:%[^ ]*]] = icmp ugt i64 [[SubEnd1]], [[A0]] ++; LV-NEXT: [[CmpPos1:%[^ ]*]] = icmp ult i64 [[AddEnd1]], [[A0]] + ; LV-NEXT: [[Cmp:%[^ ]*]] = select i1 false, i1 [[CmpNeg1]], i1 [[CmpPos1]] + ; LV-NEXT: [[PredCheck1:%[^ ]*]] = or i1 [[Cmp]], [[OFMulOverflow1]] + +@@ -233,10 +233,10 @@ for.end: ; preds = %for.body + ; LV: [[OFMul1:%[^ ]*]] = call { i64, i1 } @llvm.umul.with.overflow.i64(i64 4, i64 [[BE:%[^ ]*]]) + ; LV-NEXT: [[OFMulResult1:%[^ ]*]] = extractvalue { i64, i1 } [[OFMul1]], 0 + ; LV-NEXT: [[OFMulOverflow1:%[^ ]*]] = extractvalue { i64, i1 } [[OFMul1]], 1 +-; LV-NEXT: [[AddEnd1:%[^ ]*]] = add i64 %a2, [[OFMulResult1]] +-; LV-NEXT: [[SubEnd1:%[^ ]*]] = sub i64 %a2, [[OFMulResult1]] +-; LV-NEXT: [[CmpNeg1:%[^ ]*]] = icmp ugt i64 [[SubEnd1]], %a2 +-; LV-NEXT: [[CmpPos1:%[^ ]*]] = icmp ult i64 [[AddEnd1]], %a2 ++; LV-NEXT: [[AddEnd1:%[^ ]*]] = add i64 [[A0:%[^ ]*]], [[OFMulResult1]] ++; LV-NEXT: [[SubEnd1:%[^ ]*]] = sub i64 [[A0]], [[OFMulResult1]] ++; LV-NEXT: [[CmpNeg1:%[^ ]*]] = icmp ugt i64 [[SubEnd1]], [[A0]] ++; LV-NEXT: [[CmpPos1:%[^ ]*]] = icmp ult i64 [[AddEnd1]], [[A0]] + ; LV-NEXT: [[Cmp:%[^ ]*]] = select i1 false, i1 [[CmpNeg1]], i1 [[CmpPos1]] + ; LV-NEXT: [[PredCheck1:%[^ ]*]] = or i1 [[Cmp]], [[OFMulOverflow1]] + diff --git a/deps/patches/llvm-D50010-VNCoercion-ni.patch b/deps/patches/llvm-D50010-VNCoercion-ni.patch new file mode 100644 index 0000000..cb658d1 --- /dev/null +++ b/deps/patches/llvm-D50010-VNCoercion-ni.patch @@ -0,0 +1,89 @@ +commit 8eb2b102a203d83fb713f3bf79acf235dabdd8cd +Author: Keno Fischer +Date: Mon Jul 30 16:59:08 2018 -0400 + + [VNCoercion] Disallow coercion between different ni addrspaces + + Summary: + I'm not sure if it would be legal by the IR reference to introduce + an addrspacecast here, since the IR reference is a bit vague on + the exact semantics, but at least for our usage of it (and I + suspect for many other's usage) it is not. For us, addrspacecasts + between non-integral address spaces carry frontend information that the + optimizer cannot deduce afterwards in a generic way (though we + have frontend specific passes in our pipline that do propagate + these). In any case, I'm sure nobody is using it this way at + the moment, since it would have introduced inttoptrs, which + are definitely illegal. + + Fixes PR38375 + + Reviewers: sanjoy, reames, dberlin + + Subscribers: llvm-commits + + Differential Revision: https://reviews.llvm.org/D50010 + +diff --git a/lib/Transforms/Utils/VNCoercion.cpp b/lib/Transforms/Utils/VNCoercion.cpp +index c3feea6a0a4..735d1e7b792 100644 +--- a/lib/Transforms/Utils/VNCoercion.cpp ++++ b/lib/Transforms/Utils/VNCoercion.cpp +@@ -20,14 +20,21 @@ bool canCoerceMustAliasedValueToLoad(Value *StoredVal, Type *LoadTy, + StoredVal->getType()->isStructTy() || StoredVal->getType()->isArrayTy()) + return false; + ++ Type *StoredValTy = StoredVal->getType(); ++ + // The store has to be at least as big as the load. + if (DL.getTypeSizeInBits(StoredVal->getType()) < DL.getTypeSizeInBits(LoadTy)) + return false; + +- // Don't coerce non-integral pointers to integers or vice versa. +- if (DL.isNonIntegralPointerType(StoredVal->getType()) != +- DL.isNonIntegralPointerType(LoadTy)) ++ bool StoredNI = DL.isNonIntegralPointerType(StoredValTy); ++ bool LoadNI = DL.isNonIntegralPointerType(LoadTy); ++ if (StoredNI != LoadNI) { + return false; ++ } else if (StoredNI && LoadNI && ++ cast(StoredValTy)->getAddressSpace() != ++ cast(LoadTy)->getAddressSpace()) { ++ return false; ++ } + + return true; + } +diff --git a/test/Transforms/GVN/non-integral-pointers.ll b/test/Transforms/GVN/non-integral-pointers.ll +index 9ae4132231d..5217fc1a06a 100644 +--- a/test/Transforms/GVN/non-integral-pointers.ll ++++ b/test/Transforms/GVN/non-integral-pointers.ll +@@ -1,6 +1,6 @@ + ; RUN: opt -gvn -S < %s | FileCheck %s + +-target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128-ni:4" ++target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128-ni:4:5" + target triple = "x86_64-unknown-linux-gnu" + + define void @f0(i1 %alwaysFalse, i64 %val, i64* %loc) { +@@ -37,3 +37,21 @@ define i64 @f1(i1 %alwaysFalse, i8 addrspace(4)* %val, i8 addrspace(4)** %loc) { + alwaysTaken: + ret i64 42 + } ++ ++ define i8 addrspace(5)* @multini(i1 %alwaysFalse, i8 addrspace(4)* %val, i8 addrspace(4)** %loc) { ++ ; CHECK-LABEL: @multini( ++ ; CHECK-NOT: inttoptr ++ ; CHECK-NOT: ptrtoint ++ ; CHECK-NOT: addrspacecast ++ entry: ++ store i8 addrspace(4)* %val, i8 addrspace(4)** %loc ++ br i1 %alwaysFalse, label %neverTaken, label %alwaysTaken ++ ++ neverTaken: ++ %loc.bc = bitcast i8 addrspace(4)** %loc to i8 addrspace(5)** ++ %differentas = load i8 addrspace(5)*, i8 addrspace(5)** %loc.bc ++ ret i8 addrspace(5)* %differentas ++ ++ alwaysTaken: ++ ret i8 addrspace(5)* null ++ } diff --git a/deps/patches/llvm-D50167-scev-umin.patch b/deps/patches/llvm-D50167-scev-umin.patch new file mode 100644 index 0000000..5a968a4 --- /dev/null +++ b/deps/patches/llvm-D50167-scev-umin.patch @@ -0,0 +1,1153 @@ +commit 556c30af1c797be294edde0ce621884f5acf11f0 +Author: Keno Fischer +Date: Wed Aug 1 20:45:11 2018 -0400 + + RFC: [SCEV] Add explicit representations of umin/smin + + Summary: + Currently we express umin as `~umax(~x, ~y)`. However, this becomes + a problem for operands in non-integral pointer spaces, because `~x` + is not something we can compute for `x` non-integral. However, since + comparisons are generally still allowed, we are actually able to + express `umin(x, y)` directly as long as we don't try to express is + as a umax. Support this by adding an explicit umin/smin representation + to SCEV. We do this by factoring the existing getUMax/getSMax functions + into a new function that does all four. The previous two functions + were largely identical, except that the SMax variant used `isKnownPredicate` + while the UMax variant used `isKnownViaNonRecursiveReasoning`. + + Trying to make the UMax variant also use `isKnownPredicate` yields to + an infinite recursion, while trying to make the `SMax` variant use + `isKnownViaNonRecursiveReasoning` causes + `Transforms/IndVarSimplify/backedge-on-min-max.ll` to fail. + + I would appreciate any insight into which predicate is correct here. + + Reviewers: reames, sanjoy + + Subscribers: javed.absar, llvm-commits + + Differential Revision: https://reviews.llvm.org/D50167 + +diff --git a/include/llvm/Analysis/ScalarEvolution.h b/include/llvm/Analysis/ScalarEvolution.h +index 21b72f3e13c..9fd6794395c 100644 +--- a/include/llvm/Analysis/ScalarEvolution.h ++++ b/include/llvm/Analysis/ScalarEvolution.h +@@ -582,12 +582,15 @@ public: + /// \p IndexExprs The expressions for the indices. + const SCEV *getGEPExpr(GEPOperator *GEP, + const SmallVectorImpl &IndexExprs); ++ const SCEV *getUSMinMaxExpr(unsigned Kind, SmallVectorImpl &Operands); + const SCEV *getSMaxExpr(const SCEV *LHS, const SCEV *RHS); + const SCEV *getSMaxExpr(SmallVectorImpl &Operands); + const SCEV *getUMaxExpr(const SCEV *LHS, const SCEV *RHS); + const SCEV *getUMaxExpr(SmallVectorImpl &Operands); + const SCEV *getSMinExpr(const SCEV *LHS, const SCEV *RHS); ++ const SCEV *getSMinExpr(SmallVectorImpl &Operands); + const SCEV *getUMinExpr(const SCEV *LHS, const SCEV *RHS); ++ const SCEV *getUMinExpr(SmallVectorImpl &Operands); + const SCEV *getUnknown(Value *V); + const SCEV *getCouldNotCompute(); + +diff --git a/include/llvm/Analysis/ScalarEvolutionExpander.h b/include/llvm/Analysis/ScalarEvolutionExpander.h +index 3df04e98bd2..9e407c63abc 100644 +--- a/include/llvm/Analysis/ScalarEvolutionExpander.h ++++ b/include/llvm/Analysis/ScalarEvolutionExpander.h +@@ -367,6 +367,10 @@ namespace llvm { + + Value *visitUMaxExpr(const SCEVUMaxExpr *S); + ++ Value *visitSMinExpr(const SCEVSMinExpr *S); ++ ++ Value *visitUMinExpr(const SCEVUMinExpr *S); ++ + Value *visitUnknown(const SCEVUnknown *S) { + return S->getValue(); + } +diff --git a/include/llvm/Analysis/ScalarEvolutionExpressions.h b/include/llvm/Analysis/ScalarEvolutionExpressions.h +index acf83455cdc..0d20a1bcdcc 100644 +--- a/include/llvm/Analysis/ScalarEvolutionExpressions.h ++++ b/include/llvm/Analysis/ScalarEvolutionExpressions.h +@@ -40,7 +40,7 @@ class Type; + // These should be ordered in terms of increasing complexity to make the + // folders simpler. + scConstant, scTruncate, scZeroExtend, scSignExtend, scAddExpr, scMulExpr, +- scUDivExpr, scAddRecExpr, scUMaxExpr, scSMaxExpr, ++ scUDivExpr, scAddRecExpr, scUMaxExpr, scSMaxExpr, scUMinExpr, scSMinExpr, + scUnknown, scCouldNotCompute + }; + +@@ -187,6 +187,8 @@ class Type; + S->getSCEVType() == scMulExpr || + S->getSCEVType() == scSMaxExpr || + S->getSCEVType() == scUMaxExpr || ++ S->getSCEVType() == scSMinExpr || ++ S->getSCEVType() == scUMinExpr || + S->getSCEVType() == scAddRecExpr; + } + }; +@@ -204,7 +206,9 @@ class Type; + return S->getSCEVType() == scAddExpr || + S->getSCEVType() == scMulExpr || + S->getSCEVType() == scSMaxExpr || +- S->getSCEVType() == scUMaxExpr; ++ S->getSCEVType() == scUMaxExpr || ++ S->getSCEVType() == scSMinExpr || ++ S->getSCEVType() == scUMinExpr; + } + + /// Set flags for a non-recurrence without clearing previously set flags. +@@ -396,6 +400,42 @@ class Type; + } + }; + ++ /// This class represents a signed minimum selection. ++ class SCEVSMinExpr : public SCEVCommutativeExpr { ++ friend class ScalarEvolution; ++ ++ SCEVSMinExpr(const FoldingSetNodeIDRef ID, ++ const SCEV *const *O, size_t N) ++ : SCEVCommutativeExpr(ID, scSMinExpr, O, N) { ++ // Min never overflows. ++ setNoWrapFlags((NoWrapFlags)(FlagNUW | FlagNSW)); ++ } ++ ++ public: ++ /// Methods for support type inquiry through isa, cast, and dyn_cast: ++ static bool classof(const SCEV *S) { ++ return S->getSCEVType() == scSMinExpr; ++ } ++ }; ++ ++ /// This class represents an unsigned minimum selection. ++ class SCEVUMinExpr : public SCEVCommutativeExpr { ++ friend class ScalarEvolution; ++ ++ SCEVUMinExpr(const FoldingSetNodeIDRef ID, ++ const SCEV *const *O, size_t N) ++ : SCEVCommutativeExpr(ID, scUMinExpr, O, N) { ++ // Min never overflows. ++ setNoWrapFlags((NoWrapFlags)(FlagNUW | FlagNSW)); ++ } ++ ++ public: ++ /// Methods for support type inquiry through isa, cast, and dyn_cast: ++ static bool classof(const SCEV *S) { ++ return S->getSCEVType() == scUMinExpr; ++ } ++ }; ++ + /// This means that we are dealing with an entirely unknown SCEV + /// value, and only represent it as its LLVM Value. This is the + /// "bottom" value for the analysis. +@@ -468,6 +508,10 @@ class Type; + return ((SC*)this)->visitSMaxExpr((const SCEVSMaxExpr*)S); + case scUMaxExpr: + return ((SC*)this)->visitUMaxExpr((const SCEVUMaxExpr*)S); ++ case scSMinExpr: ++ return ((SC*)this)->visitSMinExpr((const SCEVSMinExpr*)S); ++ case scUMinExpr: ++ return ((SC*)this)->visitUMinExpr((const SCEVUMinExpr*)S); + case scUnknown: + return ((SC*)this)->visitUnknown((const SCEVUnknown*)S); + case scCouldNotCompute: +@@ -521,6 +565,8 @@ class Type; + case scMulExpr: + case scSMaxExpr: + case scUMaxExpr: ++ case scSMinExpr: ++ case scUMinExpr: + case scAddRecExpr: + for (const auto *Op : cast(S)->operands()) + push(Op); +@@ -683,6 +729,26 @@ class Type; + return !Changed ? Expr : SE.getUMaxExpr(Operands); + } + ++ const SCEV *visitSMinExpr(const SCEVSMinExpr *Expr) { ++ SmallVector Operands; ++ bool Changed = false; ++ for (auto *Op : Expr->operands()) { ++ Operands.push_back(((SC *)this)->visit(Op)); ++ Changed |= Op != Operands.back(); ++ } ++ return !Changed ? Expr : SE.getSMinExpr(Operands); ++ } ++ ++ const SCEV *visitUMinExpr(const SCEVUMinExpr *Expr) { ++ SmallVector Operands; ++ bool Changed = false; ++ for (auto *Op : Expr->operands()) { ++ Operands.push_back(((SC*)this)->visit(Op)); ++ Changed |= Op != Operands.back(); ++ } ++ return !Changed ? Expr : SE.getUMinExpr(Operands); ++ } ++ + const SCEV *visitUnknown(const SCEVUnknown *Expr) { + return Expr; + } +diff --git a/lib/Analysis/ScalarEvolution.cpp b/lib/Analysis/ScalarEvolution.cpp +index bfff7afb5b4..750c1fdfdfb 100644 +--- a/lib/Analysis/ScalarEvolution.cpp ++++ b/lib/Analysis/ScalarEvolution.cpp +@@ -271,7 +271,9 @@ void SCEV::print(raw_ostream &OS) const { + case scAddExpr: + case scMulExpr: + case scUMaxExpr: +- case scSMaxExpr: { ++ case scSMaxExpr: ++ case scUMinExpr: ++ case scSMinExpr: { + const SCEVNAryExpr *NAry = cast(this); + const char *OpStr = nullptr; + switch (NAry->getSCEVType()) { +@@ -279,6 +281,8 @@ void SCEV::print(raw_ostream &OS) const { + case scMulExpr: OpStr = " * "; break; + case scUMaxExpr: OpStr = " umax "; break; + case scSMaxExpr: OpStr = " smax "; break; ++ case scUMinExpr: OpStr = " umin "; break; ++ case scSMinExpr: OpStr = " smin "; break; + } + OS << "("; + for (SCEVNAryExpr::op_iterator I = NAry->op_begin(), E = NAry->op_end(); +@@ -347,6 +351,8 @@ Type *SCEV::getType() const { + case scMulExpr: + case scUMaxExpr: + case scSMaxExpr: ++ case scUMinExpr: ++ case scSMinExpr: + return cast(this)->getType(); + case scAddExpr: + return cast(this)->getType(); +@@ -718,7 +724,9 @@ static int CompareSCEVComplexity( + case scAddExpr: + case scMulExpr: + case scSMaxExpr: +- case scUMaxExpr: { ++ case scUMaxExpr: ++ case scSMinExpr: ++ case scUMinExpr: { + const SCEVNAryExpr *LC = cast(LHS); + const SCEVNAryExpr *RC = cast(RHS); + +@@ -922,6 +930,8 @@ public: + void visitUDivExpr(const SCEVUDivExpr *Numerator) {} + void visitSMaxExpr(const SCEVSMaxExpr *Numerator) {} + void visitUMaxExpr(const SCEVUMaxExpr *Numerator) {} ++ void visitSMinExpr(const SCEVSMinExpr *Numerator) {} ++ void visitUMinExpr(const SCEVUMinExpr *Numerator) {} + void visitUnknown(const SCEVUnknown *Numerator) {} + void visitCouldNotCompute(const SCEVCouldNotCompute *Numerator) {} + +@@ -2276,6 +2286,8 @@ bool ScalarEvolution::isAvailableAtLoopEntry(const SCEV *S, const Loop *L) { + case scMulExpr: + case scUMaxExpr: + case scSMaxExpr: ++ case scUMinExpr: ++ case scSMinExpr: + case scUDivExpr: + return true; + case scUnknown: +@@ -3405,23 +3417,20 @@ ScalarEvolution::getGEPExpr(GEPOperator *GEP, + return getAddExpr(BaseExpr, TotalOffset, Wrap); + } + +-const SCEV *ScalarEvolution::getSMaxExpr(const SCEV *LHS, +- const SCEV *RHS) { +- SmallVector Ops = {LHS, RHS}; +- return getSMaxExpr(Ops); +-} +- + const SCEV * +-ScalarEvolution::getSMaxExpr(SmallVectorImpl &Ops) { +- assert(!Ops.empty() && "Cannot get empty smax!"); ++ScalarEvolution::getUSMinMaxExpr(unsigned Kind, SmallVectorImpl &Ops) { ++ assert(!Ops.empty() && "Cannot get empty (u|s)(min|max)!"); + if (Ops.size() == 1) return Ops[0]; + #ifndef NDEBUG + Type *ETy = getEffectiveSCEVType(Ops[0]->getType()); + for (unsigned i = 1, e = Ops.size(); i != e; ++i) + assert(getEffectiveSCEVType(Ops[i]->getType()) == ETy && +- "SCEVSMaxExpr operand types don't match!"); ++ "Operand types don't match!"); + #endif + ++ bool IsSigned = Kind == scSMaxExpr || Kind == scSMinExpr; ++ bool IsMax = Kind == scSMaxExpr || Kind == scUMaxExpr; ++ + // Sort by complexity, this groups all similar expression types together. + GroupByComplexity(Ops, &LI, DT); + +@@ -3430,61 +3439,85 @@ ScalarEvolution::getSMaxExpr(SmallVectorImpl &Ops) { + if (const SCEVConstant *LHSC = dyn_cast(Ops[0])) { + ++Idx; + assert(Idx < Ops.size()); ++ auto &FoldOp = ++ Kind == scSMaxExpr ? APIntOps::smax : ++ Kind == scSMinExpr ? APIntOps::smin : ++ Kind == scUMaxExpr ? APIntOps::umax : ++ APIntOps::umin; + while (const SCEVConstant *RHSC = dyn_cast(Ops[Idx])) { + // We found two constants, fold them together! + ConstantInt *Fold = ConstantInt::get( +- getContext(), APIntOps::smax(LHSC->getAPInt(), RHSC->getAPInt())); ++ getContext(), FoldOp(LHSC->getAPInt(), RHSC->getAPInt())); + Ops[0] = getConstant(Fold); + Ops.erase(Ops.begin()+1); // Erase the folded element + if (Ops.size() == 1) return Ops[0]; + LHSC = cast(Ops[0]); + } + +- // If we are left with a constant minimum-int, strip it off. +- if (cast(Ops[0])->getValue()->isMinValue(true)) { +- Ops.erase(Ops.begin()); +- --Idx; +- } else if (cast(Ops[0])->getValue()->isMaxValue(true)) { +- // If we have an smax with a constant maximum-int, it will always be +- // maximum-int. +- return Ops[0]; ++ if (IsMax) { ++ // If we are left with a constant minimum-int, strip it off. ++ if (cast(Ops[0])->getValue()->isMinValue(IsSigned)) { ++ Ops.erase(Ops.begin()); ++ --Idx; ++ } else if (cast(Ops[0])->getValue()->isMaxValue(IsSigned)) { ++ // If we have an smax with a constant maximum-int, it will always be ++ // maximum-int. ++ return Ops[0]; ++ } ++ } else { ++ // If we are left with a constant maximum-int, strip it off. ++ if (cast(Ops[0])->getValue()->isMaxValue(IsSigned)) { ++ Ops.erase(Ops.begin()); ++ --Idx; ++ } else if (cast(Ops[0])->getValue()->isMinValue(IsSigned)) { ++ // If we have an smax with a constant minimum-int, it will always be ++ // maximum-int. ++ return Ops[0]; ++ } + } + + if (Ops.size() == 1) return Ops[0]; + } + +- // Find the first SMax +- while (Idx < Ops.size() && Ops[Idx]->getSCEVType() < scSMaxExpr) ++ // Find the first operation of the same kind ++ while (Idx < Ops.size() && Ops[Idx]->getSCEVType() != Kind) + ++Idx; + + // Check to see if one of the operands is an SMax. If so, expand its operands + // onto our operand list, and recurse to simplify. + if (Idx < Ops.size()) { +- bool DeletedSMax = false; +- while (const SCEVSMaxExpr *SMax = dyn_cast(Ops[Idx])) { ++ bool DeletedAny = false; ++ while (Ops[Idx]->getSCEVType() == Kind) { ++ const SCEVCommutativeExpr *SCE = cast(Ops[Idx]); + Ops.erase(Ops.begin()+Idx); +- Ops.append(SMax->op_begin(), SMax->op_end()); +- DeletedSMax = true; ++ Ops.append(SCE->op_begin(), SCE->op_end()); ++ DeletedAny = true; + } + +- if (DeletedSMax) +- return getSMaxExpr(Ops); ++ if (DeletedAny) ++ return getUSMinMaxExpr(Kind, Ops); + } + + // Okay, check to see if the same value occurs in the operand list twice. If + // so, delete one. Since we sorted the list, these values are required to + // be adjacent. +- for (unsigned i = 0, e = Ops.size()-1; i != e; ++i) +- // X smax Y smax Y --> X smax Y +- // X smax Y --> X, if X is always greater than Y +- if (Ops[i] == Ops[i+1] || +- isKnownPredicate(ICmpInst::ICMP_SGE, Ops[i], Ops[i+1])) { +- Ops.erase(Ops.begin()+i+1, Ops.begin()+i+2); +- --i; --e; +- } else if (isKnownPredicate(ICmpInst::ICMP_SLE, Ops[i], Ops[i+1])) { +- Ops.erase(Ops.begin()+i, Ops.begin()+i+1); +- --i; --e; +- } ++ llvm::CmpInst::Predicate GEPred = IsSigned ? ICmpInst::ICMP_SGE : ICmpInst::ICMP_UGE; ++ llvm::CmpInst::Predicate LEPred = IsSigned ? ICmpInst::ICMP_SLE : ICmpInst::ICMP_ULE; ++ llvm::CmpInst::Predicate FirstPred = IsMax ? GEPred : LEPred; ++ llvm::CmpInst::Predicate SecondPred = IsMax ? LEPred : GEPred; ++ for (unsigned i = 0, e = Ops.size()-1; i != e; ++i) { ++ if (Ops[i] == Ops[i+1] || ++ isKnownPredicate(FirstPred, Ops[i], Ops[i+1])) { ++ // X op Y op Y --> X op Y ++ // X op Y --> X, if we know X, Y are ordered appropriately ++ Ops.erase(Ops.begin()+i+1, Ops.begin()+i+2); ++ --i; --e; ++ } else if (isKnownPredicate(SecondPred, Ops[i], Ops[i+1])) { ++ // X op Y --> Y, if we know X, Y are ordered appropriately ++ Ops.erase(Ops.begin()+i, Ops.begin()+i+1); ++ --i; --e; ++ } ++ } + + if (Ops.size() == 1) return Ops[0]; + +@@ -3493,132 +3526,73 @@ ScalarEvolution::getSMaxExpr(SmallVectorImpl &Ops) { + // Okay, it looks like we really DO need an smax expr. Check to see if we + // already have one, otherwise create a new one. + FoldingSetNodeID ID; +- ID.AddInteger(scSMaxExpr); ++ ID.AddInteger(Kind); + for (unsigned i = 0, e = Ops.size(); i != e; ++i) + ID.AddPointer(Ops[i]); + void *IP = nullptr; + if (const SCEV *S = UniqueSCEVs.FindNodeOrInsertPos(ID, IP)) return S; + const SCEV **O = SCEVAllocator.Allocate(Ops.size()); + std::uninitialized_copy(Ops.begin(), Ops.end(), O); +- SCEV *S = new (SCEVAllocator) SCEVSMaxExpr(ID.Intern(SCEVAllocator), +- O, Ops.size()); ++ SCEV *S = nullptr; ++ ++ if (Kind == scSMaxExpr) { ++ S = new (SCEVAllocator) SCEVSMaxExpr(ID.Intern(SCEVAllocator), ++ O, Ops.size()); ++ } else if (Kind == scUMaxExpr) { ++ S = new (SCEVAllocator) SCEVUMaxExpr(ID.Intern(SCEVAllocator), ++ O, Ops.size()); ++ } else if (Kind == scSMinExpr) { ++ S = new (SCEVAllocator) SCEVSMinExpr(ID.Intern(SCEVAllocator), ++ O, Ops.size()); ++ } else { ++ assert(Kind == scUMinExpr); ++ S = new (SCEVAllocator) SCEVUMinExpr(ID.Intern(SCEVAllocator), ++ O, Ops.size()); ++ } ++ + UniqueSCEVs.InsertNode(S, IP); + addToLoopUseLists(S); + return S; + } + +-const SCEV *ScalarEvolution::getUMaxExpr(const SCEV *LHS, ++const SCEV *ScalarEvolution::getSMaxExpr(const SCEV *LHS, + const SCEV *RHS) { + SmallVector Ops = {LHS, RHS}; +- return getUMaxExpr(Ops); ++ return getSMaxExpr(Ops); + } + +-const SCEV * +-ScalarEvolution::getUMaxExpr(SmallVectorImpl &Ops) { +- assert(!Ops.empty() && "Cannot get empty umax!"); +- if (Ops.size() == 1) return Ops[0]; +-#ifndef NDEBUG +- Type *ETy = getEffectiveSCEVType(Ops[0]->getType()); +- for (unsigned i = 1, e = Ops.size(); i != e; ++i) +- assert(getEffectiveSCEVType(Ops[i]->getType()) == ETy && +- "SCEVUMaxExpr operand types don't match!"); +-#endif +- +- // Sort by complexity, this groups all similar expression types together. +- GroupByComplexity(Ops, &LI, DT); +- +- // If there are any constants, fold them together. +- unsigned Idx = 0; +- if (const SCEVConstant *LHSC = dyn_cast(Ops[0])) { +- ++Idx; +- assert(Idx < Ops.size()); +- while (const SCEVConstant *RHSC = dyn_cast(Ops[Idx])) { +- // We found two constants, fold them together! +- ConstantInt *Fold = ConstantInt::get( +- getContext(), APIntOps::umax(LHSC->getAPInt(), RHSC->getAPInt())); +- Ops[0] = getConstant(Fold); +- Ops.erase(Ops.begin()+1); // Erase the folded element +- if (Ops.size() == 1) return Ops[0]; +- LHSC = cast(Ops[0]); +- } +- +- // If we are left with a constant minimum-int, strip it off. +- if (cast(Ops[0])->getValue()->isMinValue(false)) { +- Ops.erase(Ops.begin()); +- --Idx; +- } else if (cast(Ops[0])->getValue()->isMaxValue(false)) { +- // If we have an umax with a constant maximum-int, it will always be +- // maximum-int. +- return Ops[0]; +- } +- +- if (Ops.size() == 1) return Ops[0]; +- } +- +- // Find the first UMax +- while (Idx < Ops.size() && Ops[Idx]->getSCEVType() < scUMaxExpr) +- ++Idx; +- +- // Check to see if one of the operands is a UMax. If so, expand its operands +- // onto our operand list, and recurse to simplify. +- if (Idx < Ops.size()) { +- bool DeletedUMax = false; +- while (const SCEVUMaxExpr *UMax = dyn_cast(Ops[Idx])) { +- Ops.erase(Ops.begin()+Idx); +- Ops.append(UMax->op_begin(), UMax->op_end()); +- DeletedUMax = true; +- } +- +- if (DeletedUMax) +- return getUMaxExpr(Ops); +- } +- +- // Okay, check to see if the same value occurs in the operand list twice. If +- // so, delete one. Since we sorted the list, these values are required to +- // be adjacent. +- for (unsigned i = 0, e = Ops.size()-1; i != e; ++i) +- // X umax Y umax Y --> X umax Y +- // X umax Y --> X, if X is always greater than Y +- if (Ops[i] == Ops[i+1] || +- isKnownPredicate(ICmpInst::ICMP_UGE, Ops[i], Ops[i+1])) { +- Ops.erase(Ops.begin()+i+1, Ops.begin()+i+2); +- --i; --e; +- } else if (isKnownPredicate(ICmpInst::ICMP_ULE, Ops[i], Ops[i+1])) { +- Ops.erase(Ops.begin()+i, Ops.begin()+i+1); +- --i; --e; +- } +- +- if (Ops.size() == 1) return Ops[0]; ++const SCEV *ScalarEvolution::getSMaxExpr(SmallVectorImpl &Ops) { ++ return getUSMinMaxExpr(scSMaxExpr, Ops); ++} + +- assert(!Ops.empty() && "Reduced umax down to nothing!"); ++const SCEV *ScalarEvolution::getUMaxExpr(const SCEV *LHS, ++ const SCEV *RHS) { ++ SmallVector Ops = {LHS, RHS}; ++ return getUMaxExpr(Ops); ++} + +- // Okay, it looks like we really DO need a umax expr. Check to see if we +- // already have one, otherwise create a new one. +- FoldingSetNodeID ID; +- ID.AddInteger(scUMaxExpr); +- for (unsigned i = 0, e = Ops.size(); i != e; ++i) +- ID.AddPointer(Ops[i]); +- void *IP = nullptr; +- if (const SCEV *S = UniqueSCEVs.FindNodeOrInsertPos(ID, IP)) return S; +- const SCEV **O = SCEVAllocator.Allocate(Ops.size()); +- std::uninitialized_copy(Ops.begin(), Ops.end(), O); +- SCEV *S = new (SCEVAllocator) SCEVUMaxExpr(ID.Intern(SCEVAllocator), +- O, Ops.size()); +- UniqueSCEVs.InsertNode(S, IP); +- addToLoopUseLists(S); +- return S; ++const SCEV *ScalarEvolution::getUMaxExpr(SmallVectorImpl &Ops) { ++ return getUSMinMaxExpr(scUMaxExpr, Ops); + } + + const SCEV *ScalarEvolution::getSMinExpr(const SCEV *LHS, + const SCEV *RHS) { +- // ~smax(~x, ~y) == smin(x, y). +- return getNotSCEV(getSMaxExpr(getNotSCEV(LHS), getNotSCEV(RHS))); ++ SmallVector Ops = { LHS, RHS }; ++ return getSMinExpr(Ops); ++} ++ ++const SCEV *ScalarEvolution::getSMinExpr(SmallVectorImpl &Ops) { ++ return getUSMinMaxExpr(scSMinExpr, Ops); + } + + const SCEV *ScalarEvolution::getUMinExpr(const SCEV *LHS, + const SCEV *RHS) { +- // ~umax(~x, ~y) == umin(x, y) +- return getNotSCEV(getUMaxExpr(getNotSCEV(LHS), getNotSCEV(RHS))); ++ SmallVector Ops = { LHS, RHS }; ++ return getUMinExpr(Ops); ++} ++ ++const SCEV *ScalarEvolution::getUMinExpr(SmallVectorImpl &Ops) { ++ return getUSMinMaxExpr(scUMinExpr, Ops); + } + + const SCEV *ScalarEvolution::getSizeOfExpr(Type *IntTy, Type *AllocTy) { +@@ -5002,6 +4976,7 @@ static bool IsAvailableOnEntry(const Loop *L, DominatorTree &DT, const SCEV *S, + switch (S->getSCEVType()) { + case scConstant: case scTruncate: case scZeroExtend: case scSignExtend: + case scAddExpr: case scMulExpr: case scUMaxExpr: case scSMaxExpr: ++ case scUMinExpr: case scSMinExpr: + // These expressions are available if their operand(s) is/are. + return true; + +@@ -7885,7 +7860,9 @@ static Constant *BuildConstantFromSCEV(const SCEV *V) { + } + case scSMaxExpr: + case scUMaxExpr: +- break; // TODO: smax, umax. ++ case scSMinExpr: ++ case scUMinExpr: ++ break; // TODO: smax, umax, smin, umax. + } + return nullptr; + } +@@ -8015,6 +7992,10 @@ const SCEV *ScalarEvolution::computeSCEVAtScope(const SCEV *V, const Loop *L) { + return getSMaxExpr(NewOps); + if (isa(Comm)) + return getUMaxExpr(NewOps); ++ if (isa(Comm)) ++ return getSMinExpr(NewOps); ++ if (isa(Comm)) ++ return getUMinExpr(NewOps); + llvm_unreachable("Unknown commutative SCEV type!"); + } + } +@@ -10998,7 +10979,9 @@ ScalarEvolution::computeLoopDisposition(const SCEV *S, const Loop *L) { + case scAddExpr: + case scMulExpr: + case scUMaxExpr: +- case scSMaxExpr: { ++ case scSMaxExpr: ++ case scUMinExpr: ++ case scSMinExpr: { + bool HasVarying = false; + for (auto *Op : cast(S)->operands()) { + LoopDisposition D = getLoopDisposition(Op, L); +@@ -11085,7 +11068,9 @@ ScalarEvolution::computeBlockDisposition(const SCEV *S, const BasicBlock *BB) { + case scAddExpr: + case scMulExpr: + case scUMaxExpr: +- case scSMaxExpr: { ++ case scSMaxExpr: ++ case scUMinExpr: ++ case scSMinExpr: { + const SCEVNAryExpr *NAry = cast(S); + bool Proper = true; + for (const SCEV *NAryOp : NAry->operands()) { +diff --git a/lib/Analysis/ScalarEvolutionExpander.cpp b/lib/Analysis/ScalarEvolutionExpander.cpp +index 01a8732b0b8..8160a1eaa0b 100644 +--- a/lib/Analysis/ScalarEvolutionExpander.cpp ++++ b/lib/Analysis/ScalarEvolutionExpander.cpp +@@ -1634,14 +1634,15 @@ Value *SCEVExpander::visitSMaxExpr(const SCEVSMaxExpr *S) { + for (int i = S->getNumOperands()-2; i >= 0; --i) { + // In the case of mixed integer and pointer types, do the + // rest of the comparisons as integer. +- if (S->getOperand(i)->getType() != Ty) { ++ Type *OpTy = S->getOperand(i)->getType(); ++ if (OpTy->isIntegerTy() != Ty->isIntegerTy()) { + Ty = SE.getEffectiveSCEVType(Ty); + LHS = InsertNoopCastOfTo(LHS, Ty); + } + Value *RHS = expandCodeFor(S->getOperand(i), Ty); + Value *ICmp = Builder.CreateICmpSGT(LHS, RHS); + rememberInstruction(ICmp); +- Value *Sel = Builder.CreateSelect(ICmp, LHS, RHS, "smax"); ++ Value *Sel = Builder.CreateSelect(ICmp, LHS, RHS, "smin"); + rememberInstruction(Sel); + LHS = Sel; + } +@@ -1658,14 +1659,15 @@ Value *SCEVExpander::visitUMaxExpr(const SCEVUMaxExpr *S) { + for (int i = S->getNumOperands()-2; i >= 0; --i) { + // In the case of mixed integer and pointer types, do the + // rest of the comparisons as integer. +- if (S->getOperand(i)->getType() != Ty) { ++ Type *OpTy = S->getOperand(i)->getType(); ++ if (OpTy->isIntegerTy() != Ty->isIntegerTy()) { + Ty = SE.getEffectiveSCEVType(Ty); + LHS = InsertNoopCastOfTo(LHS, Ty); + } + Value *RHS = expandCodeFor(S->getOperand(i), Ty); + Value *ICmp = Builder.CreateICmpUGT(LHS, RHS); + rememberInstruction(ICmp); +- Value *Sel = Builder.CreateSelect(ICmp, LHS, RHS, "umax"); ++ Value *Sel = Builder.CreateSelect(ICmp, LHS, RHS, "umin"); + rememberInstruction(Sel); + LHS = Sel; + } +@@ -1671,6 +1671,56 @@ Value *SCEVExpander::visitUMaxExpr(const SCEVUMaxExpr *S) { + return LHS; + } + ++Value *SCEVExpander::visitSMinExpr(const SCEVSMinExpr *S) { ++ Value *LHS = expand(S->getOperand(S->getNumOperands()-1)); ++ Type *Ty = LHS->getType(); ++ for (int i = S->getNumOperands()-2; i >= 0; --i) { ++ // In the case of mixed integer and pointer types, do the ++ // rest of the comparisons as integer. ++ Type *OpTy = S->getOperand(i)->getType(); ++ if (OpTy->isIntegerTy() != Ty->isIntegerTy()) { ++ Ty = SE.getEffectiveSCEVType(Ty); ++ LHS = InsertNoopCastOfTo(LHS, Ty); ++ } ++ Value *RHS = expandCodeFor(S->getOperand(i), Ty); ++ Value *ICmp = Builder.CreateICmpSLT(LHS, RHS); ++ rememberInstruction(ICmp); ++ Value *Sel = Builder.CreateSelect(ICmp, LHS, RHS, "smax"); ++ rememberInstruction(Sel); ++ LHS = Sel; ++ } ++ // In the case of mixed integer and pointer types, cast the ++ // final result back to the pointer type. ++ if (LHS->getType() != S->getType()) ++ LHS = InsertNoopCastOfTo(LHS, S->getType()); ++ return LHS; ++} ++ ++Value *SCEVExpander::visitUMinExpr(const SCEVUMinExpr *S) { ++ Value *LHS = expand(S->getOperand(S->getNumOperands()-1)); ++ Type *Ty = LHS->getType(); ++ for (int i = S->getNumOperands()-2; i >= 0; --i) { ++ // In the case of mixed integer and pointer types, do the ++ // rest of the comparisons as integer. ++ Type *OpTy = S->getOperand(i)->getType(); ++ if (OpTy->isIntegerTy() != Ty->isIntegerTy()) { ++ Ty = SE.getEffectiveSCEVType(Ty); ++ LHS = InsertNoopCastOfTo(LHS, Ty); ++ } ++ Value *RHS = expandCodeFor(S->getOperand(i), Ty); ++ Value *ICmp = Builder.CreateICmpULT(LHS, RHS); ++ rememberInstruction(ICmp); ++ Value *Sel = Builder.CreateSelect(ICmp, LHS, RHS, "umax"); ++ rememberInstruction(Sel); ++ LHS = Sel; ++ } ++ // In the case of mixed integer and pointer types, cast the ++ // final result back to the pointer type. ++ if (LHS->getType() != S->getType()) ++ LHS = InsertNoopCastOfTo(LHS, S->getType()); ++ return LHS; ++} ++ + Value *SCEVExpander::expandCodeFor(const SCEV *SH, Type *Ty, + Instruction *IP) { + setInsertPoint(IP); +diff --git a/test/Analysis/LoopAccessAnalysis/memcheck-ni.ll b/test/Analysis/LoopAccessAnalysis/memcheck-ni.ll +new file mode 100644 +index 00000000000..a08632f38d1 +--- /dev/null ++++ b/test/Analysis/LoopAccessAnalysis/memcheck-ni.ll +@@ -0,0 +1,50 @@ ++; RUN: opt -loop-versioning -S < %s | FileCheck %s ++ ++; NB: addrspaces 10-13 are non-integral ++target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128-ni:10:11:12:13" ++ ++%jl_value_t = type opaque ++%jl_array_t = type { i8 addrspace(13)*, i64, i16, i16, i32 } ++ ++define void @"japi1_permutedims!_33509"(%jl_value_t addrspace(10)**) { ++; CHECK: [[CMP:%[^ ]*]] = icmp ult double addrspace(13)* [[A:%[^ ]*]], [[B:%[^ ]*]] ++; CHECK: [[SELECT:%[^ ]*]] = select i1 %18, double addrspace(13)* [[A]], double addrspace(13)* [[B]] ++top: ++ %1 = alloca [3 x i64], align 8 ++ %2 = load %jl_value_t addrspace(10)*, %jl_value_t addrspace(10)** %0, align 8 ++ %3 = getelementptr inbounds %jl_value_t addrspace(10)*, %jl_value_t addrspace(10)** %0, i64 1 ++ %4 = load %jl_value_t addrspace(10)*, %jl_value_t addrspace(10)** %3, align 8 ++ %5 = getelementptr inbounds [3 x i64], [3 x i64]* %1, i64 0, i64 0 ++ store i64 1, i64* %5, align 8 ++ %6 = getelementptr inbounds [3 x i64], [3 x i64]* %1, i64 0, i64 1 ++ %7 = load i64, i64* inttoptr (i64 24 to i64*), align 8 ++ %8 = addrspacecast %jl_value_t addrspace(10)* %4 to %jl_value_t addrspace(11)* ++ %9 = bitcast %jl_value_t addrspace(11)* %8 to double addrspace(13)* addrspace(11)* ++ %10 = load double addrspace(13)*, double addrspace(13)* addrspace(11)* %9, align 8 ++ %11 = addrspacecast %jl_value_t addrspace(10)* %2 to %jl_value_t addrspace(11)* ++ %12 = bitcast %jl_value_t addrspace(11)* %11 to double addrspace(13)* addrspace(11)* ++ %13 = load double addrspace(13)*, double addrspace(13)* addrspace(11)* %12, align 8 ++ %14 = load i64, i64* %6, align 8 ++ br label %L74 ++ ++L74: ++ %value_phi20 = phi i64 [ 1, %top ], [ %22, %L74 ] ++ %value_phi21 = phi i64 [ 1, %top ], [ %23, %L74 ] ++ %value_phi22 = phi i64 [ 1, %top ], [ %25, %L74 ] ++ %15 = add i64 %value_phi21, -1 ++ %16 = getelementptr inbounds double, double addrspace(13)* %10, i64 %15 ++ %17 = bitcast double addrspace(13)* %16 to i64 addrspace(13)* ++ %18 = load i64, i64 addrspace(13)* %17, align 8 ++ %19 = add i64 %value_phi20, -1 ++ %20 = getelementptr inbounds double, double addrspace(13)* %13, i64 %19 ++ %21 = bitcast double addrspace(13)* %20 to i64 addrspace(13)* ++ store i64 %18, i64 addrspace(13)* %21, align 8 ++ %22 = add i64 %value_phi20, 1 ++ %23 = add i64 %14, %value_phi21 ++ %24 = icmp eq i64 %value_phi22, %7 ++ %25 = add i64 %value_phi22, 1 ++ br i1 %24, label %L94, label %L74 ++ ++L94: ++ ret void ++} +diff --git a/test/Analysis/LoopAccessAnalysis/reverse-memcheck-bounds.ll b/test/Analysis/LoopAccessAnalysis/reverse-memcheck-bounds.ll +index 405a47554e4..4285ef0f117 100644 +--- a/test/Analysis/LoopAccessAnalysis/reverse-memcheck-bounds.ll ++++ b/test/Analysis/LoopAccessAnalysis/reverse-memcheck-bounds.ll +@@ -58,7 +58,7 @@ for.end: ; preds = %for.body + + ; Here it is not obvious what the limits are, since 'step' could be negative. + +-; CHECK: Low: (-1 + (-1 * ((-60001 + (-1 * %a)) umax (-60001 + (40000 * %step) + (-1 * %a))))) ++; CHECK: Low: ((60000 + %a) umin (60000 + (-40000 * %step) + %a)) + ; CHECK: High: (4 + ((60000 + %a) umax (60000 + (-40000 * %step) + %a))) + + define void @g(i64 %step) { +diff --git a/test/Analysis/ScalarEvolution/2008-07-29-SMinExpr.ll b/test/Analysis/ScalarEvolution/2008-07-29-SMinExpr.ll +index 3542ad2a41e..53e024a68fb 100644 +--- a/test/Analysis/ScalarEvolution/2008-07-29-SMinExpr.ll ++++ b/test/Analysis/ScalarEvolution/2008-07-29-SMinExpr.ll +@@ -22,5 +22,5 @@ afterfor: ; preds = %forinc, %entry + ret i32 %j.0.lcssa + } + +-; CHECK: backedge-taken count is (-2147483632 + ((-1 + (-1 * %{{[xy]}})) smax (-1 + (-1 * %{{[xy]}})))) ++; CHECK: backedge-taken count is (-2147483633 + (-1 * (%x smin %y))) + +diff --git a/test/Analysis/ScalarEvolution/min-max-exprs.ll b/test/Analysis/ScalarEvolution/min-max-exprs.ll +index e8c1e33e095..51f72c643cc 100644 +--- a/test/Analysis/ScalarEvolution/min-max-exprs.ll ++++ b/test/Analysis/ScalarEvolution/min-max-exprs.ll +@@ -33,7 +33,7 @@ bb2: ; preds = %bb1 + %tmp9 = select i1 %tmp4, i64 %tmp5, i64 %tmp6 + ; min(N, i+3) + ; CHECK: select i1 %tmp4, i64 %tmp5, i64 %tmp6 +-; CHECK-NEXT: --> (-1 + (-1 * ((-1 + (-1 * (sext i32 {3,+,1}<%bb1> to i64))) smax (-1 + (-1 * (sext i32 %N to i64)))))) ++; CHECK-NEXT: --> ((sext i32 {3,+,1}<%bb1> to i64) smin (sext i32 %N to i64)) + %tmp11 = getelementptr inbounds i32, i32* %A, i64 %tmp9 + %tmp12 = load i32, i32* %tmp11, align 4 + %tmp13 = shl nsw i32 %tmp12, 1 +diff --git a/test/Analysis/ScalarEvolution/pr28705.ll b/test/Analysis/ScalarEvolution/pr28705.ll +index 8fbc08e3ca6..7d797a15bd5 100644 +--- a/test/Analysis/ScalarEvolution/pr28705.ll ++++ b/test/Analysis/ScalarEvolution/pr28705.ll +@@ -5,7 +5,7 @@ + ; with "%.sroa.speculated + 1". + ; + ; CHECK-LABEL: @foo( +-; CHECK: %[[EXIT:.+]] = sub i32 %.sroa.speculated, -1 ++; CHECK: %[[EXIT:.+]] = add i32 %.sroa.speculated, 1 + ; CHECK: %DB.sroa.9.0.lcssa = phi i32 [ 1, %entry ], [ %[[EXIT]], %loopexit ] + ; + define void @foo(i32 %sub.ptr.div.i, i8* %ref.i1174) local_unnamed_addr { +diff --git a/test/Analysis/ScalarEvolution/predicated-trip-count.ll b/test/Analysis/ScalarEvolution/predicated-trip-count.ll +index 2db0a8b5777..b07662ed95f 100644 +--- a/test/Analysis/ScalarEvolution/predicated-trip-count.ll ++++ b/test/Analysis/ScalarEvolution/predicated-trip-count.ll +@@ -80,7 +80,7 @@ return: ; preds = %bb5 + ; CHECK-NEXT: --> (sext i16 {%Start,+,-1}<%bb3> to i32) + ; CHECK: Loop %bb3: Unpredictable backedge-taken count. + ; CHECK-NEXT: Loop %bb3: Unpredictable max backedge-taken count. +-; CHECK-NEXT: Loop %bb3: Predicated backedge-taken count is (2 + (sext i16 %Start to i32) + ((-2 + (-1 * (sext i16 %Start to i32))) smax (-1 + (-1 * %M)))) ++; CHECK-NEXT: Loop %bb3: Predicated backedge-taken count is (1 + (sext i16 %Start to i32) + (-1 * ((1 + (sext i16 %Start to i32)) smin %M))) + ; CHECK-NEXT: Predicates: + ; CHECK-NEXT: {%Start,+,-1}<%bb3> Added Flags: + +diff --git a/test/Analysis/ScalarEvolution/trip-count3.ll b/test/Analysis/ScalarEvolution/trip-count3.ll +index cce0182d649..7f20b4e71be 100644 +--- a/test/Analysis/ScalarEvolution/trip-count3.ll ++++ b/test/Analysis/ScalarEvolution/trip-count3.ll +@@ -4,7 +4,7 @@ + ; dividing by the stride will have a remainder. This could theoretically + ; be teaching it how to use a more elaborate trip count computation. + +-; CHECK: Loop %bb3.i: backedge-taken count is ((64 + (-64 smax (-1 + (-1 * %0))) + %0) /u 64) ++; CHECK: Loop %bb3.i: backedge-taken count is ((63 + (-1 * (63 smin %0)) + %0) /u 64) + ; CHECK: Loop %bb3.i: max backedge-taken count is 33554431 + + %struct.FILE = type { i32, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, %struct._IO_marker*, %struct.FILE*, i32, i32, i64, i16, i8, [1 x i8], i8*, i64, i8*, i8*, i8*, i8*, i64, i32, [20 x i8] } +diff --git a/test/Transforms/IRCE/conjunctive-checks.ll b/test/Transforms/IRCE/conjunctive-checks.ll +index f6a909e432c..d9bf485df3a 100644 +--- a/test/Transforms/IRCE/conjunctive-checks.ll ++++ b/test/Transforms/IRCE/conjunctive-checks.ll +@@ -4,16 +4,6 @@ define void @f_0(i32 *%arr, i32 *%a_len_ptr, i32 %n, i1* %cond_buf) { + ; CHECK-LABEL: @f_0( + + ; CHECK: loop.preheader: +-; CHECK: [[not_n:[^ ]+]] = sub i32 -1, %n +-; CHECK: [[not_safe_range_end:[^ ]+]] = sub i32 3, %len +-; CHECK: [[not_exit_main_loop_at_hiclamp_cmp:[^ ]+]] = icmp sgt i32 [[not_n]], [[not_safe_range_end]] +-; CHECK: [[not_exit_main_loop_at_hiclamp:[^ ]+]] = select i1 [[not_exit_main_loop_at_hiclamp_cmp]], i32 [[not_n]], i32 [[not_safe_range_end]] +-; CHECK: [[exit_main_loop_at_hiclamp:[^ ]+]] = sub i32 -1, [[not_exit_main_loop_at_hiclamp]] +-; CHECK: [[exit_main_loop_at_loclamp_cmp:[^ ]+]] = icmp sgt i32 [[exit_main_loop_at_hiclamp]], 0 +-; CHECK: [[exit_main_loop_at_loclamp:[^ ]+]] = select i1 [[exit_main_loop_at_loclamp_cmp]], i32 [[exit_main_loop_at_hiclamp]], i32 0 +-; CHECK: [[enter_main_loop:[^ ]+]] = icmp slt i32 0, [[exit_main_loop_at_loclamp]] +-; CHECK: br i1 [[enter_main_loop]], label %loop.preheader2, label %main.pseudo.exit +- + ; CHECK: loop.preheader2: + ; CHECK: br label %loop + +@@ -57,14 +47,10 @@ define void @f_1( + ; CHECK-LABEL: @f_1( + + ; CHECK: loop.preheader: +-; CHECK: [[not_len_b:[^ ]+]] = sub i32 -1, %len.b +-; CHECK: [[not_len_a:[^ ]+]] = sub i32 -1, %len.a +-; CHECK: [[smax_not_len_cond:[^ ]+]] = icmp sgt i32 [[not_len_b]], [[not_len_a]] +-; CHECK: [[smax_not_len:[^ ]+]] = select i1 [[smax_not_len_cond]], i32 [[not_len_b]], i32 [[not_len_a]] +-; CHECK: [[not_n:[^ ]+]] = sub i32 -1, %n +-; CHECK: [[not_upper_limit_cond_loclamp:[^ ]+]] = icmp sgt i32 [[smax_not_len]], [[not_n]] +-; CHECK: [[not_upper_limit_loclamp:[^ ]+]] = select i1 [[not_upper_limit_cond_loclamp]], i32 [[smax_not_len]], i32 [[not_n]] +-; CHECK: [[upper_limit_loclamp:[^ ]+]] = sub i32 -1, [[not_upper_limit_loclamp]] ++; CHECK: [[smax_len_cond:[^ ]+]] = icmp slt i32 %len.b, %len.a ++; CHECK: [[smax_len:[^ ]+]] = select i1 [[smax_len_cond]], i32 %len.b, i32 %len.a ++; CHECK: [[upper_limit_cond_loclamp:[^ ]+]] = icmp slt i32 [[smax_len]], %n ++; CHECK: [[upper_limit_loclamp:[^ ]+]] = select i1 [[upper_limit_cond_loclamp]], i32 [[smax_len]], i32 %n + ; CHECK: [[upper_limit_cmp:[^ ]+]] = icmp sgt i32 [[upper_limit_loclamp]], 0 + ; CHECK: [[upper_limit:[^ ]+]] = select i1 [[upper_limit_cmp]], i32 [[upper_limit_loclamp]], i32 0 + +diff --git a/test/Transforms/IRCE/decrementing-loop.ll b/test/Transforms/IRCE/decrementing-loop.ll +index fac873b4a24..30663da9e9f 100644 +--- a/test/Transforms/IRCE/decrementing-loop.ll ++++ b/test/Transforms/IRCE/decrementing-loop.ll +@@ -28,11 +28,8 @@ define void @decrementing_loop(i32 *%arr, i32 *%a_len_ptr, i32 %n) { + ret void + + ; CHECK: loop.preheader: +-; CHECK: [[not_len:[^ ]+]] = sub i32 -1, %len +-; CHECK: [[not_n:[^ ]+]] = sub i32 -1, %n +-; CHECK: [[not_len_hiclamp_cmp:[^ ]+]] = icmp sgt i32 [[not_len]], [[not_n]] +-; CHECK: [[not_len_hiclamp:[^ ]+]] = select i1 [[not_len_hiclamp_cmp]], i32 [[not_len]], i32 [[not_n]] +-; CHECK: [[len_hiclamp:[^ ]+]] = sub i32 -1, [[not_len_hiclamp]] ++; CHECK: [[len_hiclamp_cmp:[^ ]+]] = icmp slt i32 %len, %n ++; CHECK: [[len_hiclamp:[^ ]+]] = select i1 [[len_hiclamp_cmp]], i32 %len, i32 %n + ; CHECK: [[not_exit_preloop_at_cmp:[^ ]+]] = icmp sgt i32 [[len_hiclamp]], 0 + ; CHECK: [[not_exit_preloop_at:[^ ]+]] = select i1 [[not_exit_preloop_at_cmp]], i32 [[len_hiclamp]], i32 0 + ; CHECK: %exit.preloop.at = add i32 [[not_exit_preloop_at]], -1 +diff --git a/test/Transforms/IRCE/multiple-access-no-preloop.ll b/test/Transforms/IRCE/multiple-access-no-preloop.ll +index 31bfe7881b6..e693b1b8ef4 100644 +--- a/test/Transforms/IRCE/multiple-access-no-preloop.ll ++++ b/test/Transforms/IRCE/multiple-access-no-preloop.ll +@@ -37,14 +37,10 @@ define void @multiple_access_no_preloop( + ; CHECK-LABEL: @multiple_access_no_preloop( + + ; CHECK: loop.preheader: +-; CHECK: [[not_len_b:[^ ]+]] = sub i32 -1, %len.b +-; CHECK: [[not_len_a:[^ ]+]] = sub i32 -1, %len.a +-; CHECK: [[smax_not_len_cond:[^ ]+]] = icmp sgt i32 [[not_len_b]], [[not_len_a]] +-; CHECK: [[smax_not_len:[^ ]+]] = select i1 [[smax_not_len_cond]], i32 [[not_len_b]], i32 [[not_len_a]] +-; CHECK: [[not_n:[^ ]+]] = sub i32 -1, %n +-; CHECK: [[not_upper_limit_cond_loclamp:[^ ]+]] = icmp sgt i32 [[smax_not_len]], [[not_n]] +-; CHECK: [[not_upper_limit_loclamp:[^ ]+]] = select i1 [[not_upper_limit_cond_loclamp]], i32 [[smax_not_len]], i32 [[not_n]] +-; CHECK: [[upper_limit_loclamp:[^ ]+]] = sub i32 -1, [[not_upper_limit_loclamp]] ++; CHECK: [[smax_len_cond:[^ ]+]] = icmp slt i32 %len.b, %len.a ++; CHECK: [[smax_len:[^ ]+]] = select i1 [[smax_len_cond]], i32 %len.b, i32 %len.a ++; CHECK: [[upper_limit_cond_loclamp:[^ ]+]] = icmp slt i32 [[smax_len]], %n ++; CHECK: [[upper_limit_loclamp:[^ ]+]] = select i1 [[upper_limit_cond_loclamp]], i32 [[smax_len]], i32 %n + ; CHECK: [[upper_limit_cmp:[^ ]+]] = icmp sgt i32 [[upper_limit_loclamp]], 0 + ; CHECK: [[upper_limit:[^ ]+]] = select i1 [[upper_limit_cmp]], i32 [[upper_limit_loclamp]], i32 0 + +diff --git a/test/Transforms/IRCE/ranges_of_different_types.ll b/test/Transforms/IRCE/ranges_of_different_types.ll +index c38ef24bc18..5694906a4c5 100644 +--- a/test/Transforms/IRCE/ranges_of_different_types.ll ++++ b/test/Transforms/IRCE/ranges_of_different_types.ll +@@ -22,12 +22,11 @@ define void @test_01(i32* %arr, i32* %a_len_ptr) #0 { + ; CHECK-NOT: preloop + ; CHECK: entry: + ; CHECK-NEXT: %len = load i32, i32* %a_len_ptr, !range !0 +-; CHECK-NEXT: [[SUB1:%[^ ]+]] = sub i32 12, %len +-; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp sgt i32 [[SUB1]], -102 +-; CHECK-NEXT: [[SMAX:%[^ ]+]] = select i1 [[CMP1]], i32 [[SUB1]], i32 -102 +-; CHECK-NEXT: [[SUB2:%[^ ]+]] = sub i32 -1, [[SMAX]] +-; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp sgt i32 [[SUB2]], 0 +-; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP2]], i32 [[SUB2]], i32 0 ++; CHECK-NEXT: [[SUB1:%[^ ]+]] = add i32 %len, -13 ++; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp slt i32 [[SUB1]], 101 ++; CHECK-NEXT: [[SMAX:%[^ ]+]] = select i1 [[CMP1]], i32 [[SUB1]], i32 101 ++; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp sgt i32 [[SMAX]], 0 ++; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP2]], i32 [[SMAX]], i32 0 + ; CHECK-NEXT: [[GOTO_LOOP:%[^ ]+]] = icmp slt i32 0, %exit.mainloop.at + ; CHECK-NEXT: br i1 [[GOTO_LOOP]], label %loop.preheader, label %main.pseudo.exit + ; CHECK: loop +@@ -82,13 +81,11 @@ define void @test_02(i32* %arr, i32* %a_len_ptr) #0 { + ; CHECK-NEXT: [[LEN_MINUS_SMAX:%[^ ]+]] = add i32 %len, -2147483647 + ; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp sgt i32 [[LEN_MINUS_SMAX]], -13 + ; CHECK-NEXT: [[SMAX1:%[^ ]+]] = select i1 [[CMP1]], i32 [[LEN_MINUS_SMAX]], i32 -13 +-; CHECK-NEXT: [[ADD1:%[^ ]+]] = add i32 [[SMAX1]], -1 +-; CHECK-NEXT: [[SUB1:%[^ ]+]] = sub i32 [[ADD1]], %len +-; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp sgt i32 [[SUB1]], -102 +-; CHECK-NEXT: [[SMAX2:%[^ ]+]] = select i1 [[CMP2]], i32 [[SUB1]], i32 -102 +-; CHECK-NEXT: [[SUB2:%[^ ]+]] = sub i32 -1, [[SMAX2]] +-; CHECK-NEXT: [[CMP3:%[^ ]+]] = icmp sgt i32 [[SUB2]], 0 +-; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP3]], i32 [[SUB2]], i32 0 ++; CHECK-NEXT: [[SUB1:%[^ ]+]] = sub i32 %len, [[SMAX1]] ++; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp slt i32 [[SUB1]], 101 ++; CHECK-NEXT: [[SMAX2:%[^ ]+]] = select i1 [[CMP2]], i32 [[SUB1]], i32 101 ++; CHECK-NEXT: [[CMP3:%[^ ]+]] = icmp sgt i32 [[SMAX2]], 0 ++; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP3]], i32 [[SMAX2]], i32 0 + ; CHECK-NEXT: br i1 true, label %loop.preloop.preheader + ; CHECK: loop.preloop: + ; CHECK-NEXT: %idx.preloop = phi i32 [ %idx.next.preloop, %in.bounds.preloop ], [ 0, %loop.preloop.preheader ] +@@ -150,14 +147,11 @@ define void @test_03(i32* %arr, i32* %a_len_ptr) #0 { + ; CHECK-NOT: preloop + ; CHECK: entry: + ; CHECK-NEXT: %len = load i32, i32* %a_len_ptr, !range !0 +-; CHECK-NEXT: [[SUB1:%[^ ]+]] = sub i32 -2, %len +-; CHECK-NEXT: [[SUB2:%[^ ]+]] = sub i32 -1, %len +-; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp sgt i32 [[SUB2]], -14 +-; CHECK-NEXT: [[SMAX1:%[^ ]+]] = select i1 [[CMP1]], i32 [[SUB2]], i32 -14 +-; CHECK-NEXT: [[SUB3:%[^ ]+]] = sub i32 [[SUB1]], [[SMAX1]] +-; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp ugt i32 [[SUB3]], -102 +-; CHECK-NEXT: [[UMAX1:%[^ ]+]] = select i1 [[CMP2]], i32 [[SUB3]], i32 -102 +-; CHECK-NEXT: %exit.mainloop.at = sub i32 -1, [[UMAX1]] ++; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp slt i32 %len, 13 ++; CHECK-NEXT: [[SMAX1:%[^ ]+]] = select i1 [[CMP1]], i32 %len, i32 13 ++; CHECK-NEXT: [[SUB3:%[^ ]+]] = sub i32 %len, [[SMAX1]] ++; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp ult i32 [[SUB3]], 101 ++; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP2]], i32 [[SUB3]], i32 101 + ; CHECK-NEXT: [[CMP3:%[^ ]+]] = icmp ult i32 0, %exit.mainloop.at + ; CHECK-NEXT: br i1 [[CMP3]], label %loop.preheader, label %main.pseudo.exit + ; CHECK: postloop: +@@ -207,10 +201,9 @@ define void @test_04(i32* %arr, i32* %a_len_ptr) #0 { + ; CHECK-LABEL: test_04( + ; CHECK: entry: + ; CHECK-NEXT: %len = load i32, i32* %a_len_ptr, !range !0 +-; CHECK-NEXT: [[SUB1:%[^ ]+]] = sub i32 -14, %len +-; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp ugt i32 [[SUB1]], -102 +-; CHECK-NEXT: [[UMAX1:%[^ ]+]] = select i1 [[CMP1]], i32 [[SUB1]], i32 -102 +-; CHECK-NEXT: %exit.mainloop.at = sub i32 -1, [[UMAX1]] ++; CHECK-NEXT: [[SUB1:%[^ ]+]] = add i32 %len, 13 ++; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp ult i32 [[SUB1]], 101 ++; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP1]], i32 [[SUB1]], i32 101 + ; CHECK-NEXT: br i1 true, label %loop.preloop.preheader + ; CHECK: in.bounds.preloop: + ; CHECK-NEXT: %addr.preloop = getelementptr i32, i32* %arr, i32 %idx.preloop +@@ -251,12 +244,11 @@ define void @test_05(i32* %arr, i32* %a_len_ptr) #0 { + ; CHECK-NOT: preloop + ; CHECK: entry: + ; CHECK-NEXT: %len = load i32, i32* %a_len_ptr, !range !0 +-; CHECK-NEXT: [[SUB1:%[^ ]+]] = sub i32 12, %len +-; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp sgt i32 [[SUB1]], -102 +-; CHECK-NEXT: [[SMAX:%[^ ]+]] = select i1 [[CMP1]], i32 [[SUB1]], i32 -102 +-; CHECK-NEXT: [[SUB2:%[^ ]+]] = sub i32 -1, [[SMAX]] +-; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp sgt i32 [[SUB2]], 0 +-; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP2]], i32 [[SUB2]], i32 0 ++; CHECK-NEXT: [[SUB1:%[^ ]+]] = add i32 %len, -13 ++; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp slt i32 [[SUB1]], 101 ++; CHECK-NEXT: [[SMAX:%[^ ]+]] = select i1 [[CMP1]], i32 [[SUB1]], i32 101 ++; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp sgt i32 [[SMAX]], 0 ++; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP2]], i32 [[SMAX]], i32 0 + ; CHECK-NEXT: [[GOTO_LOOP:%[^ ]+]] = icmp slt i32 0, %exit.mainloop.at + ; CHECK-NEXT: br i1 [[GOTO_LOOP]], label %loop.preheader, label %main.pseudo.exit + ; CHECK: loop +@@ -296,13 +288,11 @@ define void @test_06(i32* %arr, i32* %a_len_ptr) #0 { + ; CHECK-NEXT: [[LEN_MINUS_SMAX:%[^ ]+]] = add i32 %len, -2147483647 + ; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp sgt i32 [[LEN_MINUS_SMAX]], -13 + ; CHECK-NEXT: [[SMAX1:%[^ ]+]] = select i1 [[CMP1]], i32 [[LEN_MINUS_SMAX]], i32 -13 +-; CHECK-NEXT: [[ADD1:%[^ ]+]] = add i32 [[SMAX1]], -1 +-; CHECK-NEXT: [[SUB1:%[^ ]+]] = sub i32 [[ADD1]], %len +-; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp sgt i32 [[SUB1]], -102 +-; CHECK-NEXT: [[SMAX2:%[^ ]+]] = select i1 [[CMP2]], i32 [[SUB1]], i32 -102 +-; CHECK-NEXT: [[SUB2:%[^ ]+]] = sub i32 -1, [[SMAX2]] +-; CHECK-NEXT: [[CMP3:%[^ ]+]] = icmp sgt i32 [[SUB2]], 0 +-; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP3]], i32 [[SUB2]], i32 0 ++; CHECK-NEXT: [[SUB1:%[^ ]+]] = sub i32 %len, [[SMAX1]] ++; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp slt i32 [[SUB1]], 101 ++; CHECK-NEXT: [[SMAX2:%[^ ]+]] = select i1 [[CMP2]], i32 [[SUB1]], i32 101 ++; CHECK-NEXT: [[CMP3:%[^ ]+]] = icmp sgt i32 [[SMAX2]], 0 ++; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP3]], i32 [[SMAX2]], i32 0 + ; CHECK-NEXT: br i1 true, label %loop.preloop.preheader + ; CHECK: in.bounds.preloop: + ; CHECK-NEXT: %addr.preloop = getelementptr i32, i32* %arr, i32 %idx.preloop +@@ -343,14 +333,11 @@ define void @test_07(i32* %arr, i32* %a_len_ptr) #0 { + ; CHECK-NOT: preloop + ; CHECK: entry: + ; CHECK-NEXT: %len = load i32, i32* %a_len_ptr, !range !0 +-; CHECK-NEXT: [[SUB1:%[^ ]+]] = sub i32 -2, %len +-; CHECK-NEXT: [[SUB2:%[^ ]+]] = sub i32 -1, %len +-; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp sgt i32 [[SUB2]], -14 +-; CHECK-NEXT: [[SMAX1:%[^ ]+]] = select i1 [[CMP1]], i32 [[SUB2]], i32 -14 +-; CHECK-NEXT: [[SUB3:%[^ ]+]] = sub i32 [[SUB1]], [[SMAX1]] +-; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp ugt i32 [[SUB3]], -102 +-; CHECK-NEXT: [[UMAX1:%[^ ]+]] = select i1 [[CMP2]], i32 [[SUB3]], i32 -102 +-; CHECK-NEXT: %exit.mainloop.at = sub i32 -1, [[UMAX1]] ++; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp slt i32 %len, 13 ++; CHECK-NEXT: [[SMAX1:%[^ ]+]] = select i1 [[CMP1]], i32 %len, i32 13 ++; CHECK-NEXT: [[SUB3:%[^ ]+]] = sub i32 %len, [[SMAX1]] ++; CHECK-NEXT: [[CMP2:%[^ ]+]] = icmp ult i32 [[SUB3]], 101 ++; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP2]], i32 [[SUB3]], i32 101 + ; CHECK-NEXT: [[CMP3:%[^ ]+]] = icmp ult i32 0, %exit.mainloop.at + ; CHECK-NEXT: br i1 [[CMP3]], label %loop.preheader, label %main.pseudo.exit + ; CHECK: loop +@@ -387,10 +374,9 @@ define void @test_08(i32* %arr, i32* %a_len_ptr) #0 { + ; CHECK-LABEL: test_08( + ; CHECK: entry: + ; CHECK-NEXT: %len = load i32, i32* %a_len_ptr, !range !0 +-; CHECK-NEXT: [[SUB1:%[^ ]+]] = sub i32 -14, %len +-; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp ugt i32 [[SUB1]], -102 +-; CHECK-NEXT: [[UMAX1:%[^ ]+]] = select i1 [[CMP1]], i32 [[SUB1]], i32 -102 +-; CHECK-NEXT: %exit.mainloop.at = sub i32 -1, [[UMAX1]] ++; CHECK-NEXT: [[SUB1:%[^ ]+]] = add i32 %len, 13 ++; CHECK-NEXT: [[CMP1:%[^ ]+]] = icmp ult i32 [[SUB1]], 101 ++; CHECK-NEXT: %exit.mainloop.at = select i1 [[CMP1]], i32 [[SUB1]], i32 101 + ; CHECK-NEXT: br i1 true, label %loop.preloop.preheader + ; CHECK: in.bounds.preloop: + ; CHECK-NEXT: %addr.preloop = getelementptr i32, i32* %arr, i32 %idx.preloop +diff --git a/test/Transforms/IRCE/single-access-no-preloop.ll b/test/Transforms/IRCE/single-access-no-preloop.ll +index 53f430d0ba3..cbbdf81d46c 100644 +--- a/test/Transforms/IRCE/single-access-no-preloop.ll ++++ b/test/Transforms/IRCE/single-access-no-preloop.ll +@@ -85,11 +85,9 @@ define void @single_access_no_preloop_with_offset(i32 *%arr, i32 *%a_len_ptr, i3 + ; CHECK-LABEL: @single_access_no_preloop_with_offset( + + ; CHECK: loop.preheader: +-; CHECK: [[not_n:[^ ]+]] = sub i32 -1, %n +-; CHECK: [[not_safe_range_end:[^ ]+]] = sub i32 3, %len +-; CHECK: [[not_exit_main_loop_at_hiclamp_cmp:[^ ]+]] = icmp sgt i32 [[not_n]], [[not_safe_range_end]] +-; CHECK: [[not_exit_main_loop_at_hiclamp:[^ ]+]] = select i1 [[not_exit_main_loop_at_hiclamp_cmp]], i32 [[not_n]], i32 [[not_safe_range_end]] +-; CHECK: [[exit_main_loop_at_hiclamp:[^ ]+]] = sub i32 -1, [[not_exit_main_loop_at_hiclamp]] ++; CHECK: [[safe_range_end:[^ ]+]] = add i32 %len, -4 ++; CHECK: [[exit_main_loop_at_hiclamp_cmp:[^ ]+]] = icmp slt i32 %n, [[safe_range_end]] ++; CHECK: [[exit_main_loop_at_hiclamp:[^ ]+]] = select i1 [[exit_main_loop_at_hiclamp_cmp]], i32 %n, i32 [[safe_range_end]] + ; CHECK: [[exit_main_loop_at_loclamp_cmp:[^ ]+]] = icmp sgt i32 [[exit_main_loop_at_hiclamp]], 0 + ; CHECK: [[exit_main_loop_at_loclamp:[^ ]+]] = select i1 [[exit_main_loop_at_loclamp_cmp]], i32 [[exit_main_loop_at_hiclamp]], i32 0 + ; CHECK: [[enter_main_loop:[^ ]+]] = icmp slt i32 0, [[exit_main_loop_at_loclamp]] +diff --git a/test/Transforms/IRCE/single-access-with-preloop.ll b/test/Transforms/IRCE/single-access-with-preloop.ll +index 4b93122b6e7..3e2395dd100 100644 +--- a/test/Transforms/IRCE/single-access-with-preloop.ll ++++ b/test/Transforms/IRCE/single-access-with-preloop.ll +@@ -33,11 +33,9 @@ define void @single_access_with_preloop(i32 *%arr, i32 *%a_len_ptr, i32 %n, i32 + ; CHECK: [[check_min_sint_offset:[^ ]+]] = icmp sgt i32 %offset, -2147483647 + ; CHECK: [[safe_offset_preloop:[^ ]+]] = select i1 [[check_min_sint_offset]], i32 %offset, i32 -2147483647 + ; If Offset was a SINT_MIN, we could have an overflow here. That is why we calculated its safe version. +-; CHECK: [[not_safe_start:[^ ]+]] = add i32 [[safe_offset_preloop]], -1 +-; CHECK: [[not_n:[^ ]+]] = sub i32 -1, %n +-; CHECK: [[not_exit_preloop_at_cond_loclamp:[^ ]+]] = icmp sgt i32 [[not_safe_start]], [[not_n]] +-; CHECK: [[not_exit_preloop_at_loclamp:[^ ]+]] = select i1 [[not_exit_preloop_at_cond_loclamp]], i32 [[not_safe_start]], i32 [[not_n]] +-; CHECK: [[exit_preloop_at_loclamp:[^ ]+]] = sub i32 -1, [[not_exit_preloop_at_loclamp]] ++; CHECK: [[safe_start:[^ ]+]] = sub i32 0, [[safe_offset_preloop]] ++; CHECK: [[exit_preloop_at_cond_loclamp:[^ ]+]] = icmp slt i32 %n, [[safe_start]] ++; CHECK: [[exit_preloop_at_loclamp:[^ ]+]] = select i1 [[exit_preloop_at_cond_loclamp]], i32 %n, i32 [[safe_start]] + ; CHECK: [[exit_preloop_at_cond:[^ ]+]] = icmp sgt i32 [[exit_preloop_at_loclamp]], 0 + ; CHECK: [[exit_preloop_at:[^ ]+]] = select i1 [[exit_preloop_at_cond]], i32 [[exit_preloop_at_loclamp]], i32 0 + +@@ -45,17 +43,15 @@ define void @single_access_with_preloop(i32 *%arr, i32 *%a_len_ptr, i32 %n, i32 + ; CHECK: [[len_minus_sint_max:[^ ]+]] = add i32 %len, -2147483647 + ; CHECK: [[check_len_min_sint_offset:[^ ]+]] = icmp sgt i32 %offset, [[len_minus_sint_max]] + ; CHECK: [[safe_offset_mainloop:[^ ]+]] = select i1 [[check_len_min_sint_offset]], i32 %offset, i32 [[len_minus_sint_max]] +-; CHECK: [[not_safe_start_2:[^ ]+]] = add i32 [[safe_offset_mainloop]], -1 + ; If Offset was a SINT_MIN, we could have an overflow here. That is why we calculated its safe version. +-; CHECK: [[not_safe_upper_end:[^ ]+]] = sub i32 [[not_safe_start_2]], %len +-; CHECK: [[not_exit_mainloop_at_cond_loclamp:[^ ]+]] = icmp sgt i32 [[not_safe_upper_end]], [[not_n]] +-; CHECK: [[not_exit_mainloop_at_loclamp:[^ ]+]] = select i1 [[not_exit_mainloop_at_cond_loclamp]], i32 [[not_safe_upper_end]], i32 [[not_n]] ++; CHECK: [[safe_upper_end:[^ ]+]] = sub i32 %len, [[safe_offset_mainloop]] ++; CHECK: [[exit_mainloop_at_cond_loclamp:[^ ]+]] = icmp slt i32 %n, [[safe_upper_end]] ++; CHECK: [[exit_mainloop_at_loclamp:[^ ]+]] = select i1 [[exit_mainloop_at_cond_loclamp]], i32 %n, i32 [[safe_upper_end]] + ; CHECK: [[check_offset_mainloop_2:[^ ]+]] = icmp sgt i32 %offset, 0 + ; CHECK: [[safe_offset_mainloop_2:[^ ]+]] = select i1 [[check_offset_mainloop_2]], i32 %offset, i32 0 +-; CHECK: [[not_safe_lower_end:[^ ]+]] = add i32 [[safe_offset_mainloop_2]], -2147483648 +-; CHECK: [[not_exit_mainloop_at_cond_hiclamp:[^ ]+]] = icmp sgt i32 [[not_exit_mainloop_at_loclamp]], [[not_safe_lower_end]] +-; CHECK: [[not_exit_mainloop_at_hiclamp:[^ ]+]] = select i1 [[not_exit_mainloop_at_cond_hiclamp]], i32 [[not_exit_mainloop_at_loclamp]], i32 [[not_safe_lower_end]] +-; CHECK: [[exit_mainloop_at_hiclamp:[^ ]+]] = sub i32 -1, [[not_exit_mainloop_at_hiclamp]] ++; CHECK: [[safe_lower_end:[^ ]+]] = sub i32 2147483647, [[safe_offset_mainloop_2]] ++; CHECK: [[exit_mainloop_at_cond_hiclamp:[^ ]+]] = icmp slt i32 [[exit_mainloop_at_loclamp]], [[safe_lower_end]] ++; CHECK: [[exit_mainloop_at_hiclamp:[^ ]+]] = select i1 [[exit_mainloop_at_cond_hiclamp]], i32 [[exit_mainloop_at_loclamp]], i32 [[safe_lower_end]] + ; CHECK: [[exit_mainloop_at_cmp:[^ ]+]] = icmp sgt i32 [[exit_mainloop_at_hiclamp]], 0 + ; CHECK: [[exit_mainloop_at:[^ ]+]] = select i1 [[exit_mainloop_at_cmp]], i32 [[exit_mainloop_at_hiclamp]], i32 0 + +diff --git a/test/Transforms/LoopStrengthReduce/2013-01-14-ReuseCast.ll b/test/Transforms/LoopStrengthReduce/2013-01-14-ReuseCast.ll +index ea3f6077231..d5232e1874c 100644 +--- a/test/Transforms/LoopStrengthReduce/2013-01-14-ReuseCast.ll ++++ b/test/Transforms/LoopStrengthReduce/2013-01-14-ReuseCast.ll +@@ -14,8 +14,6 @@ target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f3 + ; current LSR cost model. + ; CHECK-NOT: = ptrtoint i8* undef to i64 + ; CHECK: .lr.ph +-; CHECK: [[TMP:%[^ ]+]] = add i64 %tmp{{[0-9]+}}, -1 +-; CHECK: sub i64 [[TMP]], %tmp{{[0-9]+}} + ; CHECK: ret void + define void @VerifyDiagnosticConsumerTest() unnamed_addr nounwind uwtable align 2 { + bb: diff --git a/deps/patches/llvm-D51842-win64-byval-cc.patch b/deps/patches/llvm-D51842-win64-byval-cc.patch new file mode 100644 index 0000000..459060f --- /dev/null +++ b/deps/patches/llvm-D51842-win64-byval-cc.patch @@ -0,0 +1,164 @@ +commit 11e52448390e0d8a80565ccd1ca4865df7787f21 +Author: Keno Fischer +Date: Sun Sep 9 16:45:35 2018 -0400 + + [X86ISel] Implement byval lowering for Win64 calling convention + + Summary: + The IR reference for the `byval` attribute states: + + ``` + This indicates that the pointer parameter should really be passed by value + to the function. The attribute implies that a hidden copy of the pointee is + made between the caller and the callee, so the callee is unable to modify + the value in the caller. This attribute is only valid on LLVM pointer arguments. + ``` + + However, on Win64, this attribute is unimplemented and the raw pointer is + passed to the callee instead. This is problematic, because frontend authors + relying on the implicit hidden copy (as happens for every other calling + convention) will see the passed value silently (if mutable memory) or + loudly (by means of a crash) modified because the callee treats the + location as scratch memory space it is allowed to mutate. + + At this point, it's worth taking a step back to understand the context. + In most calling conventions, aggregates that are too large to be passed + in registers, instead get *copied* to the stack at a fixed (computable + from the signature) offset of the stack pointer. At the LLVM, we hide + this hidden copy behind the byval attribute. The caller passes a pointer + to the desired data and the callee receives a pointer, but these pointers + are not the same. In particular, the pointer that the callee receives + points to temporary stack memory allocated as part of the call lowering. + In most calling conventions, this pointer is never realized in registers + or memory. The temporary memory is simply defined by an implicit + offset from the stack pointer at function entry. + + Win64, uniquely, works differently. The structure is still passed in + memory, but instead of being stored at an implicit memory offset, the + caller computes a pointer to the temporary memory and passes it to + the callee as a regular pointer (taking up a register, or if all + registers are taken up, an additional stack slot). Presumably, this + was done to allow eliding the copy when passing aggregates through + several functions on the stack. + + This explains why ignoring the `byval` attribute mostly works on Win64. + The argument simply gets passed as a pointer and as long as we're ok + with the callee trampling all over that memory, there are no ill effects. + However, it does contradict the documentation of the `byval` attribute + which specifies that there is to be an implicit copy. + + Frontends can of course work around this by never emitting the `byval` + attribute for Win64 and creating `alloca`s for the requisite temporary + stack slots (and that does appear to be what frontends are doing). + However, the presence of the `byval` attribute is not a trap for + frontend authors, since it seems to work, but silently modifies the + passed memory contrary to documentation. + + I see two solutions: + - Disallow the `byval` attribute in the verifier if using the Win64 + calling convention. + - Make it work by simply emitting a temporary stack copy as we would + with any other calling convention (frontends can of course always + not use the attribute if they want to elide the copy). + + This patch implements the second option (make it work), though I would + be fine with the first also. + + Ref: https://github.com/JuliaLang/julia/issues/28338 + + Reviewers: rnk + + Subscribers: llvm-commits + + Differential Revision: https://reviews.llvm.org/D51842 + +diff --git a/lib/Target/X86/X86CallingConv.td b/lib/Target/X86/X86CallingConv.td +index 5d806fe60b8..7a82b299301 100644 +--- a/lib/Target/X86/X86CallingConv.td ++++ b/lib/Target/X86/X86CallingConv.td +@@ -583,9 +583,11 @@ def CC_X86_64_HHVM_C : CallingConv<[ + + // Calling convention used on Win64 + def CC_X86_Win64_C : CallingConv<[ +- // FIXME: Handle byval stuff. + // FIXME: Handle varargs. + ++ // Byval aggregates are passed by pointer ++ CCIfByVal>, ++ + // Promote i1/i8/i16/v1i1 arguments to i32. + CCIfType<[i1, i8, i16, v1i1], CCPromoteToType>, + +diff --git a/lib/Target/X86/X86ISelLowering.cpp b/lib/Target/X86/X86ISelLowering.cpp +index 86e71cba87b..412d015824b 100644 +--- a/lib/Target/X86/X86ISelLowering.cpp ++++ b/lib/Target/X86/X86ISelLowering.cpp +@@ -3062,7 +3062,8 @@ SDValue X86TargetLowering::LowerFormalArguments( + } + + // If value is passed via pointer - do a load. +- if (VA.getLocInfo() == CCValAssign::Indirect) ++ if (VA.getLocInfo() == CCValAssign::Indirect && ++ !Ins[I].Flags.isByVal()) + ArgValue = + DAG.getLoad(VA.getValVT(), dl, Chain, ArgValue, MachinePointerInfo()); + +@@ -3540,13 +3541,27 @@ X86TargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, + Arg = DAG.getBitcast(RegVT, Arg); + break; + case CCValAssign::Indirect: { +- // Store the argument. +- SDValue SpillSlot = DAG.CreateStackTemporary(VA.getValVT()); +- int FI = cast(SpillSlot)->getIndex(); +- Chain = DAG.getStore( +- Chain, dl, Arg, SpillSlot, +- MachinePointerInfo::getFixedStack(DAG.getMachineFunction(), FI)); +- Arg = SpillSlot; ++ if (isByVal) { ++ // Copy the argument into a temportary spill slot ++ int FrameIdx = MF.getFrameInfo().CreateStackObject( ++ Flags.getByValSize(), std::max(16, (int)Flags.getByValAlign()), ++ false); ++ SDValue SpillSlot = ++ DAG.getFrameIndex(FrameIdx, getPointerTy(DAG.getDataLayout())); ++ Chain = ++ CreateCopyOfByValArgument(Arg, SpillSlot, Chain, Flags, DAG, dl); ++ // From now on treat this as a regular pointer ++ Arg = SpillSlot; ++ isByVal = false; ++ } else { ++ // Store the argument. ++ SDValue SpillSlot = DAG.CreateStackTemporary(VA.getValVT()); ++ int FI = cast(SpillSlot)->getIndex(); ++ Chain = DAG.getStore( ++ Chain, dl, Arg, SpillSlot, ++ MachinePointerInfo::getFixedStack(DAG.getMachineFunction(), FI)); ++ Arg = SpillSlot; ++ } + break; + } + } +diff --git a/test/CodeGen/X86/win64-byval.ll b/test/CodeGen/X86/win64-byval.ll +new file mode 100644 +index 00000000000..0b762cd3cea +--- /dev/null ++++ b/test/CodeGen/X86/win64-byval.ll +@@ -0,0 +1,18 @@ ++; RUN: llc -mtriple x86_64-w64-mingw32 %s -o - | FileCheck %s ++ ++declare void @foo({ float, double }* byval); ++@G = external constant { float, double } ++ ++define void @bar() ++{ ++; Make sure we're creating a temporary stack slot, rather than just passing ++; the pointer through unmodified. ++; CHECK-LABEL: @bar ++; CHECK: movq G+8(%rip), %rax ++; CHECK: movq %rax, 40(%rsp) ++; CHECK: movq G(%rip), %rax ++; CHECK: movq %rax, 32(%rsp) ++; CHECK: leaq 32(%rsp), %rcx ++ call void @foo({ float, double }* byval @G) ++ ret void ++} diff --git a/deps/patches/llvm-D57118-powerpc.patch b/deps/patches/llvm-D57118-powerpc.patch new file mode 100644 index 0000000..328fe20 --- /dev/null +++ b/deps/patches/llvm-D57118-powerpc.patch @@ -0,0 +1,30 @@ +commit 812db527538f30ac77a19d755e24109a6db7e569 +Author: Keno Fischer +Date: Wed Jan 23 16:46:59 2019 -0500 + + [CMake][PowerPC] Recognize LLVM_NATIVE_TARGET="ppc64le" as PowerPC + + Summary: + This value is derived from the host triple, which on the machine + I'm currently using is `ppc64le-linux-redhat`. This change makes + LLVM compile. + + Reviewers: hfinkel + + Subscribers: nemanjai, mgorny, jsji, llvm-commits + + Differential Revision: https://reviews.llvm.org/D57118 + +diff --git a/cmake/config-ix.cmake b/cmake/config-ix.cmake +index 900c35ee4f0..b9c9757a4f6 100644 +--- a/cmake/config-ix.cmake ++++ b/cmake/config-ix.cmake +@@ -386,6 +386,8 @@ elseif (LLVM_NATIVE_ARCH MATCHES "sparc") + set(LLVM_NATIVE_ARCH Sparc) + elseif (LLVM_NATIVE_ARCH MATCHES "powerpc") + set(LLVM_NATIVE_ARCH PowerPC) ++elseif (LLVM_NATIVE_ARCH MATCHES "ppc64le") ++ set(LLVM_NATIVE_ARCH PowerPC) + elseif (LLVM_NATIVE_ARCH MATCHES "aarch64") + set(LLVM_NATIVE_ARCH AArch64) + elseif (LLVM_NATIVE_ARCH MATCHES "arm64") diff --git a/deps/patches/llvm-OProfile-line-num.patch b/deps/patches/llvm-OProfile-line-num.patch new file mode 100644 index 0000000..03b2ca8 --- /dev/null +++ b/deps/patches/llvm-OProfile-line-num.patch @@ -0,0 +1,48 @@ +commit 4840cf7299bb312125d41fc84733c15c2370f18e +Author: DokFaust +Date: Fri Jun 8 19:23:01 2018 +0200 + + Add debug line-level code information to OProfile module + +diff --git a/lib/ExecutionEngine/OProfileJIT/LLVMBuild.txt b/lib/ExecutionEngine/OProfileJIT/LLVMBuild.txt +index 7d5550046a5..ea100286318 100644 +--- a/lib/ExecutionEngine/OProfileJIT/LLVMBuild.txt ++++ b/lib/ExecutionEngine/OProfileJIT/LLVMBuild.txt +@@ -24 +24 @@ parent = ExecutionEngine +-required_libraries = Support Object ExecutionEngine ++required_libraries = DebugInfoDWARF Support Object ExecutionEngine +diff --git a/lib/ExecutionEngine/OProfileJIT/OProfileJITEventListener.cpp b/lib/ExecutionEngine/OProfileJIT/OProfileJITEventListener.cpp +index 3581d645839..045ecb82853 100644 +--- a/lib/ExecutionEngine/OProfileJIT/OProfileJITEventListener.cpp ++++ b/lib/ExecutionEngine/OProfileJIT/OProfileJITEventListener.cpp +@@ -26,0 +27,2 @@ ++#include "llvm/DebugInfo/DIContext.h" ++#include "llvm/DebugInfo/DWARF/DWARFContext.h" +@@ -86,0 +89,2 @@ void OProfileJITEventListener::NotifyObjectEmitted( ++ std::unique_ptr Context = DWARFContext::create(DebugObj); ++ std::string SourceFileName; +@@ -111 +115,23 @@ void OProfileJITEventListener::NotifyObjectEmitted( +- // TODO: support line number info (similar to IntelJITEventListener.cpp) ++ DILineInfoTable Lines = Context->getLineInfoForAddressRange(Addr, Size); ++ DILineInfoTable::iterator Begin = Lines.begin(); ++ DILineInfoTable::iterator End = Lines.end(); ++ size_t i = 0; ++ ++ size_t num_entries = std::distance(Begin, End); ++ static struct debug_line_info* debug_line; ++ debug_line = (struct debug_line_info * )calloc(num_entries, sizeof(struct debug_line_info)); ++ ++ for(DILineInfoTable::iterator It=Begin; It != End; ++It){ ++ i = std::distance(Begin,It); ++ debug_line[i].vma = (unsigned long) It->first; ++ debug_line[i].lineno = It->second.Line; ++ SourceFileName = Lines.front().second.FileName; ++ debug_line[i].filename = const_cast(SourceFileName.c_str()); ++ } ++ ++ if(Wrapper->op_write_debug_line_info((void*) Addr, num_entries, debug_line) == -1) { ++ DEBUG(dbgs() << "Failed to tell OProfiler about debug object at [" ++ << (void*) Addr << "-" << ((char *) Addr + Size) ++ << "]\n"); ++ continue; ++ } diff --git a/deps/patches/llvm-PPC-addrspaces.patch b/deps/patches/llvm-PPC-addrspaces.patch new file mode 100644 index 0000000..7f51b3b --- /dev/null +++ b/deps/patches/llvm-PPC-addrspaces.patch @@ -0,0 +1,29 @@ +From 15899eaab58e96bb7bbe7a14099674e255656a50 Mon Sep 17 00:00:00 2001 +From: Valentin Churavy +Date: Fri, 23 Feb 2018 14:41:20 -0500 +Subject: [PATCH] Make AddrSpaceCast noops on PPC + +PPC as AArch64 doesn't have address-spaces so we can drop them in the backend +--- + lib/Target/PowerPC/PPCISelLowering.h | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/lib/Target/PowerPC/PPCISelLowering.h b/lib/Target/PowerPC/PPCISelLowering.h +index e60504507d3..c9b89773968 100644 +--- a/lib/Target/PowerPC/PPCISelLowering.h ++++ b/lib/Target/PowerPC/PPCISelLowering.h +@@ -761,6 +761,11 @@ namespace llvm { + ReuseLoadInfo() : IsInvariant(false), Alignment(0), Ranges(nullptr) {} + }; + ++ bool isNoopAddrSpaceCast(unsigned SrcAS, unsigned DestAS) const override { ++ // Addrspacecasts are always noops. ++ return true; ++ } ++ + bool canReuseLoadAddress(SDValue Op, EVT MemVT, ReuseLoadInfo &RLI, + SelectionDAG &DAG, + ISD::LoadExtType ET = ISD::NON_EXTLOAD) const; +-- +2.16.2 + diff --git a/deps/patches/llvm-r355582-avxminmax.patch b/deps/patches/llvm-r355582-avxminmax.patch new file mode 100644 index 0000000..3d3e037 --- /dev/null +++ b/deps/patches/llvm-r355582-avxminmax.patch @@ -0,0 +1,48 @@ +From 7f82bf14c02915ff1bf0e931465fc208287d24c6 Mon Sep 17 00:00:00 2001 +From: nic +Date: Thu, 7 Mar 2019 22:01:01 +0100 +Subject: [PATCH] Backporting r355582 on release/6.x + +--- + lib/Target/X86/X86ISelLowering.cpp | 13 +++++++------ + 1 file changed, 7 insertions(+), 6 deletions(-) + +diff --git a/lib/Target/X86/X86ISelLowering.cpp b/lib/Target/X86/X86ISelLowering.cpp +index c1ddb771e2f..2a96373ed10 100644 +--- a/lib/Target/X86/X86ISelLowering.cpp ++++ b/lib/Target/X86/X86ISelLowering.cpp +@@ -35751,15 +35751,17 @@ static SDValue combineFMinNumFMaxNum(SDNode *N, SelectionDAG &DAG, + if (Subtarget.useSoftFloat()) + return SDValue(); + ++ const TargetLowering &TLI = DAG.getTargetLoweringInfo(); ++ + // TODO: Check for global or instruction-level "nnan". In that case, we + // should be able to lower to FMAX/FMIN alone. + // TODO: If an operand is already known to be a NaN or not a NaN, this + // should be an optional swap and FMAX/FMIN. + + EVT VT = N->getValueType(0); +- if (!((Subtarget.hasSSE1() && (VT == MVT::f32 || VT == MVT::v4f32)) || +- (Subtarget.hasSSE2() && (VT == MVT::f64 || VT == MVT::v2f64)) || +- (Subtarget.hasAVX() && (VT == MVT::v8f32 || VT == MVT::v4f64)))) ++ if (!((Subtarget.hasSSE1() && VT == MVT::f32) || ++ (Subtarget.hasSSE2() && VT == MVT::f64) || ++ (VT.isVector() && TLI.isTypeLegal(VT)))) + return SDValue(); + + // This takes at least 3 instructions, so favor a library call when operating +@@ -35770,9 +35772,8 @@ static SDValue combineFMinNumFMaxNum(SDNode *N, SelectionDAG &DAG, + SDValue Op0 = N->getOperand(0); + SDValue Op1 = N->getOperand(1); + SDLoc DL(N); +- EVT SetCCType = DAG.getTargetLoweringInfo().getSetCCResultType( +- DAG.getDataLayout(), *DAG.getContext(), VT); +- ++ EVT SetCCType = TLI.getSetCCResultType(DAG.getDataLayout(), *DAG.getContext(), ++ VT); + // There are 4 possibilities involving NaN inputs, and these are the required + // outputs: + // Op1 +-- +2.14.1 diff --git a/deps/patches/llvm-rL323946-LSRTy.patch b/deps/patches/llvm-rL323946-LSRTy.patch new file mode 100644 index 0000000..ae1a7ac --- /dev/null +++ b/deps/patches/llvm-rL323946-LSRTy.patch @@ -0,0 +1,45 @@ +commit ab60b05a472e8651cbe53c19513b7e62b9ff32df +Author: Mikael Holmen +Date: Thu Feb 1 06:38:34 2018 +0000 + + [LSR] Don't force bases of foldable formulae to the final type. + + Summary: + Before emitting code for scaled registers, we prevent + SCEVExpander from hoisting any scaled addressing mode + by emitting all the bases first. However, these bases + are being forced to the final type, resulting in some + odd code. + + For example, if the type of the base is an integer and + the final type is a pointer, we will emit an inttoptr + for the base, a ptrtoint for the scale, and then a + 'reverse' GEP where the GEP pointer is actually the base + integer and the index is the pointer. It's more intuitive + to use the pointer as a pointer and the integer as index. + + Patch by: Bevin Hansson + + Reviewers: atrick, qcolombet, sanjoy + + Reviewed By: qcolombet + + Subscribers: llvm-commits + + Differential Revision: https://reviews.llvm.org/D42103 + + git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@323946 91177308-0d34-0410-b5e6-96231b3b80d8 + +diff --git a/lib/Transforms/Scalar/LoopStrengthReduce.cpp b/lib/Transforms/Scalar/LoopStrengthReduce.cpp +index 332c074a1df..4b8e2286ed9 100644 +--- a/lib/Transforms/Scalar/LoopStrengthReduce.cpp ++++ b/lib/Transforms/Scalar/LoopStrengthReduce.cpp +@@ -4993,7 +4993,7 @@ Value *LSRInstance::Expand(const LSRUse &LU, const LSRFixup &LF, + // Unless the addressing mode will not be folded. + if (!Ops.empty() && LU.Kind == LSRUse::Address && + isAMCompletelyFolded(TTI, LU, F)) { +- Value *FullV = Rewriter.expandCodeFor(SE.getAddExpr(Ops), Ty); ++ Value *FullV = Rewriter.expandCodeFor(SE.getAddExpr(Ops), nullptr); + Ops.clear(); + Ops.push_back(SE.getUnknown(FullV)); + } diff --git a/deps/patches/llvm-rL326843-missing-header.patch b/deps/patches/llvm-rL326843-missing-header.patch new file mode 100644 index 0000000..68dcfa3 --- /dev/null +++ b/deps/patches/llvm-rL326843-missing-header.patch @@ -0,0 +1,31 @@ +From 7c9054610e354340f9474dcd13a927f929912d1d Mon Sep 17 00:00:00 2001 +From: Eugene Zelenko +Date: Tue, 6 Mar 2018 23:06:13 +0000 +Subject: [PATCH] [Transforms] Add missing header for InstructionCombining.cpp, + in order to export LLVMInitializeInstCombine as extern "C". Fixes PR35947. + +Patch by Brenton Bostick. + +Differential revision: https://reviews.llvm.org/D44140 + + +git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@326843 91177308-0d34-0410-b5e6-96231b3b80d8 +--- + lib/Transforms/InstCombine/InstructionCombining.cpp | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/lib/Transforms/InstCombine/InstructionCombining.cpp b/lib/Transforms/InstCombine/InstructionCombining.cpp +index a3b2fe9e5d2..7ec73433e8f 100644 +--- a/lib/Transforms/InstCombine/InstructionCombining.cpp ++++ b/lib/Transforms/InstCombine/InstructionCombining.cpp +@@ -34,6 +34,7 @@ + //===----------------------------------------------------------------------===// + + #include "InstCombineInternal.h" ++#include "llvm-c/Initialization.h" + #include "llvm/ADT/APInt.h" + #include "llvm/ADT/ArrayRef.h" + #include "llvm/ADT/DenseMap.h" +-- +2.16.2 + diff --git a/deps/patches/llvm-rL326967-aligned-load.patch b/deps/patches/llvm-rL326967-aligned-load.patch new file mode 100644 index 0000000..62c1123 --- /dev/null +++ b/deps/patches/llvm-rL326967-aligned-load.patch @@ -0,0 +1,301 @@ +commit b398d8e1fa5a5a914957fa22d0a64db97f6c265e +Author: Craig Topper +Date: Thu Mar 8 00:21:17 2018 +0000 + + [X86] Fix some isel patterns that used aligned vector load instructions with unaligned predicates. + + These patterns weren't checking the alignment of the load, but were using the aligned instructions. This will cause a GP fault if the data isn't aligned. + + I believe these were introduced in r312450. + + git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@326967 91177308-0d34-0410-b5e6-96231b3b80d8 + +diff --git a/lib/Target/X86/X86InstrVecCompiler.td b/lib/Target/X86/X86InstrVecCompiler.td +index db3dfe56531..50c7763a2c3 100644 +--- a/lib/Target/X86/X86InstrVecCompiler.td ++++ b/lib/Target/X86/X86InstrVecCompiler.td +@@ -261,10 +261,10 @@ let Predicates = [HasVLX] in { + // will zero the upper bits. + // TODO: Is there a safe way to detect whether the producing instruction + // already zeroed the upper bits? +-multiclass subvector_zero_lowering { ++multiclass subvector_zero_lowering { + def : Pat<(DstTy (insert_subvector (bitconvert (ZeroTy immAllZerosV)), + (SrcTy RC:$src), (iPTR 0))), + (SUBREG_TO_REG (i64 0), +@@ -274,91 +274,91 @@ multiclass subvector_zero_lowering("VMOV"#MoveStr#"rm") addr:$src), SubIdx)>; ++ (!cast("VMOV"#LoadStr#"rm") addr:$src), SubIdx)>; + } + + let Predicates = [HasAVX, NoVLX] in { +- defm : subvector_zero_lowering<"APD", VR128, v4f64, v2f64, v8i32, loadv2f64, +- sub_xmm>; +- defm : subvector_zero_lowering<"APS", VR128, v8f32, v4f32, v8i32, loadv4f32, +- sub_xmm>; +- defm : subvector_zero_lowering<"DQA", VR128, v4i64, v2i64, v8i32, loadv2i64, +- sub_xmm>; +- defm : subvector_zero_lowering<"DQA", VR128, v8i32, v4i32, v8i32, loadv2i64, +- sub_xmm>; +- defm : subvector_zero_lowering<"DQA", VR128, v16i16, v8i16, v8i32, loadv2i64, +- sub_xmm>; +- defm : subvector_zero_lowering<"DQA", VR128, v32i8, v16i8, v8i32, loadv2i64, +- sub_xmm>; +-} +- +-let Predicates = [HasVLX] in { +- defm : subvector_zero_lowering<"APDZ128", VR128X, v4f64, v2f64, v8i32, ++ defm : subvector_zero_lowering<"APD", "UPD", VR128, v4f64, v2f64, v8i32, + loadv2f64, sub_xmm>; +- defm : subvector_zero_lowering<"APSZ128", VR128X, v8f32, v4f32, v8i32, ++ defm : subvector_zero_lowering<"APS", "UPS", VR128, v8f32, v4f32, v8i32, + loadv4f32, sub_xmm>; +- defm : subvector_zero_lowering<"DQA64Z128", VR128X, v4i64, v2i64, v8i32, ++ defm : subvector_zero_lowering<"DQA", "DQU", VR128, v4i64, v2i64, v8i32, + loadv2i64, sub_xmm>; +- defm : subvector_zero_lowering<"DQA64Z128", VR128X, v8i32, v4i32, v8i32, ++ defm : subvector_zero_lowering<"DQA", "DQU", VR128, v8i32, v4i32, v8i32, + loadv2i64, sub_xmm>; +- defm : subvector_zero_lowering<"DQA64Z128", VR128X, v16i16, v8i16, v8i32, ++ defm : subvector_zero_lowering<"DQA", "DQU", VR128, v16i16, v8i16, v8i32, + loadv2i64, sub_xmm>; +- defm : subvector_zero_lowering<"DQA64Z128", VR128X, v32i8, v16i8, v8i32, +- loadv2i64, sub_xmm>; +- +- defm : subvector_zero_lowering<"APDZ128", VR128X, v8f64, v2f64, v16i32, +- loadv2f64, sub_xmm>; +- defm : subvector_zero_lowering<"APSZ128", VR128X, v16f32, v4f32, v16i32, +- loadv4f32, sub_xmm>; +- defm : subvector_zero_lowering<"DQA64Z128", VR128X, v8i64, v2i64, v16i32, +- loadv2i64, sub_xmm>; +- defm : subvector_zero_lowering<"DQA64Z128", VR128X, v16i32, v4i32, v16i32, +- loadv2i64, sub_xmm>; +- defm : subvector_zero_lowering<"DQA64Z128", VR128X, v32i16, v8i16, v16i32, +- loadv2i64, sub_xmm>; +- defm : subvector_zero_lowering<"DQA64Z128", VR128X, v64i8, v16i8, v16i32, ++ defm : subvector_zero_lowering<"DQA", "DQU", VR128, v32i8, v16i8, v8i32, + loadv2i64, sub_xmm>; ++} + +- defm : subvector_zero_lowering<"APDZ256", VR256X, v8f64, v4f64, v16i32, +- loadv4f64, sub_ymm>; +- defm : subvector_zero_lowering<"APSZ256", VR256X, v16f32, v8f32, v16i32, +- loadv8f32, sub_ymm>; +- defm : subvector_zero_lowering<"DQA64Z256", VR256X, v8i64, v4i64, v16i32, +- loadv4i64, sub_ymm>; +- defm : subvector_zero_lowering<"DQA64Z256", VR256X, v16i32, v8i32, v16i32, +- loadv4i64, sub_ymm>; +- defm : subvector_zero_lowering<"DQA64Z256", VR256X, v32i16, v16i16, v16i32, +- loadv4i64, sub_ymm>; +- defm : subvector_zero_lowering<"DQA64Z256", VR256X, v64i8, v32i8, v16i32, +- loadv4i64, sub_ymm>; ++let Predicates = [HasVLX] in { ++ defm : subvector_zero_lowering<"APDZ128", "UPDZ128", VR128X, v4f64, ++ v2f64, v8i32, loadv2f64, sub_xmm>; ++ defm : subvector_zero_lowering<"APSZ128", "UPSZ128", VR128X, v8f32, ++ v4f32, v8i32, loadv4f32, sub_xmm>; ++ defm : subvector_zero_lowering<"DQA64Z128", "DQU64Z128", VR128X, v4i64, ++ v2i64, v8i32, loadv2i64, sub_xmm>; ++ defm : subvector_zero_lowering<"DQA64Z128", "DQU64Z128", VR128X, v8i32, ++ v4i32, v8i32, loadv2i64, sub_xmm>; ++ defm : subvector_zero_lowering<"DQA64Z128", "DQU64Z128", VR128X, v16i16, ++ v8i16, v8i32, loadv2i64, sub_xmm>; ++ defm : subvector_zero_lowering<"DQA64Z128", "DQU64Z128", VR128X, v32i8, ++ v16i8, v8i32, loadv2i64, sub_xmm>; ++ ++ defm : subvector_zero_lowering<"APDZ128", "UPDZ128", VR128X, v8f64, ++ v2f64, v16i32, loadv2f64, sub_xmm>; ++ defm : subvector_zero_lowering<"APSZ128", "UPSZ128", VR128X, v16f32, ++ v4f32, v16i32, loadv4f32, sub_xmm>; ++ defm : subvector_zero_lowering<"DQA64Z128", "DQU64Z128", VR128X, v8i64, ++ v2i64, v16i32, loadv2i64, sub_xmm>; ++ defm : subvector_zero_lowering<"DQA64Z128", "DQU64Z128", VR128X, v16i32, ++ v4i32, v16i32, loadv2i64, sub_xmm>; ++ defm : subvector_zero_lowering<"DQA64Z128", "DQU64Z128", VR128X, v32i16, ++ v8i16, v16i32, loadv2i64, sub_xmm>; ++ defm : subvector_zero_lowering<"DQA64Z128", "DQU64Z128", VR128X, v64i8, ++ v16i8, v16i32, loadv2i64, sub_xmm>; ++ ++ defm : subvector_zero_lowering<"APDZ256", "UPDZ256", VR256X, v8f64, ++ v4f64, v16i32, loadv4f64, sub_ymm>; ++ defm : subvector_zero_lowering<"APSZ256", "UPDZ256", VR256X, v16f32, ++ v8f32, v16i32, loadv8f32, sub_ymm>; ++ defm : subvector_zero_lowering<"DQA64Z256", "DQU64Z256", VR256X, v8i64, ++ v4i64, v16i32, loadv4i64, sub_ymm>; ++ defm : subvector_zero_lowering<"DQA64Z256", "DQU64Z256", VR256X, v16i32, ++ v8i32, v16i32, loadv4i64, sub_ymm>; ++ defm : subvector_zero_lowering<"DQA64Z256", "DQU64Z256", VR256X, v32i16, ++ v16i16, v16i32, loadv4i64, sub_ymm>; ++ defm : subvector_zero_lowering<"DQA64Z256", "DQU64Z256", VR256X, v64i8, ++ v32i8, v16i32, loadv4i64, sub_ymm>; + } + + let Predicates = [HasAVX512, NoVLX] in { +- defm : subvector_zero_lowering<"APD", VR128, v8f64, v2f64, v16i32, loadv2f64, +- sub_xmm>; +- defm : subvector_zero_lowering<"APS", VR128, v16f32, v4f32, v16i32, loadv4f32, +- sub_xmm>; +- defm : subvector_zero_lowering<"DQA", VR128, v8i64, v2i64, v16i32, loadv2i64, +- sub_xmm>; +- defm : subvector_zero_lowering<"DQA", VR128, v16i32, v4i32, v16i32, loadv2i64, +- sub_xmm>; +- defm : subvector_zero_lowering<"DQA", VR128, v32i16, v8i16, v16i32, loadv2i64, +- sub_xmm>; +- defm : subvector_zero_lowering<"DQA", VR128, v64i8, v16i8, v16i32, loadv2i64, +- sub_xmm>; +- +- defm : subvector_zero_lowering<"APDY", VR256, v8f64, v4f64, v16i32, +- loadv4f64, sub_ymm>; +- defm : subvector_zero_lowering<"APSY", VR256, v16f32, v8f32, v16i32, +- loadv8f32, sub_ymm>; +- defm : subvector_zero_lowering<"DQAY", VR256, v8i64, v4i64, v16i32, +- loadv4i64, sub_ymm>; +- defm : subvector_zero_lowering<"DQAY", VR256, v16i32, v8i32, v16i32, +- loadv4i64, sub_ymm>; +- defm : subvector_zero_lowering<"DQAY", VR256, v32i16, v16i16, v16i32, +- loadv4i64, sub_ymm>; +- defm : subvector_zero_lowering<"DQAY", VR256, v64i8, v32i8, v16i32, +- loadv4i64, sub_ymm>; ++ defm : subvector_zero_lowering<"APD", "UPD", VR128, v8f64, v2f64, ++ v16i32,loadv2f64, sub_xmm>; ++ defm : subvector_zero_lowering<"APS", "UPS", VR128, v16f32, v4f32, ++ v16i32, loadv4f32, sub_xmm>; ++ defm : subvector_zero_lowering<"DQA", "DQU", VR128, v8i64, v2i64, ++ v16i32, loadv2i64, sub_xmm>; ++ defm : subvector_zero_lowering<"DQA", "DQU", VR128, v16i32, v4i32, ++ v16i32, loadv2i64, sub_xmm>; ++ defm : subvector_zero_lowering<"DQA", "DQU", VR128, v32i16, v8i16, ++ v16i32, loadv2i64, sub_xmm>; ++ defm : subvector_zero_lowering<"DQA", "DQU", VR128, v64i8, v16i8, ++ v16i32, loadv2i64, sub_xmm>; ++ ++ defm : subvector_zero_lowering<"APDY", "UPDY", VR256, v8f64, v4f64, ++ v16i32, loadv4f64, sub_ymm>; ++ defm : subvector_zero_lowering<"APSY", "UPSY", VR256, v16f32, v8f32, ++ v16i32, loadv8f32, sub_ymm>; ++ defm : subvector_zero_lowering<"DQAY", "DQUY", VR256, v8i64, v4i64, ++ v16i32, loadv4i64, sub_ymm>; ++ defm : subvector_zero_lowering<"DQAY", "DQUY", VR256, v16i32, v8i32, ++ v16i32, loadv4i64, sub_ymm>; ++ defm : subvector_zero_lowering<"DQAY", "DQUY", VR256, v32i16, v16i16, ++ v16i32, loadv4i64, sub_ymm>; ++ defm : subvector_zero_lowering<"DQAY", "DQUY", VR256, v64i8, v32i8, ++ v16i32, loadv4i64, sub_ymm>; + } + + // List of opcodes that guaranteed to zero the upper elements of vector regs. +diff --git a/test/CodeGen/X86/merge-consecutive-loads-256.ll b/test/CodeGen/X86/merge-consecutive-loads-256.ll +index 6ecd8116443..0f2cf594b1c 100644 +--- a/test/CodeGen/X86/merge-consecutive-loads-256.ll ++++ b/test/CodeGen/X86/merge-consecutive-loads-256.ll +@@ -28,13 +28,13 @@ define <4 x double> @merge_4f64_2f64_23(<2 x double>* %ptr) nounwind uwtable noi + define <4 x double> @merge_4f64_2f64_2z(<2 x double>* %ptr) nounwind uwtable noinline ssp { + ; AVX-LABEL: merge_4f64_2f64_2z: + ; AVX: # %bb.0: +-; AVX-NEXT: vmovaps 32(%rdi), %xmm0 ++; AVX-NEXT: vmovups 32(%rdi), %xmm0 + ; AVX-NEXT: retq + ; + ; X32-AVX-LABEL: merge_4f64_2f64_2z: + ; X32-AVX: # %bb.0: + ; X32-AVX-NEXT: movl {{[0-9]+}}(%esp), %eax +-; X32-AVX-NEXT: vmovaps 32(%eax), %xmm0 ++; X32-AVX-NEXT: vmovups 32(%eax), %xmm0 + ; X32-AVX-NEXT: retl + %ptr0 = getelementptr inbounds <2 x double>, <2 x double>* %ptr, i64 2 + %val0 = load <2 x double>, <2 x double>* %ptr0 +@@ -109,13 +109,13 @@ define <4 x double> @merge_4f64_f64_34uu(double* %ptr) nounwind uwtable noinline + define <4 x double> @merge_4f64_f64_45zz(double* %ptr) nounwind uwtable noinline ssp { + ; AVX-LABEL: merge_4f64_f64_45zz: + ; AVX: # %bb.0: +-; AVX-NEXT: vmovaps 32(%rdi), %xmm0 ++; AVX-NEXT: vmovups 32(%rdi), %xmm0 + ; AVX-NEXT: retq + ; + ; X32-AVX-LABEL: merge_4f64_f64_45zz: + ; X32-AVX: # %bb.0: + ; X32-AVX-NEXT: movl {{[0-9]+}}(%esp), %eax +-; X32-AVX-NEXT: vmovaps 32(%eax), %xmm0 ++; X32-AVX-NEXT: vmovups 32(%eax), %xmm0 + ; X32-AVX-NEXT: retl + %ptr0 = getelementptr inbounds double, double* %ptr, i64 4 + %ptr1 = getelementptr inbounds double, double* %ptr, i64 5 +@@ -155,13 +155,13 @@ define <4 x double> @merge_4f64_f64_34z6(double* %ptr) nounwind uwtable noinline + define <4 x i64> @merge_4i64_2i64_3z(<2 x i64>* %ptr) nounwind uwtable noinline ssp { + ; AVX-LABEL: merge_4i64_2i64_3z: + ; AVX: # %bb.0: +-; AVX-NEXT: vmovaps 48(%rdi), %xmm0 ++; AVX-NEXT: vmovups 48(%rdi), %xmm0 + ; AVX-NEXT: retq + ; + ; X32-AVX-LABEL: merge_4i64_2i64_3z: + ; X32-AVX: # %bb.0: + ; X32-AVX-NEXT: movl {{[0-9]+}}(%esp), %eax +-; X32-AVX-NEXT: vmovaps 48(%eax), %xmm0 ++; X32-AVX-NEXT: vmovups 48(%eax), %xmm0 + ; X32-AVX-NEXT: retl + %ptr0 = getelementptr inbounds <2 x i64>, <2 x i64>* %ptr, i64 3 + %val0 = load <2 x i64>, <2 x i64>* %ptr0 +@@ -217,13 +217,13 @@ define <4 x i64> @merge_4i64_i64_1zzu(i64* %ptr) nounwind uwtable noinline ssp { + define <4 x i64> @merge_4i64_i64_23zz(i64* %ptr) nounwind uwtable noinline ssp { + ; AVX-LABEL: merge_4i64_i64_23zz: + ; AVX: # %bb.0: +-; AVX-NEXT: vmovaps 16(%rdi), %xmm0 ++; AVX-NEXT: vmovups 16(%rdi), %xmm0 + ; AVX-NEXT: retq + ; + ; X32-AVX-LABEL: merge_4i64_i64_23zz: + ; X32-AVX: # %bb.0: + ; X32-AVX-NEXT: movl {{[0-9]+}}(%esp), %eax +-; X32-AVX-NEXT: vmovaps 16(%eax), %xmm0 ++; X32-AVX-NEXT: vmovups 16(%eax), %xmm0 + ; X32-AVX-NEXT: retl + %ptr0 = getelementptr inbounds i64, i64* %ptr, i64 2 + %ptr1 = getelementptr inbounds i64, i64* %ptr, i64 3 +diff --git a/test/CodeGen/X86/merge-consecutive-loads-512.ll b/test/CodeGen/X86/merge-consecutive-loads-512.ll +index 62102eb382c..3c6eaf65292 100644 +--- a/test/CodeGen/X86/merge-consecutive-loads-512.ll ++++ b/test/CodeGen/X86/merge-consecutive-loads-512.ll +@@ -106,13 +106,13 @@ define <8 x double> @merge_8f64_f64_23uuuuu9(double* %ptr) nounwind uwtable noin + define <8 x double> @merge_8f64_f64_12zzuuzz(double* %ptr) nounwind uwtable noinline ssp { + ; ALL-LABEL: merge_8f64_f64_12zzuuzz: + ; ALL: # %bb.0: +-; ALL-NEXT: vmovaps 8(%rdi), %xmm0 ++; ALL-NEXT: vmovups 8(%rdi), %xmm0 + ; ALL-NEXT: retq + ; + ; X32-AVX512F-LABEL: merge_8f64_f64_12zzuuzz: + ; X32-AVX512F: # %bb.0: + ; X32-AVX512F-NEXT: movl {{[0-9]+}}(%esp), %eax +-; X32-AVX512F-NEXT: vmovaps 8(%eax), %xmm0 ++; X32-AVX512F-NEXT: vmovups 8(%eax), %xmm0 + ; X32-AVX512F-NEXT: retl + %ptr0 = getelementptr inbounds double, double* %ptr, i64 1 + %ptr1 = getelementptr inbounds double, double* %ptr, i64 2 +@@ -190,7 +190,7 @@ define <8 x i64> @merge_8i64_4i64_z3(<4 x i64>* %ptr) nounwind uwtable noinline + define <8 x i64> @merge_8i64_i64_56zz9uzz(i64* %ptr) nounwind uwtable noinline ssp { + ; ALL-LABEL: merge_8i64_i64_56zz9uzz: + ; ALL: # %bb.0: +-; ALL-NEXT: vmovaps 40(%rdi), %xmm0 ++; ALL-NEXT: vmovups 40(%rdi), %xmm0 + ; ALL-NEXT: vmovsd {{.*#+}} xmm1 = mem[0],zero + ; ALL-NEXT: vinsertf64x4 $1, %ymm1, %zmm0, %zmm0 + ; ALL-NEXT: retq +@@ -198,7 +198,7 @@ define <8 x i64> @merge_8i64_i64_56zz9uzz(i64* %ptr) nounwind uwtable noinline s + ; X32-AVX512F-LABEL: merge_8i64_i64_56zz9uzz: + ; X32-AVX512F: # %bb.0: + ; X32-AVX512F-NEXT: movl {{[0-9]+}}(%esp), %eax +-; X32-AVX512F-NEXT: vmovaps 40(%eax), %xmm0 ++; X32-AVX512F-NEXT: vmovups 40(%eax), %xmm0 + ; X32-AVX512F-NEXT: vmovsd {{.*#+}} xmm1 = mem[0],zero + ; X32-AVX512F-NEXT: vinsertf64x4 $1, %ymm1, %zmm0, %zmm0 + ; X32-AVX512F-NEXT: retl diff --git a/deps/patches/llvm-rL327898.patch b/deps/patches/llvm-rL327898.patch new file mode 100644 index 0000000..f4d9a43 --- /dev/null +++ b/deps/patches/llvm-rL327898.patch @@ -0,0 +1,6131 @@ +commit 64c3384f94a1eb3e3510d6f66c3bccdfc9d9050b +Author: Nirav Dave +Date: Thu Feb 1 16:11:59 2018 +0000 + + r327898/dependencies roll up + + This is a squash of 13 commits required in the lead up to r327898, + which fixes https://github.com/JuliaLang/julia/issues/27603. The squashed + commits are: + + 332d15e981e86b9e058087174bb288ba18a15807 + b659d3fca5d24c25ee73f979edb382f7f24e05e2 + c01d1363ea080170fc5143d72f26eecd9270f03b + eab8a177a4caef9e42ef1d2aeb4ba15dc788d3f2 + bedb1391781b009ace95f5586e7fae5f03fe0689 + 11d041a905f82ac78e7ccf2394773e80b93d147c + e1ec36c55a0127988f42a3329ca835617b30de09 + b8d2903300c13d8fd151c8e5dc71017269617539 + 00884fea345f47ab05174a8f314ecd60d1676d02 + 28ab04cec0d9888af9d29946b3a048b8340abe0f + 3dd52e62ea3087efcca63c3772183d9471abc742 + bd3649ff6d6b4d18b3c6de253179d987a120518a + aea03035b9c633e6d745b6d3fc5b6378699f576c + + Their commit messages follow below: + + [SelectionDAG] Fix UpdateChains handling of TokenFactors + + Summary: + In Instruction Selection UpdateChains replaces all matched Nodes' + chain references including interior token factors and deletes them. + This may allow nodes which depend on these interior nodes but are not + part of the set of matched nodes to be left with a dangling dependence. + Avoid this by doing the replacement for matched non-TokenFactor nodes. + + Fixes PR36164. + + Reviewers: jonpa, RKSimon, bogner + + Subscribers: llvm-commits, hiraditya + + Differential Revision: https://reviews.llvm.org/D42754 + + git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@323977 91177308-0d34-0410-b5e6-96231b3b80d8 + + Regenerate test result for vastart-defs-eflags.ll. NFC. + + git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@323596 91177308-0d34-0410-b5e6-96231b3b80d8 + + Regenerate test result for testb-je-fusion.ll. NFC. + + git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@323595 91177308-0d34-0410-b5e6-96231b3b80d8 + + [X86] Avoid using high register trick for test instruction + + Summary: + It seems it's main effect is to create addition copies when values are inr register that do not support this trick, which increase register pressure and makes the code bigger. + + Reviewers: craig.topper, niravd, spatel, hfinkel + + Subscribers: llvm-commits + + Differential Revision: https://reviews.llvm.org/D42646 + + git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@323888 91177308-0d34-0410-b5e6-96231b3b80d8 + + Add a regression test for problems caused by D42646 . NFC + + git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@323868 91177308-0d34-0410-b5e6-96231b3b80d8 + + Add test case for truncated and promotion to test. NFC + + git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@323663 91177308-0d34-0410-b5e6-96231b3b80d8 + + [X86] Add test case to ensure testw is generated when optimizing for size. NFC + + git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@323687 91177308-0d34-0410-b5e6-96231b3b80d8 + + [X86] Generate testl instruction through truncates. + + Summary: + This was introduced in D42646 but ended up being reverted because the original implementation was buggy. + + Depends on D42646 + + Reviewers: craig.topper, niravd, spatel, hfinkel + + Subscribers: llvm-commits + + Differential Revision: https://reviews.llvm.org/D42741 + + git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@323899 91177308-0d34-0410-b5e6-96231b3b80d8 + + [X86] Don't look for TEST instruction shrinking opportunities when the root node is a X86ISD::SUB. + + I don't believe we ever create an X86ISD::SUB with a 0 constant which is what the TEST handling needs. The ternary operator at the end of this code shows up as only going one way in the llvm-cov report from the bots. + + git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@324865 91177308-0d34-0410-b5e6-96231b3b80d8 + + [X86] Teach LowerBUILD_VECTOR to recognize pair-wise splats of 32-bit elements and use a 64-bit broadcast + + If we are splatting pairs of 32-bit elements, we can use a 64-bit broadcast to get the job done. + + We could probably could probably do this with other sizes too, for example four 16-bit elements. Or we could broadcast pairs of 16-bit elements using a 32-bit element broadcast. But I've left that as a future improvement. + + I've also restricted this to AVX2 only because we can only broadcast loads under AVX. + + Differential Revision: https://reviews.llvm.org/D42086 + + git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@322730 91177308-0d34-0410-b5e6-96231b3b80d8 + + [DAG, X86] Revert r327197 "Revert r327170, r327171, r327172" + + Reland ISel cycle checking improvements after simplifying node id + invariant traversal and correcting typo. + + git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@327898 91177308-0d34-0410-b5e6-96231b3b80d8 + + [ Modified for cherry-pick: Dropped Hexagon and SystemZ changes" + + [DAG, X86] Fix ISel-time node insertion ids + + As in SystemZ backend, correctly propagate node ids when inserting new + unselected nodes into the DAG during instruction Seleciton for X86 + target. + + Fixes PR36865. + + Reviewers: jyknight, craig.topper + + Subscribers: hiraditya, llvm-commits + + Differential Revision: https://reviews.llvm.org/D44797 + + git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@328233 91177308-0d34-0410-b5e6-96231b3b80d8 + + [DAG] Fix node id invalidation in Instruction Selection. + + Invalidation should be bit negation. Add missing negation. + + git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@328287 91177308-0d34-0410-b5e6-96231b3b80d8 + + Remove failing tests + + This removes tests that are failing due to codegen differences, + after the latest set of backports. Fixing thse for the backport + branch does not seem worth it. + +diff --git a/include/llvm/CodeGen/SelectionDAGISel.h b/include/llvm/CodeGen/SelectionDAGISel.h +index de6849a1eae..e56eafc437c 100644 +--- a/include/llvm/CodeGen/SelectionDAGISel.h ++++ b/include/llvm/CodeGen/SelectionDAGISel.h +@@ -110,6 +110,11 @@ public: + CodeGenOpt::Level OptLevel, + bool IgnoreChains = false); + ++ static void InvalidateNodeId(SDNode *N); ++ static int getUninvalidatedNodeId(SDNode *N); ++ ++ static void EnforceNodeIdInvariant(SDNode *N); ++ + // Opcodes used by the DAG state machine: + enum BuiltinOpcodes { + OPC_Scope, +@@ -199,23 +204,28 @@ protected: + /// of the new node T. + void ReplaceUses(SDValue F, SDValue T) { + CurDAG->ReplaceAllUsesOfValueWith(F, T); ++ EnforceNodeIdInvariant(T.getNode()); + } + + /// ReplaceUses - replace all uses of the old nodes F with the use + /// of the new nodes T. + void ReplaceUses(const SDValue *F, const SDValue *T, unsigned Num) { + CurDAG->ReplaceAllUsesOfValuesWith(F, T, Num); ++ for (unsigned i = 0; i < Num; ++i) ++ EnforceNodeIdInvariant(T[i].getNode()); + } + + /// ReplaceUses - replace all uses of the old node F with the use + /// of the new node T. + void ReplaceUses(SDNode *F, SDNode *T) { + CurDAG->ReplaceAllUsesWith(F, T); ++ EnforceNodeIdInvariant(T); + } + + /// Replace all uses of \c F with \c T, then remove \c F from the DAG. + void ReplaceNode(SDNode *F, SDNode *T) { + CurDAG->ReplaceAllUsesWith(F, T); ++ EnforceNodeIdInvariant(T); + CurDAG->RemoveDeadNode(F); + } + +diff --git a/include/llvm/CodeGen/SelectionDAGNodes.h b/include/llvm/CodeGen/SelectionDAGNodes.h +index 522c2f1b2cb..2d974234abf 100644 +--- a/include/llvm/CodeGen/SelectionDAGNodes.h ++++ b/include/llvm/CodeGen/SelectionDAGNodes.h +@@ -796,16 +796,44 @@ public: + /// searches to be performed in parallel, caching of results across + /// queries and incremental addition to Worklist. Stops early if N is + /// found but will resume. Remember to clear Visited and Worklists +- /// if DAG changes. ++ /// if DAG changes. MaxSteps gives a maximum number of nodes to visit before ++ /// giving up. The TopologicalPrune flag signals that positive NodeIds are ++ /// topologically ordered (Operands have strictly smaller node id) and search ++ /// can be pruned leveraging this. + static bool hasPredecessorHelper(const SDNode *N, + SmallPtrSetImpl &Visited, + SmallVectorImpl &Worklist, +- unsigned int MaxSteps = 0) { ++ unsigned int MaxSteps = 0, ++ bool TopologicalPrune = false) { ++ SmallVector DeferredNodes; + if (Visited.count(N)) + return true; ++ ++ // Node Id's are assigned in three places: As a topological ++ // ordering (> 0), during legalization (results in values set to ++ // 0), new nodes (set to -1). If N has a topolgical id then we ++ // know that all nodes with ids smaller than it cannot be ++ // successors and we need not check them. Filter out all node ++ // that can't be matches. We add them to the worklist before exit ++ // in case of multiple calls. Note that during selection the topological id ++ // may be violated if a node's predecessor is selected before it. We mark ++ // this at selection negating the id of unselected successors and ++ // restricting topological pruning to positive ids. ++ ++ int NId = N->getNodeId(); ++ // If we Invalidated the Id, reconstruct original NId. ++ if (NId < -1) ++ NId = -(NId + 1); ++ ++ bool Found = false; + while (!Worklist.empty()) { + const SDNode *M = Worklist.pop_back_val(); +- bool Found = false; ++ int MId = M->getNodeId(); ++ if (TopologicalPrune && M->getOpcode() != ISD::TokenFactor && (NId > 0) && ++ (MId > 0) && (MId < NId)) { ++ DeferredNodes.push_back(M); ++ continue; ++ } + for (const SDValue &OpV : M->op_values()) { + SDNode *Op = OpV.getNode(); + if (Visited.insert(Op).second) +@@ -814,11 +842,16 @@ public: + Found = true; + } + if (Found) +- return true; ++ break; + if (MaxSteps != 0 && Visited.size() >= MaxSteps) +- return false; ++ break; + } +- return false; ++ // Push deferred nodes back on worklist. ++ Worklist.append(DeferredNodes.begin(), DeferredNodes.end()); ++ // If we bailed early, conservatively return found. ++ if (MaxSteps != 0 && Visited.size() >= MaxSteps) ++ return true; ++ return Found; + } + + /// Return true if all the users of N are contained in Nodes. +diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp b/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp +index bd9fcfb5c1e..17e42240133 100644 +--- a/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp ++++ b/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp +@@ -937,6 +937,58 @@ public: + + } // end anonymous namespace + ++// This function is used to enforce the topological node id property ++// property leveraged during Instruction selection. Before selection all ++// nodes are given a non-negative id such that all nodes have a larger id than ++// their operands. As this holds transitively we can prune checks that a node N ++// is a predecessor of M another by not recursively checking through M's ++// operands if N's ID is larger than M's ID. This is significantly improves ++// performance of for various legality checks (e.g. IsLegalToFold / ++// UpdateChains). ++ ++// However, when we fuse multiple nodes into a single node ++// during selection we may induce a predecessor relationship between inputs and ++// outputs of distinct nodes being merged violating the topological property. ++// Should a fused node have a successor which has yet to be selected, our ++// legality checks would be incorrect. To avoid this we mark all unselected ++// sucessor nodes, i.e. id != -1 as invalid for pruning by bit-negating (x => ++// (-(x+1))) the ids and modify our pruning check to ignore negative Ids of M. ++// We use bit-negation to more clearly enforce that node id -1 can only be ++// achieved by selected nodes). As the conversion is reversable the original Id, ++// topological pruning can still be leveraged when looking for unselected nodes. ++// This method is call internally in all ISel replacement calls. ++void SelectionDAGISel::EnforceNodeIdInvariant(SDNode *Node) { ++ SmallVector Nodes; ++ Nodes.push_back(Node); ++ ++ while (!Nodes.empty()) { ++ SDNode *N = Nodes.pop_back_val(); ++ for (auto *U : N->uses()) { ++ auto UId = U->getNodeId(); ++ if (UId > 0) { ++ InvalidateNodeId(U); ++ Nodes.push_back(U); ++ } ++ } ++ } ++} ++ ++// InvalidateNodeId - As discusses in EnforceNodeIdInvariant, mark a ++// NodeId with the equivalent node id which is invalid for topological ++// pruning. ++void SelectionDAGISel::InvalidateNodeId(SDNode *N) { ++ int InvalidId = -(N->getNodeId() + 1); ++ N->setNodeId(InvalidId); ++} ++ ++// getUninvalidatedNodeId - get original uninvalidated node id. ++int SelectionDAGISel::getUninvalidatedNodeId(SDNode *N) { ++ int Id = N->getNodeId(); ++ if (Id < -1) ++ return -(Id + 1); ++ return Id; ++} ++ + void SelectionDAGISel::DoInstructionSelection() { + DEBUG(dbgs() << "===== Instruction selection begins: " + << printMBBReference(*FuncInfo->MBB) << " '" +@@ -972,6 +1024,33 @@ void SelectionDAGISel::DoInstructionSelection() { + if (Node->use_empty()) + continue; + ++#ifndef NDEBUG ++ SmallVector Nodes; ++ Nodes.push_back(Node); ++ ++ while (!Nodes.empty()) { ++ auto N = Nodes.pop_back_val(); ++ if (N->getOpcode() == ISD::TokenFactor || N->getNodeId() < 0) ++ continue; ++ for (const SDValue &Op : N->op_values()) { ++ if (Op->getOpcode() == ISD::TokenFactor) ++ Nodes.push_back(Op.getNode()); ++ else { ++ // We rely on topological ordering of node ids for checking for ++ // cycles when fusing nodes during selection. All unselected nodes ++ // successors of an already selected node should have a negative id. ++ // This assertion will catch such cases. If this assertion triggers ++ // it is likely you using DAG-level Value/Node replacement functions ++ // (versus equivalent ISEL replacement) in backend-specific ++ // selections. See comment in EnforceNodeIdInvariant for more ++ // details. ++ assert(Op->getNodeId() != -1 && ++ "Node has already selected predecessor node"); ++ } ++ } ++ } ++#endif ++ + // When we are using non-default rounding modes or FP exception behavior + // FP operations are represented by StrictFP pseudo-operations. They + // need to be simplified here so that the target-specific instruction +@@ -2134,52 +2213,44 @@ static SDNode *findGlueUse(SDNode *N) { + return nullptr; + } + +-/// findNonImmUse - Return true if "Use" is a non-immediate use of "Def". +-/// This function iteratively traverses up the operand chain, ignoring +-/// certain nodes. +-static bool findNonImmUse(SDNode *Use, SDNode* Def, SDNode *ImmedUse, +- SDNode *Root, SmallPtrSetImpl &Visited, ++/// findNonImmUse - Return true if "Def" is a predecessor of "Root" via a path ++/// beyond "ImmedUse". We may ignore chains as they are checked separately. ++static bool findNonImmUse(SDNode *Root, SDNode *Def, SDNode *ImmedUse, + bool IgnoreChains) { +- // The NodeID's are given uniques ID's where a node ID is guaranteed to be +- // greater than all of its (recursive) operands. If we scan to a point where +- // 'use' is smaller than the node we're scanning for, then we know we will +- // never find it. +- // +- // The Use may be -1 (unassigned) if it is a newly allocated node. This can +- // happen because we scan down to newly selected nodes in the case of glue +- // uses. +- std::vector WorkList; +- WorkList.push_back(Use); +- +- while (!WorkList.empty()) { +- Use = WorkList.back(); +- WorkList.pop_back(); +- if (Use->getNodeId() < Def->getNodeId() && Use->getNodeId() != -1) +- continue; ++ SmallPtrSet Visited; ++ SmallVector WorkList; ++ // Only check if we have non-immediate uses of Def. ++ if (ImmedUse->isOnlyUserOf(Def)) ++ return false; + +- // Don't revisit nodes if we already scanned it and didn't fail, we know we +- // won't fail if we scan it again. +- if (!Visited.insert(Use).second) ++ // We don't care about paths to Def that go through ImmedUse so mark it ++ // visited and mark non-def operands as used. ++ Visited.insert(ImmedUse); ++ for (const SDValue &Op : ImmedUse->op_values()) { ++ SDNode *N = Op.getNode(); ++ // Ignore chain deps (they are validated by ++ // HandleMergeInputChains) and immediate uses ++ if ((Op.getValueType() == MVT::Other && IgnoreChains) || N == Def) + continue; ++ if (!Visited.insert(N).second) ++ continue; ++ WorkList.push_back(N); ++ } + +- for (const SDValue &Op : Use->op_values()) { +- // Ignore chain uses, they are validated by HandleMergeInputChains. +- if (Op.getValueType() == MVT::Other && IgnoreChains) +- continue; +- ++ // Initialize worklist to operands of Root. ++ if (Root != ImmedUse) { ++ for (const SDValue &Op : Root->op_values()) { + SDNode *N = Op.getNode(); +- if (N == Def) { +- if (Use == ImmedUse || Use == Root) +- continue; // We are not looking for immediate use. +- assert(N != Root); +- return true; +- } +- +- // Traverse up the operand chain. ++ // Ignore chains (they are validated by HandleMergeInputChains) ++ if ((Op.getValueType() == MVT::Other && IgnoreChains) || N == Def) ++ continue; ++ if (!Visited.insert(N).second) ++ continue; + WorkList.push_back(N); + } + } +- return false; ++ ++ return SDNode::hasPredecessorHelper(Def, Visited, WorkList, 0, true); + } + + /// IsProfitableToFold - Returns true if it's profitable to fold the specific +@@ -2251,13 +2322,12 @@ bool SelectionDAGISel::IsLegalToFold(SDValue N, SDNode *U, SDNode *Root, + + // If our query node has a glue result with a use, we've walked up it. If + // the user (which has already been selected) has a chain or indirectly uses +- // the chain, our WalkChainUsers predicate will not consider it. Because of ++ // the chain, HandleMergeInputChains will not consider it. Because of + // this, we cannot ignore chains in this predicate. + IgnoreChains = false; + } + +- SmallPtrSet Visited; +- return !findNonImmUse(Root, N.getNode(), U, Root, Visited, IgnoreChains); ++ return !findNonImmUse(Root, N.getNode(), U, IgnoreChains); + } + + void SelectionDAGISel::Select_INLINEASM(SDNode *N) { +@@ -2360,7 +2430,8 @@ void SelectionDAGISel::UpdateChains( + std::replace(ChainNodesMatched.begin(), ChainNodesMatched.end(), N, + static_cast(nullptr)); + }); +- CurDAG->ReplaceAllUsesOfValueWith(ChainVal, InputChain); ++ if (ChainNode->getOpcode() != ISD::TokenFactor) ++ ReplaceUses(ChainVal, InputChain); + + // If the node became dead and we haven't already seen it, delete it. + if (ChainNode != NodeToMatch && ChainNode->use_empty() && +@@ -2375,143 +2446,6 @@ void SelectionDAGISel::UpdateChains( + DEBUG(dbgs() << "ISEL: Match complete!\n"); + } + +-enum ChainResult { +- CR_Simple, +- CR_InducesCycle, +- CR_LeadsToInteriorNode +-}; +- +-/// WalkChainUsers - Walk down the users of the specified chained node that is +-/// part of the pattern we're matching, looking at all of the users we find. +-/// This determines whether something is an interior node, whether we have a +-/// non-pattern node in between two pattern nodes (which prevent folding because +-/// it would induce a cycle) and whether we have a TokenFactor node sandwiched +-/// between pattern nodes (in which case the TF becomes part of the pattern). +-/// +-/// The walk we do here is guaranteed to be small because we quickly get down to +-/// already selected nodes "below" us. +-static ChainResult +-WalkChainUsers(const SDNode *ChainedNode, +- SmallVectorImpl &ChainedNodesInPattern, +- DenseMap &TokenFactorResult, +- SmallVectorImpl &InteriorChainedNodes) { +- ChainResult Result = CR_Simple; +- +- for (SDNode::use_iterator UI = ChainedNode->use_begin(), +- E = ChainedNode->use_end(); UI != E; ++UI) { +- // Make sure the use is of the chain, not some other value we produce. +- if (UI.getUse().getValueType() != MVT::Other) continue; +- +- SDNode *User = *UI; +- +- if (User->getOpcode() == ISD::HANDLENODE) // Root of the graph. +- continue; +- +- // If we see an already-selected machine node, then we've gone beyond the +- // pattern that we're selecting down into the already selected chunk of the +- // DAG. +- unsigned UserOpcode = User->getOpcode(); +- if (User->isMachineOpcode() || +- UserOpcode == ISD::CopyToReg || +- UserOpcode == ISD::CopyFromReg || +- UserOpcode == ISD::INLINEASM || +- UserOpcode == ISD::EH_LABEL || +- UserOpcode == ISD::LIFETIME_START || +- UserOpcode == ISD::LIFETIME_END) { +- // If their node ID got reset to -1 then they've already been selected. +- // Treat them like a MachineOpcode. +- if (User->getNodeId() == -1) +- continue; +- } +- +- // If we have a TokenFactor, we handle it specially. +- if (User->getOpcode() != ISD::TokenFactor) { +- // If the node isn't a token factor and isn't part of our pattern, then it +- // must be a random chained node in between two nodes we're selecting. +- // This happens when we have something like: +- // x = load ptr +- // call +- // y = x+4 +- // store y -> ptr +- // Because we structurally match the load/store as a read/modify/write, +- // but the call is chained between them. We cannot fold in this case +- // because it would induce a cycle in the graph. +- if (!std::count(ChainedNodesInPattern.begin(), +- ChainedNodesInPattern.end(), User)) +- return CR_InducesCycle; +- +- // Otherwise we found a node that is part of our pattern. For example in: +- // x = load ptr +- // y = x+4 +- // store y -> ptr +- // This would happen when we're scanning down from the load and see the +- // store as a user. Record that there is a use of ChainedNode that is +- // part of the pattern and keep scanning uses. +- Result = CR_LeadsToInteriorNode; +- InteriorChainedNodes.push_back(User); +- continue; +- } +- +- // If we found a TokenFactor, there are two cases to consider: first if the +- // TokenFactor is just hanging "below" the pattern we're matching (i.e. no +- // uses of the TF are in our pattern) we just want to ignore it. Second, +- // the TokenFactor can be sandwiched in between two chained nodes, like so: +- // [Load chain] +- // ^ +- // | +- // [Load] +- // ^ ^ +- // | \ DAG's like cheese +- // / \ do you? +- // / | +- // [TokenFactor] [Op] +- // ^ ^ +- // | | +- // \ / +- // \ / +- // [Store] +- // +- // In this case, the TokenFactor becomes part of our match and we rewrite it +- // as a new TokenFactor. +- // +- // To distinguish these two cases, do a recursive walk down the uses. +- auto MemoizeResult = TokenFactorResult.find(User); +- bool Visited = MemoizeResult != TokenFactorResult.end(); +- // Recursively walk chain users only if the result is not memoized. +- if (!Visited) { +- auto Res = WalkChainUsers(User, ChainedNodesInPattern, TokenFactorResult, +- InteriorChainedNodes); +- MemoizeResult = TokenFactorResult.insert(std::make_pair(User, Res)).first; +- } +- switch (MemoizeResult->second) { +- case CR_Simple: +- // If the uses of the TokenFactor are just already-selected nodes, ignore +- // it, it is "below" our pattern. +- continue; +- case CR_InducesCycle: +- // If the uses of the TokenFactor lead to nodes that are not part of our +- // pattern that are not selected, folding would turn this into a cycle, +- // bail out now. +- return CR_InducesCycle; +- case CR_LeadsToInteriorNode: +- break; // Otherwise, keep processing. +- } +- +- // Okay, we know we're in the interesting interior case. The TokenFactor +- // is now going to be considered part of the pattern so that we rewrite its +- // uses (it may have uses that are not part of the pattern) with the +- // ultimate chain result of the generated code. We will also add its chain +- // inputs as inputs to the ultimate TokenFactor we create. +- Result = CR_LeadsToInteriorNode; +- if (!Visited) { +- ChainedNodesInPattern.push_back(User); +- InteriorChainedNodes.push_back(User); +- } +- } +- +- return Result; +-} +- + /// HandleMergeInputChains - This implements the OPC_EmitMergeInputChains + /// operation for when the pattern matched at least one node with a chains. The + /// input vector contains a list of all of the chained nodes that we match. We +@@ -2521,47 +2455,56 @@ WalkChainUsers(const SDNode *ChainedNode, + static SDValue + HandleMergeInputChains(SmallVectorImpl &ChainNodesMatched, + SelectionDAG *CurDAG) { +- // Used for memoization. Without it WalkChainUsers could take exponential +- // time to run. +- DenseMap TokenFactorResult; +- // Walk all of the chained nodes we've matched, recursively scanning down the +- // users of the chain result. This adds any TokenFactor nodes that are caught +- // in between chained nodes to the chained and interior nodes list. +- SmallVector InteriorChainedNodes; +- for (unsigned i = 0, e = ChainNodesMatched.size(); i != e; ++i) { +- if (WalkChainUsers(ChainNodesMatched[i], ChainNodesMatched, +- TokenFactorResult, +- InteriorChainedNodes) == CR_InducesCycle) +- return SDValue(); // Would induce a cycle. +- } + +- // Okay, we have walked all the matched nodes and collected TokenFactor nodes +- // that we are interested in. Form our input TokenFactor node. ++ SmallPtrSet Visited; ++ SmallVector Worklist; + SmallVector InputChains; +- for (unsigned i = 0, e = ChainNodesMatched.size(); i != e; ++i) { +- // Add the input chain of this node to the InputChains list (which will be +- // the operands of the generated TokenFactor) if it's not an interior node. +- SDNode *N = ChainNodesMatched[i]; +- if (N->getOpcode() != ISD::TokenFactor) { +- if (std::count(InteriorChainedNodes.begin(),InteriorChainedNodes.end(),N)) +- continue; ++ unsigned int Max = 8192; + +- // Otherwise, add the input chain. +- SDValue InChain = ChainNodesMatched[i]->getOperand(0); +- assert(InChain.getValueType() == MVT::Other && "Not a chain"); +- InputChains.push_back(InChain); +- continue; +- } ++ // Quick exit on trivial merge. ++ if (ChainNodesMatched.size() == 1) ++ return ChainNodesMatched[0]->getOperand(0); + +- // If we have a token factor, we want to add all inputs of the token factor +- // that are not part of the pattern we're matching. +- for (const SDValue &Op : N->op_values()) { +- if (!std::count(ChainNodesMatched.begin(), ChainNodesMatched.end(), +- Op.getNode())) +- InputChains.push_back(Op); +- } ++ // Add chains that aren't already added (internal). Peek through ++ // token factors. ++ std::function AddChains = [&](const SDValue V) { ++ if (V.getValueType() != MVT::Other) ++ return; ++ if (V->getOpcode() == ISD::EntryToken) ++ return; ++ if (!Visited.insert(V.getNode()).second) ++ return; ++ if (V->getOpcode() == ISD::TokenFactor) { ++ for (const SDValue &Op : V->op_values()) ++ AddChains(Op); ++ } else ++ InputChains.push_back(V); ++ }; ++ ++ for (auto *N : ChainNodesMatched) { ++ Worklist.push_back(N); ++ Visited.insert(N); + } + ++ while (!Worklist.empty()) ++ AddChains(Worklist.pop_back_val()->getOperand(0)); ++ ++ // Skip the search if there are no chain dependencies. ++ if (InputChains.size() == 0) ++ return CurDAG->getEntryNode(); ++ ++ // If one of these chains is a successor of input, we must have a ++ // node that is both the predecessor and successor of the ++ // to-be-merged nodes. Fail. ++ Visited.clear(); ++ for (SDValue V : InputChains) ++ Worklist.push_back(V.getNode()); ++ ++ for (auto *N : ChainNodesMatched) ++ if (SDNode::hasPredecessorHelper(N, Visited, Worklist, Max, true)) ++ return SDValue(); ++ ++ // Return merged chain. + if (InputChains.size() == 1) + return InputChains[0]; + return CurDAG->getNode(ISD::TokenFactor, SDLoc(ChainNodesMatched[0]), +@@ -2606,8 +2549,8 @@ MorphNode(SDNode *Node, unsigned TargetOpc, SDVTList VTList, + // Move the glue if needed. + if ((EmitNodeInfo & OPFL_GlueOutput) && OldGlueResultNo != -1 && + (unsigned)OldGlueResultNo != ResNumResults-1) +- CurDAG->ReplaceAllUsesOfValueWith(SDValue(Node, OldGlueResultNo), +- SDValue(Res, ResNumResults-1)); ++ ReplaceUses(SDValue(Node, OldGlueResultNo), ++ SDValue(Res, ResNumResults - 1)); + + if ((EmitNodeInfo & OPFL_GlueOutput) != 0) + --ResNumResults; +@@ -2615,14 +2558,15 @@ MorphNode(SDNode *Node, unsigned TargetOpc, SDVTList VTList, + // Move the chain reference if needed. + if ((EmitNodeInfo & OPFL_Chain) && OldChainResultNo != -1 && + (unsigned)OldChainResultNo != ResNumResults-1) +- CurDAG->ReplaceAllUsesOfValueWith(SDValue(Node, OldChainResultNo), +- SDValue(Res, ResNumResults-1)); ++ ReplaceUses(SDValue(Node, OldChainResultNo), ++ SDValue(Res, ResNumResults - 1)); + + // Otherwise, no replacement happened because the node already exists. Replace + // Uses of the old node with the new one. + if (Res != Node) { +- CurDAG->ReplaceAllUsesWith(Node, Res); +- CurDAG->RemoveDeadNode(Node); ++ ReplaceNode(Node, Res); ++ } else { ++ EnforceNodeIdInvariant(Res); + } + + return Res; +@@ -2939,8 +2883,7 @@ void SelectionDAGISel::SelectCodeCommon(SDNode *NodeToMatch, + return; + case ISD::AssertSext: + case ISD::AssertZext: +- CurDAG->ReplaceAllUsesOfValueWith(SDValue(NodeToMatch, 0), +- NodeToMatch->getOperand(0)); ++ ReplaceUses(SDValue(NodeToMatch, 0), NodeToMatch->getOperand(0)); + CurDAG->RemoveDeadNode(NodeToMatch); + return; + case ISD::INLINEASM: +@@ -3702,7 +3645,7 @@ void SelectionDAGISel::SelectCodeCommon(SDNode *NodeToMatch, + NodeToMatch->getValueType(i).getSizeInBits() == + Res.getValueSizeInBits()) && + "invalid replacement"); +- CurDAG->ReplaceAllUsesOfValueWith(SDValue(NodeToMatch, i), Res); ++ ReplaceUses(SDValue(NodeToMatch, i), Res); + } + + // Update chain uses. +@@ -3715,8 +3658,8 @@ void SelectionDAGISel::SelectCodeCommon(SDNode *NodeToMatch, + if (NodeToMatch->getValueType(NodeToMatch->getNumValues() - 1) == + MVT::Glue && + InputGlue.getNode()) +- CurDAG->ReplaceAllUsesOfValueWith( +- SDValue(NodeToMatch, NodeToMatch->getNumValues() - 1), InputGlue); ++ ReplaceUses(SDValue(NodeToMatch, NodeToMatch->getNumValues() - 1), ++ InputGlue); + + assert(NodeToMatch->use_empty() && + "Didn't replace all uses of the node?"); +diff --git a/lib/Target/AMDGPU/AMDGPUISelDAGToDAG.cpp b/lib/Target/AMDGPU/AMDGPUISelDAGToDAG.cpp +index f4776adb069..be5345e422d 100644 +--- a/lib/Target/AMDGPU/AMDGPUISelDAGToDAG.cpp ++++ b/lib/Target/AMDGPU/AMDGPUISelDAGToDAG.cpp +@@ -759,12 +759,11 @@ void AMDGPUDAGToDAGISel::SelectADD_SUB_I64(SDNode *N) { + + if (ProduceCarry) { + // Replace the carry-use +- CurDAG->ReplaceAllUsesOfValueWith(SDValue(N, 1), SDValue(AddHi, 1)); ++ ReplaceUses(SDValue(N, 1), SDValue(AddHi, 1)); + } + + // Replace the remaining uses. +- CurDAG->ReplaceAllUsesWith(N, RegSequence); +- CurDAG->RemoveDeadNode(N); ++ ReplaceNode(N, RegSequence); + } + + void AMDGPUDAGToDAGISel::SelectUADDO_USUBO(SDNode *N) { +diff --git a/lib/Target/ARM/ARMISelDAGToDAG.cpp b/lib/Target/ARM/ARMISelDAGToDAG.cpp +index 8d32510e200..0f504718f28 100644 +--- a/lib/Target/ARM/ARMISelDAGToDAG.cpp ++++ b/lib/Target/ARM/ARMISelDAGToDAG.cpp +@@ -498,7 +498,7 @@ bool ARMDAGToDAGISel::canExtractShiftFromMul(const SDValue &N, + + void ARMDAGToDAGISel::replaceDAGValue(const SDValue &N, SDValue M) { + CurDAG->RepositionNode(N.getNode()->getIterator(), M.getNode()); +- CurDAG->ReplaceAllUsesWith(N, M); ++ ReplaceUses(N, M); + } + + bool ARMDAGToDAGISel::SelectImmShifterOperand(SDValue N, +diff --git a/lib/Target/Hexagon/HexagonISelDAGToDAG.cpp b/lib/Target/Hexagon/HexagonISelDAGToDAG.cpp +index a6ac4e3df74..3721856ff45 100644 +--- a/lib/Target/Hexagon/HexagonISelDAGToDAG.cpp ++++ b/lib/Target/Hexagon/HexagonISelDAGToDAG.cpp +@@ -777,7 +777,7 @@ void HexagonDAGToDAGISel::SelectBitcast(SDNode *N) { + return; + } + +- CurDAG->ReplaceAllUsesOfValueWith(SDValue(N,0), N->getOperand(0)); ++ ReplaceUses(SDValue(N, 0), N->getOperand(0)); + CurDAG->RemoveDeadNode(N); + } + +@@ -2182,4 +2182,3 @@ void HexagonDAGToDAGISel::rebalanceAddressTrees() { + RootHeights.clear(); + RootWeights.clear(); + } +- +diff --git a/lib/Target/Hexagon/HexagonISelDAGToDAGHVX.cpp b/lib/Target/Hexagon/HexagonISelDAGToDAGHVX.cpp +index f08c5054065..0608f06ef7e 100644 +--- a/lib/Target/Hexagon/HexagonISelDAGToDAGHVX.cpp ++++ b/lib/Target/Hexagon/HexagonISelDAGToDAGHVX.cpp +@@ -1914,7 +1914,6 @@ void HvxSelector::selectShuffle(SDNode *N) { + // If the mask is all -1's, generate "undef". + if (!UseLeft && !UseRight) { + ISel.ReplaceNode(N, ISel.selectUndef(SDLoc(SN), ResTy).getNode()); +- DAG.RemoveDeadNode(N); + return; + } + +@@ -1970,7 +1969,6 @@ void HvxSelector::selectRor(SDNode *N) { + NewN = DAG.getMachineNode(Hexagon::V6_vror, dl, Ty, {VecV, RotV}); + + ISel.ReplaceNode(N, NewN); +- DAG.RemoveDeadNode(N); + } + + void HexagonDAGToDAGISel::SelectHvxShuffle(SDNode *N) { +@@ -2017,8 +2015,7 @@ void HexagonDAGToDAGISel::SelectV65GatherPred(SDNode *N) { + MemOp[0] = cast(N)->getMemOperand(); + cast(Result)->setMemRefs(MemOp, MemOp + 1); + +- ReplaceUses(N, Result); +- CurDAG->RemoveDeadNode(N); ++ ReplaceNode(N, Result); + } + + void HexagonDAGToDAGISel::SelectV65Gather(SDNode *N) { +@@ -2056,8 +2053,7 @@ void HexagonDAGToDAGISel::SelectV65Gather(SDNode *N) { + MemOp[0] = cast(N)->getMemOperand(); + cast(Result)->setMemRefs(MemOp, MemOp + 1); + +- ReplaceUses(N, Result); +- CurDAG->RemoveDeadNode(N); ++ ReplaceNode(N, Result); + } + + void HexagonDAGToDAGISel::SelectHVXDualOutput(SDNode *N) { +@@ -2100,5 +2096,3 @@ void HexagonDAGToDAGISel::SelectHVXDualOutput(SDNode *N) { + ReplaceUses(SDValue(N, 1), SDValue(Result, 1)); + CurDAG->RemoveDeadNode(N); + } +- +- +diff --git a/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp b/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp +index ce6f3d37f5c..fe59d820c88 100644 +--- a/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp ++++ b/lib/Target/SystemZ/SystemZISelDAGToDAG.cpp +@@ -589,10 +589,16 @@ bool SystemZDAGToDAGISel::selectAddress(SDValue Addr, + // The selection DAG must no longer depend on their uniqueness when this + // function is used. + static void insertDAGNode(SelectionDAG *DAG, SDNode *Pos, SDValue N) { +- if (N.getNode()->getNodeId() == -1 || +- N.getNode()->getNodeId() > Pos->getNodeId()) { ++ if (N->getNodeId() == -1 || ++ (SelectionDAGISel::getUninvalidatedNodeId(N.getNode()) > ++ SelectionDAGISel::getUninvalidatedNodeId(Pos))) { + DAG->RepositionNode(Pos->getIterator(), N.getNode()); +- N.getNode()->setNodeId(Pos->getNodeId()); ++ // Mark Node as invalid for pruning as after this it may be a successor to a ++ // selected node but otherwise be in the same position of Pos. ++ // Conservatively mark it with the same -abs(Id) to assure node id ++ // invariant is preserved. ++ N->setNodeId(Pos->getNodeId()); ++ SelectionDAGISel::InvalidateNodeId(N.getNode()); + } + } + +@@ -1022,8 +1028,7 @@ bool SystemZDAGToDAGISel::tryRISBGZero(SDNode *N) { + }; + SDValue New = convertTo( + DL, VT, SDValue(CurDAG->getMachineNode(Opcode, DL, OpcodeVT, Ops), 0)); +- ReplaceUses(N, New.getNode()); +- CurDAG->RemoveDeadNode(N); ++ ReplaceNode(N, New.getNode()); + return true; + } + +@@ -1114,8 +1119,7 @@ void SystemZDAGToDAGISel::splitLargeImmediate(unsigned Opcode, SDNode *Node, + SDValue Lower = CurDAG->getConstant(LowerVal, DL, VT); + SDValue Or = CurDAG->getNode(Opcode, DL, VT, Upper, Lower); + +- ReplaceUses(Node, Or.getNode()); +- CurDAG->RemoveDeadNode(Node); ++ ReplaceNode(Node, Or.getNode()); + + SelectCode(Or.getNode()); + } +diff --git a/lib/Target/X86/X86ISelDAGToDAG.cpp b/lib/Target/X86/X86ISelDAGToDAG.cpp +index d79fd0ca4da..ee2d221e31c 100644 +--- a/lib/Target/X86/X86ISelDAGToDAG.cpp ++++ b/lib/Target/X86/X86ISelDAGToDAG.cpp +@@ -988,10 +988,16 @@ bool X86DAGToDAGISel::matchAdd(SDValue N, X86ISelAddressMode &AM, + // IDs! The selection DAG must no longer depend on their uniqueness when this + // is used. + static void insertDAGNode(SelectionDAG &DAG, SDValue Pos, SDValue N) { +- if (N.getNode()->getNodeId() == -1 || +- N.getNode()->getNodeId() > Pos.getNode()->getNodeId()) { +- DAG.RepositionNode(Pos.getNode()->getIterator(), N.getNode()); +- N.getNode()->setNodeId(Pos.getNode()->getNodeId()); ++ if (N->getNodeId() == -1 || ++ (SelectionDAGISel::getUninvalidatedNodeId(N.getNode()) > ++ SelectionDAGISel::getUninvalidatedNodeId(Pos.getNode()))) { ++ DAG.RepositionNode(Pos->getIterator(), N.getNode()); ++ // Mark Node as invalid for pruning as after this it may be a successor to a ++ // selected node but otherwise be in the same position of Pos. ++ // Conservatively mark it with the same -abs(Id) to assure node id ++ // invariant is preserved. ++ N->setNodeId(Pos->getNodeId()); ++ SelectionDAGISel::InvalidateNodeId(N.getNode()); + } + } + +@@ -2092,50 +2098,84 @@ static bool isFusableLoadOpStorePattern(StoreSDNode *StoreNode, + LoadNode->getOffset() != StoreNode->getOffset()) + return false; + +- // Check if the chain is produced by the load or is a TokenFactor with +- // the load output chain as an operand. Return InputChain by reference. ++ bool FoundLoad = false; ++ SmallVector ChainOps; ++ SmallVector LoopWorklist; ++ SmallPtrSet Visited; ++ const unsigned int Max = 1024; ++ ++ // Visualization of Load-Op-Store fusion: ++ // ------------------------- ++ // Legend: ++ // *-lines = Chain operand dependencies. ++ // |-lines = Normal operand dependencies. ++ // Dependencies flow down and right. n-suffix references multiple nodes. ++ // ++ // C Xn C ++ // * * * ++ // * * * ++ // Xn A-LD Yn TF Yn ++ // * * \ | * | ++ // * * \ | * | ++ // * * \ | => A--LD_OP_ST ++ // * * \| \ ++ // TF OP \ ++ // * | \ Zn ++ // * | \ ++ // A-ST Zn ++ // ++ ++ // This merge induced dependences from: #1: Xn -> LD, OP, Zn ++ // #2: Yn -> LD ++ // #3: ST -> Zn ++ ++ // Ensure the transform is safe by checking for the dual ++ // dependencies to make sure we do not induce a loop. ++ ++ // As LD is a predecessor to both OP and ST we can do this by checking: ++ // a). if LD is a predecessor to a member of Xn or Yn. ++ // b). if a Zn is a predecessor to ST. ++ ++ // However, (b) can only occur through being a chain predecessor to ++ // ST, which is the same as Zn being a member or predecessor of Xn, ++ // which is a subset of LD being a predecessor of Xn. So it's ++ // subsumed by check (a). ++ + SDValue Chain = StoreNode->getChain(); + +- bool ChainCheck = false; ++ // Gather X elements in ChainOps. + if (Chain == Load.getValue(1)) { +- ChainCheck = true; +- InputChain = LoadNode->getChain(); ++ FoundLoad = true; ++ ChainOps.push_back(Load.getOperand(0)); + } else if (Chain.getOpcode() == ISD::TokenFactor) { +- SmallVector ChainOps; + for (unsigned i = 0, e = Chain.getNumOperands(); i != e; ++i) { + SDValue Op = Chain.getOperand(i); + if (Op == Load.getValue(1)) { +- ChainCheck = true; ++ FoundLoad = true; + // Drop Load, but keep its chain. No cycle check necessary. + ChainOps.push_back(Load.getOperand(0)); + continue; + } +- +- // Make sure using Op as part of the chain would not cause a cycle here. +- // In theory, we could check whether the chain node is a predecessor of +- // the load. But that can be very expensive. Instead visit the uses and +- // make sure they all have smaller node id than the load. +- int LoadId = LoadNode->getNodeId(); +- for (SDNode::use_iterator UI = Op.getNode()->use_begin(), +- UE = UI->use_end(); UI != UE; ++UI) { +- if (UI.getUse().getResNo() != 0) +- continue; +- if (UI->getNodeId() > LoadId) +- return false; +- } +- ++ LoopWorklist.push_back(Op.getNode()); + ChainOps.push_back(Op); + } +- +- if (ChainCheck) +- // Make a new TokenFactor with all the other input chains except +- // for the load. +- InputChain = CurDAG->getNode(ISD::TokenFactor, SDLoc(Chain), +- MVT::Other, ChainOps); + } +- if (!ChainCheck) ++ ++ if (!FoundLoad) ++ return false; ++ ++ // Worklist is currently Xn. Add Yn to worklist. ++ for (SDValue Op : StoredVal->ops()) ++ if (Op.getNode() != LoadNode) ++ LoopWorklist.push_back(Op.getNode()); ++ ++ // Check (a) if Load is a predecessor to Xn + Yn ++ if (SDNode::hasPredecessorHelper(Load.getNode(), Visited, LoopWorklist, Max, ++ true)) + return false; + ++ InputChain = ++ CurDAG->getNode(ISD::TokenFactor, SDLoc(Chain), MVT::Other, ChainOps); + return true; + } + +@@ -2335,6 +2375,8 @@ bool X86DAGToDAGISel::foldLoadStoreIntoMemOperand(SDNode *Node) { + MemOp[1] = LoadNode->getMemOperand(); + Result->setMemRefs(MemOp, MemOp + 2); + ++ // Update Load Chain uses as well. ++ ReplaceUses(SDValue(LoadNode, 1), SDValue(Result, 1)); + ReplaceUses(SDValue(StoreNode, 0), SDValue(Result, 1)); + ReplaceUses(SDValue(StoredVal.getNode(), 1), SDValue(Result, 0)); + CurDAG->RemoveDeadNode(Node); +@@ -2946,12 +2988,7 @@ void X86DAGToDAGISel::Select(SDNode *Node) { + return; + } + +- case X86ISD::CMP: +- case X86ISD::SUB: { +- // Sometimes a SUB is used to perform comparison. +- if (Opcode == X86ISD::SUB && Node->hasAnyUseOfValue(0)) +- // This node is not a CMP. +- break; ++ case X86ISD::CMP: { + SDValue N0 = Node->getOperand(0); + SDValue N1 = Node->getOperand(1); + +@@ -2971,95 +3008,52 @@ void X86DAGToDAGISel::Select(SDNode *Node) { + if (!C) break; + uint64_t Mask = C->getZExtValue(); + +- // For example, convert "testl %eax, $8" to "testb %al, $8" ++ MVT VT; ++ int SubRegOp; ++ unsigned Op; ++ + if (isUInt<8>(Mask) && + (!(Mask & 0x80) || hasNoSignedComparisonUses(Node))) { +- SDValue Imm = CurDAG->getTargetConstant(Mask, dl, MVT::i8); +- SDValue Reg = N0.getOperand(0); +- +- // Extract the l-register. +- SDValue Subreg = CurDAG->getTargetExtractSubreg(X86::sub_8bit, dl, +- MVT::i8, Reg); +- +- // Emit a testb. +- SDNode *NewNode = CurDAG->getMachineNode(X86::TEST8ri, dl, MVT::i32, +- Subreg, Imm); +- // Replace SUB|CMP with TEST, since SUB has two outputs while TEST has +- // one, do not call ReplaceAllUsesWith. +- ReplaceUses(SDValue(Node, (Opcode == X86ISD::SUB ? 1 : 0)), +- SDValue(NewNode, 0)); +- CurDAG->RemoveDeadNode(Node); +- return; ++ // For example, convert "testl %eax, $8" to "testb %al, $8" ++ VT = MVT::i8; ++ SubRegOp = X86::sub_8bit; ++ Op = X86::TEST8ri; ++ } else if (OptForMinSize && isUInt<16>(Mask) && ++ (!(Mask & 0x8000) || hasNoSignedComparisonUses(Node))) { ++ // For example, "testl %eax, $32776" to "testw %ax, $32776". ++ // NOTE: We only want to form TESTW instructions if optimizing for ++ // min size. Otherwise we only save one byte and possibly get a length ++ // changing prefix penalty in the decoders. ++ VT = MVT::i16; ++ SubRegOp = X86::sub_16bit; ++ Op = X86::TEST16ri; ++ } else if (isUInt<32>(Mask) && N0.getValueType() != MVT::i16 && ++ (!(Mask & 0x80000000) || hasNoSignedComparisonUses(Node))) { ++ // For example, "testq %rax, $268468232" to "testl %eax, $268468232". ++ // NOTE: We only want to run that transform if N0 is 32 or 64 bits. ++ // Otherwize, we find ourselves in a position where we have to do ++ // promotion. If previous passes did not promote the and, we assume ++ // they had a good reason not to and do not promote here. ++ VT = MVT::i32; ++ SubRegOp = X86::sub_32bit; ++ Op = X86::TEST32ri; ++ } else { ++ // No eligible transformation was found. ++ break; + } + +- // For example, "testl %eax, $2048" to "testb %ah, $8". +- if (isShiftedUInt<8, 8>(Mask) && +- (!(Mask & 0x8000) || hasNoSignedComparisonUses(Node))) { +- // Shift the immediate right by 8 bits. +- SDValue ShiftedImm = CurDAG->getTargetConstant(Mask >> 8, dl, MVT::i8); +- SDValue Reg = N0.getOperand(0); +- +- // Extract the h-register. +- SDValue Subreg = CurDAG->getTargetExtractSubreg(X86::sub_8bit_hi, dl, +- MVT::i8, Reg); +- +- // Emit a testb. The EXTRACT_SUBREG becomes a COPY that can only +- // target GR8_NOREX registers, so make sure the register class is +- // forced. +- SDNode *NewNode = CurDAG->getMachineNode(X86::TEST8ri_NOREX, dl, +- MVT::i32, Subreg, ShiftedImm); +- // Replace SUB|CMP with TEST, since SUB has two outputs while TEST has +- // one, do not call ReplaceAllUsesWith. +- ReplaceUses(SDValue(Node, (Opcode == X86ISD::SUB ? 1 : 0)), +- SDValue(NewNode, 0)); +- CurDAG->RemoveDeadNode(Node); +- return; +- } ++ SDValue Imm = CurDAG->getTargetConstant(Mask, dl, VT); ++ SDValue Reg = N0.getOperand(0); + +- // For example, "testl %eax, $32776" to "testw %ax, $32776". +- // NOTE: We only want to form TESTW instructions if optimizing for +- // min size. Otherwise we only save one byte and possibly get a length +- // changing prefix penalty in the decoders. +- if (OptForMinSize && isUInt<16>(Mask) && N0.getValueType() != MVT::i16 && +- (!(Mask & 0x8000) || hasNoSignedComparisonUses(Node))) { +- SDValue Imm = CurDAG->getTargetConstant(Mask, dl, MVT::i16); +- SDValue Reg = N0.getOperand(0); +- +- // Extract the 16-bit subregister. +- SDValue Subreg = CurDAG->getTargetExtractSubreg(X86::sub_16bit, dl, +- MVT::i16, Reg); +- +- // Emit a testw. +- SDNode *NewNode = CurDAG->getMachineNode(X86::TEST16ri, dl, MVT::i32, +- Subreg, Imm); +- // Replace SUB|CMP with TEST, since SUB has two outputs while TEST has +- // one, do not call ReplaceAllUsesWith. +- ReplaceUses(SDValue(Node, (Opcode == X86ISD::SUB ? 1 : 0)), +- SDValue(NewNode, 0)); +- CurDAG->RemoveDeadNode(Node); +- return; +- } ++ // Extract the subregister if necessary. ++ if (N0.getValueType() != VT) ++ Reg = CurDAG->getTargetExtractSubreg(SubRegOp, dl, VT, Reg); + +- // For example, "testq %rax, $268468232" to "testl %eax, $268468232". +- if (isUInt<32>(Mask) && N0.getValueType() == MVT::i64 && +- (!(Mask & 0x80000000) || hasNoSignedComparisonUses(Node))) { +- SDValue Imm = CurDAG->getTargetConstant(Mask, dl, MVT::i32); +- SDValue Reg = N0.getOperand(0); +- +- // Extract the 32-bit subregister. +- SDValue Subreg = CurDAG->getTargetExtractSubreg(X86::sub_32bit, dl, +- MVT::i32, Reg); +- +- // Emit a testl. +- SDNode *NewNode = CurDAG->getMachineNode(X86::TEST32ri, dl, MVT::i32, +- Subreg, Imm); +- // Replace SUB|CMP with TEST, since SUB has two outputs while TEST has +- // one, do not call ReplaceAllUsesWith. +- ReplaceUses(SDValue(Node, (Opcode == X86ISD::SUB ? 1 : 0)), +- SDValue(NewNode, 0)); +- CurDAG->RemoveDeadNode(Node); +- return; +- } ++ // Emit a testl or testw. ++ SDNode *NewNode = CurDAG->getMachineNode(Op, dl, MVT::i32, Reg, Imm); ++ // Replace CMP with TEST. ++ ReplaceNode(Node, NewNode); ++ return; + } + break; + } +diff --git a/lib/Target/X86/X86ISelLowering.cpp b/lib/Target/X86/X86ISelLowering.cpp +index c1ddb771e2f..86e71cba87b 100644 +--- a/lib/Target/X86/X86ISelLowering.cpp ++++ b/lib/Target/X86/X86ISelLowering.cpp +@@ -8131,6 +8131,32 @@ X86TargetLowering::LowerBUILD_VECTOR(SDValue Op, SelectionDAG &DAG) const { + return LD; + } + ++ // If this is a splat of pairs of 32-bit elements, we can use a narrower ++ // build_vector and broadcast it. ++ // TODO: We could probably generalize this more. ++ if (Subtarget.hasAVX2() && EVTBits == 32 && Values.size() == 2) { ++ SDValue Ops[4] = { Op.getOperand(0), Op.getOperand(1), ++ DAG.getUNDEF(ExtVT), DAG.getUNDEF(ExtVT) }; ++ auto CanSplat = [](SDValue Op, unsigned NumElems, ArrayRef Ops) { ++ // Make sure all the even/odd operands match. ++ for (unsigned i = 2; i != NumElems; ++i) ++ if (Ops[i % 2] != Op.getOperand(i)) ++ return false; ++ return true; ++ }; ++ if (CanSplat(Op, NumElems, Ops)) { ++ MVT WideEltVT = VT.isFloatingPoint() ? MVT::f64 : MVT::i64; ++ MVT NarrowVT = MVT::getVectorVT(ExtVT, 4); ++ // Create a new build vector and cast to v2i64/v2f64. ++ SDValue NewBV = DAG.getBitcast(MVT::getVectorVT(WideEltVT, 2), ++ DAG.getBuildVector(NarrowVT, dl, Ops)); ++ // Broadcast from v2i64/v2f64 and cast to final VT. ++ MVT BcastVT = MVT::getVectorVT(WideEltVT, NumElems/2); ++ return DAG.getBitcast(VT, DAG.getNode(X86ISD::VBROADCAST, dl, BcastVT, ++ NewBV)); ++ } ++ } ++ + // For AVX-length vectors, build the individual 128-bit pieces and use + // shuffles to put them in place. + if (VT.is256BitVector() || VT.is512BitVector()) { +diff --git a/lib/Target/X86/X86InstrArithmetic.td b/lib/Target/X86/X86InstrArithmetic.td +index 98cc8fb7439..3d5de637da2 100644 +--- a/lib/Target/X86/X86InstrArithmetic.td ++++ b/lib/Target/X86/X86InstrArithmetic.td +@@ -1257,14 +1257,6 @@ let isCompare = 1 in { + def TEST32mi : BinOpMI_F<0xF6, "test", Xi32, X86testpat, MRM0m>; + let Predicates = [In64BitMode] in + def TEST64mi32 : BinOpMI_F<0xF6, "test", Xi64, X86testpat, MRM0m>; +- +- // When testing the result of EXTRACT_SUBREG sub_8bit_hi, make sure the +- // register class is constrained to GR8_NOREX. This pseudo is explicitly +- // marked side-effect free, since it doesn't have an isel pattern like +- // other test instructions. +- let isPseudo = 1, hasSideEffects = 0 in +- def TEST8ri_NOREX : I<0, Pseudo, (outs), (ins GR8_NOREX:$src, i8imm:$mask), +- "", [], IIC_BIN_NONMEM>, Sched<[WriteALU]>; + } // Defs = [EFLAGS] + + def TEST8i8 : BinOpAI_F<0xA8, "test", Xi8 , AL, +diff --git a/lib/Target/X86/X86InstrInfo.cpp b/lib/Target/X86/X86InstrInfo.cpp +index 11ada51a870..84a9200a0ef 100644 +--- a/lib/Target/X86/X86InstrInfo.cpp ++++ b/lib/Target/X86/X86InstrInfo.cpp +@@ -7854,9 +7854,6 @@ bool X86InstrInfo::expandPostRAPseudo(MachineInstr &MI) const { + case X86::VMOVUPSZ256mr_NOVLX: + return expandNOVLXStore(MIB, &getRegisterInfo(), get(X86::VMOVUPSYmr), + get(X86::VEXTRACTF64x4Zmr), X86::sub_ymm); +- case X86::TEST8ri_NOREX: +- MI.setDesc(get(X86::TEST8ri)); +- return true; + case X86::MOV32ri64: + MI.setDesc(get(X86::MOV32ri)); + return true; +diff --git a/lib/Target/X86/X86MacroFusion.cpp b/lib/Target/X86/X86MacroFusion.cpp +index 67d95c2233d..4e11397dec4 100644 +--- a/lib/Target/X86/X86MacroFusion.cpp ++++ b/lib/Target/X86/X86MacroFusion.cpp +@@ -86,7 +86,6 @@ static bool shouldScheduleAdjacent(const TargetInstrInfo &TII, + case X86::TEST16mr: + case X86::TEST32mr: + case X86::TEST64mr: +- case X86::TEST8ri_NOREX: + case X86::AND16i16: + case X86::AND16ri: + case X86::AND16ri8: +diff --git a/test/CodeGen/SystemZ/pr36164.ll b/test/CodeGen/SystemZ/pr36164.ll +new file mode 100644 +index 00000000000..0c850091d31 +--- /dev/null ++++ b/test/CodeGen/SystemZ/pr36164.ll +@@ -0,0 +1,113 @@ ++; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py ++; RUN: llc %s -o - -mtriple=s390x-linux-gnu -mcpu=z13 -disable-basicaa | FileCheck %s ++ ++; This test checks that we do not a reference to a deleted node. ++ ++%0 = type { i32 } ++ ++@g_11 = external dso_local unnamed_addr global i1, align 4 ++@g_69 = external dso_local global i32, align 4 ++@g_73 = external dso_local unnamed_addr global i32, align 4 ++@g_832 = external dso_local constant %0, align 4 ++@g_938 = external dso_local unnamed_addr global i64, align 8 ++ ++; Function Attrs: nounwind ++define void @main() local_unnamed_addr #0 { ++; CHECK-LABEL: main: ++; CHECK: # %bb.0: ++; CHECK-NEXT: stmg %r12, %r15, 96(%r15) ++; CHECK-NEXT: .cfi_offset %r12, -64 ++; CHECK-NEXT: .cfi_offset %r13, -56 ++; CHECK-NEXT: .cfi_offset %r14, -48 ++; CHECK-NEXT: .cfi_offset %r15, -40 ++; CHECK-NEXT: lhi %r0, 1 ++; CHECK-NEXT: larl %r1, g_938 ++; CHECK-NEXT: lhi %r2, 2 ++; CHECK-NEXT: lhi %r3, 3 ++; CHECK-NEXT: lhi %r4, 0 ++; CHECK-NEXT: lhi %r5, 4 ++; CHECK-NEXT: larl %r14, g_11 ++; CHECK-NEXT: .LBB0_1: # =>This Inner Loop Header: Depth=1 ++; CHECK-NEXT: strl %r0, g_73 ++; CHECK-NEXT: lrl %r13, g_832 ++; CHECK-NEXT: lrl %r13, g_832 ++; CHECK-NEXT: lrl %r13, g_832 ++; CHECK-NEXT: lrl %r13, g_832 ++; CHECK-NEXT: lrl %r13, g_832 ++; CHECK-NEXT: lrl %r13, g_832 ++; CHECK-NEXT: lrl %r13, g_832 ++; CHECK-NEXT: lrl %r13, g_832 ++; CHECK-NEXT: lrl %r13, g_832 ++; CHECK-NEXT: lrl %r13, g_832 ++; CHECK-NEXT: lrl %r13, g_832 ++; CHECK-NEXT: lrl %r13, g_832 ++; CHECK-NEXT: lrl %r13, g_832 ++; CHECK-NEXT: lrl %r13, g_832 ++; CHECK-NEXT: lrl %r13, g_832 ++; CHECK-NEXT: strl %r0, g_69 ++; CHECK-NEXT: lrl %r13, g_832 ++; CHECK-NEXT: lghi %r13, 24 ++; CHECK-NEXT: strl %r2, g_69 ++; CHECK-NEXT: ag %r13, 0(%r1) ++; CHECK-NEXT: lrl %r12, g_832 ++; CHECK-NEXT: strl %r3, g_69 ++; CHECK-NEXT: lrl %r12, g_832 ++; CHECK-NEXT: strl %r4, g_69 ++; CHECK-NEXT: lrl %r12, g_832 ++; CHECK-NEXT: strl %r0, g_69 ++; CHECK-NEXT: lrl %r12, g_832 ++; CHECK-NEXT: strl %r2, g_69 ++; CHECK-NEXT: lrl %r12, g_832 ++; CHECK-NEXT: strl %r3, g_69 ++; CHECK-NEXT: stgrl %r13, g_938 ++; CHECK-NEXT: lrl %r13, g_832 ++; CHECK-NEXT: strl %r5, g_69 ++; CHECK-NEXT: mvi 0(%r14), 1 ++; CHECK-NEXT: j .LBB0_1 ++ br label %1 ++ ++;

+ +This directory contains various details related to building Julia: + +* [Detailed build instructions](https://github.com/JuliaLang/julia/blob/master/doc/build/build.md) + +Notes for various OSes: + +* [Linux](https://github.com/JuliaLang/julia/blob/master/doc/build/linux.md) +* [macOS](https://github.com/JuliaLang/julia/blob/master/doc/build/macos.md) +* [Windows](https://github.com/JuliaLang/julia/blob/master/doc/build/windows.md) +* [FreeBSD](https://github.com/JuliaLang/julia/blob/master/doc/build/freebsd.md) + +Notes for various architectures: + +* [ARM](https://github.com/JuliaLang/julia/blob/master/doc/build/arm.md) + +Notes for building Julia for distribution: + +* [Distribution Notes](https://github.com/JuliaLang/julia/blob/master/doc/build/distributing.md) \ No newline at end of file diff --git a/doc/build/arm.md b/doc/build/arm.md new file mode 100644 index 0000000..2cecfba --- /dev/null +++ b/doc/build/arm.md @@ -0,0 +1,87 @@ +# ARM (Linux) + +Julia fully supports ARMv8 (AArch64) processors, and supports ARMv7 and ARMv6 +(AArch32) with some caveats. This file provides general guidelines for compilation, +in addition to instructions for specific devices. + +A list of [known issues](https://github.com/JuliaLang/julia/labels/arm) for ARM is +available. If you encounter difficulties, please create an issue including the output +from `cat /proc/cpuinfo`. + +## 32-bit (ARMv6, ARMv7) + +Julia has been successfully compiled on several variants of the following ARMv6 & ARMv7 devices: + +* ARMv7 / Cortex A15 Samsung Chromebooks running Ubuntu Linux under Crouton; +* [Raspberry Pi](https://www.raspberrypi.org). +* [Odroid](https://www.hardkernel.com). + +Julia requires at least the `armv6` and `vfpv2` instruction sets. It's recommended to use `armv7-a`. +`armv5` or soft float are not supported. + +### Raspberry Pi 1 / Raspberry Pi Zero + +If the type of ARM CPU used in the Raspberry Pi is not detected by LLVM, then explicitly set the +CPU target by adding the following to `Make.user`: + +```` +JULIA_CPU_TARGET=arm1176jzf-s +```` + +To complete the build, you may need to increase the swap file size. To do so, edit +`/etc/dphys-swapfile`, changing the line: + + CONF_SWAPSIZE=100 + +to: + + CONF_SWAPSIZE=512 + +before restarting the swapfile service: + + sudo /etc/init.d/dphys-swapfile stop + sudo /etc/init.d/dphys-swapfile start + +### Raspberry Pi 2 + +The type of ARM CPU used in the Raspberry Pi 2 is not detected by LLVM. Explicitly set the +CPU target by adding the following to `Make.user`: + +```JULIA_CPU_TARGET=cortex-a7``` + +Depending on the exact compiler and distribution, there might be a build failure +due to unsupported inline assembly. In that case, add `MARCH=armv7-a` to +`Make.user`. + +## AArch64 (ARMv8) + +Julia has been successfully built on the following ARMv8 devices: + +* [nVidia Jetson TX1 & TX2](https://www.nvidia.com/object/embedded-systems-dev-kits-modules.html); +* [X-Gene 1](https://www.apm.com/products/data-center/x-gene-family/x-gene/); +* [Overdrive 3000](https://softiron.com/products/overdrive-3000/); +* [Cavium ThunderX](https://www.cavium.com/ThunderX_ARM_Processors.html) on [packet.net](https://www.packet.net). + +Compilation on `ARMv8-A` requires that `Make.user` is configured as follows: + +``` +MARCH=armv8-a +``` + +### nVidia Jetson TX2 + +Julia builds and runs on the [nVidia Jetson TX2](https://www.nvidia.com/object/embedded-systems-dev-kits-modules.html) +platform with minimal configuration changes. + +After configuring `Make.user` as per the `AArch64` instructions in this document, +follow the general [build instructions](https://github.com/JuliaLang/julia/blob/master/README.md). +The majority of the build dependencies specified in the instructions are installed by +the default configuration flashed by [Jetpack 3.0](https://developer.nvidia.com/embedded/jetpack). The remaining tools can be installed by issuing the following command: + +``` +sudo apt-get install gfortran wget cmake +``` + +A full parallel build, including LLVM, +will complete in around two hours. All tests pass and CUDA functionality is available +through, e.g., [CUDAdrv](https://github.com/JuliaGPU/CUDAdrv.jl). diff --git a/doc/build/build.md b/doc/build/build.md new file mode 100644 index 0000000..a6d6f72 --- /dev/null +++ b/doc/build/build.md @@ -0,0 +1,259 @@ +# Building Julia (Detailed) + +## Downloading the Julia source code + +If you are behind a firewall, you may need to use the `https` protocol instead of the `git` protocol: + +```sh +git config --global url."https://".insteadOf git:// +``` + +Be sure to also configure your system to use the appropriate proxy +settings, e.g. by setting the `https_proxy` and `http_proxy` +variables. + +## Building Julia + +When compiled the first time, the build will automatically download +pre-built [external +dependencies](#required-build-tools-and-external-libraries). If you +prefer to build all the dependencies on your own, add the following in +`Make.user` + +``` +USE_BINARYBUILDER=0 +``` + +Building Julia requires 5GiB if building all dependencies and approximately 4GiB of virtual memory. + +To perform a parallel build, use `make -j N` and supply the maximum +number of concurrent processes. If the defaults in the build do not work for you, and +you need to set specific make parameters, you can save them in +`Make.user`, and place the file in the root of your Julia source. The +build will automatically check for the existence of `Make.user` and +use it if it exists. + +You can create out-of-tree builds of Julia by specifying `make +O= configure` on the command line. This will create a +directory mirror, with all of the necessary Makefiles to build Julia, +in the specified directory. These builds will share the source files +in Julia and `deps/srccache`. Each out-of-tree build directory can +have its own `Make.user` file to override the global `Make.user` file +in the top-level folder. + +If everything works correctly, you will see a Julia banner and an +interactive prompt into which you can enter expressions for +evaluation. (Errors related to libraries might be caused by old, +incompatible libraries sitting around in your PATH. In this case, try +moving the `julia` directory earlier in the PATH). Note that most of +the instructions above apply to unix systems. + +To run julia from anywhere you can: +- add an alias (in `bash`: `echo "alias julia='/path/to/install/folder/bin/julia'" >> ~/.bashrc && source ~/.bashrc`), or + +- add a soft link to the `julia` executable in the `julia` directory to `/usr/local/bin` (or any suitable directory already in your path), or + +- add the `julia` directory to your executable path for this shell session (in `bash`: `export PATH="$(pwd):$PATH"` ; in `csh` or `tcsh`: +`set path= ( $path $cwd )` ), or + +- add the `julia` directory to your executable path permanently (e.g. in `.bash_profile`), or + +- write `prefix=/path/to/install/folder` into `Make.user` and then run `make install`. If there is a version of Julia already installed in this folder, you should delete it before running `make install`. + +Now you should be able to run Julia like this: + + julia + +If you are building a Julia package for distribution on Linux, macOS, +or Windows, take a look at the detailed notes in +[distributing.md](https://github.com/JuliaLang/julia/blob/master/doc/build/distributing.md). + +## Updating an existing source tree + +If you have previously downloaded `julia` using `git clone`, you can update the +existing source tree using `git pull` rather than starting anew: +```sh +cd julia +git pull && make +``` +Assuming that you had made no changes to the source tree that will conflict +with upstream updates, these commands will trigger a build to update to the +latest version. + +## General troubleshooting + +1. Over time, the base library may accumulate enough changes such that the + bootstrapping process in building the system image will fail. If this + happens, the build may fail with an error like + + ```sh + *** This error is usually fixed by running 'make clean'. If the error persists, try 'make cleanall' *** + ``` + + As described, running `make clean && make` is usually sufficient. + Occasionally, the stronger cleanup done by `make cleanall` is needed. + +2. New versions of external dependencies may be introduced which may + occasionally cause conflicts with existing builds of older versions. + + a. Special `make` targets exist to help wipe the existing build of a + dependency. For example, `make -C deps clean-llvm` will clean out the + existing build of `llvm` so that `llvm` will be rebuilt from the + downloaded source distribution the next time `make` is called. + `make -C deps distclean-llvm` is a stronger wipe which will also delete + the downloaded source distribution, ensuring that a fresh copy of the + source distribution will be downloaded and that any new patches will be + applied the next time `make` is called. + + b. To delete existing binaries of `julia` and all its dependencies, + delete the `./usr` directory _in the source tree_. + +3. If you've updated macOS recently, be sure to run `xcode-select --install` to update the command line tools. + Otherwise, you could run into errors for missing headers and libraries, such as + ```ld: library not found for -lcrt1.10.6.o```. + +4. If you've moved the source directory, you might get errors such as + ```CMake Error: The current CMakeCache.txt directory ... is different than the directory ... where CMakeCache.txt was created.```, in which case you may delete the offending dependency under `deps` + +5. In extreme cases, you may wish to reset the source tree to a pristine state. + The following git commands may be helpful: + + ```sh + git reset --hard #Forcibly remove any changes to any files under version control + git clean -x -f -d #Forcibly remove any file or directory not under version control + ``` + + _To avoid losing work, make sure you know what these commands do before you + run them. `git` will not be able to undo these changes!_ + +## Platform-Specific Notes + +Notes for various operating systems: + +* [Linux](https://github.com/JuliaLang/julia/blob/master/doc/build/linux.md) +* [macOS](https://github.com/JuliaLang/julia/blob/master/doc/build/macos.md) +* [Windows](https://github.com/JuliaLang/julia/blob/master/doc/build/windows.md) +* [FreeBSD](https://github.com/JuliaLang/julia/blob/master/doc/build/freebsd.md) + +Notes for various architectures: + +* [ARM](https://github.com/JuliaLang/julia/blob/master/doc/build/arm.md) + +## Required Build Tools and External Libraries + +Building Julia requires that the following software be installed: + +- **[GNU make]** — building dependencies. +- **[gcc & g++][gcc]** (>= 4.7) or **[Clang][clang]** (>= 3.1, Xcode 4.3.3 on macOS) — compiling and linking C, C++. +- **[libatomic][gcc]** — provided by **[gcc]** and needed to support atomic operations. +- **[python]** (>=2.7) — needed to build LLVM. +- **[gfortran]** — compiling and linking Fortran libraries. +- **[perl]** — preprocessing of header files of libraries. +- **[wget]**, **[curl]**, or **[fetch]** (FreeBSD) — to automatically download external libraries. +- **[m4]** — needed to build GMP. +- **[awk]** — helper tool for Makefiles. +- **[patch]** — for modifying source code. +- **[cmake]** (>= 3.4.3) — needed to build `libgit2`. +- **[pkg-config]** — needed to build `libgit2` correctly, especially for proxy support. + +On Debian-based distributions (e.g. Ubuntu), you can easily install them with `apt-get`: +``` +sudo apt-get install build-essential libatomic1 python gfortran perl wget m4 cmake pkg-config +``` + +Julia uses the following external libraries, which are automatically +downloaded (or in a few cases, included in the Julia source +repository) and then compiled from source the first time you run +`make`: + +- **[LLVM]** (6.0 + [patches](https://github.com/JuliaLang/julia/tree/master/deps/patches)) — compiler infrastructure (see [note below](#llvm)). +- **[FemtoLisp]** — packaged with Julia source, and used to implement the compiler front-end. +- **[libuv]** (custom fork) — portable, high-performance event-based I/O library. +- **[OpenLibm]** — portable libm library containing elementary math functions. +- **[DSFMT]** — fast Mersenne Twister pseudorandom number generator library. +- **[OpenBLAS]** — fast, open, and maintained [basic linear algebra subprograms (BLAS)](https://en.wikipedia.org/wiki/Basic_Linear_Algebra_Subprograms) library, based on [Kazushige Goto's](https://en.wikipedia.org/wiki/Kazushige_Goto) famous [GotoBLAS](https://www.tacc.utexas.edu/research-development/tacc-software/gotoblas2) (see [note below](#blas-and-lapack)). +- **[LAPACK]** (>= 3.5) — library of linear algebra routines for solving systems of simultaneous linear equations, least-squares solutions of linear systems of equations, eigenvalue problems, and singular value problems. +- **[MKL]** (optional) – OpenBLAS and LAPACK may be replaced by Intel's MKL library. +- **[SuiteSparse]** (>= 4.1) — library of linear algebra routines for sparse matrices. +- **[PCRE]** (>= 10.00) — Perl-compatible regular expressions library. +- **[GMP]** (>= 5.0) — GNU multiple precision arithmetic library, needed for `BigInt` support. +- **[MPFR]** (>= 4.0) — GNU multiple precision floating point library, needed for arbitrary precision floating point (`BigFloat`) support. +- **[libgit2]** (>= 0.23) — Git linkable library, used by Julia's package manager. +- **[curl]** (>= 7.50) — libcurl provides download and proxy support for Julia's package manager. +- **[libssh2]** (>= 1.7) — library for SSH transport, used by libgit2 for packages with SSH remotes. +- **[mbedtls]** (>= 2.2) — library used for cryptography and transport layer security, used by libssh2 +- **[utf8proc]** (>= 2.1) — a library for processing UTF-8 encoded Unicode strings. +- **[libosxunwind]** — fork of [libunwind], a library that determines the call-chain of a program. + +[GNU make]: https://www.gnu.org/software/make +[patch]: https://www.gnu.org/software/patch +[wget]: https://www.gnu.org/software/wget +[m4]: https://www.gnu.org/software/m4 +[awk]: https://www.gnu.org/software/gawk +[gcc]: https://gcc.gnu.org +[clang]: https://clang.llvm.org +[python]: https://www.python.org/ +[gfortran]: https://gcc.gnu.org/fortran/ +[curl]: https://curl.haxx.se +[fetch]: https://www.freebsd.org/cgi/man.cgi?fetch(1) +[perl]: https://www.perl.org +[cmake]: https://www.cmake.org +[OpenLibm]: https://github.com/JuliaLang/openlibm +[DSFMT]: http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/#dSFMT +[OpenBLAS]: https://github.com/xianyi/OpenBLAS +[LAPACK]: https://www.netlib.org/lapack +[MKL]: https://software.intel.com/en-us/articles/intel-mkl +[SuiteSparse]: http://faculty.cse.tamu.edu/davis/suitesparse.html +[PCRE]: https://www.pcre.org +[LLVM]: https://www.llvm.org +[FemtoLisp]: https://github.com/JeffBezanson/femtolisp +[GMP]: https://gmplib.org +[MPFR]: https://www.mpfr.org +[libuv]: https://github.com/JuliaLang/libuv +[libgit2]: https://libgit2.org/ +[utf8proc]: https://julialang.org/utf8proc/ +[libosxunwind]: https://github.com/JuliaLang/libosxunwind +[libunwind]: https://www.nongnu.org/libunwind +[libssh2]: https://www.libssh2.org +[mbedtls]: https://tls.mbed.org/ +[pkg-config]: https://www.freedesktop.org/wiki/Software/pkg-config/ + +## Build dependencies + +If you already have one or more of these packages installed on your system, you can prevent Julia from compiling duplicates of these libraries by passing `USE_SYSTEM_...=1` to `make` or adding the line to `Make.user`. The complete list of possible flags can be found in `Make.inc`. + +Please be aware that this procedure is not officially supported, as it introduces additional variability into the installation and versioning of the dependencies, and is recommended only for system package maintainers. Unexpected compile errors may result, as the build system will do no further checking to ensure the proper packages are installed. + +### LLVM + +The most complicated dependency is LLVM, for which we require additional patches from upstream (LLVM is not backward compatible). + +For packaging Julia with LLVM, we recommend either: + - bundling a Julia-only LLVM library inside the Julia package, or + - adding the patches to the LLVM package of the distribution. + * A complete list of patches is available in `deps/llvm.mk`, and the patches themselves are in `deps/patches/`. + * The only Julia-specific patch is the lib renaming (`llvm-symver-jlprefix.patch`), which should _not_ be applied to a system LLVM. + * The remaining patches are all upstream bug fixes, and have been contributed into upstream LLVM. + +Using an unpatched or different version of LLVM will result in errors and/or poor performance. Though Julia can be built with newer LLVM versions, support for this should be regarded as experimental and not suitable for packaging. + +### libuv + +Julia uses a custom fork of libuv. It is a small dependency, and can be safely bundled in the same package as Julia, and will not conflict with the system library. Julia builds should _not_ try to use the system libuv. + +### BLAS and LAPACK + +As a high-performance numerical language, Julia should be linked to a multi-threaded BLAS and LAPACK, such as OpenBLAS or ATLAS, which will provide much better performance than the reference `libblas` implementations which may be default on some systems. + +### Intel MKL + +For a 64-bit architecture, the environment should be set up as follows: +```sh +# bash +source /path/to/intel/bin/compilervars.sh intel64 +``` +Add the following to the `Make.user` file: + + USE_INTEL_MKL = 1 + +It is highly recommended to start with a fresh clone of the Julia repository. diff --git a/doc/build/distributing.md b/doc/build/distributing.md new file mode 100644 index 0000000..dcbf0d3 --- /dev/null +++ b/doc/build/distributing.md @@ -0,0 +1,589 @@ +Notes for building binary distributions +======================================= + +These notes are for those wishing to compile a binary distribution of Julia +for distribution on various platforms. We love users spreading Julia as +far and wide as they can, trying it out on as wide an array of +operating systems and hardware configurations as possible. As each +platform has specific gotchas and processes that must be followed in +order to create a portable, working Julia distribution, we have +separated most of the notes by OS. + +Note that while the code for Julia is +[MIT-licensed, with a few exceptions](https://github.com/JuliaLang/julia/blob/master/LICENSE.md), +the distribution created by the techniques described herein will be +GPL licensed, as various dependent libraries such as `SuiteSparse` are +GPL licensed. We do hope to have a non-GPL distribution of Julia in the future. + +Versioning and Git +------------------ +The Makefile uses both the `VERSION` file and commit hashes and tags from the +git repository to generate the `base/version_git.jl` with information we use to +fill the splash screen and the `versioninfo()` output. If you for some reason +don't want to have the git repository available when building you should +pregenerate the `base/version_git.jl` file with: + + make -C base version_git.jl.phony + +Julia has lots of build dependencies where we use patched versions that has not +yet been included by the popular package managers. These dependencies will usually +be automatically downloaded when you build, but if you want to be able to build +Julia on a computer without internet access you should create a full-source-dist +archive with the special make target + + make full-source-dist + +that creates a julia-version-commit.tar.gz archive with all required dependencies. + +When compiling a tagged release in the git repository, we don't display the +branch/commit hash info in the splash screen. You can use this line to show +a release description of up to 45 characters. To set this line you have +to create a Make.user file containing: + + override TAGGED_RELEASE_BANNER = "my-package-repository build" + +Target Architectures +-------------------- + +By default, Julia optimizes its system image to the native architecture of +the build machine. This is usually not what you want when building packages, +as it will make Julia fail at startup on any machine with incompatible CPUs +(in particular older ones with more restricted instruction sets). + +We therefore recommend that you pass the `MARCH` variable when calling `make`, +setting it to the baseline target you intend to support. This will determine +the target CPU for both the Julia executable and libraries, and the system +image (the latter can also be set using `JULIA_CPU_TARGET`). Typically useful +values for x86 CPUs are `x86-64` and `core2` (for 64-bit builds) and +`pentium4` (for 32-bit builds). Unfortunately, CPUs older than Pentium 4 +are currently not supported (see +[this issue](https://github.com/JuliaLang/julia/issues/7185)). + +The full list of CPU targets supported by LLVM can be obtained by running +`llc -mattr=help`. + +Linux +----- + +On Linux, `make binary-dist` creates a tarball that contains a fully +functional Julia installation. If you wish to create a distribution +package such as a `.deb`, or `.rpm`, some extra effort is needed. See the +[julia-debian](https://github.com/staticfloat/julia-debian) repository +for an example of what metadata is needed for creating `.deb` packages +for Debian and Ubuntu-based systems. See the +[Fedora package](https://admin.fedoraproject.org/pkgdb/package/julia/) +for RPM-based distributions. Although we have not yet experimented +with it, [Alien](https://wiki.debian.org/Alien) could be used to +generate Julia packages for various Linux distributions. + +Julia supports overriding standard installation directories via `prefix` +and other environment variables you can pass when calling `make` and +`make install`. See Make.inc for their list. `DESTDIR` can also be used +to force the installation into a temporary directory. + +By default, Julia loads `$prefix/etc/julia/startup.jl` as an +installation-wide initialization file. This file can be used by +distribution managers to set up custom paths or initialization code. +For Linux distribution packages, if `$prefix` is +set to `/usr`, there is no `/usr/etc` to look into. This requires +the path to Julia's private `etc` directory to be changed. This can +be done via the `sysconfdir` make variable when building. Simply +pass `sysconfdir=/etc` to `make` when building and Julia will first +check `/etc/julia/startup.jl` before trying +`$prefix/etc/julia/startup.jl`. + +OS X +---- + +To create a binary distribution on OSX, build Julia first, then cd to +`contrib/mac/app`, and run `make` with the same makevars that were used +with `make` when building Julia proper. This will then +create a `.dmg` file in the `contrib/mac/app` directory holding a +completely self-contained Julia.app. + +Alternatively, Julia may be built as a framework by invoking `make` with the +`darwinframework` target and `DARWIN_FRAMEWORK=1` set. For example, +`make DARWIN_FRAMEWORK=1 darwinframework`. + +Windows +------- + +The best supported method of creating a Julia distribution on Windows +is to cross-compile from a Linux distribution such as Ubuntu. In-depth +compilation instructions [are +available](https://github.com/JuliaLang/julia/blob/master/README.windows.md). +However the important steps for redistribution are to ensure to `make +win-extras` in between `make` and `make binary-dist`. After that process is +completed, the `.zip` file created in the head Julia directory will +hold a completely self-contained Julia. + +Notes on BLAS and LAPACK +------------------------ + +Julia builds OpenBLAS by default, which includes the BLAS and LAPACK +libraries. On 32-bit architectures, Julia builds OpenBLAS to use +32-bit integers, while on 64-bit architectures, Julia builds OpenBLAS +to use 64-bit integers (ILP64). It is essential that all Julia functions +that call BLAS and LAPACK API routines use integers of the correct width. + +Most BLAS and LAPACK distributions provided on linux distributions, +and even commercial implementations ship libraries that use 32-bit +APIs. In many cases, a 64-bit API is provided as a separate library. + +When using vendor provided or OS provided libraries, a `make` option +called `USE_BLAS64` is available as part of the Julia build. When doing +`make USE_BLAS64=0`, Julia will call BLAS and LAPACK assuming a 32-bit +API, where all integers are 32-bit wide, even on a 64-bit architecture. + +Other libraries that Julia uses, such as SuiteSparse also +use BLAS and LAPACK internally. The APIs need to be consistent across +all libraries that depend on BLAS and LAPACK. The Julia build process +will build all these libraries correctly, but when overriding defaults +and using system provided libraries, this consistency must be ensured. + +Also note that Linux distributions sometimes ship several versions of +OpenBLAS, some of which enable multithreading, and others only working +in a serial fashion. For example, in Fedora, `libopenblasp.so` is threaded, +but `libopenblas.so` is not. We recommend using the former for optimal +performance. To choose an OpenBLAS library whose name is different from +the default `libopenblas.so`, pass `LIBBLAS=-l$(YOURBLAS)` and +`LIBBLASNAME=lib$(YOURBLAS)` to `make`, replacing `$(YOURBLAS)` with the +name of your library. You can also add `.so.0` to the name of the library +if you want your package to work without requiring the unversioned `.so` +symlink. + +Finally, OpenBLAS includes its own optimized version of LAPACK. If you +set `USE_SYSTEM_BLAS=1` and `USE_SYSTEM_LAPACK=1`, you should also set +`LIBLAPACK=-l$(YOURBLAS)` and `LIBLAPACKNAME=lib$(YOURBLAS)`. Else, the +reference LAPACK will be used and performance will typically be much lower. + +Compilation scripts +=================== + +The [julia-nightly-packaging](https://github.com/staticfloat/julia-nightly-packaging) +repository contains multiple example scripts to ease the creation of +binary packages. It also includes miscellaneous tools to do things such as +fetching the last good commit that passed the +[Travis](https://travis-ci.org/JuliaLang/julia/builds) tests. + + +# Point releasing 101 + +Creating a point/patch release consists of several distinct steps. + +## Backporting commits + +Some pull requests are labeled "backport pending x.y", e.g. "backport pending 0.6". +This designates that the next subsequent release tagged from the release-x.y branch +should include the commit(s) in that pull request. +Once the pull request is merged into master, each of the commits should be [cherry +picked](https://git-scm.com/docs/git-cherry-pick) to a dedicated branch that will +ultimately be merged into release-x.y. + +### Creating a backports branch + +First, create a new branch based on release-x.y. +The typical convention for Julia branches is to prefix the branch name with your +initials if it's intended to be a personal branch. +For the sake of example, we'll say that the author of the branch is Jane Smith. + +``` +git fetch origin +git checkout release-x.y +git rebase origin/release-x.y +git checkout -b js/backport-x.y +``` + +This ensures that your local copy of release-x.y is up to date with origin before +you create a new branch from it. + +### Cherry picking commits + +Now we do the actual backporting. +Find all merged pull requests labeled "backport pending x.y" in the GitHub web UI. +For each of these, scroll to the bottom where it says "someperson merged commit +`123abc` into `master` XX minutes ago". +Note that the commit name is a link; if you click it, you'll be shown the contents +of the commit. +If this page shows that `123abc` is a merge commit, go back to the PR page---we +don't want merge commits, we want the actual commits. +However, if this does not show a merge commit, it means that the PR was squash-merged. +In that case, use the git SHA of the commit, listed next to commit on this page. + +Once you have the SHA of the commit, cherry-pick it onto the backporting branch: + +``` +git cherry-pick -x -e +``` + +There may be conflicts which need to be resolved manually. +Once conflicts are resolved (if applicable), add a reference to the GitHub pull +request that introduced the commit in the body of the commit message. + +After all of the relevant commits are on the backports branch, push the branch to +GitHub. + +## Checking for performance regressions + +Point releases should never introduce performance regressions. +Luckily the Julia benchmarking bot, Nanosoldier, can run benchmarks against any +branch, not just master. +In this case we want to check the benchmark results of js/backport-x.y against +release-x.y. +To do this, awaken the Nanosoldier from his robotic slumber using a comment on +your backporting pull request: + +```markdown +@nanosoldier `runbenchmarks(ALL, vs=":release-x.y")` +``` + +This will run all registered benchmarks on release-x.y and js/backport-x.y and +produce a summary of results, marking all improvements and regressions. + +If Nanosoldier finds any regressions, try verifying locally and rerun Nanosoldier +if necessary. +If the regressions are deemed to be real rather than just noise, you'll have to +find a commit on master to backport that fixes it if one exists, otherwise you +should determine what caused the regression and submit a patch (or get someone who +knows the code to submit a patch) to master, then backport the commit once that's +merged. +(Or submit a patch directly to the backport branch if appropriate.) + +## Building test binaries + +After the backport PR has been merged into the `release-x.y` branch, update your local +clone of Julia, then get the SHA of the branch using + +``` +git rev-parse origin/release-x.y +``` + +Keep that handy, as it's what you'll enter in the "Revision" field in the buildbot UI. + +For now, all you need are binaries for Linux x86-64, since this is what's used for +running PackageEvaluator. +Go to https://buildog.julialang.org, submit a job for `nuke_linux64`, then queue up a +job for `package_linux64`, providing the SHA as the revision. +When the packaging job completes, it will upload the binary to the `julialang2` bucket +on AWS. +Retrieve the URL, as it will be used for PackageEvaluator. + +## Checking for package breakages + +Point releases should never break packages, with the possible exception of packages +that are doing some seriously questionable hacks using Base internals that are +not intended to be user-facing. +(In those cases, maybe have a word with the package author.) + +Checking whether changes made in the forthcoming new version will break packages can +be accomplished using [PackageEvaluator](https://github.com/JuliaCI/PackageEvaluator.jl), +often called "PkgEval" for short. +PkgEval is what populates the status badges on GitHub repos and on pkg.julialang.org. +It typically runs on one of the non-benchmarking nodes of Nanosoldier and uses Vagrant +to perform its duties in separate, parallel VirtualBox virtual machines. + +### Setting up PackageEvaluator + +Clone PackageEvaluator and create a branch called `backport-x.y.z`, and check it out. +Note that the required changes are a little hacky and confusing, and hopefully that will +be addressed in a future version of PackageEvaluator. +The changes to make will be modeled off of +[this commit](https://github.com/JuliaCI/PackageEvaluator.jl/commit/5ba6a3b000e7a3793391d16f695c8704b91d6016). + +The setup script takes its first argument as the version of Julia to run and the second +as the range of package names (AK for packages named A-K, LZ for L-Z). +The basic idea is that we're going to tweak that a bit to run only two versions of Julia, +the current x.y release and our backport version, each with three ranges of packages. + +In the linked diff, we're saying that if the second argument is LZ, use the binaries +built from our backport branch, otherwise (AK) use the release binaries. +Then we're using the first argument to run a section of the package list: A-F for input +0.4, G-N for 0.5, and O-Z for 0.6. + +### Running PackageEvaluator + +To run PkgEval, find a hefty enough machine (such as Nanosoldier node 1), then run + +``` +git clone https://github.com/JuliaCI/PackageEvaluator.jl.git +cd PackageEvaluator.jl/scripts +git checkout backport-x.y.z +./runvagrant.sh +``` + +This produces some folders in the scripts/ directory. +The folder names and their contents are decoded below: + +| Folder name | Julia version | Package range | +| :---------: | :-----------: | :-----------: | +| 0.4AK | Release | A-F | +| 0.4LZ | Backport | A-F | +| 0.5AK | Release | G-N | +| 0.5LZ | Backport | G-N | +| 0.6AK | Release | O-Z | +| 0.6LZ | Backport | O-Z | + +### Investigating results + +Once that's done, you can use `./summary.sh` from that same directory to produce +a summary report of the findings. +We'll do so for each of the folders to aggregate overall results by version. + +``` +./summary.sh 0.4AK/*.json > summary_release.txt +./summary.sh 0.5AK/*.json >> summary_release.txt +./summary.sh 0.6AK/*.json >> summary_release.txt +./summary.sh 0.4LZ/*.json > summary_backport.txt +./summary.sh 0.5LZ/*.json >> summary_backport.txt +./summary.sh 0.6LZ/*.json >> summary_backport.txt +``` + +Now we have two files, `summary_release.txt` and `summary_backport.txt`, containing +the PackageEvaluator test results (pass/fail) for each package for the two versions. + +To make these easier to ingest into a Julia, we'll convert them into CSV files then +use the DataFrames package to process the results. +To convert to CSV, copy each .txt file to a corresponding .csv file, then enter Vim +and execute `ggVGI"` then `:%s/\.json /",/g`. +(You don't have to use Vim; this just is one way to do it.) +Now process the results with Julia code similar to the following. + +```julia +using DataFrames + +release = readtable("summary_release.csv", header=false, names=[:package, :release]) +backport = readtable("summary_backport.csv", header=false, names=[:package, :backport]) + +results = join(release, backport, on=:package, kind=:outer) + +for result in eachrow(results) + a = result[:release] + b = result[:backport] + if (isna(a) && !isna(b)) || (isna(b) && !isna(a)) + color = :yellow + elseif a != b && occursin("pass", b) + color = :green + elseif a != b + color = :red + else + continue + end + printstyled(result[:package], ": Release ", a, " -> Backport ", b, "\n", color=color) +end +``` + +This will write color-coded lines to `stdout`. +All lines in red must be investigated as they signify potential breakages caused by the +backport version. +Lines in yellow should be looked into since it means a package ran on one version but +not on the other for some reason. +If you find that your backported branch is causing breakages, use `git bisect` to +identify the problematic commits, `git revert` those commits, and repeat the process. + +## Merging backports into the release branch + +After you have ensured that + +* the backported commits pass all of Julia's unit tests, +* there are no performance regressions introduced by the backported commits as compared + to the release branch, and +* the backported commits do not break any registered packages, + +then the backport branch is ready to be merged into release-x.y. +Once it's merged, go through and remove the "backport pending x.y" label from all pull +requests containing the commits that have been backported. +Do not remove the label from PRs that have not been backported. + +The release-x.y branch should now contain all of the new commits. +The last thing we want to do to the branch is to adjust the version number. +To do this, submit a PR against release-x.y that edits the VERSION file to remove `-pre` +from the version number. +Once that's merged, we're ready to tag. + +## Tagging the release + +It's time! +Check out the release-x.y branch and make sure that your local copy of the branch is +up to date with the remote branch. +At the command line, run + +``` +git tag v$(cat VERSION) +git push --tags +``` + +This creates the tag locally and pushes it to GitHub. + +After tagging the release, submit another PR to release-x.y to bump the patch number +and add `-pre` back to the end. +This denotes that the branch state reflects a prerelease version of the next point +release in the x.y series. + +Follow the remaining directions in the Makefile. + +## Signing binaries + +Some of these steps will require secure passwords. +To obtain the appropriate passwords, contact Elliot Saba (staticfloat) or Alex Arslan +(ararslan). +Note that code signing for each platform must be performed on that platform (e.g. Windows +signing must be done on Windows, etc.). + +### Linux + +Code signing must be done manually on Linux, but it's quite simple. +First obtain the file `julia.key` from the CodeSigning folder in the `juliasecure` AWS +bucket. +Add this to your GnuPG keyring using + +``` +gpg --import julia.key +``` + +This will require entering a password that you must obtain from Elliot or Alex. +Next, set the trust level for the key to maximum. +Start by entering a `gpg` session: + +``` +gpg --edit-key julia +``` + +At the prompt, type `trust`, then when asked for a trust level, provide the maximum +available (likely 5). +Exit GnuPG. + +Now, for each of the Linux tarballs that were built on the buildbots, enter + +``` +gpg -u julia --armor --detach-sig julia-x.y.z-linux-.tar.gz +``` + +This will produce a corresponding .asc file for each tarball. +And that's it! + +### macOS + +Code signing should happen automatically on the macOS buildbots. +However, it's important to verify that it was successful. +On a system or virtual machine running macOS, download the .dmg file that was built on +the buildbots. +For the sake of example, say that the .dmg file is called `julia-x.y.z-osx.dmg`. +Run + +``` +mkdir ./jlmnt +hdiutil mount -readonly -mountpoint ./jlmnt julia-x.y.z-osx.dmg +codesign -v jlmnt/Julia-x.y.app +``` + +Be sure to note the name of the mounted disk listed when mounting! +For the sake of example, we'll assume this is `disk3`. +If the code signing verification exited successfully, there will be no output from the +`codesign` step. +If it was indeed successful, you can detach the .dmg now: + +``` +hdiutil eject /dev/disk3 +rm -rf ./jlmnt +``` + +If you get a message like + +> Julia-x.y.app: code object is not signed at all + +then you'll need to sign manually. + +To sign manually, first retrieve the OS X certificates from the CodeSigning folder +in the `juliasecure` bucket on AWS. +Add the .p12 file to your keychain using Keychain.app. +Ask Elliot Saba (staticfloat) or Alex Arslan (ararslan) for the password for the key. +Now run + +``` +hdiutil convert julia-x.y.z-osx.dmg -format UDRW -o julia-x.y.z-osx_writable.dmg +mkdir ./jlmnt +hdiutil mount -mountpoint julia-x.y.z-osx_writable.dmg +codesign -s "AFB379C0B4CBD9DB9A762797FC2AB5460A2B0DBE" --deep jlmnt/Julia-x.y.app +``` + +This may fail with a message like + +> Julia-x.y.app: resource fork, Finder information, or similar detritus not allowed + +If that's the case, you'll need to remove extraneous attributes: + +``` +xattr -cr jlmnt/Julia-x.y.app +``` + +Then retry code signing. +If that produces no errors, retry verification. +If all is now well, unmount the writable .dmg and convert it back to read-only: + +``` +hdiutil eject /dev/disk3 +rm -rf ./jlmnt +hdiutil convert julia-x.y.z-osx_writable.dmg -format UDZO -o julia-x.y.z-osx_fixed.dmg +``` + +Verify that the resulting .dmg is in fact fixed by double clicking it. +If everything looks good, eject it then drop the `_fixed` suffix from the name. +And that's it! + +### Windows + +Signing must be performed manually on Windows. +First obtain the Windows 10 SDK, which contains the necessary signing utilities, from +the Microsoft website. +We need the `SignTool` utility which should have been installed somewhere like +`C:\Program Files (x86)\Windows Kits\10\App Certification Kit`. +Grab the Windows certificate files from CodeSigning on `juliasecure` and put them +in the same directory as the executables. +Open a Windows CMD window, `cd` to where all the files are, and run + +``` +set PATH=%PATH%;C:\Program Files (x86)\Windows Kits\10\App Certification Kit; +signtool sign /f julia-windows-code-sign_2017.p12 /p "PASSWORD" ^ + /t http://timestamp.verisign.com/scripts/timstamp.dll ^ + /v julia-x.y.z-win32.exe +``` + +Note that `^` is a line continuation character in Windows CMD and `PASSWORD` is a +placeholder for the password for this certificate. +As usual, contact Elliot or Alex for passwords. +If there are no errors, we're all good! + +## Uploading binaries + +Now that everything is signed, we need to upload the binaries to AWS. +You can use a program like Cyberduck or the `aws` command line utility. +The binaries should go in the `julialang2` bucket in the appropriate folders. +For example, Linux x86-64 goes in `julialang2/bin/linux/x.y`. +Be sure to delete the current `julia-x.y-latest-linux-.tar.gz` file and replace +it with a duplicate of `julia-x.y.z-linux-.tar.gz`. + +We also need to upload the checksums for everything we've built, including the source +tarballs and all release binaries. +This is simple: + +``` +shasum -a 256 julia-x.y.z* | grep -v -e sha256 -e md5 -e asc > julia-x.y.z.sha256 +md5sum julia-x.y.z* | grep -v -e sha256 -e md5 -e asc > julia-x.y.z.md5 +``` + +Note that if you're running those commands on macOS, you'll get very slightly different +output, which can be reformatted by looking at an existing file. +Mac users will also need to use `md5 -r` instead of `md5sum`. +Upload the .md5 and .sha256 files to `julialang2/bin/checksums` on AWS. + +Ensure that the permissions on AWS for all uploaded files are set to "Everyone: READ." + +For each file we've uploaded, we need to purge the Fastly cache so that the links on +the website point to the updated files. +As an example: + +``` +curl -X PURGE https://julialang-s3.julialang.org/bin/checksums/julia-x.y.z.sha256 +``` + +Sometimes this isn't necessary but it's good to do anyway. diff --git a/doc/build/freebsd.md b/doc/build/freebsd.md new file mode 100644 index 0000000..51b16d5 --- /dev/null +++ b/doc/build/freebsd.md @@ -0,0 +1,19 @@ +## FreeBSD + +Clang is the default compiler on FreeBSD 11.0-RELEASE and above. +The remaining build tools are available from the Ports Collection, and can be installed using +`pkg install git gcc gmake cmake pkgconf`. +To build Julia, simply run `gmake`. +(Note that `gmake` must be used rather than `make`, since `make` on FreeBSD corresponds to the incompatible BSD Make rather than GNU Make.) + +As mentioned above, it is important to note that the `USE_SYSTEM_*` flags should be used with caution on FreeBSD. +This is because many system libraries, and even libraries from the Ports Collection, link to the system's `libgcc_s.so.1`, +or to another library which links to the system `libgcc_s`. +This library declares its GCC version to be 4.6, which is too old to build Julia, and conflicts with other libraries when linking. +Thus it is highly recommended to simply allow Julia to build all of its dependencies. +If you do choose to use the `USE_SYSTEM_*` flags, note that `/usr/local` is not on the compiler path by default, so you may need +to add `LDFLAGS=-L/usr/local/lib` and `CPPFLAGS=-I/usr/local/include` to your `Make.user`, though doing so may interfere with +other dependencies. + +Note that the x86 architecture does not support threading due to lack of compiler runtime library support, so you may need to +set `JULIA_THREADS=0` in your `Make.user` if you're on a 32-bit system. diff --git a/doc/build/linux.md b/doc/build/linux.md new file mode 100644 index 0000000..4aa85c4 --- /dev/null +++ b/doc/build/linux.md @@ -0,0 +1,22 @@ +## Linux + +* GCC version 4.7 or later is required to build Julia. +* To use external shared libraries not in the system library search path, set `USE_SYSTEM_XXX=1` and `LDFLAGS=-Wl,-rpath,/path/to/dir/contains/libXXX.so` in `Make.user`. +* Instead of setting `LDFLAGS`, putting the library directory into the environment variable `LD_LIBRARY_PATH` (at both compile and run time) also works. +* The `USE_SYSTEM_*` flags should be used with caution. These are meant only for troubleshooting, porting, and packaging, where package maintainers work closely with the Julia developers to make sure that Julia is built correctly. Production use cases should use the officially provided binaries. Issues arising from the use of these flags will generally not be accepted. +* See also the [external dependencies](#required-build-tools-and-external-libraries). + +### Architecture Customization + +Julia can be built for a non-generic architecture by configuring the `ARCH` Makefile variable in a `Make.user` file. See the appropriate section of `Make.inc` for additional customization options, such as `MARCH` and `JULIA_CPU_TARGET`. + +For example, to build for Pentium 4, set `MARCH=pentium4` and install the necessary system libraries for linking. On Ubuntu, these may include lib32gfortran-6-dev, lib32gcc1, and lib32stdc++6, among others. + +You can also set `MARCH=native` in `Make.user` for a maximum-performance build customized for the current machine CPU. + +### Linux Build Troubleshooting + + Problem | Possible Solution +------------------------|--------------------- + OpenBLAS build failure | Set one of the following build options in `Make.user` and build again:
  • `OPENBLAS_TARGET_ARCH=BARCELONA` (AMD CPUs) or `OPENBLAS_TARGET_ARCH=NEHALEM` (Intel CPUs)
      Set `OPENBLAS_DYNAMIC_ARCH = 0` to disable compiling multiple architectures in a single binary.
  • `OPENBLAS_NO_AVX2 = 1` disables AVX2 instructions, allowing OpenBLAS to compile with `OPENBLAS_DYNAMIC_ARCH = 1` using old versions of binutils
  • `USE_SYSTEM_BLAS=1` uses the system provided `libblas`
    • Set `LIBBLAS=-lopenblas` and `LIBBLASNAME=libopenblas` to force the use of the system provided OpenBLAS when multiple BLAS versions are installed.

If you get an error that looks like ```../kernel/x86_64/dgemm_kernel_4x4_haswell.S:1709: Error: no such instruction: `vpermpd $ 0xb1,%ymm0,%ymm0'```, then you need to set `OPENBLAS_DYNAMIC_ARCH = 0` or `OPENBLAS_NO_AVX2 = 1`, or you need a newer version of `binutils` (2.18 or newer). ([Issue #7653](https://github.com/JuliaLang/julia/issues/7653))

If the linker cannot find `gfortran` and you get an error like `julia /usr/bin/x86_64-linux-gnu-ld: cannot find -lgfortran`, check the path with `gfortran -print-file-name=libgfortran.so` and use the output to export something similar to this: `export LDFLAGS=-L/usr/lib/gcc/x86_64-linux-gnu/8/`. See [Issue #6150](https://github.com/JuliaLang/julia/issues/6150#issuecomment-37546803).

+Illegal Instruction error | Check if your CPU supports AVX while your OS does not (e.g. through virtualization, as described in [this issue](https://github.com/JuliaLang/julia/issues/3263)). diff --git a/doc/build/macos.md b/doc/build/macos.md new file mode 100644 index 0000000..94f74af --- /dev/null +++ b/doc/build/macos.md @@ -0,0 +1,7 @@ +## macOS + +You need to have the current Xcode command line utilities installed: run `xcode-select --install` in the terminal. +You will need to rerun this terminal command after each macOS update, otherwise you may run into errors involving missing libraries or headers. +You will also need a 64-bit gfortran to compile Julia dependencies. The gfortran-4.7 (and newer) compilers in Homebrew work for building Julia. + +If you have set `LD_LIBRARY_PATH` or `DYLD_LIBRARY_PATH` in your `.bashrc` or equivalent, Julia may be unable to find various libraries that come bundled with it. These environment variables need to be unset for Julia to work. diff --git a/doc/build/windows.md b/doc/build/windows.md new file mode 100644 index 0000000..ae27643 --- /dev/null +++ b/doc/build/windows.md @@ -0,0 +1,316 @@ +# Julia on Windows + +This file describes how to install, or build, and use Julia on Windows. + +For more general information about Julia, please see the +[main README](https://github.com/JuliaLang/julia/blob/master/README.md) +or the [documentation](https://docs.julialang.org/). + + +## General Information for Windows + + +### Unicode font support + +The built-in Windows fonts have rather poor coverage of the Unicode character +space. The free [`DejaVu Sans Mono`](https://dejavu-fonts.github.io/) font can be used +as a replacement font in the Windows console. Since Windows 2000, simply +downloading the font and installing it is insufficient, since Windows keeps a +list of approved fonts in the registry. + +Instructions for adding fonts to the terminal are available at +[this answer on superuser.com](https://superuser.com/a/5079) + +Additionally, rather than sticking with the default command prompt, you may want +to use a different terminal emulator program, such as +[Conemu](https://conemu.github.io/) or [Mintty]( +https://github.com/mintty/mintty) (note that running Julia on Mintty needs a +copy of `stty.exe` in your `%PATH%` to work properly). Alternatively, you may +prefer the features of a more full-function IDE, such as [Juno](http://junolab.org), +[Sublime-IJulia](https://github.com/quinnj/Sublime-IJulia), or +[IJulia](https://github.com/JuliaLang/IJulia.jl). + + +### Line endings + +Julia uses binary-mode files exclusively. Unlike many other Windows programs, +if you write `\n` to a file, you get a `\n` in the file, not some other bit +pattern. This matches the behavior exhibited by other operating systems. If +you have installed Git for Windows, it is suggested, but not required, that you +configure your system Git to use the same convention: +```sh +git config --global core.eol lf +git config --global core.autocrlf input +``` +or edit `%USERPROFILE%\.gitconfig` and add/edit the lines: +``` +[core] + eol = lf + autocrlf = input +``` + +## Binary distribution + +Julia runs on Windows 7 and later. +Both the 32-bit and 64-bit versions are supported. +The 32-bit (i686) binary will run on either a 32-bit and 64-bit operating system. +The 64-bit (x86_64) binary will only run on 64-bit Windows and will otherwise refuse to launch. + + 1. [Download](https://julialang.org/downloads) the latest version of Julia. + Extract the binary to a reasonable destination folder, e.g. `C:\julia`. + + 2. Double-click the `julia` shortcut to launch Julia. + + 3. Julia's home directory is the location pointed to by the Windows environment + variable `%HOME%`: this directory is for instance where the startup file + `.julia/config/startup.jl` resides. `%HOMEDRIVE%\%HOMEPATH%` is used as a fallback if + `%HOME%` is not defined. + +## Source distribution + +### Supported build platforms + + - Windows 10: supported (32 and 64 bits) + - Windows 8: supported (32 and 64 bits) + - Windows 7: supported (32 and 64 bits) + +### Cygwin-to-MinGW cross-compiling + +The recommended way of compiling Julia from source on Windows is by cross +compiling from [Cygwin](https://www.cygwin.com), using versions of the +MinGW-w64 compilers available through Cygwin's package manager. + + 1. Download and run Cygwin setup for [32 bit](https://cygwin.com/setup-x86.exe) + or [64 bit](https://cygwin.com/setup-x86_64.exe). Note, that you can compile + either 32 or 64 bit Julia from either 32 or 64 bit Cygwin. 64 bit Cygwin + has a slightly smaller but often more up-to-date selection of packages. + + Advanced: you may skip steps 2-4 by running: + + setup-x86_64.exe -s -q -P cmake,gcc-g++,git,make,patch,curl,m4,python,p7zip,mingw64-i686-gcc-g++,mingw64-i686-gcc-fortran,mingw64-x86_64-gcc-g++,mingw64-x86_64-gcc-fortran + :: replace with a site from https://cygwin.com/mirrors.html + :: or run setup manually first and select a mirror + + 2. Select installation location and download mirror. + + 3. At the '*Select Packages'* step, select the following: + + 1. From the *Devel* category: `cmake`, `gcc-g++`, `git`, `make`, `patch` + 2. From the *Net* category: `curl` + 3. From *Interpreters* (or *Python*) category: `m4`, `python` + 4. From the *Archive* category: `p7zip` + 5. For 32 bit Julia, and also from the *Devel* category: + `mingw64-i686-gcc-g++` and `mingw64-i686-gcc-fortran` + 6. For 64 bit Julia, and also from the *Devel* category: + `mingw64-x86_64-gcc-g++` and `mingw64-x86_64-gcc-fortran` + + 4. At the *'Resolving Dependencies'* step, be sure to leave *'Select required + packages (RECOMMENDED)'* enabled. + + 5. Allow Cygwin installation to finish, then start from the installed shortcut + a *'Cygwin Terminal'*, or *'Cygwin64 Terminal'*, respectively. + + 6. Build Julia and its dependencies from source: + + 1. Get the Julia sources + ```sh + git clone https://github.com/JuliaLang/julia.git + cd julia + ``` + Tip: If you get an `error: cannot fork() for fetch-pack: Resource + temporarily unavailable` from git, add `alias git="env PATH=/usr/bin git"` + to `~/.bashrc` and restart Cygwin. + + 2. Set the `XC_HOST` variable in `Make.user` to indicate MinGW-w64 cross + compilation + ```sh + echo 'XC_HOST = i686-w64-mingw32' > Make.user # for 32 bit Julia + # or + echo 'XC_HOST = x86_64-w64-mingw32' > Make.user # for 64 bit Julia + ``` + + 3. Start the build + ```sh + make -j 4 # Adjust the number of threads (4) to match your build environment. + ``` + + + > Protip: build both! + > ```sh + > make O=julia-win32 configure + > make O=julia-win64 configure + > echo 'XC_HOST = i686-w64-mingw32' > julia-win32/Make.user + > echo 'XC_HOST = x86_64-w64-mingw32' > julia-win64/Make.user + > echo 'ifeq ($(BUILDROOT),$(JULIAHOME)) + > $(error "in-tree build disabled") + > endif' >> Make.user + > make -C julia-win32 # build for Windows x86 in julia-win32 folder + > make -C julia-win64 # build for Windows x86-64 in julia-win64 folder + > ``` + + 7. Run Julia using the Julia executables directly + ```sh + usr/bin/julia.exe + usr/bin/julia-debug.exe + ``` + +### Compiling with MinGW/MSYS2 + +Compiling Julia from source using [MSYS2](https://msys2.github.io) has worked +in the past but is not actively supported. Pull requests to restore support +would be welcome. See a [past version of this file]( +https://github.com/JuliaLang/julia/blob/v0.6.0/README.windows.md) +for the former instructions for compiling using MSYS2. + + +### Cross-compiling from Unix + +You can also use MinGW-w64 cross compilers to build a Windows version of Julia from +Linux, Mac, or the Windows Subsystem for Linux (WSL). + +For maximum compatibility with packages that use [WinRPM.jl]( +https://github.com/JuliaLang/WinRPM.jl) for binary dependencies on Windows, it +is recommended that you use OpenSUSE 42.2 for cross-compiling a Windows build +of Julia. If you use a different Linux distribution or OS X, install +[Vagrant](https://www.vagrantup.com/downloads.html) and use the following `Vagrantfile`: + +``` +# Vagrantfile for MinGW-w64 cross-compilation of Julia + +$script = <