From f16f87e6482b04fdfdf563c1df9a672fc5b4b8b0 Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Sun, 25 Oct 2020 14:04:33 +0000 Subject: [PATCH] Import julia_1.5.2+dfsg.orig.tar.xz [dgit import orig julia_1.5.2+dfsg.orig.tar.xz] --- .appveyor.yml | 65 + .devcontainer/Dockerfile | 3 + .devcontainer/devcontainer.json | 8 + .gitattributes | 2 + .gitignore | 33 + .mailmap | 259 + .travis.yml | 153 + CITATION.bib | 75 + CONTRIBUTING.md | 310 + HISTORY.md | 5468 ++++++++++++ LICENSE.md | 82 + Make.inc | 1495 ++++ Makefile | 618 ++ NEWS.md | 298 + README.md | 150 + VERSION | 1 + base/.gitignore | 9 + base/Base.jl | 427 + base/Enums.jl | 210 + base/Makefile | 243 + base/abstractarray.jl | 2322 +++++ base/abstractarraymath.jl | 502 ++ base/abstractdict.jl | 574 ++ base/abstractset.jl | 425 + base/accumulate.jl | 455 + base/array.jl | 2567 ++++++ base/arraymath.jl | 303 + base/arrayshow.jl | 537 ++ base/asyncevent.jl | 299 + base/asyncmap.jl | 421 + base/atomics.jl | 461 + base/baseext.jl | 35 + base/bitarray.jl | 1835 ++++ base/bitset.jl | 426 + base/bool.jl | 116 + base/boot.jl | 744 ++ base/broadcast.jl | 1267 +++ base/c.jl | 702 ++ base/cartesian.jl | 404 + base/channels.jl | 458 + base/char.jl | 316 + base/checked.jl | 352 + base/client.jl | 514 ++ base/cmd.jl | 390 + base/combinatorics.jl | 343 + base/compiler/abstractinterpretation.jl | 1350 +++ base/compiler/bootstrap.jl | 33 + base/compiler/compiler.jl | 117 + base/compiler/inferenceresult.jl | 173 + base/compiler/inferencestate.jl | 294 + base/compiler/optimize.jl | 452 + base/compiler/params.jl | 72 + base/compiler/ssair/domtree.jl | 327 + base/compiler/ssair/driver.jl | 155 + base/compiler/ssair/inlining.jl | 1330 +++ base/compiler/ssair/ir.jl | 1316 +++ base/compiler/ssair/legacy.jl | 100 + base/compiler/ssair/passes.jl | 1155 +++ base/compiler/ssair/queries.jl | 87 + base/compiler/ssair/show.jl | 753 ++ base/compiler/ssair/slot2ssa.jl | 872 ++ base/compiler/ssair/verify.jl | 221 + base/compiler/tfuncs.jl | 1556 ++++ base/compiler/typeinfer.jl | 645 ++ base/compiler/typelattice.jl | 328 + base/compiler/typelimits.jl | 508 ++ base/compiler/typeutils.jl | 178 + base/compiler/utilities.jl | 248 + base/compiler/validation.jl | 237 + base/complex.jl | 1030 +++ base/condition.jl | 168 + base/coreio.jl | 32 + base/ctypes.jl | 115 + base/deepcopy.jl | 128 + base/deprecated.jl | 212 + base/dict.jl | 791 ++ base/div.jl | 280 + base/docs/Docs.jl | 623 ++ base/docs/basedocs.jl | 2512 ++++++ base/docs/bindings.jl | 47 + base/docs/core.jl | 33 + base/docs/utils.jl | 93 + base/download.jl | 104 + base/env.jl | 168 + base/error.jl | 303 + base/errorshow.jl | 750 ++ base/essentials.jl | 858 ++ base/experimental.jl | 215 + base/exports.jl | 1000 +++ base/expr.jl | 432 + base/fastmath.jl | 383 + base/file.jl | 1016 +++ base/filesystem.jl | 248 + base/float.jl | 926 ++ base/floatfuncs.jl | 345 + base/gcutils.jl | 165 + base/generator.jl | 128 + base/gmp.jl | 746 ++ 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 | 216 + base/hashing.jl | 77 + base/hashing2.jl | 243 + base/iddict.jl | 162 + base/idset.jl | 35 + base/indices.jl | 489 ++ base/initdefs.jl | 350 + base/int.jl | 958 +++ base/intfuncs.jl | 960 +++ base/io.jl | 1138 +++ base/iobuffer.jl | 526 ++ base/iostream.jl | 534 ++ base/irrationals.jl | 208 + base/iterators.jl | 1318 +++ base/libc.jl | 403 + base/libuv.jl | 147 + base/linked_list.jl | 151 + base/loading.jl | 1536 ++++ base/lock.jl | 340 + base/locks-mt.jl | 90 + base/logging.jl | 573 ++ base/math.jl | 1201 +++ base/mathconstants.jl | 98 + base/meta.jl | 353 + base/methodshow.jl | 431 + base/missing.jl | 417 + base/mpfr.jl | 1046 +++ base/multidimensional.jl | 1789 ++++ base/multimedia.jl | 417 + base/multinverses.jl | 159 + base/namedtuple.jl | 383 + base/ntuple.jl | 69 + base/number.jl | 320 + base/operators.jl | 1178 +++ base/options.jl | 85 + base/ordering.jl | 103 + base/osutils.jl | 38 + base/pair.jl | 75 + base/parse.jl | 382 + base/path.jl | 522 ++ base/pcre.jl | 237 + base/permuteddimsarray.jl | 263 + base/pointer.jl | 164 + base/process.jl | 644 ++ base/promotion.jl | 413 + base/range.jl | 1110 +++ base/rational.jl | 487 ++ base/reduce.jl | 888 ++ base/reducedim.jl | 952 +++ base/reflection.jl | 1383 +++ base/refpointer.jl | 156 + base/refvalue.jl | 33 + base/regex.jl | 729 ++ base/reinterpretarray.jl | 328 + base/reshapedarray.jl | 293 + base/rounding.jl | 249 + base/ryu/LICENSE.md | 25 + base/ryu/Ryu.jl | 130 + base/ryu/exp.jl | 264 + base/ryu/fixed.jl | 210 + base/ryu/shortest.jl | 496 ++ base/ryu/utils.jl | 410 + base/secretbuffer.jl | 188 + base/set.jl | 664 ++ base/shell.jl | 307 + base/show.jl | 2305 +++++ base/simdloop.jl | 139 + base/some.jl | 97 + base/sort.jl | 1210 +++ 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 | 328 + base/special/trig.jl | 1125 +++ base/stacktraces.jl | 289 + base/stat.jl | 346 + base/stream.jl | 1303 +++ base/strings/basic.jl | 753 ++ base/strings/io.jl | 698 ++ base/strings/search.jl | 535 ++ base/strings/string.jl | 363 + base/strings/strings.jl | 10 + base/strings/substring.jl | 216 + base/strings/unicode.jl | 684 ++ base/strings/util.jl | 704 ++ base/subarray.jl | 432 + base/summarysize.jl | 181 + base/sysimg.jl | 105 + base/sysinfo.jl | 521 ++ base/task.jl | 723 ++ base/threadcall.jl | 100 + base/threadingconstructs.jl | 182 + base/threads.jl | 35 + base/timing.jl | 315 + base/traits.jl | 60 + base/ttyhascolor.jl | 27 + base/tuple.jl | 428 + base/twiceprecision.jl | 731 ++ base/util.jl | 542 ++ base/uuid.jl | 72 + base/version.jl | 302 + base/version_git.sh | 94 + base/views.jl | 228 + base/weakkeydict.jl | 124 + contrib/README.md | 34 + contrib/add_license_to_files.jl | 199 + 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 | 163 + contrib/fixup-libstdc++.sh | 34 + contrib/fixup-rpath.sh | 34 + contrib/generate_precompile.jl | 205 + contrib/install.sh | 35 + contrib/julia-config.jl | 136 + contrib/julia.appdata.xml | 35 + contrib/julia.desktop | 8 + contrib/mac/app/.gitignore | 4 + contrib/mac/app/Entitlements.plist | 24 + contrib/mac/app/Makefile | 99 + contrib/mac/app/README.md | 16 + contrib/mac/app/julia.icns | Bin 0 -> 164450 bytes contrib/mac/app/notarize_check.sh | 66 + contrib/mac/app/renotarize_dmg.sh | 35 + contrib/mac/app/startup.applescript | 5 + contrib/mac/framework/Julia.h | 6 + contrib/mac/framework/Makefile | 188 + 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 -> 5865 bytes .../AppIcon.appiconset/128@2x.png | Bin 0 -> 12858 bytes .../Assets.xcassets/AppIcon.appiconset/16.png | Bin 0 -> 703 bytes .../AppIcon.appiconset/16@2x.png | Bin 0 -> 1276 bytes .../AppIcon.appiconset/256.png | Bin 0 -> 13006 bytes .../AppIcon.appiconset/256@2x.png | Bin 0 -> 28597 bytes .../Assets.xcassets/AppIcon.appiconset/32.png | Bin 0 -> 1294 bytes .../AppIcon.appiconset/32@2x.png | Bin 0 -> 2743 bytes .../AppIcon.appiconset/512.png | Bin 0 -> 28591 bytes .../AppIcon.appiconset/512@2x.png | Bin 0 -> 63825 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 | 67 + contrib/relative_path.py | 10 + 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 | 47 + deps/Versions.make | 44 + deps/blas.mk | 230 + 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-4.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-4.i686-linux-gnu.tar.gz/md5 | 1 + .../GMP.v6.1.2-4.i686-linux-gnu.tar.gz/sha512 | 1 + .../GMP.v6.1.2-4.i686-linux-musl.tar.gz/md5 | 1 + .../sha512 | 1 + .../GMP.v6.1.2-4.i686-w64-mingw32.tar.gz/md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../GMP.v6.1.2-4.x86_64-linux-gnu.tar.gz/md5 | 1 + .../sha512 | 1 + .../GMP.v6.1.2-4.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 + .../MPFR.v4.1.0-1.i686-linux-gnu.tar.gz/md5 | 1 + .../sha512 | 1 + .../MPFR.v4.1.0-1.i686-linux-musl.tar.gz/md5 | 1 + .../sha512 | 1 + .../MPFR.v4.1.0-1.i686-w64-mingw32.tar.gz/md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../MPFR.v4.1.0-1.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 + 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 + deps/checksums/UnicodeData-13.0.0.txt/md5 | 1 + deps/checksums/UnicodeData-13.0.0.txt/sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../Zlib.v1.2.11-10.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/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-2020-07-22.pem/md5 | 1 + deps/checksums/cacert-2020-07-22.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 + deps/checksums/curl-7.66.0.tar.bz2/md5 | 1 + deps/checksums/curl-7.66.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.9.0.tgz/md5 | 1 + deps/checksums/lapack-3.9.0.tgz/sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + deps/checksums/libosxunwind-0.0.6.tar.gz/md5 | 1 + .../libosxunwind-0.0.6.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-10.0.0.src.tar.xz/md5 | 1 + deps/checksums/llvm-10.0.0.src.tar.xz/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/llvm-8.0.1.src.tar.xz/md5 | 1 + deps/checksums/llvm-8.0.1.src.tar.xz/sha512 | 1 + deps/checksums/llvm-9.0.0.src.tar.xz/md5 | 1 + deps/checksums/llvm-9.0.0.src.tar.xz/sha512 | 1 + deps/checksums/llvm-9.0.1.src.tar.xz/md5 | 1 + deps/checksums/llvm-9.0.1.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.1.0.tar.bz2/md5 | 1 + deps/checksums/mpfr-4.1.0.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 | 34 + deps/llvm-ver.make | 12 + deps/llvm.mk | 518 ++ deps/mbedtls.mk | 98 + deps/mpfr.mk | 81 + 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 + .../llvm-10.0-PPC-LI-Elimination.patch | 161 + deps/patches/llvm-10.0-PPC_SELECT_CC.patch | 135 + .../patches/llvm-6.0-DISABLE_ABI_CHECKS.patch | 39 + deps/patches/llvm-7.0-D44650.patch | 13 + deps/patches/llvm-8.0-D50167-scev-umin.patch | 1870 ++++ .../llvm-8.0-D55758-tablegen-cond.patch | 794 ++ .../llvm-8.0-D59389-refactor-wmma.patch | 899 ++ .../llvm-8.0-D59393-mma-ptx63-fix.patch | 510 ++ .../llvm-8.0-D63688-wasm-isLocal.patch | 39 + .../llvm-8.0-D65174-limit-merge-stores.patch | 119 + .../patches/llvm-8.0-D66401-mingw-reloc.patch | 69 + .../llvm-8.0-D66657-codegen-degenerate.patch | 65 + .../llvm-8.0-D71495-vectorize-freduce.patch | 94 + .../llvm-8.0-D75072-SCEV-add-type.patch | 415 + .../llvm-9.0-D65174-limit-merge-stores.patch | 116 + deps/patches/llvm-9.0-D78196.patch | 15 + deps/patches/llvm-9.0-D85499.patch | 425 + ...lvm-D27629-AArch64-large_model_6.0.1.patch | 53 + deps/patches/llvm-D57118-powerpc.patch | 30 + deps/patches/llvm-D75072-SCEV-add-type.patch | 415 + deps/patches/llvm-exegesis-mingw.patch | 24 + deps/patches/llvm-symver-jlprefix.patch | 18 + deps/patches/llvm-test-plugin-mingw.patch | 24 + deps/patches/llvm7-D50010-VNCoercion-ni.patch | 67 + deps/patches/llvm7-revert-D44485.patch | 94 + deps/patches/llvm7-symver-jlprefix.patch | 18 + deps/patches/llvm7-windows-race.patch | 15 + .../patches/llvm8-D34078-vectorize-fdiv.patch | 42 + deps/patches/llvm9-D50010-VNCoercion-ni.patch | 64 + .../llvm9-D71443-PPC-MC-redef-symbol.patch | 47 + ...las-fix-initialization-to-zero-arm64.patch | 67 + deps/patches/openblas-ofast-power.patch | 33 + deps/patches/openblas-winexit.patch | 178 + 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 | 157 + deps/tools/bb-install.mk | 68 + deps/tools/common.mk | 239 + deps/tools/git-external.mk | 75 + deps/tools/jlchecksum | 108 + deps/tools/jldownload | 47 + deps/tools/stdlib-external.mk | 27 + deps/tools/uninstallers.mk | 31 + 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 | 55 + doc/Manifest.toml | 99 + doc/NEWS-update.jl | 19 + doc/Project.toml | 3 + doc/README.md | 29 + doc/build/README.md | 25 + doc/build/arm.md | 87 + doc/build/build.md | 273 + doc/build/distributing.md | 589 ++ doc/build/freebsd.md | 19 + doc/build/linux.md | 22 + doc/build/macos.md | 10 + doc/build/windows.md | 317 + doc/make.jl | 266 + doc/man/julia.1 | 217 + doc/src/assets/julia-manual.css | 11 + doc/src/assets/logo-dark.svg | 1 + doc/src/assets/logo.svg | 1 + doc/src/base/arrays.md | 189 + doc/src/base/base.md | 425 + doc/src/base/c.md | 53 + doc/src/base/collections.md | 293 + doc/src/base/constants.md | 28 + doc/src/base/file.md | 66 + doc/src/base/io-network.md | 138 + doc/src/base/iterators.md | 23 + doc/src/base/libc.md | 18 + doc/src/base/math.md | 206 + doc/src/base/multi-threading.md | 52 + doc/src/base/numbers.md | 129 + doc/src/base/parallel.md | 61 + doc/src/base/punctuation.md | 56 + doc/src/base/simd-types.md | 35 + doc/src/base/sort.md | 189 + doc/src/base/stacktraces.md | 15 + doc/src/base/strings.md | 84 + doc/src/devdocs/ast.md | 715 ++ 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 | 249 + doc/src/devdocs/eval.md | 190 + doc/src/devdocs/functions.md | 258 + doc/src/devdocs/gc-sa.md | 303 + doc/src/devdocs/inference.md | 144 + doc/src/devdocs/init.md | 230 + doc/src/devdocs/isbitsunionarrays.md | 23 + doc/src/devdocs/llvm.md | 331 + doc/src/devdocs/locks.md | 157 + 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 | 194 + doc/src/devdocs/stdio.md | 104 + doc/src/devdocs/subarrays.md | 243 + doc/src/devdocs/sysimg.md | 113 + doc/src/devdocs/types.md | 525 ++ doc/src/devdocs/valgrind.md | 63 + doc/src/index.md | 109 + doc/src/manual/arrays.md | 1071 +++ doc/src/manual/asynchronous-programming.md | 337 + doc/src/manual/calling-c-and-fortran-code.md | 1101 +++ 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 | 828 ++ doc/src/manual/conversion-and-promotion.md | 350 + doc/src/manual/distributed-computing.md | 1400 +++ doc/src/manual/documentation.md | 592 ++ doc/src/manual/embedding.md | 553 ++ doc/src/manual/environment-variables.md | 331 + doc/src/manual/faq.md | 982 +++ doc/src/manual/functions.md | 901 ++ doc/src/manual/getting-started.md | 140 + .../handling-operating-system-variation.md | 41 + .../integers-and-floating-point-numbers.md | 735 ++ doc/src/manual/interfaces.md | 736 ++ doc/src/manual/mathematical-operations.md | 569 ++ doc/src/manual/metaprogramming.md | 1444 ++++ doc/src/manual/methods.md | 1091 +++ doc/src/manual/missing.md | 398 + doc/src/manual/modules.md | 365 + doc/src/manual/multi-threading.md | 350 + doc/src/manual/networking-and-streams.md | 314 + doc/src/manual/noteworthy-differences.md | 355 + doc/src/manual/parallel-computing.md | 20 + doc/src/manual/performance-tips.md | 1588 ++++ doc/src/manual/profile.md | 353 + doc/src/manual/running-external-programs.md | 367 + doc/src/manual/stacktraces.md | 312 + doc/src/manual/strings.md | 1172 +++ doc/src/manual/style-guide.md | 415 + doc/src/manual/types.md | 1435 ++++ doc/src/manual/unicode-input.md | 83 + doc/src/manual/variables-and-scoping.md | 698 ++ 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 | 389 + src/abi_aarch64.cpp | 379 + src/abi_arm.cpp | 283 + src/abi_llvm.cpp | 58 + src/abi_ppc64le.cpp | 155 + src/abi_win32.cpp | 71 + src/abi_win64.cpp | 78 + src/abi_x86.cpp | 89 + src/abi_x86_64.cpp | 267 + src/anticodegen.c | 68 + src/aotcompile.cpp | 978 +++ src/array.c | 1293 +++ src/ast.c | 1282 +++ src/ast.scm | 513 ++ src/atomics.h | 329 + src/bin2hex.scm | 14 + src/builtin_proto.h | 49 + src/builtins.c | 1622 ++++ src/ccall.cpp | 2026 +++++ src/ccalltest.c | 983 +++ src/cgmemmgr.cpp | 916 ++ src/cgutils.cpp | 2936 +++++++ src/clangsa/GCChecker.cpp | 1658 ++++ src/codegen.cpp | 7612 +++++++++++++++++ src/codegen_shared.h | 71 + src/common_symbols1.inc | 100 + src/common_symbols2.inc | 254 + src/crc32c-tables.c | 30 + src/crc32c.c | 595 ++ src/datatype.c | 1104 +++ src/debuginfo.cpp | 1691 ++++ src/debuginfo.h | 22 + src/disasm.cpp | 1062 +++ src/dlload.c | 314 + src/dump.c | 3419 ++++++++ src/features_aarch32.h | 36 + src/features_aarch64.h | 83 + src/features_x86.h | 116 + src/file_constants.h | 28 + src/flisp/.gitignore | 12 + src/flisp/Makefile | 133 + 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 | 1420 +++ src/flisp/equal.c | 384 + src/flisp/equalhash.c | 26 + src/flisp/equalhash.h | 16 + src/flisp/flisp.boot | 403 + src/flisp/flisp.c | 2495 ++++++ src/flisp/flisp.h | 513 ++ src/flisp/flmain.c | 79 + src/flisp/iostream.c | 473 + src/flisp/julia_charmap.h | 7 + src/flisp/julia_extensions.c | 370 + 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 | 729 ++ src/flisp/string.c | 300 + src/flisp/system.lsp | 1003 +++ src/flisp/table.c | 217 + src/flisp/types.c | 96 + src/flisp/unittest.lsp | 267 + src/gc-debug.c | 1379 +++ src/gc-pages.c | 322 + src/gc-stacks.c | 238 + src/gc.c | 3711 ++++++++ src/gc.h | 700 ++ src/gen_sysimg_symtab.jl | 73 + src/getopt.c | 147 + src/getopt.h | 56 + src/gf.c | 2853 ++++++ src/iddict.c | 188 + src/init.c | 862 ++ src/interpreter.c | 711 ++ src/intrinsics.cpp | 1310 +++ src/intrinsics.h | 112 + src/jitlayers.cpp | 1001 +++ src/jitlayers.h | 259 + src/jl_uv.c | 1034 +++ src/jlapi.c | 480 ++ src/jlfrontend.scm | 260 + src/jloptions.c | 666 ++ src/jltypes.c | 2366 +++++ src/jsvm-emscripten/asyncify_setup.js | 144 + src/jsvm-emscripten/task.js | 15 + src/julia-parser.scm | 2486 ++++++ src/julia-syntax.scm | 4467 ++++++++++ src/julia.expmap | 44 + src/julia.h | 2101 +++++ src/julia_assert.h | 27 + src/julia_gcext.h | 134 + src/julia_internal.h | 1214 +++ src/julia_threads.h | 323 + src/llvm-alloc-opt.cpp | 1527 ++++ src/llvm-api.cpp | 267 + src/llvm-final-gc-lowering.cpp | 348 + src/llvm-gc-invariant-verifier.cpp | 196 + src/llvm-late-gc-lowering.cpp | 2450 ++++++ src/llvm-lower-handlers.cpp | 251 + src/llvm-muladd.cpp | 106 + src/llvm-multiversioning.cpp | 1088 +++ src/llvm-pass-helpers.cpp | 276 + src/llvm-pass-helpers.h | 157 + src/llvm-propagate-addrspaces.cpp | 303 + src/llvm-ptls.cpp | 288 + src/llvm-remove-addrspaces.cpp | 461 + src/llvm-remove-ni.cpp | 49 + src/llvm-simdloop.cpp | 264 + src/llvm-version.h | 19 + src/llvmcalltest.cpp | 57 + src/locks.h | 162 + src/macroexpand.scm | 598 ++ src/match.scm | 247 + src/method.c | 812 ++ src/mk_julia_flisp_boot.scm | 5 + src/module.c | 811 ++ src/options.h | 182 + src/partr.c | 527 ++ src/precompile.c | 415 + src/processor.cpp | 850 ++ src/processor.h | 212 + src/processor_arm.cpp | 1800 ++++ src/processor_fallback.cpp | 162 + src/processor_x86.cpp | 1090 +++ src/rtutils.c | 1266 +++ src/runtime_ccall.cpp | 340 + src/runtime_intrinsics.c | 923 ++ src/safepoint.c | 225 + src/signal-handling.c | 293 + src/signals-mach.c | 517 ++ src/signals-unix.c | 828 ++ src/signals-win.c | 424 + src/simplevector.c | 91 + src/smallintset.c | 193 + src/stackwalk.c | 738 ++ src/staticdata.c | 1707 ++++ src/subtype.c | 3873 +++++++++ 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 | 388 + 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 | 1247 +++ src/support/ios.h | 224 + src/support/libsupport.h | 31 + src/support/libsupportinit.c | 29 + src/support/operators.c | 238 + src/support/platform.h | 108 + src/support/ptrhash.c | 30 + src/support/ptrhash.h | 18 + src/support/strptime.c | 822 ++ src/support/strtod.c | 287 + src/support/strtod.h | 17 + src/support/timefuncs.c | 73 + src/support/timefuncs.h | 23 + src/support/tzfile.h | 170 + src/support/utf8.c | 600 ++ 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 | 151 + src/sys.c | 656 ++ src/task.c | 1179 +++ src/threading.c | 514 ++ src/threading.h | 35 + src/timing.c | 84 + src/timing.h | 158 + src/tls.h | 41 + src/toplevel.c | 948 ++ src/typemap.c | 959 +++ src/utils.scm | 95 + src/uv_constants.h | 27 + stdlib/.gitignore | 5 + 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 | 607 ++ stdlib/Dates/src/parse.jl | 325 + stdlib/Dates/src/periods.jl | 476 ++ stdlib/Dates/src/query.jl | 668 ++ stdlib/Dates/src/ranges.jl | 67 + stdlib/Dates/src/rounding.jl | 285 + stdlib/Dates/src/types.jl | 440 + stdlib/Dates/test/accessors.jl | 222 + stdlib/Dates/test/adjusters.jl | 502 ++ stdlib/Dates/test/arithmetic.jl | 491 ++ stdlib/Dates/test/conversions.jl | 126 + stdlib/Dates/test/io.jl | 550 ++ stdlib/Dates/test/periods.jl | 455 + stdlib/Dates/test/query.jl | 218 + 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 | 279 + stdlib/DelimitedFiles/Project.toml | 12 + stdlib/DelimitedFiles/docs/src/index.md | 11 + stdlib/DelimitedFiles/src/DelimitedFiles.jl | 831 ++ stdlib/DelimitedFiles/test/runtests.jl | 332 + stdlib/Distributed/Project.toml | 14 + stdlib/Distributed/docs/src/index.md | 68 + stdlib/Distributed/src/Distributed.jl | 99 + stdlib/Distributed/src/cluster.jl | 1343 +++ stdlib/Distributed/src/clusterserialize.jl | 291 + stdlib/Distributed/src/macros.jl | 356 + stdlib/Distributed/src/managers.jl | 606 ++ stdlib/Distributed/src/messages.jl | 219 + stdlib/Distributed/src/pmap.jl | 298 + stdlib/Distributed/src/process_messages.jl | 397 + stdlib/Distributed/src/remotecall.jl | 645 ++ stdlib/Distributed/src/workerpool.jl | 356 + stdlib/Distributed/test/distributed_exec.jl | 1697 ++++ stdlib/Distributed/test/managers.jl | 22 + stdlib/Distributed/test/runtests.jl | 14 + stdlib/Distributed/test/splitrange.jl | 33 + 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 | 427 + 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 | 28 + .../InteractiveUtils/src/InteractiveUtils.jl | 382 + stdlib/InteractiveUtils/src/clipboard.jl | 157 + stdlib/InteractiveUtils/src/codeview.jl | 158 + stdlib/InteractiveUtils/src/editless.jl | 265 + stdlib/InteractiveUtils/src/macros.jl | 320 + stdlib/InteractiveUtils/test/runtests.jl | 500 ++ stdlib/LibGit2/Project.toml | 14 + 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 | 110 + stdlib/LibGit2/src/gitcredential.jl | 301 + 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 | 1447 ++++ 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 | 3070 +++++++ 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 | 681 ++ stdlib/LinearAlgebra/src/LinearAlgebra.jl | 453 + stdlib/LinearAlgebra/src/adjtrans.jl | 319 + stdlib/LinearAlgebra/src/bidiag.jl | 952 +++ stdlib/LinearAlgebra/src/bitarray.jl | 258 + stdlib/LinearAlgebra/src/blas.jl | 1941 +++++ stdlib/LinearAlgebra/src/bunchkaufman.jl | 434 + stdlib/LinearAlgebra/src/cholesky.jl | 726 ++ stdlib/LinearAlgebra/src/dense.jl | 1539 ++++ stdlib/LinearAlgebra/src/deprecated.jl | 7 + stdlib/LinearAlgebra/src/diagonal.jl | 720 ++ stdlib/LinearAlgebra/src/eigen.jl | 623 ++ stdlib/LinearAlgebra/src/exceptions.jl | 62 + stdlib/LinearAlgebra/src/factorization.jl | 152 + stdlib/LinearAlgebra/src/generic.jl | 1726 ++++ stdlib/LinearAlgebra/src/givens.jl | 400 + stdlib/LinearAlgebra/src/hessenberg.jl | 609 ++ stdlib/LinearAlgebra/src/lapack.jl | 6430 ++++++++++++++ stdlib/LinearAlgebra/src/ldlt.jl | 218 + stdlib/LinearAlgebra/src/lq.jl | 342 + stdlib/LinearAlgebra/src/lu.jl | 735 ++ stdlib/LinearAlgebra/src/matmul.jl | 1009 +++ stdlib/LinearAlgebra/src/qr.jl | 941 ++ stdlib/LinearAlgebra/src/schur.jl | 385 + stdlib/LinearAlgebra/src/special.jl | 329 + .../LinearAlgebra/src/structuredbroadcast.jl | 220 + stdlib/LinearAlgebra/src/svd.jl | 546 ++ stdlib/LinearAlgebra/src/symmetric.jl | 1073 +++ stdlib/LinearAlgebra/src/transpose.jl | 203 + stdlib/LinearAlgebra/src/triangular.jl | 2712 ++++++ stdlib/LinearAlgebra/src/tridiag.jl | 842 ++ stdlib/LinearAlgebra/src/uniformscaling.jl | 447 + stdlib/LinearAlgebra/test/addmul.jl | 221 + stdlib/LinearAlgebra/test/adjtrans.jl | 560 ++ stdlib/LinearAlgebra/test/ambiguous_exec.jl | 4 + stdlib/LinearAlgebra/test/bidiag.jl | 591 ++ stdlib/LinearAlgebra/test/blas.jl | 593 ++ stdlib/LinearAlgebra/test/bunchkaufman.jl | 174 + stdlib/LinearAlgebra/test/cholesky.jl | 430 + stdlib/LinearAlgebra/test/dense.jl | 942 ++ stdlib/LinearAlgebra/test/diagonal.jl | 720 ++ stdlib/LinearAlgebra/test/eigen.jl | 144 + stdlib/LinearAlgebra/test/generic.jl | 489 ++ stdlib/LinearAlgebra/test/givens.jl | 73 + stdlib/LinearAlgebra/test/hessenberg.jl | 136 + stdlib/LinearAlgebra/test/lapack.jl | 705 ++ stdlib/LinearAlgebra/test/ldlt.jl | 26 + stdlib/LinearAlgebra/test/lq.jl | 206 + stdlib/LinearAlgebra/test/lu.jl | 376 + stdlib/LinearAlgebra/test/matmul.jl | 634 ++ stdlib/LinearAlgebra/test/pinv.jl | 187 + stdlib/LinearAlgebra/test/qr.jl | 300 + stdlib/LinearAlgebra/test/runtests.jl | 5 + stdlib/LinearAlgebra/test/schur.jl | 137 + stdlib/LinearAlgebra/test/special.jl | 437 + .../LinearAlgebra/test/structuredbroadcast.jl | 208 + stdlib/LinearAlgebra/test/svd.jl | 193 + stdlib/LinearAlgebra/test/symmetric.jl | 681 ++ stdlib/LinearAlgebra/test/testgroups | 27 + stdlib/LinearAlgebra/test/testutils.jl | 27 + stdlib/LinearAlgebra/test/triangular.jl | 659 ++ stdlib/LinearAlgebra/test/trickyarithmetic.jl | 62 + stdlib/LinearAlgebra/test/tridiag.jl | 558 ++ stdlib/LinearAlgebra/test/uniformscaling.jl | 472 + stdlib/Logging/Project.toml | 8 + stdlib/Logging/docs/src/index.md | 311 + stdlib/Logging/src/ConsoleLogger.jl | 164 + stdlib/Logging/src/Logging.jl | 62 + stdlib/Logging/test/runtests.jl | 259 + stdlib/Makefile | 40 + stdlib/Markdown/Project.toml | 11 + stdlib/Markdown/docs/src/index.md | 395 + 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 | 173 + stdlib/Markdown/src/IPython/IPython.jl | 32 + stdlib/Markdown/src/Julia/Julia.jl | 15 + stdlib/Markdown/src/Julia/interp.jl | 61 + stdlib/Markdown/src/Markdown.jl | 62 + stdlib/Markdown/src/parse/config.jl | 82 + stdlib/Markdown/src/parse/parse.jl | 96 + stdlib/Markdown/src/parse/util.jl | 204 + 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 | 176 + stdlib/Markdown/test/runtests.jl | 1160 +++ stdlib/Mmap/Project.toml | 9 + stdlib/Mmap/docs/src/index.md | 7 + stdlib/Mmap/src/Mmap.jl | 351 + 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 | 1298 +++ stdlib/Printf/test/runtests.jl | 302 + stdlib/Profile/Project.toml | 12 + stdlib/Profile/docs/src/index.md | 17 + stdlib/Profile/src/Profile.jl | 863 ++ stdlib/Profile/test/runtests.jl | 113 + stdlib/REPL/Project.toml | 14 + stdlib/REPL/docs/src/index.md | 604 ++ stdlib/REPL/src/LineEdit.jl | 2476 ++++++ stdlib/REPL/src/REPL.jl | 1261 +++ stdlib/REPL/src/REPLCompletions.jl | 754 ++ 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 | 165 + stdlib/REPL/src/docview.jl | 706 ++ stdlib/REPL/src/emoji_symbols.jl | 859 ++ stdlib/REPL/src/latex_symbols.jl | 2611 ++++++ 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 | 897 ++ stdlib/REPL/test/repl.jl | 1249 +++ stdlib/REPL/test/replcompletions.jl | 1073 +++ stdlib/REPL/test/runtests.jl | 14 + stdlib/Random/Project.toml | 14 + stdlib/Random/docs/src/index.md | 342 + stdlib/Random/src/DSFMT.jl | 267 + stdlib/Random/src/RNGs.jl | 658 ++ stdlib/Random/src/Random.jl | 428 + stdlib/Random/src/generation.jl | 530 ++ stdlib/Random/src/misc.jl | 385 + stdlib/Random/src/normal.jl | 703 ++ stdlib/Random/test/runtests.jl | 803 ++ 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 | 1411 +++ stdlib/Serialization/test/runtests.jl | 611 ++ 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 | 33 + stdlib/Sockets/src/IPAddr.jl | 312 + stdlib/Sockets/src/PipeServer.jl | 97 + stdlib/Sockets/src/Sockets.jl | 855 ++ stdlib/Sockets/src/addrinfo.jl | 356 + stdlib/Sockets/test/nettest.jl | 183 + stdlib/Sockets/test/runtests.jl | 617 ++ stdlib/SparseArrays/Project.toml | 14 + stdlib/SparseArrays/docs/src/index.md | 234 + stdlib/SparseArrays/src/SparseArrays.jl | 63 + stdlib/SparseArrays/src/abstractsparse.jl | 124 + stdlib/SparseArrays/src/deprecated.jl | 5 + stdlib/SparseArrays/src/higherorderfns.jl | 1157 +++ stdlib/SparseArrays/src/linalg.jl | 1549 ++++ stdlib/SparseArrays/src/sparseconvert.jl | 286 + stdlib/SparseArrays/src/sparsematrix.jl | 3686 ++++++++ stdlib/SparseArrays/src/sparsevector.jl | 2080 +++++ stdlib/SparseArrays/test/ambiguous_exec.jl | 4 + stdlib/SparseArrays/test/forbidproperties.jl | 5 + stdlib/SparseArrays/test/higherorderfns.jl | 690 ++ stdlib/SparseArrays/test/runtests.jl | 5 + stdlib/SparseArrays/test/simplesmatrix.jl | 52 + stdlib/SparseArrays/test/sparse.jl | 2967 +++++++ stdlib/SparseArrays/test/sparsevector.jl | 1433 ++++ stdlib/SparseArrays/test/testgroups | 3 + stdlib/Statistics.version | 3 + stdlib/SuiteSparse/Project.toml | 18 + stdlib/SuiteSparse/src/SuiteSparse.jl | 31 + stdlib/SuiteSparse/src/cholmod.jl | 1871 ++++ stdlib/SuiteSparse/src/cholmod_h.jl | 79 + stdlib/SuiteSparse/src/deprecated.jl | 1 + stdlib/SuiteSparse/src/spqr.jl | 458 + stdlib/SuiteSparse/src/umfpack.jl | 643 ++ stdlib/SuiteSparse/src/umfpack_h.jl | 43 + stdlib/SuiteSparse/test/cholmod.jl | 887 ++ stdlib/SuiteSparse/test/runtests.jl | 30 + stdlib/SuiteSparse/test/spqr.jl | 134 + stdlib/SuiteSparse/test/threads.jl | 22 + stdlib/SuiteSparse/test/umfpack.jl | 236 + stdlib/Test/Project.toml | 8 + stdlib/Test/docs/src/index.md | 269 + stdlib/Test/src/Test.jl | 1733 ++++ stdlib/Test/src/logging.jl | 263 + stdlib/Test/test/runtests.jl | 932 ++ stdlib/Test/test/test_pop_testset_exec.jl | 8 + stdlib/UUIDs/Project.toml | 12 + stdlib/UUIDs/docs/src/index.md | 8 + stdlib/UUIDs/src/UUIDs.jl | 136 + stdlib/UUIDs/test/runtests.jl | 56 + stdlib/Unicode/Project.toml | 9 + stdlib/Unicode/docs/src/index.md | 7 + stdlib/Unicode/src/Unicode.jl | 92 + stdlib/Unicode/test/runtests.jl | 406 + sysimage.mk | 87 + test/.gitignore | 4 + test/Makefile | 42 + test/TestPkg/Manifest.toml | 2 + test/TestPkg/Project.toml | 6 + test/TestPkg/src/TestPkg.jl | 7 + test/abstractarray.jl | 1124 +++ test/ambiguous.jl | 331 + test/arrayops.jl | 2830 ++++++ test/asyncmap.jl | 63 + test/atexit.jl | 154 + test/backtrace.jl | 260 + test/bitarray.jl | 1658 ++++ test/bitset.jl | 350 + test/boundscheck.jl | 18 + test/boundscheck_exec.jl | 255 + test/broadcast.jl | 935 ++ test/cartesian.jl | 70 + test/ccall.jl | 1688 ++++ test/channels.jl | 540 ++ test/char.jl | 302 + test/checked.jl | 336 + test/choosetests.jl | 158 + test/clangsa/.gitignore | 1 + test/clangsa/GCPushPop.cpp | 36 + test/clangsa/Makefile | 22 + test/clangsa/MissingRoots.c | 418 + test/clangsa/lit.cfg.py | 27 + test/client.jl | 37 + test/cmdlineargs.jl | 700 ++ test/combinatorics.jl | 113 + test/compiler/codegen.jl | 465 + test/compiler/contextual.jl | 137 + test/compiler/inference.jl | 2649 ++++++ test/compiler/inline.jl | 290 + test/compiler/interpreter_exec.jl | 107 + test/compiler/irpasses.jl | 297 + test/compiler/ssair.jl | 165 + test/compiler/validation.jl | 132 + test/complex.jl | 1189 +++ test/copy.jl | 236 + test/core.jl | 7333 ++++++++++++++++ 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 | 1110 +++ test/docs.jl | 1206 +++ test/download.jl | 59 + 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 | 212 + test/env.jl | 110 + test/error.jl | 83 + test/errorshow.jl | 706 ++ test/euler.jl | 655 ++ test/exceptions.jl | 385 + test/fastmath.jl | 254 + test/file.jl | 1512 ++++ test/filesystem.jl | 35 + test/float16.jl | 182 + test/floatapprox.jl | 72 + test/floatfuncs.jl | 169 + 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/gmp.jl | 491 ++ test/goto.jl | 170 + test/grisu.jl | 1764 ++++ test/hashing.jl | 258 + test/int.jl | 392 + test/interpreter.jl | 32 + test/intfuncs.jl | 448 + test/intrinsics.jl | 108 + test/iobuffer.jl | 343 + test/iostream.jl | 122 + test/iterators.jl | 843 ++ test/keywordargs.jl | 368 + test/llvmcall.jl | 243 + test/llvmcall2.jl | 46 + test/llvmpasses/.gitignore | 1 + test/llvmpasses/Makefile | 14 + test/llvmpasses/aliasscopes.jl | 61 + test/llvmpasses/alloc-opt.jl | 264 + test/llvmpasses/alloc-opt2.jl | 92 + test/llvmpasses/fastmath.jl | 18 + test/llvmpasses/final-lower-gc.ll | 73 + test/llvmpasses/fmf.jl | 29 + test/llvmpasses/gcroots.ll | 724 ++ test/llvmpasses/late-lower-gc.ll | 94 + test/llvmpasses/lit.cfg.py | 21 + test/llvmpasses/llvmcall.jl | 32 + 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/remove-addrspaces.ll | 44 + test/llvmpasses/returnstwicegc.ll | 30 + test/llvmpasses/safepoint_stress.jl | 27 + test/llvmpasses/simdloop.ll | 95 + test/loading.jl | 692 ++ test/logging.jl | 395 + test/math.jl | 1057 +++ test/meta.jl | 212 + test/misc.jl | 832 ++ test/missing.jl | 541 ++ test/mod2pi.jl | 268 + test/mpfr.jl | 995 +++ test/namedtuple.jl | 304 + test/netload/memtest.jl | 62 + test/numbers.jl | 2688 ++++++ test/offsetarray.jl | 611 ++ test/operators.jl | 240 + test/ordering.jl | 40 + test/osutils.jl | 57 + test/parse.jl | 312 + test/path.jl | 290 + test/precompile.jl | 850 ++ 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 | 9 + test/project/deps/Foo1/src/SubFoo1.jl | 5 + test/project/deps/Foo1/src/subdir/SubFoo2.jl | 5 + test/project/deps/Foo2.jl/src/Foo.jl | 6 + test/project/deps/Qux.jl | 5 + test/ranges.jl | 1627 ++++ test/rational.jl | 596 ++ test/read.jl | 613 ++ test/reduce.jl | 558 ++ test/reducedim.jl | 443 + test/reflection.jl | 906 ++ test/regex.jl | 145 + test/reinterpretarray.jl | 205 + test/rounding.jl | 328 + test/runtests.jl | 382 + test/ryu.jl | 770 ++ test/secretbuffer.jl | 124 + test/sets.jl | 721 ++ test/show.jl | 1988 +++++ test/simdloop.jl | 176 + test/some.jl | 100 + test/sorting.jl | 545 ++ test/spawn.jl | 766 ++ test/specificity.jl | 308 + test/stack_overflow.jl | 19 + test/stacktraces.jl | 193 + test/staged.jl | 306 + test/stress.jl | 88 + test/stress_fd_exec.jl | 24 + test/strings/basic.jl | 1045 +++ test/strings/io.jl | 272 + test/strings/search.jl | 410 + test/strings/types.jl | 333 + test/strings/util.jl | 388 + test/subarray.jl | 700 ++ test/subtype.jl | 1772 ++++ test/syntax.jl | 2326 +++++ test/sysinfo.jl | 8 + test/test_exec.jl | 5 + test/test_sourcepath.jl | 23 + test/testdefs.jl | 49 + test/testenv.jl | 39 + test/testhelpers/FakePTYs.jl | 65 + test/testhelpers/Furlongs.jl | 84 + test/testhelpers/MacroCalls.jl | 17 + test/testhelpers/OffsetArrays.jl | 145 + test/testhelpers/PhysQuantities.jl | 26 + test/testhelpers/Quaternions.jl | 39 + test/testhelpers/allocation_file.jl | 8 + test/testhelpers/arrayindexingtypes.jl | 56 + test/testhelpers/coverage_file.info | 13 + test/testhelpers/coverage_file.jl | 23 + test/testhelpers/llvmpasses.jl | 27 + test/threads.jl | 131 + test/threads_exec.jl | 847 ++ test/triplequote.jl | 75 + test/tuple.jl | 571 ++ test/unicode/utf8.jl | 38 + test/util/segfault.jl | 3 + test/util/throw_error_exception.jl | 3 + test/vecelement.jl | 122 + test/version.jl | 229 + test/worlds.jl | 201 + ui/.gitignore | 5 + ui/Makefile | 94 + ui/repl.c | 234 + 1848 files changed, 474087 insertions(+) create mode 100644 .appveyor.yml create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 .gitattributes 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/div.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/iddict.jl create mode 100644 base/idset.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/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/ryu/LICENSE.md create mode 100644 base/ryu/Ryu.jl create mode 100644 base/ryu/exp.jl create mode 100644 base/ryu/fixed.jl create mode 100644 base/ryu/shortest.jl create mode 100644 base/ryu/utils.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/timing.jl create mode 100644 base/traits.jl create mode 100644 base/ttyhascolor.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/mac/app/.gitignore create mode 100644 contrib/mac/app/Entitlements.plist 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 100755 contrib/mac/app/notarize_check.sh create mode 100755 contrib/mac/app/renotarize_dmg.sh 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.py 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-4.aarch64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-4.aarch64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-4.aarch64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-4.aarch64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-4.armv7l-linux-gnueabihf.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-4.armv7l-linux-gnueabihf.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-4.armv7l-linux-musleabihf.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-4.armv7l-linux-musleabihf.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-4.i686-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-4.i686-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-4.i686-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-4.i686-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-4.i686-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-4.i686-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-4.powerpc64le-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-4.powerpc64le-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-4.x86_64-apple-darwin14.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-4.x86_64-apple-darwin14.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-4.x86_64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-4.x86_64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-4.x86_64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-4.x86_64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-4.x86_64-unknown-freebsd11.1.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-4.x86_64-unknown-freebsd11.1.tar.gz/sha512 create mode 100644 deps/checksums/GMP.v6.1.2-4.x86_64-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/GMP.v6.1.2-4.x86_64-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.aarch64-linux-gnu-cxx03.tar.gz/md5 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.aarch64-linux-gnu-cxx03.tar.gz/sha512 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.aarch64-linux-gnu-cxx11.tar.gz/md5 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.aarch64-linux-gnu-cxx11.tar.gz/sha512 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.aarch64-linux-musl-cxx03.tar.gz/md5 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.aarch64-linux-musl-cxx03.tar.gz/sha512 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.aarch64-linux-musl-cxx11.tar.gz/md5 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.aarch64-linux-musl-cxx11.tar.gz/sha512 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.armv7l-linux-gnueabihf-cxx03.tar.gz/md5 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.armv7l-linux-gnueabihf-cxx03.tar.gz/sha512 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.armv7l-linux-gnueabihf-cxx11.tar.gz/md5 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.armv7l-linux-gnueabihf-cxx11.tar.gz/sha512 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.armv7l-linux-musleabihf-cxx03.tar.gz/md5 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.armv7l-linux-musleabihf-cxx03.tar.gz/sha512 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.armv7l-linux-musleabihf-cxx11.tar.gz/md5 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.armv7l-linux-musleabihf-cxx11.tar.gz/sha512 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.i686-linux-gnu-cxx03.tar.gz/md5 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.i686-linux-gnu-cxx03.tar.gz/sha512 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.i686-linux-gnu-cxx11.tar.gz/md5 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.i686-linux-gnu-cxx11.tar.gz/sha512 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.i686-linux-musl-cxx03.tar.gz/md5 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.i686-linux-musl-cxx03.tar.gz/sha512 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.i686-linux-musl-cxx11.tar.gz/md5 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.i686-linux-musl-cxx11.tar.gz/sha512 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.i686-w64-mingw32-cxx03.tar.gz/md5 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.i686-w64-mingw32-cxx03.tar.gz/sha512 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.i686-w64-mingw32-cxx11.tar.gz/md5 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.i686-w64-mingw32-cxx11.tar.gz/sha512 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.powerpc64le-linux-gnu-cxx03.tar.gz/md5 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.powerpc64le-linux-gnu-cxx03.tar.gz/sha512 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.powerpc64le-linux-gnu-cxx11.tar.gz/md5 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.powerpc64le-linux-gnu-cxx11.tar.gz/sha512 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.x86_64-apple-darwin14-cxx03.tar.gz/md5 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.x86_64-apple-darwin14-cxx03.tar.gz/sha512 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.x86_64-apple-darwin14-cxx11.tar.gz/md5 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.x86_64-apple-darwin14-cxx11.tar.gz/sha512 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.x86_64-linux-gnu-cxx03.tar.gz/md5 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.x86_64-linux-gnu-cxx03.tar.gz/sha512 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.x86_64-linux-gnu-cxx11.tar.gz/md5 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.x86_64-linux-gnu-cxx11.tar.gz/sha512 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.x86_64-linux-musl-cxx03.tar.gz/md5 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.x86_64-linux-musl-cxx03.tar.gz/sha512 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.x86_64-linux-musl-cxx11.tar.gz/md5 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.x86_64-linux-musl-cxx11.tar.gz/sha512 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.x86_64-unknown-freebsd11.1-cxx03.tar.gz/md5 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.x86_64-unknown-freebsd11.1-cxx03.tar.gz/sha512 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.x86_64-unknown-freebsd11.1-cxx11.tar.gz/md5 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.x86_64-unknown-freebsd11.1-cxx11.tar.gz/sha512 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.x86_64-w64-mingw32-cxx03.tar.gz/md5 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.x86_64-w64-mingw32-cxx03.tar.gz/sha512 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.x86_64-w64-mingw32-cxx11.tar.gz/md5 create mode 100644 deps/checksums/LLVM_full.v9.0.1-8.x86_64-w64-mingw32-cxx11.tar.gz/sha512 create mode 100644 deps/checksums/LibCURL.v7.66.0-1.aarch64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibCURL.v7.66.0-1.aarch64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibCURL.v7.66.0-1.aarch64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/LibCURL.v7.66.0-1.aarch64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/LibCURL.v7.66.0-1.armv7l-linux-gnueabihf.tar.gz/md5 create mode 100644 deps/checksums/LibCURL.v7.66.0-1.armv7l-linux-gnueabihf.tar.gz/sha512 create mode 100644 deps/checksums/LibCURL.v7.66.0-1.armv7l-linux-musleabihf.tar.gz/md5 create mode 100644 deps/checksums/LibCURL.v7.66.0-1.armv7l-linux-musleabihf.tar.gz/sha512 create mode 100644 deps/checksums/LibCURL.v7.66.0-1.i686-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibCURL.v7.66.0-1.i686-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibCURL.v7.66.0-1.i686-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/LibCURL.v7.66.0-1.i686-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/LibCURL.v7.66.0-1.i686-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/LibCURL.v7.66.0-1.i686-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/LibCURL.v7.66.0-1.powerpc64le-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibCURL.v7.66.0-1.powerpc64le-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibCURL.v7.66.0-1.x86_64-apple-darwin14.tar.gz/md5 create mode 100644 deps/checksums/LibCURL.v7.66.0-1.x86_64-apple-darwin14.tar.gz/sha512 create mode 100644 deps/checksums/LibCURL.v7.66.0-1.x86_64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibCURL.v7.66.0-1.x86_64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibCURL.v7.66.0-1.x86_64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/LibCURL.v7.66.0-1.x86_64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/LibCURL.v7.66.0-1.x86_64-unknown-freebsd11.1.tar.gz/md5 create mode 100644 deps/checksums/LibCURL.v7.66.0-1.x86_64-unknown-freebsd11.1.tar.gz/sha512 create mode 100644 deps/checksums/LibCURL.v7.66.0-1.x86_64-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/LibCURL.v7.66.0-1.x86_64-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.2-1.aarch64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.2-1.aarch64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.2-1.aarch64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.2-1.aarch64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.2-1.armv7l-linux-gnueabihf.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.2-1.armv7l-linux-gnueabihf.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.2-1.armv7l-linux-musleabihf.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.2-1.armv7l-linux-musleabihf.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.2-1.i686-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.2-1.i686-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.2-1.i686-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.2-1.i686-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.2-1.i686-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.2-1.i686-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.2-1.powerpc64le-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.2-1.powerpc64le-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.2-1.x86_64-apple-darwin14.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.2-1.x86_64-apple-darwin14.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.2-1.x86_64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.2-1.x86_64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.2-1.x86_64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.2-1.x86_64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.2-1.x86_64-unknown-freebsd11.1.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.2-1.x86_64-unknown-freebsd11.1.tar.gz/sha512 create mode 100644 deps/checksums/LibGit2.v0.28.2-1.x86_64-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/LibGit2.v0.28.2-1.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/LibOSXUnwind.v0.0.6-0.x86_64-apple-darwin14.tar.gz/md5 create mode 100644 deps/checksums/LibOSXUnwind.v0.0.6-0.x86_64-apple-darwin14.tar.gz/sha512 create mode 100644 deps/checksums/LibSSH2.v1.9.0-1.aarch64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibSSH2.v1.9.0-1.aarch64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibSSH2.v1.9.0-1.aarch64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/LibSSH2.v1.9.0-1.aarch64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/LibSSH2.v1.9.0-1.armv7l-linux-gnueabihf.tar.gz/md5 create mode 100644 deps/checksums/LibSSH2.v1.9.0-1.armv7l-linux-gnueabihf.tar.gz/sha512 create mode 100644 deps/checksums/LibSSH2.v1.9.0-1.armv7l-linux-musleabihf.tar.gz/md5 create mode 100644 deps/checksums/LibSSH2.v1.9.0-1.armv7l-linux-musleabihf.tar.gz/sha512 create mode 100644 deps/checksums/LibSSH2.v1.9.0-1.i686-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibSSH2.v1.9.0-1.i686-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibSSH2.v1.9.0-1.i686-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/LibSSH2.v1.9.0-1.i686-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/LibSSH2.v1.9.0-1.i686-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/LibSSH2.v1.9.0-1.i686-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/LibSSH2.v1.9.0-1.powerpc64le-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibSSH2.v1.9.0-1.powerpc64le-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibSSH2.v1.9.0-1.x86_64-apple-darwin14.tar.gz/md5 create mode 100644 deps/checksums/LibSSH2.v1.9.0-1.x86_64-apple-darwin14.tar.gz/sha512 create mode 100644 deps/checksums/LibSSH2.v1.9.0-1.x86_64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/LibSSH2.v1.9.0-1.x86_64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/LibSSH2.v1.9.0-1.x86_64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/LibSSH2.v1.9.0-1.x86_64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/LibSSH2.v1.9.0-1.x86_64-unknown-freebsd11.1.tar.gz/md5 create mode 100644 deps/checksums/LibSSH2.v1.9.0-1.x86_64-unknown-freebsd11.1.tar.gz/sha512 create mode 100644 deps/checksums/LibSSH2.v1.9.0-1.x86_64-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/LibSSH2.v1.9.0-1.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.armv7l-linux-gnueabihf.tar.gz/md5 create mode 100644 deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.armv7l-linux-gnueabihf.tar.gz/sha512 create mode 100644 deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.armv7l-linux-musleabihf.tar.gz/md5 create mode 100644 deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.armv7l-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.armv7l-linux-gnueabihf.tar.gz/md5 create mode 100644 deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.armv7l-linux-gnueabihf.tar.gz/sha512 create mode 100644 deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.armv7l-linux-musleabihf.tar.gz/md5 create mode 100644 deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.armv7l-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.armv7l-linux-gnueabihf.tar.gz/md5 create mode 100644 deps/checksums/LibUnwind.v1.3.1-4.armv7l-linux-gnueabihf.tar.gz/sha512 create mode 100644 deps/checksums/LibUnwind.v1.3.1-4.armv7l-linux-musleabihf.tar.gz/md5 create mode 100644 deps/checksums/LibUnwind.v1.3.1-4.armv7l-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.1.0-1.aarch64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/MPFR.v4.1.0-1.aarch64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/MPFR.v4.1.0-1.aarch64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/MPFR.v4.1.0-1.aarch64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/MPFR.v4.1.0-1.armv7l-linux-gnueabihf.tar.gz/md5 create mode 100644 deps/checksums/MPFR.v4.1.0-1.armv7l-linux-gnueabihf.tar.gz/sha512 create mode 100644 deps/checksums/MPFR.v4.1.0-1.armv7l-linux-musleabihf.tar.gz/md5 create mode 100644 deps/checksums/MPFR.v4.1.0-1.armv7l-linux-musleabihf.tar.gz/sha512 create mode 100644 deps/checksums/MPFR.v4.1.0-1.i686-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/MPFR.v4.1.0-1.i686-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/MPFR.v4.1.0-1.i686-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/MPFR.v4.1.0-1.i686-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/MPFR.v4.1.0-1.i686-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/MPFR.v4.1.0-1.i686-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/MPFR.v4.1.0-1.powerpc64le-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/MPFR.v4.1.0-1.powerpc64le-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/MPFR.v4.1.0-1.x86_64-apple-darwin14.tar.gz/md5 create mode 100644 deps/checksums/MPFR.v4.1.0-1.x86_64-apple-darwin14.tar.gz/sha512 create mode 100644 deps/checksums/MPFR.v4.1.0-1.x86_64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/MPFR.v4.1.0-1.x86_64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/MPFR.v4.1.0-1.x86_64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/MPFR.v4.1.0-1.x86_64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/MPFR.v4.1.0-1.x86_64-unknown-freebsd11.1.tar.gz/md5 create mode 100644 deps/checksums/MPFR.v4.1.0-1.x86_64-unknown-freebsd11.1.tar.gz/sha512 create mode 100644 deps/checksums/MPFR.v4.1.0-1.x86_64-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/MPFR.v4.1.0-1.x86_64-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/MbedTLS.v2.16.0-1.aarch64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/MbedTLS.v2.16.0-1.aarch64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/MbedTLS.v2.16.0-1.aarch64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/MbedTLS.v2.16.0-1.aarch64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/MbedTLS.v2.16.0-1.armv7l-linux-gnueabihf.tar.gz/md5 create mode 100644 deps/checksums/MbedTLS.v2.16.0-1.armv7l-linux-gnueabihf.tar.gz/sha512 create mode 100644 deps/checksums/MbedTLS.v2.16.0-1.armv7l-linux-musleabihf.tar.gz/md5 create mode 100644 deps/checksums/MbedTLS.v2.16.0-1.armv7l-linux-musleabihf.tar.gz/sha512 create mode 100644 deps/checksums/MbedTLS.v2.16.0-1.i686-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/MbedTLS.v2.16.0-1.i686-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/MbedTLS.v2.16.0-1.i686-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/MbedTLS.v2.16.0-1.i686-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/MbedTLS.v2.16.0-1.i686-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/MbedTLS.v2.16.0-1.i686-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/MbedTLS.v2.16.0-1.powerpc64le-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/MbedTLS.v2.16.0-1.powerpc64le-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/MbedTLS.v2.16.0-1.x86_64-apple-darwin14.tar.gz/md5 create mode 100644 deps/checksums/MbedTLS.v2.16.0-1.x86_64-apple-darwin14.tar.gz/sha512 create mode 100644 deps/checksums/MbedTLS.v2.16.0-1.x86_64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/MbedTLS.v2.16.0-1.x86_64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/MbedTLS.v2.16.0-1.x86_64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/MbedTLS.v2.16.0-1.x86_64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/MbedTLS.v2.16.0-1.x86_64-unknown-freebsd11.1.tar.gz/md5 create mode 100644 deps/checksums/MbedTLS.v2.16.0-1.x86_64-unknown-freebsd11.1.tar.gz/sha512 create mode 100644 deps/checksums/MbedTLS.v2.16.0-1.x86_64-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/MbedTLS.v2.16.0-1.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.armv7l-linux-gnueabihf.tar.gz/md5 create mode 100644 deps/checksums/Objconv.v2.49.0-0.armv7l-linux-gnueabihf.tar.gz/sha512 create mode 100644 deps/checksums/Objconv.v2.49.0-0.armv7l-linux-musleabihf.tar.gz/md5 create mode 100644 deps/checksums/Objconv.v2.49.0-0.armv7l-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.9-4.aarch64-linux-gnu-libgfortran3.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-gnu-libgfortran3.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-gnu-libgfortran4.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-gnu-libgfortran4.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-gnu-libgfortran5.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-gnu-libgfortran5.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-musl-libgfortran3.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-musl-libgfortran3.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-musl-libgfortran4.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-musl-libgfortran4.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-musl-libgfortran5.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-musl-libgfortran5.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-gnueabihf-libgfortran3.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-gnueabihf-libgfortran3.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-gnueabihf-libgfortran4.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-gnueabihf-libgfortran4.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-gnueabihf-libgfortran5.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-gnueabihf-libgfortran5.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-musleabihf-libgfortran3.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-musleabihf-libgfortran3.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-musleabihf-libgfortran4.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-musleabihf-libgfortran4.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-musleabihf-libgfortran5.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-musleabihf-libgfortran5.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-gnu-libgfortran3.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-gnu-libgfortran3.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-gnu-libgfortran4.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-gnu-libgfortran4.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-gnu-libgfortran5.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-gnu-libgfortran5.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-musl-libgfortran3.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-musl-libgfortran3.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-musl-libgfortran4.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-musl-libgfortran4.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-musl-libgfortran5.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-musl-libgfortran5.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.i686-w64-mingw32-libgfortran3.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.i686-w64-mingw32-libgfortran3.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.i686-w64-mingw32-libgfortran4.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.i686-w64-mingw32-libgfortran4.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.i686-w64-mingw32-libgfortran5.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.i686-w64-mingw32-libgfortran5.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.powerpc64le-linux-gnu-libgfortran3.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.powerpc64le-linux-gnu-libgfortran3.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.powerpc64le-linux-gnu-libgfortran4.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.powerpc64le-linux-gnu-libgfortran4.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.powerpc64le-linux-gnu-libgfortran5.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.powerpc64le-linux-gnu-libgfortran5.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.x86_64-apple-darwin14-libgfortran3.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.x86_64-apple-darwin14-libgfortran3.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.x86_64-apple-darwin14-libgfortran4.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.x86_64-apple-darwin14-libgfortran4.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.x86_64-apple-darwin14-libgfortran5.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.x86_64-apple-darwin14-libgfortran5.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-gnu-libgfortran3.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-gnu-libgfortran3.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-gnu-libgfortran4.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-gnu-libgfortran4.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-gnu-libgfortran5.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-gnu-libgfortran5.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-musl-libgfortran3.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-musl-libgfortran3.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-musl-libgfortran4.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-musl-libgfortran4.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-musl-libgfortran5.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-musl-libgfortran5.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.x86_64-unknown-freebsd11.1-libgfortran3.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.x86_64-unknown-freebsd11.1-libgfortran3.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.x86_64-unknown-freebsd11.1-libgfortran4.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.x86_64-unknown-freebsd11.1-libgfortran4.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.x86_64-unknown-freebsd11.1-libgfortran5.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.x86_64-unknown-freebsd11.1-libgfortran5.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.x86_64-w64-mingw32-libgfortran3.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.x86_64-w64-mingw32-libgfortran3.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.x86_64-w64-mingw32-libgfortran4.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.x86_64-w64-mingw32-libgfortran4.tar.gz/sha512 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.x86_64-w64-mingw32-libgfortran5.tar.gz/md5 create mode 100644 deps/checksums/OpenBLAS.v0.3.9-4.x86_64-w64-mingw32-libgfortran5.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.7.0-0.aarch64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.7.0-0.aarch64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.7.0-0.aarch64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.7.0-0.aarch64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.7.0-0.armv7l-linux-gnueabihf.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.7.0-0.armv7l-linux-gnueabihf.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.7.0-0.armv7l-linux-musleabihf.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.7.0-0.armv7l-linux-musleabihf.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.7.0-0.i686-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.7.0-0.i686-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.7.0-0.i686-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.7.0-0.i686-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.7.0-0.i686-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.7.0-0.i686-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.7.0-0.powerpc64le-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.7.0-0.powerpc64le-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.7.0-0.x86_64-apple-darwin14.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.7.0-0.x86_64-apple-darwin14.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.7.0-0.x86_64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.7.0-0.x86_64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.7.0-0.x86_64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.7.0-0.x86_64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.7.0-0.x86_64-unknown-freebsd11.1.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.7.0-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 create mode 100644 deps/checksums/OpenLibm.v0.7.0-0.x86_64-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/OpenLibm.v0.7.0-0.x86_64-w64-mingw32.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.armv7l-linux-gnueabihf.tar.gz/md5 create mode 100644 deps/checksums/PCRE2.v10.31.0-0.armv7l-linux-gnueabihf.tar.gz/sha512 create mode 100644 deps/checksums/PCRE2.v10.31.0-0.armv7l-linux-musleabihf.tar.gz/md5 create mode 100644 deps/checksums/PCRE2.v10.31.0-0.armv7l-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-1542f285243b8ac31e666dee3c6690bdaf26a0be.tar.gz/md5 create mode 100644 deps/checksums/Pkg-1542f285243b8ac31e666dee3c6690bdaf26a0be.tar.gz/sha512 create mode 100644 deps/checksums/Statistics-cde87c8062032883165cd242f4a5c6b7943cb0b1.tar.gz/md5 create mode 100644 deps/checksums/Statistics-cde87c8062032883165cd242f4a5c6b7943cb0b1.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-6.aarch64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-6.aarch64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-6.aarch64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-6.aarch64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-6.armv7l-linux-gnueabihf.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-6.armv7l-linux-gnueabihf.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-6.armv7l-linux-musleabihf.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-6.armv7l-linux-musleabihf.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-6.i686-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-6.i686-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-6.i686-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-6.i686-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-6.i686-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-6.i686-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-6.powerpc64le-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-6.powerpc64le-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-6.x86_64-apple-darwin14.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-6.x86_64-apple-darwin14.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-6.x86_64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-6.x86_64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-6.x86_64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-6.x86_64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-6.x86_64-unknown-freebsd11.1.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-6.x86_64-unknown-freebsd11.1.tar.gz/sha512 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-6.x86_64-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/SuiteSparse.v5.4.0-6.x86_64-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/UnicodeData-13.0.0.txt/md5 create mode 100644 deps/checksums/UnicodeData-13.0.0.txt/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-10.aarch64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-10.aarch64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-10.aarch64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-10.aarch64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-10.armv7l-linux-gnueabihf.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-10.armv7l-linux-gnueabihf.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-10.armv7l-linux-musleabihf.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-10.armv7l-linux-musleabihf.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-10.i686-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-10.i686-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-10.i686-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-10.i686-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-10.i686-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-10.i686-w64-mingw32.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-10.powerpc64le-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-10.powerpc64le-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-10.x86_64-apple-darwin14.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-10.x86_64-apple-darwin14.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-10.x86_64-linux-gnu.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-10.x86_64-linux-gnu.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-10.x86_64-linux-musl.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-10.x86_64-linux-musl.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-10.x86_64-unknown-freebsd11.1.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-10.x86_64-unknown-freebsd11.1.tar.gz/sha512 create mode 100644 deps/checksums/Zlib.v1.2.11-10.x86_64-w64-mingw32.tar.gz/md5 create mode 100644 deps/checksums/Zlib.v1.2.11-10.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-2020-07-22.pem/md5 create mode 100644 deps/checksums/cacert-2020-07-22.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/curl-7.66.0.tar.bz2/md5 create mode 100644 deps/checksums/curl-7.66.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.armv7l-linux-gnueabihf.tar.gz/md5 create mode 100644 deps/checksums/dSFMT.v2.2.3-0.armv7l-linux-gnueabihf.tar.gz/sha512 create mode 100644 deps/checksums/dSFMT.v2.2.3-0.armv7l-linux-musleabihf.tar.gz/md5 create mode 100644 deps/checksums/dSFMT.v2.2.3-0.armv7l-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.9.0.tgz/md5 create mode 100644 deps/checksums/lapack-3.9.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.6.tar.gz/md5 create mode 100644 deps/checksums/libosxunwind-0.0.6.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-10.0.0.src.tar.xz/md5 create mode 100644 deps/checksums/llvm-10.0.0.src.tar.xz/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/llvm-8.0.1.src.tar.xz/md5 create mode 100644 deps/checksums/llvm-8.0.1.src.tar.xz/sha512 create mode 100644 deps/checksums/llvm-9.0.0.src.tar.xz/md5 create mode 100644 deps/checksums/llvm-9.0.0.src.tar.xz/sha512 create mode 100644 deps/checksums/llvm-9.0.1.src.tar.xz/md5 create mode 100644 deps/checksums/llvm-9.0.1.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.1.0.tar.bz2/md5 create mode 100644 deps/checksums/mpfr-4.1.0.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-5f36f18148603facb6c3540e673610d6b24cbfbb.tar.gz/md5 create mode 100644 deps/checksums/openblas-5f36f18148603facb6c3540e673610d6b24cbfbb.tar.gz/sha512 create mode 100644 deps/checksums/openlibm-5efed306d509905714e3c43fc3a43fb26f3df743.tar.gz/md5 create mode 100644 deps/checksums/openlibm-5efed306d509905714e3c43fc3a43fb26f3df743.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.armv7l-linux-gnueabihf.tar.gz/md5 create mode 100644 deps/checksums/p7zip.v16.2.0-1.armv7l-linux-gnueabihf.tar.gz/sha512 create mode 100644 deps/checksums/p7zip.v16.2.0-1.armv7l-linux-musleabihf.tar.gz/md5 create mode 100644 deps/checksums/p7zip.v16.2.0-1.armv7l-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-0890a538bf8238cded9be0c81171f57e43f2c755.tar.gz/md5 create mode 100644 deps/checksums/utf8proc-0890a538bf8238cded9be0c81171f57e43f2c755.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-10.0-PPC-LI-Elimination.patch create mode 100644 deps/patches/llvm-10.0-PPC_SELECT_CC.patch create mode 100644 deps/patches/llvm-6.0-DISABLE_ABI_CHECKS.patch create mode 100644 deps/patches/llvm-7.0-D44650.patch create mode 100644 deps/patches/llvm-8.0-D50167-scev-umin.patch create mode 100644 deps/patches/llvm-8.0-D55758-tablegen-cond.patch create mode 100644 deps/patches/llvm-8.0-D59389-refactor-wmma.patch create mode 100644 deps/patches/llvm-8.0-D59393-mma-ptx63-fix.patch create mode 100644 deps/patches/llvm-8.0-D63688-wasm-isLocal.patch create mode 100644 deps/patches/llvm-8.0-D65174-limit-merge-stores.patch create mode 100644 deps/patches/llvm-8.0-D66401-mingw-reloc.patch create mode 100644 deps/patches/llvm-8.0-D66657-codegen-degenerate.patch create mode 100644 deps/patches/llvm-8.0-D71495-vectorize-freduce.patch create mode 100644 deps/patches/llvm-8.0-D75072-SCEV-add-type.patch create mode 100644 deps/patches/llvm-9.0-D65174-limit-merge-stores.patch create mode 100644 deps/patches/llvm-9.0-D78196.patch create mode 100644 deps/patches/llvm-9.0-D85499.patch create mode 100644 deps/patches/llvm-D27629-AArch64-large_model_6.0.1.patch create mode 100644 deps/patches/llvm-D57118-powerpc.patch create mode 100644 deps/patches/llvm-D75072-SCEV-add-type.patch create mode 100644 deps/patches/llvm-exegesis-mingw.patch create mode 100644 deps/patches/llvm-symver-jlprefix.patch create mode 100644 deps/patches/llvm-test-plugin-mingw.patch create mode 100644 deps/patches/llvm7-D50010-VNCoercion-ni.patch create mode 100644 deps/patches/llvm7-revert-D44485.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/llvm9-D50010-VNCoercion-ni.patch create mode 100644 deps/patches/llvm9-D71443-PPC-MC-redef-symbol.patch create mode 100644 deps/patches/openblas-fix-initialization-to-zero-arm64.patch create mode 100644 deps/patches/openblas-ofast-power.patch create mode 100644 deps/patches/openblas-winexit.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 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/tools/uninstallers.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/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-dark.svg create mode 100644 doc/src/assets/logo.svg 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/asynchronous-programming.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/distributed-computing.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/multi-threading.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/aotcompile.cpp 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/iddict.c create mode 100644 src/init.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-remove-addrspaces.cpp create mode 100644 src/llvm-remove-ni.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/smallintset.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/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/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/managers.jl create mode 100644 stdlib/Distributed/test/runtests.jl create mode 100644 stdlib/Distributed/test/splitrange.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/ldlt.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/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/forbidproperties.jl create mode 100644 stdlib/SparseArrays/test/higherorderfns.jl create mode 100644 stdlib/SparseArrays/test/runtests.jl create mode 100644 stdlib/SparseArrays/test/simplesmatrix.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.version 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/threads.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/Test/test/test_pop_testset_exec.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/atexit.jl create mode 100644 test/backtrace.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/.gitignore create mode 100644 test/clangsa/GCPushPop.cpp create mode 100644 test/clangsa/Makefile create mode 100644 test/clangsa/MissingRoots.c create mode 100644 test/clangsa/lit.cfg.py 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/filesystem.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/gmp.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/fastmath.jl create mode 100644 test/llvmpasses/final-lower-gc.ll create mode 100644 test/llvmpasses/fmf.jl create mode 100644 test/llvmpasses/gcroots.ll create mode 100644 test/llvmpasses/late-lower-gc.ll create mode 100644 test/llvmpasses/lit.cfg.py create mode 100644 test/llvmpasses/llvmcall.jl 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/remove-addrspaces.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/ordering.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/Foo1/src/SubFoo1.jl create mode 100644 test/project/deps/Foo1/src/subdir/SubFoo2.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/ryu.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/stress_fd_exec.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/allocation_file.jl create mode 100644 test/testhelpers/arrayindexingtypes.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/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..a688be1 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,3 @@ +FROM julia:latest + +RUN apt-get update && apt-get install -y build-essential libatomic1 python gfortran perl wget m4 cmake pkg-config git diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..a3747ca --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,8 @@ +{ + "extensions": [ + "julialang.language-julia", + "ms-vscode.cpptools" + ], + + "dockerFile": "Dockerfile" +} diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..a97c543 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# treat all files as files that should not be modified +* -text diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2da56ff --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +/*.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 +*.o.tmp +*.obj +*.so +*.dylib +*.dSYM +*.jl.cov +*.jl.*.cov +*.jl.mem +*.jl.*.mem +*.ji + +/perf* +.DS_Store +.idea/* 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..c2c8103 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,310 @@ +# 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) 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/v1/devdocs/backtraces/) 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 + +### 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/v1/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. + +For convenience, there are also `test-revise-*` targets for every `test-*` +target that use Revise to load any modifications to Base into the current +process before running the corresponding test. This can be useful as a shortcut +on the command line (since tests aren't always designed to be run outside the +runtest harness). + +### 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..ad5b096 --- /dev/null +++ b/HISTORY.md @@ -0,0 +1,5468 @@ +Julia v1.4 Release Notes +======================== + +New language features +--------------------- + +* Structs with all isbits and isbitsunion fields are now stored inline in arrays ([#32448]). +* `import` now allows quoted symbols, e.g. `import Base.:+` ([#33158]). +* `a[begin]` can now be used to address the first element of an integer-indexed collection `a`. + The index is computed by `firstindex(a)` ([#33946]). + +Language changes +---------------- + +* The syntax `(;)`, which used to parse as an empty block expression, is deprecated. + In the future it will indicate an empty named tuple ([#30115]). + +Multi-threading changes +----------------------- + +* Values can now be interpolated into `@async` and `@spawn` via `$`, which copies the value directly into the constructed + underlying closure ([#33119]). + +Build system changes +-------------------- + +* Windows build installer has switched to Inno Setup. Installer command line parameters have thus changed. For example, to extract the installer to a specific directory, the command line parameter is now `/DIR=x:\dirname`. Use `julia-installer.exe /?` to list all new command line parameters. + +New library functions +--------------------- + +* The new `only(x)` function returns the one-and-only element of a collection `x`, and throws an `ArgumentError` if `x` contains zero or multiple elements ([#33129]). +* `takewhile` and `dropwhile` have been added to the Iterators submodule ([#33437]). +* `accumulate` has been added to the Iterators submodule ([#34033]). +* There is a now an `evalpoly` function meant to take the role of the `@evalpoly` macro. The function is just as efficient as the macro while giving added flexibility, so it should be preferred over `@evalpoly`. `evalpoly` takes a list of coefficients as a tuple, so where one might write `@evalpoly(x, p1, p2, p3)` one would instead write `evalpoly(x, (p1, p2, p3))`. + +New library features +-------------------- + +* Function composition now supports multiple functions: `∘(f, g, h) = f ∘ g ∘ h` + and splatting `∘(fs...)` for composing an iterable collection of functions ([#33568]). +* Functions `gcd`, `lcm`, and `gcdx` now support `Rational` arguments ([#33910]). +* The `splitpath` function now accepts any `AbstractString` whereas previously it only accepted paths of type `String` ([#33012]). +* `filter` can now act on a `Tuple` ([#32968]). +* The `tempname` function now takes an optional `parent::AbstractString` argument to give it a directory in which to attempt to produce a temporary path name ([#33090]). +* The `tempname` function now takes a `cleanup::Bool` keyword argument defaulting to `true`, which causes the process to try to ensure that any file or directory at the path returned by `tempname` is deleted upon process exit ([#33090]). +* The `readdir` function now takes a `join::Bool` keyword argument defaulting to `false`, which when set causes `readdir` to join its directory argument with each listed name ([#33113]). +* `div` now accepts a rounding mode as the third argument, consistent with the corresponding argument to `rem`. Support for rounding division, by passing one of the RoundNearest modes to this function, was added. For future compatibility, library authors should now extend this function, rather than extending the two-argument `fld`/`cld`/`div` directly ([#33040]). +* `methods` now accepts a module (or a list thereof) to filter methods defined in it ([#33403]). + +Standard library changes +------------------------ + +* Calling `show` or `repr` on an `undef`/`UndefInitializer()` array initializer now shows valid Julia code ([#33211]). +* Calling `show` or `repr` on a 0-dimensional `AbstractArray` now shows valid code for creating an equivalent 0-dimensional array, instead of only showing the contained value ([#33206]). +* `readdir` output is now guaranteed to be sorted. The `sort` keyword allows opting out of sorting to get names in OS-native order ([#33542]). +* The methods of `mktemp` and `mktempdir` that take a function to pass temporary paths to no longer throw errors if the path is already deleted when the function returns ([#33091]). +* Verbose `display` of `Char` (`text/plain` output) now shows the codepoint value in standard-conforming `"U+XXXX"` format ([#33291]). +* `Iterators.partition` now uses views (or smartly re-computed ranges) for partitions of all `AbstractArray`s ([#33533]). +* Sets are now displayed less compactly in the REPL, as a column of elements, like vectors + and dictionaries ([#33300]). +* `delete!` on `WeakKeyDict`s now returns the `WeakKeyDict` itself instead of the underlying `Dict` used for implementation + +#### LinearAlgebra + +* `qr` and `qr!` functions support `blocksize` keyword argument ([#33053]). +* `dot` now admits a 3-argument method `dot(x, A, y)` to compute generalized dot products `dot(x, A*y)`, but without computing and storing the intermediate result `A*y` ([#32739]). +* `ldlt` and non-pivoted `lu` now throw a new `ZeroPivotException` type ([#33372]). +* `cond(A, p)` with `p=1` or `p=Inf` now computes the exact condition number instead of an estimate ([#33547]). +* `UniformScaling` objects may now be exponentiated such that `(a*I)^x = a^x * I`. + +#### Markdown + +* Tables now have the `align` attribute set when `show`n as HTML ([#33849]). + +#### Random + +* `AbstractRNG`s now behave like scalars when used in broadcasting ([#33213]). +* The performance of `rand(::Tuple)` is improved in some cases ([#32208]). As a consequence, the + stream of generated values produced for a given seed has changed. + +#### REPL + +* The attributes of the implicit `IOContext` used by the REPL to display objects can be + modified by the user (experimental feature) ([#29249]). + +#### SparseArrays + +* The return value of `zero(x::AbstractSparseArray)` has no stored zeros anymore ([#31835]). + Previously, it would have stored zeros wherever `x` had them. This makes the operation + constant time instead of `O()`. +* Products involving sparse arrays now allow more general sparse `eltype`s, such as `StaticArrays` ([#33205]) + + +[#29249]: https://github.com/JuliaLang/julia/issues/29249 +[#30115]: https://github.com/JuliaLang/julia/issues/30115 +[#31835]: https://github.com/JuliaLang/julia/issues/31835 +[#32208]: https://github.com/JuliaLang/julia/issues/32208 +[#32448]: https://github.com/JuliaLang/julia/issues/32448 +[#32739]: https://github.com/JuliaLang/julia/issues/32739 +[#32968]: https://github.com/JuliaLang/julia/issues/32968 +[#33012]: https://github.com/JuliaLang/julia/issues/33012 +[#33040]: https://github.com/JuliaLang/julia/issues/33040 +[#33053]: https://github.com/JuliaLang/julia/issues/33053 +[#33090]: https://github.com/JuliaLang/julia/issues/33090 +[#33091]: https://github.com/JuliaLang/julia/issues/33091 +[#33113]: https://github.com/JuliaLang/julia/issues/33113 +[#33119]: https://github.com/JuliaLang/julia/issues/33119 +[#33129]: https://github.com/JuliaLang/julia/issues/33129 +[#33158]: https://github.com/JuliaLang/julia/issues/33158 +[#33205]: https://github.com/JuliaLang/julia/issues/33205 +[#33206]: https://github.com/JuliaLang/julia/issues/33206 +[#33211]: https://github.com/JuliaLang/julia/issues/33211 +[#33213]: https://github.com/JuliaLang/julia/issues/33213 +[#33291]: https://github.com/JuliaLang/julia/issues/33291 +[#33300]: https://github.com/JuliaLang/julia/issues/33300 +[#33372]: https://github.com/JuliaLang/julia/issues/33372 +[#33403]: https://github.com/JuliaLang/julia/issues/33403 +[#33437]: https://github.com/JuliaLang/julia/issues/33437 +[#33533]: https://github.com/JuliaLang/julia/issues/33533 +[#33542]: https://github.com/JuliaLang/julia/issues/33542 +[#33547]: https://github.com/JuliaLang/julia/issues/33547 +[#33568]: https://github.com/JuliaLang/julia/issues/33568 +[#33849]: https://github.com/JuliaLang/julia/issues/33849 +[#33910]: https://github.com/JuliaLang/julia/issues/33910 +[#33946]: https://github.com/JuliaLang/julia/issues/33946 +[#34033]: https://github.com/JuliaLang/julia/issues/34033 + +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]). +* Fixed `repr` such that it displays `Time` as it would be entered in Julia ([#32103]). + +#### 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 +[#32103]: https://github.com/JuliaLang/julia/issues/32103 +[#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 + +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]). +* The new `map!(f, values(::AbstractDict))` method allows to modify in-place values of a dictionary ([#31223]). + +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/v1/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/v1/stdlib/REPL/#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/v1/manual/parallel-computing/#man-multithreading-1). + 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/v1/manual/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/v1/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/v1/manual/parallel-computing/#Channels-1 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/v1/manual/parallel-computing/#Remote-References-and-AbstractChannels-1 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/v1/stdlib/Pkg/ +[sorting functions]: https://docs.julialang.org/en/v1/base/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..d1438a5 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,82 @@ +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/3/license.html) (for strtod and joinpath implementation on Windows) [BSD-3, effectively] +- [Google Benchmark](https://github.com/google/benchmark) (for cyclecount implementation) [Apache 2.0] + +The following components included in Julia `Base` have their own separate licenses: + +- base/ryu/* [Boost] (see [ryu](https://github.com/ulfjack/ryu/blob/master/LICENSE-Boost)) +- 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] + +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..24ec6db --- /dev/null +++ b/Make.inc @@ -0,0 +1,1495 @@ +# -*- 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:= + +# We need python for things like BB triplet recognition and relative path computation. +# We don't really care about version, generally, so just find something that works: +PYTHON := "$(shell which python 2>/dev/null || which python3 2>/dev/null || which python2 2>/dev/null || echo not found)" +PYTHON_SYSTEM := $(shell $(PYTHON) -c 'from __future__ import print_function; import platform; print(platform.system())') + +# If we're running on Cygwin, but using a native-windows Python, we need to use cygpath -w +ifneq ($(and $(filter $(PYTHON_SYSTEM),Windows),$(findstring CYGWIN,$(shell uname))),) +python_cygpath = `cygpath -w $(1)` +else +python_cygpath = $(1) +endif + +# Get a relative path easily +define rel_path +$(shell $(PYTHON) $(call python_cygpath,$(JULIAHOME)/contrib/relative_path.py) $(call python_cygpath,$(1)) $(call python_cygpath,$(2))) +endef + +# 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)/$(call rel_path,$(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 + +# A helper functions for dealing with lazily-evaluated, expensive operations.. Spinning +# up a python process to, for exaxmple, parse a TOML file is expensive, and we must wait +# until the TOML files are on-disk before we can parse them. This means that we cannot +# use `:=` (since we do not want to evaluate these rules now, we want to evaluate them +# when we use them, so we use `=`) however we also do not want to re-evaluate them +# multiple times. So we define a caching mechanism where the rules are still lazily +# evaluated, but we cache the value such that the second time around we don't have to +# re-evaluate them. Usage example: +# +# EXPENSIVE_OPERATION = $(shell prog args...) +# CACHED_RESULT = $(call hit_cache,EXPENSIVE_OPERATION) +# +# The first time you use `$(CACHED_RESULT)`, it will invoke `$(EXPENSIVE_OPERATION)`, +# but after that point, it will not, unless `$(EXPENSIVE_OPERATION)` evaluated to the +# empty string, in which case it will be re-evaluated. +define hit_cache +$(if $(_CACHE-$(1)),,$(eval _CACHE-$(1) := $($(1))))$(_CACHE-$(1)) +endef + +# Calculate relative paths to libdir, private_libdir, datarootdir, and sysconfdir +define cache_rel_path +$(1)_rel_eval = $(call rel_path,$(2),$($(1))) +$(1)_rel = $$(call hit_cache,$(1)_rel_eval) +endef +$(foreach D,libdir private_libdir datarootdir libexecdir docdir sysconfdir includedir,$(eval $(call cache_rel_path,$(D),$(bindir)))) +$(foreach D,build_libdir build_private_libdir,$(eval $(call cache_rel_path,$(D),$(build_bindir)))) + +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 := -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 -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/") +ifneq ($(findstring arm, $(ARCH))$(findstring aarch64, $(ARCH)),) +MCPU := $(subst _,-,$(ARCH)) # Arm prefers MCPU over MARCH +else +MARCH := $(subst _,-,$(ARCH)) +endif +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) +endif + +# Set MCPU-specific flags +ifneq ($(MCPU),) +CC += -mcpu=$(MCPU) +CXX += -mcpu=$(MCPU) +FC += -mcpu=$(MCPU) +JULIA_CPU_TARGET ?= $(MCPU) +endif + +ifneq ($(MARCH)$(MCPU),) +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 +# 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:=-losxunwind +JCPPFLAGS+=-DLIBOSXUNWIND +else +LIBUNWIND:=-lunwind +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 + +# We need python for things like BB triplet recognition. We don't really care +# about version, generally, so just find something that works: +PYTHON := "$(shell which python 2>/dev/null || which python3 2>/dev/null || which python2 2>/dev/null || echo not found)" +PYTHON_SYSTEM := $(shell $(PYTHON) -c 'from __future__ import print_function; import platform; print(platform.system())') + +# If we're running on Cygwin, but using a native-windows Python, we need to use cygpath -w +ifneq ($(and $(filter $(PYTHON_SYSTEM),Windows),$(findstring CYGWIN,$(BUILD_OS))),) +define invoke_python +$(PYTHON) "$$(cygpath -w "$(1)")" +endef +else +define invoke_python +$(PYTHON) "$(1)" +endef +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 $(call invoke_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 $(call invoke_python,$(JULIAHOME)/contrib/normalize_triplet.py) x86_64-linux-gnu),x86_64-linux-gnu) +$(warning normalize_triplet.py appears to be non-functional (used python interpreter "$(PYTHON)"), 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) +endif + +ifeq ($(OS), Darwin) +SHLIB_EXT := dylib +OSLIBS += -framework CoreFoundation +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 or USECLANG=1) +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 + + +# Note: we're passing *FLAGS here computed based on your system compiler to +# clang. If that causes you problems, you might want to build and/or run +# specific clang-sa-* files with clang explicitly selected: +# make CC=~+/../usr/tools/clang CXX=~+/../usr/tools/clang USECLANG=1 analyzegc +# make USECLANG=1 clang-sa-* +CLANGSA_FLAGS := +CLANGSA_CXXFLAGS := +ifeq ($(OS), Darwin) # on new XCode, the files are hidden +CLANGSA_FLAGS += -isysroot $(shell xcode-select -p)/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk +CLANGSA_CXXFLAGS += -isystem $(shell xcode-select -p)/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1 +endif +ifeq ($(USEGCC),1) +# try to help clang find the c++ files for CC by guessing the value for --prefix +# by dropping lib/gcc// from the install directory it reports +CLANGSA_CXXFLAGS += --gcc-toolchain="$(abspath $(shell LANG=C $(CC) -print-search-dirs | grep '^install: ' | sed -e "s/^install: //")/../../../..)" +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 +ifeq (,$(findstring release,$(MAKECMDGOALS))) +ifneq (,$(findstring debug,$(MAKECMDGOALS))) +JULIA_BUILD_MODE := 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)) + +JULIA_SYSIMG_debug := $(build_private_libdir)/sys-debug.$(SHLIB_EXT) +JULIA_SYSIMG_release := $(build_private_libdir)/sys.$(SHLIB_EXT) +JULIA_SYSIMG := $(JULIA_SYSIMG_$(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..9e9f2c8 --- /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"\'"$$(echo $(call rel_path,$(BUILDROOT),$(JULIA_EXECUTABLE)) | tr / '\\')" '%*' > $(BUILDROOT)/julia.bat + chmod a+x $(BUILDROOT)/julia.bat +else +ifndef JULIA_VAGRANT_BUILD + @ln -sf $(call rel_path,$(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-bc : julia-stdlib julia-base julia-ui-$(JULIA_BUILD_MODE) | $(build_private_libdir) + @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT) -f sysimage.mk sysimg-bc 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))) --startup-file=no' + +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 + @JULIA_BINDIR=$(call cygpath_w,$(build_bindir)) $(call spawn, $(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-9jl +endif +ifeq ($(OS),Darwin) +JL_PRIVATE_LIBS-$(USE_SYSTEM_LIBUNWIND) += libosxunwind +else +JL_PRIVATE_LIBS-$(USE_SYSTEM_LIBUNWIND) += libunwind +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)" FC="$(FC)" $(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-$(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 +endif + cd $(BUILDROOT) && $(TAR) zcvf $(JULIA_BINARYDIST_FILENAME).tar.gz julia-$(JULIA_COMMIT) + +exe: + # run Inno Setup to compile installer + $(call spawn,$(JULIAHOME)/dist-extras/inno/iscc.exe /DAppVersion=$(JULIA_VERSION) /DSourceDir="$(call cygpath_w,$(BUILDROOT)/julia-$(JULIA_COMMIT))" /DRepoDir="$(call cygpath_w,$(JULIAHOME))" /F"$(JULIA_BINARYDIST_FILENAME)" /O"$(call cygpath_w,$(BUILDROOT))" $(call cygpath_w,$(JULIAHOME)/contrib/windows/build-installer.iss)) + chmod a+x "$(BUILDROOT)/$(JULIA_BINARYDIST_FILENAME).exe" + +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 test-* test-revise-* \ + 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) + +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) + @([ $$(( $$(date +%s) - $$(date -r $(build_private_libdir)/sys.$(SHLIB_EXT) +%s) )) -le 100 ] && \ + printf '\033[93m HINT The system image was recently rebuilt. Are you aware of the test-revise-* targets? See CONTRIBUTING.md. \033[0m\n') || true + @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT)/test $* JULIA_BUILD_MODE=$(JULIA_BUILD_MODE) + +test-revise-%: + @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT)/test revise-$* 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://www.jrsoftware.org/download.php/is.exe && \ + chmod a+x is.exe && \ + $(call spawn, $(JULIAHOME)/dist-extras/is.exe /DIR="$(call cygpath_w,$(JULIAHOME)/dist-extras/inno)" /PORTABLE=1 /CURRENTUSER /VERYSILENT) + +# 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: + @printf $(JULCOLOR)' ==> ./julia binary sizes\n'$(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))) + @printf $(JULCOLOR)' ==> ./julia launch speedtest\n'$(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..a0274f4 --- /dev/null +++ b/NEWS.md @@ -0,0 +1,298 @@ +Julia v1.5 Release Notes +======================== + +New language features +--------------------- + +* Macro calls `@foo {...}` can now also be written `@foo{...}` (without the space) ([#34498]). +* `⨟` is now parsed as a binary operator with times precedence. It can be entered in the REPL + with `\bbsemi` followed by TAB ([#34722]). +* `±` and `∓` are now unary operators as well, like `+` or `-`. Attention has to be paid in + macros and matrix constructors, which are whitespace sensitive, because expressions like + `[a ±b]` now get parsed as `[a ±(b)]` instead of `[±(a, b)]` ([#34200]). +* Passing an identifier `x` by itself as a keyword argument or named tuple element + is equivalent to `x=x`, implicitly using the name of the variable as the keyword + or named tuple field name. + Similarly, passing an `a.b` expression uses `b` as the keyword or field name ([#29333]). +* Support for Unicode 13.0.0 (via utf8proc 2.5) ([#35282]). +* The compiler optimization level can now be set per-module using the experimental macro + `Base.Experimental.@optlevel n`. For code that is not performance-critical, setting + this to 0 or 1 can provide significant latency improvements ([#34896]). + +Language changes +---------------- + +* The interactive REPL now uses "soft scope" for top-level expressions: an assignment inside a + scope block such as a `for` loop automatically assigns to a global variable if one has been + defined already. This matches the behavior of Julia versions 0.6 and prior, as well as + [IJulia](https://github.com/JuliaLang/IJulia.jl). + Note that this only affects expressions interactively typed or pasted directly into the + default REPL ([#28789], [#33864]). +* Outside of the REPL (e.g. in a file), assigning to a variable within a top-level scope + block is considered ambiguous if a global variable with the same name exists. + A warning is given if that happens, to alert you that the code will work differently + than in the REPL. + A new command line option `--warn-scope` controls this warning ([#33864]). +* Converting arbitrary tuples to `NTuple`, e.g. `convert(NTuple, (1, ""))` now gives an error, + where it used to be incorrectly allowed. This is because `NTuple` refers only to homogeneous + tuples (this meaning has not changed) ([#34272]). +* The syntax `(;)` (which was deprecated in v1.4) now creates an empty named tuple ([#30115]). +* `@inline` macro can now be applied to short-form anonymous functions ([#34953]). +* In triple-quoted string literals, whitespace stripping is now done before processing + escape sequences instead of after. For example, the syntax + ``` + """ + a\n b""" + ``` + used to yield the string " a\nb", since the single space before `b` set the indent level. + Now the result is "a\n b", since the space before `b` is no longer considered to occur + at the start of a line. The old behavior is considered a bug ([#35001]). +* `<:` and `>:` can now be broadcasted over arrays with `.<:` and `.>:` ([#35085]) +* The line number of function definitions is now added by the parser as an + additional `LineNumberNode` at the start of each function body ([#35138]). +* Statements of the form `a'` now get lowered to `var"'"(a)` instead of `Base.adjoint(a)`. This + allows for shadowing this function in local scopes, although this is generally discouraged. + By default, Base exports `var"'"` as an alias of `Base.adjoint`, so custom types should still + extend `Base.adjoint` ([#34634]). + +Compiler/Runtime improvements +----------------------------- + +* Immutable structs (including tuples) that contain references can now be allocated + on the stack, and allocated inline within arrays and other structs ([#33886]). + This significantly reduces the number of heap allocations in some workloads. + Code that requires assumptions about object layout and addresses (usually for + interoperability with C or other languages) might need to be updated; for + example any object that needs a stable address should be a `mutable struct`. + As a result, Array `view`s no longer allocate ([#34126]). + +Command-line option changes +--------------------------- + +* Deprecation warnings are no longer shown by default. i.e. if the `--depwarn=...` flag is + not passed it defaults to `--depwarn=no`. The warnings are printed from tests run by + `Pkg.test()` ([#35362]). +* Color now defaults to on when stdout and stderr are TTYs ([#34347]). +* `-t N`, `--threads N` starts Julia with `N` threads. This option takes precedence over + `JULIA_NUM_THREADS`. The specified number of threads also propagates to worker + processes spawned using the `-p`/`--procs` or `--machine-file` command line arguments. + In order to set number of threads for worker processes spawned with `addprocs` use the + `exeflags` keyword argument, e.g. ```addprocs(...; exeflags=`--threads 4`)``` ([#35108]). + +Multi-threading changes +----------------------- + +* Parts of the multi-threading API are now considered stable, with caveats. + This includes all documented identifiers from `Base.Threads` except the + `atomic_` operations. +* `@threads` now allows an optional schedule argument. Use `@threads :static ...` to + ensure that the same schedule will be used as in past versions; the default schedule + is likely to change in the future. + +Build system changes +-------------------- + +* The build system now contains a pure-make caching system for expanding expensive operations at the latest + possible moment, while still expanding it only once ([#35626]). + +New library functions +--------------------- + +* Packages can now provide custom hints to help users resolve errors by using the + experimental `Base.Experimental.register_error_hint` function. + Packages that define custom exception types can support hints by calling the + `Base.Experimental.show_error_hints` from their `showerror` method ([#35094]). +* The `@ccall` macro has been added to Base. It is a near drop-in replacement for `ccall` with more Julia-like syntax. It also wraps the new `foreigncall` API for varargs of different types, though it lacks the capability to specify an LLVM calling convention ([#32748]). +* New functions `mergewith` and `mergewith!` supersede `merge` and `merge!` with `combine` + argument. They don't have the restriction for `combine` to be a `Function` and also + provide one-argument method that returns a closure. The old methods of `merge` and + `merge!` are still available for backward compatibility ([#34296]). +* The new `isdisjoint` function indicates whether two collections are disjoint ([#34427]). +* Add function `ismutable` and deprecate `isimmutable` to check whether something is mutable ([#34652]). +* `include` now accepts an optional `mapexpr` first argument to transform the parsed + expressions before they are evaluated ([#34595]). +* New function `bitreverse` for reversing the order of bits in a fixed-width integer ([#34791]). +* New function `bitrotate(x, k)` for rotating the bits in a fixed-width integer ([#33937]). +* New function `contains(haystack, needle)` and its one argument partially applied form have been added, it acts like `occursin(needle, haystack)` ([#35132]). +* New function `Base.exit_on_sigint` is added to control if `InterruptException` is + thrown by Ctrl-C ([#29411]). +* New function `popat!(vector, index, [default])` for removing an element at an arbitrary index from a `Vector` ([#35513], [#36070]). + +New library features +-------------------- + +* Function composition now works also on one argument `∘(f) = f` (#34251). +* One argument methods `startswith(x)` and `endswith(x)` have been added, returning partially-applied versions of the functions, similar to existing methods like `isequal(x)` ([#33193]). +* `isapprox` (or `≈`) now has a one-argument "curried" method `isapprox(x)` which returns a function, like `isequal` (or `==`) ([#32305]). +* `@NamedTuple{key1::Type1, ...}` macro for convenient `NamedTuple` declarations ([#34548]). +* `Ref{NTuple{N,T}}` can be passed to `Ptr{T}`/`Ref{T}` `ccall` signatures ([#34199]). +* `x::Signed % Unsigned` and `x::Unsigned % Signed` are supported for integer bitstypes. +* `signed(unsigned_type)` is supported for integer bitstypes, `unsigned(signed_type)` has been supported. +* `accumulate`, `cumsum`, and `cumprod` now support `Tuple` ([#34654]) and arbitrary iterators ([#34656]). +* In `splice!` with no replacement, values to be removed can now be specified with an + arbitrary iterable (instead of a `UnitRange`) ([#34524]). +* The `@view` and `@views` macros now support the `a[begin]` syntax that was introduced in Julia 1.4 ([#35289]). +* `open` for files now accepts a keyword argument `lock` controlling whether file operations + will acquire locks for safe multi-threaded access. Setting it to `false` provides better + performance when only one thread will access the file ([#35426]). +* The introspection macros (`@which`, `@code_typed`, etc.) now work with `do`-block syntax ([#35283]) and with dot syntax ([#35522]). +* `count` now accepts the `dims` keyword. +* new in-place `count!` function similar to `sum!`. +* `peek` is now exported and accepts a type to peek from a stream ([#28811]). + +Standard library changes +------------------------ + +* Empty ranges now compare equal, regardless of their startpoint and step ([#32348]). +* A 1-d `Zip` iterator (where `Base.IteratorSize` is `Base.HasShape{1}()`) with defined length of `n` has now also size of `(n,)` (instead of throwing an error with truncated iterators) ([#29927]). +* The `@timed` macro now returns a `NamedTuple` ([#34149]). +* New `supertypes(T)` function returns a tuple of all supertypes of `T` ([#34419]). +* Views of builtin ranges are now recomputed ranges (like indexing returns) instead of + `SubArray`s ([#26872]). +* Sorting-related functions such as `sort` that take the keyword arguments `lt`, `rev`, `order` + and `by` now do not discard `order` if `by` or `lt` are passed. In the former case, the + order from `order` is used to compare the values of `by(element)`. In the latter case, + any order different from `Forward` or `Reverse` will raise an error about the + ambiguity. +* `close` on a file (`IOStream`) can now throw an exception if an error occurs when trying + to flush buffered data to disk ([#35303]). +* The large `StridedArray` `Union` now has special printing to avoid printing out its entire + contents ([#31149]). + +#### LinearAlgebra + +* The BLAS submodule now supports the level-2 BLAS subroutine `hpmv!` ([#34211]). +* `normalize` now supports multidimensional arrays ([#34239]). +* `lq` factorizations can now be used to compute the minimum-norm solution to under-determined systems ([#34350]). +* `sqrt(::Hermitian)` now treats slightly negative eigenvalues as zero for nearly semidefinite matrices, and accepts a new `rtol` keyword argument for this tolerance ([#35057]). +* The BLAS submodule now supports the level-2 BLAS subroutine `spmv!` ([#34320]). +* The BLAS submodule now supports the level-1 BLAS subroutine `rot!` ([#35124]). +* New generic `rotate!(x, y, c, s)` and `reflect!(x, y, c, s)` functions ([#35124]). + +#### Markdown + +* In docstrings, a level-1 markdown header "Extended help" is now interpreted as a marker + dividing "brief help" from "extended help". The REPL help mode only shows the brief help + (the content before the "Extended help" header) by default; prepend the expression with '?' + (in addition to the one that enters the help mode) to see the full docstring ([#25930]). + +#### Random + +* `randn!(::MersenneTwister, ::Array{Float64})` is faster, and as a result, for a given state of the RNG, + the corresponding generated numbers have changed ([#35078]). +* `rand!(::MersenneTwister, ::Array{Bool})` is faster, and as a result, for a given state of the RNG, + the corresponding generated numbers have changed ([#33721]). +* A new faster algorithm ("nearly division less") is used for generating random numbers + within a range ([#29240]). As a result, the streams of generated numbers are changed + (for ranges, like in `rand(1:9)`, and for collections in general, like in `rand([1, 2, 3])`). + Also, for performance, the undocumented property that, given a seed and `a, b` of type `Int`, + `rand(a:b)` produces the same stream on 32 and 64 bits architectures, is dropped. + +#### REPL + + +#### SparseArrays + +* `lu!` accepts `UmfpackLU` as an argument to make use of its symbolic factorization. +* The `trim` keyword argument for the functions `fkeep!`, `tril!`, `triu!`, + `droptol!`,`dropzeros!` and `dropzeros` has been removed in favour of always + trimming. Calling these with `trim=false` could result in invalid sparse + arrays. + +#### Dates + +* The `eps` function now accepts `TimeType` types ([#31487]). +* The `zero` function now accepts `TimeType` types ([#35554]). + +#### Statistics + + +#### Sockets + +* Joining and leaving UDP multicast groups on a `UDPSocket` is now supported + through `join_multicast_group()` and `leave_multicast_group()` ([#35521]). + +#### Distributed + +* `launch_on_machine` now supports and parses ipv6 square-bracket notation ([#34430]). + +Deprecated or removed +--------------------- + +External dependencies +--------------------- + +* OpenBLAS has been updated to v0.3.9 ([#35113]). + +Tooling Improvements +--------------------- + + + +[#25930]: https://github.com/JuliaLang/julia/issues/25930 +[#26872]: https://github.com/JuliaLang/julia/issues/26872 +[#28789]: https://github.com/JuliaLang/julia/issues/28789 +[#28811]: https://github.com/JuliaLang/julia/issues/28811 +[#29240]: https://github.com/JuliaLang/julia/issues/29240 +[#29333]: https://github.com/JuliaLang/julia/issues/29333 +[#29411]: https://github.com/JuliaLang/julia/issues/29411 +[#29927]: https://github.com/JuliaLang/julia/issues/29927 +[#30115]: https://github.com/JuliaLang/julia/issues/30115 +[#31149]: https://github.com/JuliaLang/julia/issues/31149 +[#31487]: https://github.com/JuliaLang/julia/issues/31487 +[#32305]: https://github.com/JuliaLang/julia/issues/32305 +[#32348]: https://github.com/JuliaLang/julia/issues/32348 +[#32748]: https://github.com/JuliaLang/julia/issues/32748 +[#33193]: https://github.com/JuliaLang/julia/issues/33193 +[#33721]: https://github.com/JuliaLang/julia/issues/33721 +[#33864]: https://github.com/JuliaLang/julia/issues/33864 +[#33886]: https://github.com/JuliaLang/julia/issues/33886 +[#33937]: https://github.com/JuliaLang/julia/issues/33937 +[#34126]: https://github.com/JuliaLang/julia/issues/34126 +[#34149]: https://github.com/JuliaLang/julia/issues/34149 +[#34199]: https://github.com/JuliaLang/julia/issues/34199 +[#34200]: https://github.com/JuliaLang/julia/issues/34200 +[#34211]: https://github.com/JuliaLang/julia/issues/34211 +[#34239]: https://github.com/JuliaLang/julia/issues/34239 +[#34272]: https://github.com/JuliaLang/julia/issues/34272 +[#34296]: https://github.com/JuliaLang/julia/issues/34296 +[#34320]: https://github.com/JuliaLang/julia/issues/34320 +[#34347]: https://github.com/JuliaLang/julia/issues/34347 +[#34350]: https://github.com/JuliaLang/julia/issues/34350 +[#34419]: https://github.com/JuliaLang/julia/issues/34419 +[#34427]: https://github.com/JuliaLang/julia/issues/34427 +[#34430]: https://github.com/JuliaLang/julia/issues/34430 +[#34498]: https://github.com/JuliaLang/julia/issues/34498 +[#34524]: https://github.com/JuliaLang/julia/issues/34524 +[#34548]: https://github.com/JuliaLang/julia/issues/34548 +[#34595]: https://github.com/JuliaLang/julia/issues/34595 +[#34634]: https://github.com/JuliaLang/julia/issues/34634 +[#34652]: https://github.com/JuliaLang/julia/issues/34652 +[#34654]: https://github.com/JuliaLang/julia/issues/34654 +[#34656]: https://github.com/JuliaLang/julia/issues/34656 +[#34722]: https://github.com/JuliaLang/julia/issues/34722 +[#34791]: https://github.com/JuliaLang/julia/issues/34791 +[#34896]: https://github.com/JuliaLang/julia/issues/34896 +[#34953]: https://github.com/JuliaLang/julia/issues/34953 +[#35001]: https://github.com/JuliaLang/julia/issues/35001 +[#35057]: https://github.com/JuliaLang/julia/issues/35057 +[#35078]: https://github.com/JuliaLang/julia/issues/35078 +[#35085]: https://github.com/JuliaLang/julia/issues/35085 +[#35094]: https://github.com/JuliaLang/julia/issues/35094 +[#35108]: https://github.com/JuliaLang/julia/issues/35108 +[#35113]: https://github.com/JuliaLang/julia/issues/35113 +[#35124]: https://github.com/JuliaLang/julia/issues/35124 +[#35132]: https://github.com/JuliaLang/julia/issues/35132 +[#35138]: https://github.com/JuliaLang/julia/issues/35138 +[#35282]: https://github.com/JuliaLang/julia/issues/35282 +[#35283]: https://github.com/JuliaLang/julia/issues/35283 +[#35289]: https://github.com/JuliaLang/julia/issues/35289 +[#35303]: https://github.com/JuliaLang/julia/issues/35303 +[#35362]: https://github.com/JuliaLang/julia/issues/35362 +[#35426]: https://github.com/JuliaLang/julia/issues/35426 +[#35513]: https://github.com/JuliaLang/julia/issues/35513 +[#35521]: https://github.com/JuliaLang/julia/issues/35521 +[#35522]: https://github.com/JuliaLang/julia/issues/35522 +[#35554]: https://github.com/JuliaLang/julia/issues/35554 +[#35626]: https://github.com/JuliaLang/julia/issues/35626 +[#36070]: https://github.com/JuliaLang/julia/issues/36070 diff --git a/README.md b/README.md new file mode 100644 index 0000000..8421065 --- /dev/null +++ b/README.md @@ -0,0 +1,150 @@ + + + +Code coverage: +[![coveralls][coveralls-img]](https://coveralls.io/r/JuliaLang/julia?branch=master) +[![codecov][codecov-img]](https://codecov.io/github/JuliaLang/julia?branch=master) + +Documentation: +[![version 1][docs-img]](https://docs.julialang.org) + +[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 +[docs-img]: https://img.shields.io/badge/docs-v1-blue.svg + +## 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/) +- [**Learning resources**](https://julialang.org/learning/) + +## 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.3.0 + +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/v1/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..4cda8f1 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +1.5.2 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..c5dd342 --- /dev/null +++ b/base/Base.jl @@ -0,0 +1,427 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +baremodule Base + +using Core.Intrinsics, Core.IR + +# to start, we're going to use a very simple definition of `include` +# that doesn't require any function (except what we can get from the `Core` top-module) +const _included_files = Array{Tuple{Module,String},1}() +function include(mod::Module, path::String) + ccall(:jl_array_grow_end, Cvoid, (Any, UInt), _included_files, UInt(1)) + Core.arrayset(true, _included_files, (mod, ccall(:jl_prepend_cwd, Any, (Any,), path)), arraylen(_included_files)) + Core.println(path) + ccall(:jl_uv_flush, Nothing, (Ptr{Nothing},), Core.io_pointer(Core.stdout)) + Core.include(mod, path) +end +include(path::String) = include(Base, path) + +# from now on, this is now a top-module for resolving syntax +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)) + +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("iddict.jl") +include("idset.jl") + +include("iterators.jl") +using .Iterators: zip, enumerate, only +using .Iterators: Flatten, Filter, product # for generators + +include("namedtuple.jl") + +# numeric operations +include("hashing.jl") +include("rounding.jl") +using .Rounding +include("div.jl") +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") + +# Concurrency +include("linked_list.jl") +include("condition.jl") +include("threads.jl") +include("lock.jl") +include("channels.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("ttyhascolor.jl") +include("grisu/grisu.jl") +include("secretbuffer.jl") + +# core math functions +include("floatfuncs.jl") +include("math.jl") +using .Math +const (√)=sqrt +const (∛)=cbrt + +# now switch to a simple, race-y TLS, relative include for the rest of Base +delete_method(which(include, (Module, String))) +let SOURCE_PATH = "" + global function include(mod::Module, path::String) + prev = SOURCE_PATH + path = normpath(joinpath(dirname(prev), path)) + Core.println(path) + ccall(:jl_uv_flush, Nothing, (Ptr{Nothing},), Core.io_pointer(Core.stdout)) + push!(_included_files, (mod, abspath(path))) + SOURCE_PATH = path + result = Core.include(mod, path) + SOURCE_PATH = prev + return result + end +end + +# 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 +include("gmp.jl") +using .GMP + +# float printing: requires BigInt +include("ryu/Ryu.jl") +using .Ryu + +# BigFloats +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 + +# metaprogramming +include("meta.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("timing.jl") +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 + +# finally, now make `include` point to the full version +for m in methods(include) + delete_method(m) +end +# These functions are duplicated in client.jl/include(::String) for +# nicer stacktraces. Modifications here have to be backported there +include(mod::Module, _path::AbstractString) = include(identity, mod, _path) +function include(mapexpr::Function, mod::Module, _path::AbstractString) + 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) + if mapexpr === identity + result = ccall(:jl_load, Any, (Any, Cstring), mod, path) + else + result = ccall(:jl_load_rewrite, Any, (Any, Cstring, Any), mod, path, mapexpr) + end + finally + if prev === nothing + delete!(tls, :SOURCE_PATH) + else + tls[:SOURCE_PATH] = prev + end + end + return result +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 + + +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..f8574ad --- /dev/null +++ b/base/Enums.jl @@ -0,0 +1,210 @@ +# 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, m::MIME"text/plain", t::Type{<:Enum}) + if isconcretetype(t) + 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 + else + invoke(show, Tuple{IO, MIME"text/plain", Type}, io, m, t) + 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 = (Any[ $(esc(typename))(v) for v in $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..b384575 --- /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 index($$1, "ERROR_") == 0 ? "const $$1 = Cint($$2)" : "const $$1 = UInt32($$2)"' | 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..76675dc --- /dev/null +++ b/base/abstractarray.jl @@ -0,0 +1,2322 @@ +# 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(eachindex.(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, a DimensionMismatch exception +will be thrown. +# 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 +``` +""" +function stride(A::AbstractArray, k::Integer) + st = strides(A) + k ≤ ndims(A) && return st[k] + return sum(st .* size(A)) +end + +@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]) +Float64[] + +julia> empty([1.0, 2.0, 3.0], String) +String[] +``` +""" +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!`. +""" +function copy!(dst::AbstractVector, src::AbstractVector) + if length(dst) != length(src) + resize!(dst, length(src)) + end + for i in eachindex(dst, src) + @inbounds dst[i] = src[i] + end + dst +end + +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) -> 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 +``` +""" +function copyto!(dest::AbstractArray, src::AbstractArray) + isempty(src) && return dest + src′ = unalias(dest, src) + copyto_unaliased!(IndexStyle(dest), dest, IndexStyle(src′), src′) +end + +function copyto!(deststyle::IndexStyle, dest::AbstractArray, srcstyle::IndexStyle, src::AbstractArray) + isempty(src) && return dest + src′ = unalias(dest, src) + copyto_unaliased!(deststyle, dest, srcstyle, src′) +end + +function copyto_unaliased!(deststyle::IndexStyle, dest::AbstractArray, srcstyle::IndexStyle, src::AbstractArray) + isempty(src) && return dest + destinds, srcinds = LinearIndices(dest), LinearIndices(src) + idf, isf = first(destinds), first(srcinds) + Δi = idf - isf + (checkbounds(Bool, destinds, isf+Δi) & checkbounds(Bool, destinds, last(srcinds)+Δi)) || + throw(BoundsError(dest, srcinds)) + if deststyle isa IndexLinear + if srcstyle isa IndexLinear + # Single-index implementation + @inbounds for i in srcinds + dest[i + Δi] = src[i] + end + else + # Dual-index implementation + i = idf - 1 + @inbounds for a in src + dest[i+=1] = a + end + end + else + iterdest, itersrc = eachindex(dest), eachindex(src) + if iterdest == itersrc + # Shared-iterator implementation + for I in iterdest + @inbounds dest[I] = src[I] + end + else + # Dual-iterator implementation + ret = iterate(iterdest) + @inbounds for a in src + idx, state = ret + dest[idx] = a + ret = iterate(iterdest, state) + end + end + 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 + +function copyto_axcheck!(dest, src) + @noinline checkaxs(axd, axs) = axd == axs || throw(DimensionMismatch("axes must agree, got $axd and $axs")) + + checkaxs(axes(dest), axes(src)) + copyto!(dest, src) +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) + _memory_offset(x, i) +end + +# The distance from pointer(x) to the element at x[I...] in bytes +_memory_offset(x::DenseArray, I::Vararg{Any,N}) where {N} = (_to_linear_index(x, I...) - first(LinearIndices(x)))*elsize(x) +function _memory_offset(x::AbstractArray, I::Vararg{Any,N}) where {N} + J = _to_subscript_indices(x, I...) + return sum(map((i, s, o)->s*(i-o), J, strides(x), Tuple(first(CartesianIndices(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::Integer) = i +_to_linear_index(A::AbstractVector, i::Integer, I::Integer...) = i +_to_linear_index(A::AbstractArray) = 1 +_to_linear_index(A::AbstractArray, I::Integer...) = (@_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::Integer) = (@_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::Integer) where {T} = () +_to_subscript_indices(A::AbstractArray{T,0}, I::Integer...) where {T} = () +function _to_subscript_indices(A::AbstractArray{T,N}, I::Integer...) 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) + +Return the underlying "parent array”. This parent array of objects of types `SubArray`, `ReshapedArray` +or `LinearAlgebra.Transpose` is what was passed as an argument to `view`, `reshape`, `transpose`, etc. +during object creation. If the input is not a wrapped object, return the input itself. + +# 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 + +julia> x = Matrix(undef, 3, 0) # x = [] would have created an Array{Any, 1}, but need an Array{Any, 2} +3×0 Array{Any,2} + +julia> hcat(x, [1; 2; 3]) +3×1 Array{Any,2}: + 1 + 2 + 3 +``` +""" +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 +``` +""" +function map!(f::F, dest::AbstractArray, As::AbstractArray...) where {F} + isempty(As) && throw(ArgumentError( + """map! requires at least one "source" argument""")) + map_n!(f, dest, As) +end + +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) + first_linear = first(LinearIndices(linear_to_key)) + 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 + first_linear && 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..32bd3c2 --- /dev/null +++ b/base/abstractarraymath.jl @@ -0,0 +1,502 @@ +# 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 eachindex(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(i, d, map(Slice, axes(A))...)) +@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)")) + +_negreperr(n) = throw(ArgumentError("number of $n repetitions" * + "cannot be negative")) + +@noinline function _repeat(A::AbstractArray, inner, outer) + any(<(0), inner) && _negreperr("inner") + any(<(0), outer) && _negreperr("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(isequal(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(isequal(1), outer) + return R + end + src_indices = [1:n for n in inner_shape] + dest_indices = copy(src_indices) + for i in eachindex(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 `AbstractVector` views. + +See also [`eachcol`](@ref) and [`eachslice`](@ref). + +!!! compat "Julia 1.1" + This function requires at least Julia 1.1. + +# Example + +```jldoctest +julia> a = [1 2; 3 4] +2×2 Array{Int64,2}: + 1 2 + 3 4 + +julia> first(eachrow(a)) +2-element view(::Array{Int64,2}, 1, :) with eltype Int64: + 1 + 2 + +julia> collect(eachrow(a)) +2-element Array{SubArray{Int64,1,Array{Int64,2},Tuple{Int64,Base.Slice{Base.OneTo{Int64}}},true},1}: + [1, 2] + [3, 4] +``` +""" +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 `AbstractVector` views. + +See also [`eachrow`](@ref) and [`eachslice`](@ref). + +!!! compat "Julia 1.1" + This function requires at least Julia 1.1. + +# Example + +```jldoctest +julia> a = [1 2; 3 4] +2×2 Array{Int64,2}: + 1 2 + 3 4 + +julia> first(eachcol(a)) +2-element view(::Array{Int64,2}, :, 1) with eltype Int64: + 1 + 3 + +julia> collect(eachcol(a)) +2-element Array{SubArray{Int64,1,Array{Int64,2},Tuple{Base.Slice{Base.OneTo{Int64}},Int64},true},1}: + [1, 3] + [2, 4] +``` +""" +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")) + inds_before = ntuple(d->(:), dim-1) + inds_after = ntuple(d->(:), ndims(A)-dim) + return (view(A, inds_before..., i, inds_after...) for i in axes(A, dim)) +end diff --git a/base/abstractdict.jl b/base/abstractdict.jl new file mode 100644 index 0000000..b0f5784 --- /dev/null +++ b/base/abstractdict.jl @@ -0,0 +1,574 @@ +# 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.first, secret_table_token) + if v !== secret_table_token + return valcmp(v, p.second) + 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. +When the keys are stored internally in a hash table, +as is the case for `Dict`, +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': ASCII/Unicode U+0061 (category Ll: Letter, lowercase) + 'b': ASCII/Unicode U+0062 (category Ll: Letter, lowercase) +``` +""" +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. +When the values are stored internally in a hash table, +as is the case for `Dict`, +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 + +""" + mergewith!(combine, d::AbstractDict, others::AbstractDict...) -> d + mergewith!(combine) + merge!(combine, d::AbstractDict, others::AbstractDict...) -> d + +Update collection with pairs from the other collections. +Values with the same key will be combined using the +combiner function. The curried form `mergewith!(combine)` returns the +function `(args...) -> mergewith!(combine, args...)`. + +Method `merge!(combine::Union{Function,Type}, args...)` as an alias of +`mergewith!(combine, args...)` is still available for backward +compatibility. + +!!! compat "Julia 1.5" + `mergewith!` requires Julia 1.5 or later. + +# Examples +```jldoctest +julia> d1 = Dict(1 => 2, 3 => 4); + +julia> d2 = Dict(1 => 4, 4 => 5); + +julia> mergewith!(+, d1, d2); + +julia> d1 +Dict{Int64,Int64} with 3 entries: + 4 => 5 + 3 => 4 + 1 => 6 + +julia> mergewith!(-, d1, d1); + +julia> d1 +Dict{Int64,Int64} with 3 entries: + 4 => 0 + 3 => 0 + 1 => 0 + +julia> foldl(mergewith!(+), [d1, d2]; init=Dict{Int64,Int64}()) +Dict{Int64,Int64} with 3 entries: + 4 => 5 + 3 => 0 + 1 => 4 +``` +""" +function mergewith!(combine, 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 + +mergewith!(combine) = (args...) -> mergewith!(combine, args...) + +merge!(combine::Callable, args...) = mergewith!(combine, args...) + +""" + 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...) + +""" + mergewith(combine, d::AbstractDict, others::AbstractDict...) + mergewith(combine) + 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. The curried form `mergewith(combine)` returns the function +`(args...) -> mergewith(combine, args...)`. + +Method `merge(combine::Union{Function,Type}, args...)` as an alias of +`mergewith(combine, args...)` is still available for backward compatibility. + +!!! compat "Julia 1.5" + `mergewith` requires Julia 1.5 or later. + +# 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> mergewith(+, a, b) +Dict{String,Float64} with 3 entries: + "bar" => 4753.0 + "baz" => 17.0 + "foo" => 0.0 + +julia> ans == mergewith(+)(a, b) +true +``` +""" +mergewith(combine, d::AbstractDict, others::AbstractDict...) = + mergewith!(combine, _typeddict(d, others...), others...) +mergewith(combine) = (args...) -> mergewith(combine, args...) +merge(combine::Callable, 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) + for pair in d + if f(pair) + df[pair.first] = pair.second + 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) + 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...)) + +get!(t::AbstractDict, key, default) = get!(() -> default, t, key) +function get!(default::Callable, t::AbstractDict{K,V}, key) where K where V + haskey(t, key) && return t[key] + val = default() + t[key] = val + return val +end + +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)) + +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 + +""" + 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 value type +of `dict` then it will be converted to the value type if possible and otherwise raise an error. + +!!! compat "Julia 1.2" + `map!(f, values(dict::AbstractDict))` requires Julia 1.2 or later. + +# 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)) +Base.ValueIterator for a Dict{Symbol,Int64} with 2 entries. Values: + 0 + 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..d757df5 --- /dev/null +++ b/base/abstractset.jl @@ -0,0 +1,425 @@ +# 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{Int64} with 3 elements: + 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{Int64} with 5 elements: + 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{Int64} with 1 element: + 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{Int64} with 1 element: + 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])) +Int64[] +``` +""" +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, ⊆, ⊇ + +const FASTIN_SET_THRESHOLD = 70 + +function issubset(l, r) + if haslength(r) && (isa(l, AbstractSet) || !hasfastin(r)) + rlen = length(r) # conditions above make this length computed only when needed + # check l for too many unique elements + if isa(l, AbstractSet) && length(l) > rlen + return false + end + # when `in` would be too slow and r is big enough, convert it to a Set + # this threshold was empirically determined (cf. #26198) + if !hasfastin(r) && rlen > FASTIN_SET_THRESHOLD + return issubset(l, Set(r)) + end + end + for elt in l + elt in r || return false + end + return true +end + +""" + hasfastin(T) + +Determine whether the computation `x ∈ collection` where `collection::T` can be considered +as a "fast" operation (typically constant or logarithmic complexity). +The definition `hasfastin(x) = hasfastin(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. +""" +hasfastin(::Type) = false +hasfastin(::Union{Type{<:AbstractSet},Type{<:AbstractDict},Type{<:AbstractRange}}) = true +hasfastin(x) = hasfastin(typeof(x)) + +⊇(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 + +## set disjoint comparison +""" + isdisjoint(v1, v2) -> Bool + +Return whether the collections `v1` and `v2` are disjoint, i.e. whether +their intersection is empty. + +!!! compat "Julia 1.5" + This function requires at least Julia 1.5. +""" +function isdisjoint(l, r) + function _isdisjoint(l, r) + hasfastin(r) && return !any(in(r), l) + hasfastin(l) && return !any(in(l), r) + haslength(r) && length(r) < FASTIN_SET_THRESHOLD && + return !any(in(r), l) + return !any(in(Set(r)), l) + end + if haslength(l) && haslength(r) && length(r) < length(l) + return _isdisjoint(r, l) + end + _isdisjoint(l, 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..5918850 --- /dev/null +++ b/base/accumulate.jl @@ -0,0 +1,455 @@ +# 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 +``` + +!!! note + The return array's `eltype` is `Int` for signed integers of less than system + word size and `UInt` for unsigned integers of less than system word size. + To preserve `eltype` of arrays with small signed or unsigned integer + `accumulate(+, A)` should be used. + + ```jldoctest + julia> cumsum(Int8[100, 28]) + 2-element Array{Int64,1}: + 100 + 128 + + julia> accumulate(+,Int8[100, 28]) + 2-element Array{Int8,1}: + 100 + -128 + ``` + + In the former case, the integers are widened to system word size and + therefore the result is `Int64[100, 128]`. In the latter case, no such + widening happens and integer overflow results in `Int8[100, -128]`. +""" +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(itr) + +Cumulative sum an iterator. 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). + +!!! compat "Julia 1.5" + `cumsum` on a non-array iterator requires at least Julia 1.5. + +# 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] + +julia> cumsum((1, 1, 1)) +(1, 2, 3) + +julia> cumsum(x^2 for x in 1:3) +3-element Array{Int64,1}: + 1 + 5 + 14 +``` +""" +cumsum(x::AbstractVector) = cumsum(x, dims=1) +cumsum(itr) = accumulate(add_sum, itr) + + +""" + 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(itr) + +Cumulative product of an iterator. 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). + +!!! compat "Julia 1.5" + `cumprod` on a non-array iterator requires at least Julia 1.5. + +# 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] + +julia> cumprod((1, 2, 1)) +(1, 2, 2) + +julia> cumprod(x^2 for x in 1:3) +3-element Array{Int64,1}: + 1 + 4 + 36 +``` +""" +cumprod(x::AbstractVector) = cumprod(x, dims=1) +cumprod(itr) = accumulate(mul_prod, itr) + + +""" + 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) + +!!! compat "Julia 1.5" + `accumulate` on a non-array iterator requires at least Julia 1.5. + +# 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...) + if dims === nothing && !(A isa AbstractVector) + # This branch takes care of the cases not handled by `_accumulate!`. + return collect(Iterators.accumulate(op, A; kw...)) + end + 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 + +function accumulate(op, xs::Tuple; init = _InitialValue()) + rf = BottomRF(op) + ys, = afoldl(((), init), xs...) do (ys, acc), x + acc = rf(acc, x) + (ys..., acc), acc + end + return ys +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..f3c98ed --- /dev/null +++ b/base/array.jl @@ -0,0 +1,2567 @@ +# 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_stored_inline, 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) = allocatedinline(u) +isbitsunion(x) = false + +function _unsetindex!(A::Array{T}, i::Int) where {T} + @_inline_meta + @boundscheck checkbounds(A, i) + t = @_gc_preserve_begin A + p = Ptr{Ptr{Cvoid}}(pointer(A, i)) + if !allocatedinline(T) + unsafe_store!(p, C_NULL) + elseif T isa DataType + if !datatype_pointerfree(T) + for j = 1:(Core.sizeof(T) ÷ Core.sizeof(Ptr{Cvoid})) + unsafe_store!(p, C_NULL, j) + end + end + end + @_gc_preserve_end t + 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) + isinline, sz, _ = uniontype_layout(u) + @assert isinline + 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}, Csize_t), + dest, src, n * aligned_sizeof(T)) + return dest +end + + +function _unsafe_copyto!(dest, doffs, src, soffs, n) + destp = pointer(dest, doffs) + srcp = pointer(src, soffs) + @inbounds if destp < srcp || destp > srcp + n + for i = 1:n + if isassigned(src, soffs + i - 1) + dest[doffs + i - 1] = src[soffs + i - 1] + else + _unsetindex!(dest, doffs + i - 1) + end + end + else + for i = n:-1:1 + if isassigned(src, soffs + i - 1) + dest[doffs + i - 1] = src[soffs + i - 1] + else + _unsetindex!(dest, doffs + i - 1) + end + end + end + 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 + destp = pointer(dest, doffs) + srcp = pointer(src, soffs) + if !allocatedinline(T) + ccall(:jl_array_ptr_copy, Cvoid, (Any, Ptr{Cvoid}, Any, Ptr{Cvoid}, Int), + dest, destp, src, srcp, n) + elseif isbitstype(T) + ccall(:memmove, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), + destp, srcp, n * aligned_sizeof(T)) + elseif isbitsunion(T) + ccall(:memmove, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), + destp, srcp, n * aligned_sizeof(T)) + # copy selector bytes + ccall(:memmove, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), + ccall(:jl_array_typetagdata, Ptr{UInt8}, (Any,), dest) + doffs - 1, + ccall(:jl_array_typetagdata, Ptr{UInt8}, (Any,), src) + soffs - 1, + n) + else + _unsafe_copyto!(dest, doffs, src, soffs, n) + end + @_gc_preserve_end t2 + @_gc_preserve_end t1 + return dest +end + +unsafe_copyto!(dest::Array, doffs, src::Array, soffs, n) = + _unsafe_copyto!(dest, doffs, src, soffs, n) + +""" + 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, doffs::Integer, src::Array, soffs::Integer, n::Integer) + return _copyto_impl!(dest, doffs, src, soffs, n) +end + +# this is only needed to avoid possible ambiguities with methods added in some packages +function copyto!(dest::Array{T}, doffs::Integer, src::Array{T}, soffs::Integer, n::Integer) where T + return _copyto_impl!(dest, doffs, src, soffs, n) +end + +function _copyto_impl!(dest::Array, doffs::Integer, src::Array, soffs::Integer, n::Integer) + 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, src::Array) = copyto!(dest, 1, src, 1, length(src)) + +# also to avoid ambiguities in packages +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 + 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::Tuple) + 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`. + +`dims` may be specified as either a tuple or a sequence of arguments. For example, +the common idiom `fill(x)` creates a zero-dimensional array containing the single value `x`. + +# Examples +```jldoctest +julia> fill(1.0, (2,3)) +2×3 Array{Float64,2}: + 1.0 1.0 1.0 + 1.0 1.0 1.0 + +julia> fill(42) +0-dimensional Array{Int64,0}: +42 +``` + +If `x` is an object reference, all elements will refer to the same object: +```jldoctest +julia> A = fill(zeros(2), 2); + +julia> A[1][1] = 42; # modifies both A[1][1] and A[2][1] + +julia> A +2-element Array{Array{Float64,1},1}: + [42.0, 0.0] + [42.0, 0.0] +``` +""" +function fill end + +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::Tuple) + 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::Tuple) + 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_axcheck!(Array{T,N}(undef, size(x)), x) +AbstractArray{T,N}(A::AbstractArray{S,N}) where {T,N,S} = copyto_axcheck!(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))) + f = first(LinearIndices(dest)) + copyto!(new, first(LinearIndices(new)), dest, f, i-f) + @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` in `collection`. If `collection` is an ordered container, +the items are inserted at the end (in the given order). + +# Examples +```jldoctest +julia> push!([1, 2, 3], 4, 5, 6) +6-element Array{Int64,1}: + 1 + 2 + 3 + 4 + 5 + 6 +``` + +If `collection` is ordered, use [`append!`](@ref) to add all the elements of another +collection to it. The result of the preceding example is equivalent to `append!([1, 2, 3], [4, +5, 6])`. For `AbstractSet` objects, [`union!`](@ref) can be used instead. +""" +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. + +For an ordered container `collection`, add the elements of `collection2` to the end of it. + +# 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{Int64} with 2 elements: + 2 + 1 + +julia> pop!(S) +2 + +julia> S +Set{Int64} with 1 element: + 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 + +""" + popat!(a::Vector, i::Integer, [default]) + +Remove the item at the given `i` and return it. Subsequent items +are shifted to fill the resulting gap. +When `i` is not a valid index for `a`, return `default`, or throw an error if +`default` is not specified. +See also [`deleteat!`](@ref) and [`splice!`](@ref). + +!!! compat "Julia 1.5" + This function is available as of Julia 1.5. + +# Examples +```jldoctest +julia> a = [4, 3, 2, 1]; popat!(a, 2) +3 + +julia> a +3-element Array{Int64,1}: + 4 + 2 + 1 + +julia> popat!(a, 4, missing) +missing + +julia> popat!(a, 4) +ERROR: BoundsError: attempt to access 3-element Array{Int64,1} at index [4] +[...] +``` +""" +function popat!(a::Vector, i::Integer) + x = a[i] + _deleteat!(a, i, 1) + x +end + +function popat!(a::Vector, i::Integer, default) + if 1 <= i <= length(a) + x = @inbounds a[i] + _deleteat!(a, i, 1) + x + else + default + end +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]) + +struct Nowhere; end +push!(::Nowhere, _) = nothing + +function _deleteat!(a::Vector, inds, dltd=Nowhere()) + n = length(a) + y = iterate(inds) + y === nothing && return a + (p, s) = y + checkbounds(a, p) + push!(dltd, @inbounds a[p]) + 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 + push!(dltd, @inbounds a[i]) + 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, indices, [replacement]) -> items + +Remove items at specified indices, and return a collection containing +the removed items. +Subsequent items are shifted left to fill the resulting gaps. +If specified, replacement values from an ordered collection will be spliced in +place of the removed items; in this case, `indices` must be a `UnitRange`. + +To insert `replacement` before an index `n` without removing any items, use +`splice!(collection, n:n-1, replacement)`. + +!!! compat "Julia 1.5" + Prior to Julia 1.5, `indices` must always be a `UnitRange`. + +# Examples +```jldoctest +julia> A = [-1, -2, -3, 5, 4, 3, -1]; splice!(A, 4:3, 2) +Int64[] + +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 + +splice!(a::Vector, inds) = (dltds = eltype(a)[]; _deleteat!(a, inds, dltds); dltds) + +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) + nd = 1 + for a in arrays + na = length(a) + @assert nd + na <= 1 + length(arr) # Concurrent modification of arrays? + unsafe_copyto!(arr, nd, a, 1, na) + nd += na + end + 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 = oftype(l, 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) + i = oftype(first(keys(A)), start) + l = last(keys(A)) + 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))) + +findfirst(p::Union{Fix2{typeof(isequal),Int},Fix2{typeof(==),Int}}, r::OneTo{Int}) = + 1 <= p.x <= r.stop ? p.x : nothing + +findfirst(p::Union{Fix2{typeof(isequal),T},Fix2{typeof(==),T}}, r::AbstractUnitRange) where {T<:Integer} = + first(r) <= p.x <= last(r) ? 1+Int(p.x - first(r)) : nothing + +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) + f = first(keys(A)) + i = oftype(f, start) + 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) + f = first(keys(A)) + i = oftype(f, start) + 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)) +Int64[] +``` +""" +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) + +Return a copy of collection `a`, removing elements for which `f` is `false`. +The function `f` is passed one argument. + +!!! compat "Julia 1.4" + Support for `a` as a tuple requires at least Julia 1.4. + +# 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) + +Update collection `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..dfea81d --- /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 allocatedinline(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..338f511 --- /dev/null +++ b/base/arrayshow.jl @@ -0,0 +1,537 @@ +# 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(io, 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) + + # First try 3-arg show + sx = sprint(show, "text/plain", x, context=io, sizehint=0) + + # If the output contains line breaks, try 2-arg show instead. + if occursin('\n', sx) + sx = sprint(show, x, context=io, sizehint=0) + end + 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 = j == axes(X, 2)[end] ? "" : 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, + pad_right::Bool = true) + 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 = k == length(A) && !pad_right ? + "" : + repeat(" ", max(0, w-length(vdots)-length(l))) + print(io, l, vdots, r) + else + (k != length(A) || pad_right) && 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, false) + 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, true) + print(io, ddots) + print_matrix_vdots(io, vdots, Ralign, sep, vmod, r, false) + 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 +# Note that this is for showing the content inside the array, and for `MIME"text/plain". +# There are `show(::IO, ::A) where A<:AbstractArray` methods that don't use this +# e.g. show_vector, show_zero_dim +print_array(io::IO, X::AbstractArray{<:Any, 0}) = + 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) + if isempty(X) && (get(io, :compact, false) || X isa Vector) + return show(io, X) + end + # 0) show summary before setting :compact + summary(io, X) + isempty(X) && return + print(io, ":") + show_circular(io, X) && return + + # 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 + recur_io = IOContext(io, :SHOWN_SET => X) + print_array(recur_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::AbstractArray) = summary(io, X) + +# typeinfo aware (necessarily) +function show(io::IO, X::AbstractArray) + ndims(X) == 0 && return show_zero_dim(io, X) + ndims(X) == 1 && return show_vector(io, X) + prefix, implicit = typeinfo_prefix(io, X) + if !implicit + io = IOContext(io, :typeinfo => eltype(X)) + end + isempty(X) ? + _show_empty(io, X) : + _show_nonempty(io, X, prefix) +end + +### 0-dimensional arrays (#31481) +show_zero_dim(io::IO, X::BitArray{0}) = print(io, "BitArray(", Int(X[]), ")") +function show_zero_dim(io::IO, X::AbstractArray{T, 0}) where T + if isassigned(X) + print(io, "fill(") + show(io, X[]) + else + print(io, "Array{", T, ",0}(") + show(io, undef) + end + print(io, ")") +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=']') + prefix, implicit = typeinfo_prefix(io, v) + print(io, prefix) + # directly or indirectly, the context now knows about eltype(v) + if !implicit + io = IOContext(io, :typeinfo => eltype(v)) + end + 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) + +# types that can be parsed back accurately from their un-decorated representations +function typeinfo_implicit(@nospecialize(T)) + if T === Float64 || T === Int || T === Char || T === String || T === Symbol || + issingletontype(T) + return true + end + return isconcretetype(T) && + ((T <: Array && typeinfo_implicit(eltype(T))) || + ((T <: Tuple || T <: Pair) && all(typeinfo_implicit, fieldtypes(T))) || + (T <: AbstractDict && typeinfo_implicit(keytype(T)) && typeinfo_implicit(valtype(T)))) +end + +# 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 + string(typeof(X).name), false + elseif !isempty(X) && typeinfo_implicit(keytype(X)) && typeinfo_implicit(valtype(X)) + string(typeof(X).name), true + else + string(typeof(X)), false + end + else + # Types hard-coded here are those which are created by default for a given syntax + if eltype_X == eltype_ctx + "", false + elseif !isempty(X) && typeinfo_implicit(eltype_X) + "", true + elseif print_without_params(eltype_X) + string(unwrap_unionall(eltype_X).name), false # Print "Array" rather than "Array{T,N}" + else + string(eltype_X), false + end + end +end diff --git a/base/asyncevent.jl b/base/asyncevent.jl new file mode 100644 index 0000000..ae87217 --- /dev/null +++ b/base/asyncevent.jl @@ -0,0 +1,299 @@ +# 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, timeout::Real; pollint::Real=0.1) + +Waits until `testcb` returns `true` or for `timeout` seconds, whichever is earlier. +`testcb` is polled every `pollint` seconds. The minimum duration for `timeout` and `pollint` +is 1 millisecond or `0.001`. + +Returns :ok or :timed_out +""" +function timedwait(testcb::Function, timeout::Real; pollint::Real=0.1) + pollint >= 1e-3 || throw(ArgumentError("pollint must be ≥ 1 millisecond")) + start = time_ns() + ns_timeout = 1e9 * timeout + done = Channel(1) + function timercb(aw) + try + if testcb() + put!(done, (:ok, nothing)) + elseif (time_ns() - start) > ns_timeout + put!(done, (:timed_out, nothing)) + end + catch e + put!(done, (:error, CapturedException(e, catch_backtrace()))) + finally + isready(done) && close(aw) + end + nothing + end + + try + testcb() && return :ok + catch e + throw(CapturedException(e, catch_backtrace())) + end + + t = Timer(timercb, pollint, interval = pollint) + ret, e = fetch(done) + close(t) + + ret === :error && throw(e) + + 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..1a980eb --- /dev/null +++ b/base/atomics.jl @@ -0,0 +1,461 @@ +# 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 +## +# Filter out unsupported atomic types on platforms +# - 128-bit atomics do not exist on AArch32. +# - Omitting 128-bit types on 32bit x86 and ppc64 +# - LLVM doesn't currently support atomics on floats for ppc64 +# C++20 is adding limited support for atomics on float, but as of +# now Clang does not support that yet. +if Sys.ARCH == :i686 || startswith(string(Sys.ARCH), "arm") || + Sys.ARCH === :powerpc64le || Sys.ARCH === :ppc64le + 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 +if Sys.ARCH === :powerpc64le || Sys.ARCH === :ppc64le + const atomictypes = (inttypes..., Bool) +else + const atomictypes = (arithmetictypes..., Bool) +end + +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 old + # 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..4cbb13a --- /dev/null +++ b/base/bitarray.jl @@ -0,0 +1,1835 @@ +# 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). + +!!! note + Due to its packed storage format, concurrent access to the elements of a `BitArray` + where at least one of them is a write is not thread safe. + +""" +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}: + 0 0 + 0 0 + +julia> BitArray(undef, (3, 1)) +3×1 BitArray{2}: + 0 + 0 + 0 +``` +""" +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) + +""" + BitVector(nt::Tuple{Vararg{Bool}}) + +Construct a `BitVector` from a tuple of `Bool`. +# Examples +```julia-repl +julia> nt = (true, false, true, false) +(true, false, true, false) + +julia> BitVector(nt) +4-element BitArray{1}: + 1 + 0 + 1 + 0 +``` +""" +function BitVector(nt::Tuple{Vararg{Bool}}) + bv = BitVector(undef, length(nt)) + bv .= nt +end + +## 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 + + Bc = B.chunks + + (p, s) = y + checkbounds(B, p) + 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!(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 = bitreverse(B.chunks[j]) + B.chunks[j] = 0 + @inbounds while true + i += 1 + if i == j + break + end + u = bitreverse(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 = bitreverse(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::Int) + 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, Int(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 = Int(start) + 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::Int) + 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, Int(start)) +end + +function findprevnot(B::BitArray, start::Integer) + start = Int(start) + 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..b8be658 --- /dev/null +++ b/base/bitset.jl @@ -0,0 +1,426 @@ +# 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, (word, idx) = (CHK0, 0)) + while word == 0 + idx == length(s.bits) && return nothing + idx += 1 + word = @inbounds s.bits[idx] + end + trailing_zeros(word) + (idx - 1 + s.offset) << 6, (_blsr(word), idx) +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 + t1 = @_gc_preserve_begin a1 + t2 = @_gc_preserve_begin a2 + _memcmp(pointer(a1, b2-b1+1), pointer(a2), overlap<<3) == 0 || return false + @_gc_preserve_end t2 + @_gc_preserve_end t1 + 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..871c7e8 --- /dev/null +++ b/base/bool.jl @@ -0,0 +1,116 @@ +# 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()) +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..f80fd67 --- /dev/null +++ b/base/boot.jl @@ -0,0 +1,744 @@ +# 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::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 iterate 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)))) +LineNumberNode(l::Int, f::String) = LineNumberNode(l, Symbol(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 +eval(Core, :(function Symbol(s::String) + $(Expr(:meta, :pure)) + 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) 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(::Type{To}, x) where {To} + @_inline_meta + is_top_bit_set(x) && throw_inexacterror(:check_top_bit, To, 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(Int8, x)) +toInt8(x::UInt16) = checked_trunc_sint(Int8, check_top_bit(Int8, x)) +toInt8(x::UInt32) = checked_trunc_sint(Int8, check_top_bit(Int8, x)) +toInt8(x::UInt64) = checked_trunc_sint(Int8, check_top_bit(Int8, x)) +toInt8(x::UInt128) = checked_trunc_sint(Int8, check_top_bit(Int8, 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(Int16, x)) +toInt16(x::UInt32) = checked_trunc_sint(Int16, check_top_bit(Int16, x)) +toInt16(x::UInt64) = checked_trunc_sint(Int16, check_top_bit(Int16, x)) +toInt16(x::UInt128) = checked_trunc_sint(Int16, check_top_bit(Int16, 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(Int32, x)) +toInt32(x::UInt64) = checked_trunc_sint(Int32, check_top_bit(Int32, x)) +toInt32(x::UInt128) = checked_trunc_sint(Int32, check_top_bit(Int32, 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(Int64, x)) +toInt64(x::UInt128) = checked_trunc_sint(Int64, check_top_bit(Int64, 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(Int128, x)) +toInt128(x::Bool) = and_int(zext_int(Int128, x), Int128(1)) +toUInt8(x::Int8) = bitcast(UInt8, check_top_bit(UInt8, 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(UInt16, x)) +toUInt16(x::Int16) = bitcast(UInt16, check_top_bit(UInt16, 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(UInt32, x)) +toUInt32(x::Int16) = sext_int(UInt32, check_top_bit(UInt32, x)) +toUInt32(x::Int32) = bitcast(UInt32, check_top_bit(UInt32, 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(UInt64, x)) +toUInt64(x::Int16) = sext_int(UInt64, check_top_bit(UInt64, x)) +toUInt64(x::Int32) = sext_int(UInt64, check_top_bit(UInt64, x)) +toUInt64(x::Int64) = bitcast(UInt64, check_top_bit(UInt64, 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(UInt128, x)) +toUInt128(x::Int16) = sext_int(UInt128, check_top_bit(UInt128, x)) +toUInt128(x::Int32) = sext_int(UInt128, check_top_bit(UInt128, x)) +toUInt128(x::Int64) = sext_int(UInt128, check_top_bit(UInt128, x)) +toUInt128(x::Int128) = bitcast(UInt128, check_top_bit(UInt128, 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..168431b --- /dev/null +++ b/base/broadcast.jl @@ -0,0 +1,1267 @@ +# 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} <: Base.AbstractBroadcasted + 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, ::Type{T}) where {T} = similar(bc, T, axes(bc)) +Base.similar(::Broadcasted{DefaultArrayStyle{N}}, ::Type{ElType}, dims) where {N,ElType} = + similar(Array{ElType}, dims) +Base.similar(::Broadcasted{DefaultArrayStyle{N}}, ::Type{Bool}, dims) where N = + similar(BitArray, dims) +# In cases of conflict we fall back on Array +Base.similar(::Broadcasted{ArrayConflict}, ::Type{ElType}, dims) where ElType = + similar(Array{ElType}, dims) +Base.similar(::Broadcasted{ArrayConflict}, ::Type{Bool}, dims) = + similar(BitArray, dims) + +@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) = () + +@inline Base.axes(bc::Broadcasted{<:Any, <:NTuple{N}}, d::Integer) where N = + d <= N ? axes(bc)[d] : OneTo(1) + +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.IndexStyle(bc::Broadcasted) = IndexStyle(typeof(bc)) +Base.IndexStyle(::Type{<:Broadcasted{<:Any,<:Tuple{Any}}}) = IndexLinear() +Base.IndexStyle(::Type{<:Broadcasted{<:Any}}) = IndexCartesian() + +Base.LinearIndices(bc::Broadcasted{<:Any,<:Tuple{Any}}) = axes(bc)[1] + +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...)) +@inline combine_axes(A, B) = broadcast_shape(axes(A), 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::OneTo, b::OneTo) = OneTo{Int}(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::Union{Integer,CartesianIndex}, + i2::Union{Integer,CartesianIndex}, + I::Union{Integer,CartesianIndex}..., +) = + 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, x) + return materialize!(dest, instantiate(Broadcasted(identity, (x,), axes(dest)))) +end + +@inline function materialize!(dest, bc::Broadcasted{Style}) where {Style} + return materialize!(combine_styles(dest, bc), dest, bc) +end +@inline function materialize!(::BroadcastStyle, dest, bc::Broadcasted{Style}) where {Style} + return copyto!(dest, instantiate(Broadcasted{Style}(bc.f, bc.args, 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) + length(dest) < 256 && return invoke(copyto!, Tuple{AbstractArray, Broadcasted{Nothing}}, dest, bc) + tmp = Vector{Bool}(undef, bitcache_size) + destc = dest.chunks + cind = 1 + bc′ = preprocess(dest, bc) + for P in Iterators.partition(eachindex(bc′), bitcache_size) + ind = 1 + @simd for I in P + @inbounds tmp[ind] = bc′[I] + ind += 1 + end + @simd for i in ind:bitcache_size + @inbounds tmp[i] = false + end + dumpbitcache(destc, cind, tmp) + cind += bitcache_chunks + 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]) + elseif x.head === :(<:) || x.head === :(>:) + tmp = x.head === :(<:) ? :(.<:) : :(.>:) + Expr(:call, tmp, dotargs...) + 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..ddbb984 --- /dev/null +++ b/base/c.jl @@ -0,0 +1,702 @@ +# 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). +""" +mutable 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` (this is not supported on all architectures). + +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, rt, at) + if !(isa(at, Expr) && at.head === :tuple) + throw(ArgumentError("@cfunction argument types must be a literal tuple")) + end + at.head = :call + pushfirst!(at.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, rt, at, 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 + +""" + exit_on_sigint(on::Bool) + +Set `exit_on_sigint` flag of the julia runtime. If `false`, Ctrl-C +(SIGINT) is capturable as [`InterruptException`](@ref) in `try` block. +This is the default behavior in REPL, any code run via `-e` and `-E` +and in Julia script run with `-i` option. + +If `true`, `InterruptException` is not thrown by Ctrl-C. Running code +upon such event requires [`atexit`](@ref). This is the default +behavior in Julia script run without `-i` option. + +!!! compat "Julia 1.5" + Function `exit_on_sigint` requires at least Julia 1.5. +""" +function exit_on_sigint(on::Bool) + ccall(:jl_exit_on_sigint, Cvoid, (Cint,), on) +end + +function _ccallable(rt::Type, sigt::Type) + ccall(:jl_extern_c, Cvoid, (Any, Any), rt, sigt) +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 + f = sig.args[1] + if isa(f,Expr) && f.head === :(::) + f = f.args[end] + else + f = :(typeof($f)) + end + at = map(sig.args[2:end]) do a + if isa(a,Expr) && a.head === :(::) + a.args[end] + else + :Any + end + end + return quote + $(esc(def)) + _ccallable($(esc(rt)), $(Expr(:curly, :Tuple, esc(f), map(esc, at)...))) + 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 + +# @ccall implementation +""" + ccall_macro_parse(expression) + +`ccall_macro_parse` is an implementation detail of `@ccall + +it takes an expression like `:(printf("%d"::Cstring, value::Cuint)::Cvoid)` +returns: a tuple of `(function_name, return_type, arg_types, args)` + +The above input outputs this: + + (:printf, :Cvoid, [:Cstring, :Cuint], ["%d", :value]) +""" +function ccall_macro_parse(expr::Expr) + # setup and check for errors + if !Meta.isexpr(expr, :(::)) + throw(ArgumentError("@ccall needs a function signature with a return type")) + end + rettype = expr.args[2] + + call = expr.args[1] + if !Meta.isexpr(call, :call) + throw(ArgumentError("@ccall has to take a function call")) + end + + # get the function symbols + func = let f = call.args[1] + if Meta.isexpr(f, :.) + :(($(f.args[2]), $(f.args[1]))) + elseif Meta.isexpr(f, :$) + f + elseif f isa Symbol + QuoteNode(f) + else + throw(ArgumentError("@ccall function name must be a symbol, a `.` node (e.g. `libc.printf`) or an interpolated function pointer (with `\$`)")) + end + end + + # detect varargs + varargs = nothing + argstart = 2 + callargs = call.args + if length(callargs) >= 2 && Meta.isexpr(callargs[2], :parameters) + argstart = 3 + varargs = callargs[2].args + end + + # collect args and types + args = [] + types = [] + + function pusharg!(arg) + if !Meta.isexpr(arg, :(::)) + throw(ArgumentError("args in @ccall need type annotations. '$arg' doesn't have one.")) + end + push!(args, arg.args[1]) + push!(types, arg.args[2]) + end + + for i in argstart:length(callargs) + pusharg!(callargs[i]) + end + # add any varargs if necessary + nreq = 0 + if !isnothing(varargs) + if length(args) == 0 + throw(ArgumentError("C ABI prohibits vararg without one required argument")) + end + nreq = length(args) + for a in varargs + pusharg!(a) + end + end + + return func, rettype, types, args, nreq +end + + +function ccall_macro_lower(convention, func, rettype, types, args, nreq) + lowering = [] + realargs = [] + gcroots = [] + + # if interpolation was used, ensure variable is a function pointer at runtime. + if Meta.isexpr(func, :$) + push!(lowering, Expr(:(=), :func, esc(func.args[1]))) + name = QuoteNode(func.args[1]) + func = :func + check = quote + if !isa(func, Ptr{Cvoid}) + name = $name + throw(ArgumentError("interpolated function `$name` was not a Ptr{Cvoid}, but $(typeof(func))")) + end + end + push!(lowering, check) + else + func = esc(func) + end + + for (i, (arg, type)) in enumerate(zip(args, types)) + sym = Symbol(string("arg", i, "root")) + sym2 = Symbol(string("arg", i, )) + earg, etype = esc(arg), esc(type) + push!(lowering, :($sym = Base.cconvert($etype, $earg))) + push!(lowering, :($sym2 = Base.unsafe_convert($etype, $sym))) + push!(realargs, sym2) + push!(gcroots, sym) + end + etypes = Expr(:call, Expr(:core, :svec), types...) + exp = Expr(:foreigncall, + func, + esc(rettype), + esc(etypes), + nreq, + QuoteNode(convention), + realargs..., gcroots...) + push!(lowering, exp) + + return Expr(:block, lowering...) +end + +""" + @ccall library.function_name(argvalue1::argtype1, ...)::returntype + @ccall function_name(argvalue1::argtype1, ...)::returntype + @ccall \$function_pointer(argvalue1::argtype1, ...)::returntype + +Call a function in a C-exported shared library, specified by +`library.function_name`, where `library` is a string constant or +literal. The library may be omitted, in which case the `function_name` +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`. + +Each `argvalue` to `@ccall` is 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)`. + + +# Examples + + @ccall strlen(s::Cstring)::Csize_t + +This calls the C standard library function: + + size_t strlen(char *) + +with a Julia variable named `s`. See also `ccall`. + +Varargs are supported with the following convention: + + @ccall printf("%s = %d"::Cstring ; "foo"::Cstring, foo::Cint)::Cint + +The semicolon is used to separate required arguments (of which there +must be at least one) from variadic arguments. + +Example using an external library: + + # C signature of g_uri_escape_string: + # char *g_uri_escape_string(const char *unescaped, const char *reserved_chars_allowed, gboolean allow_utf8); + + const glib = "libglib-2.0" + @ccall glib.g_uri_escape_string(my_uri::Cstring, ":/"::Cstring, true::Cint)::Cstring + +The string literal could also be used directly before the function +name, if desired `"libglib-2.0".g_uri_escape_string(...` +""" +macro ccall(expr) + return ccall_macro_lower(:ccall, ccall_macro_parse(expr)...) +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..cb3bd6e --- /dev/null +++ b/base/channels.jl @@ -0,0 +1,458 @@ +# 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: TaskFailedException: +foo +Stacktrace: +[...] +``` +""" +function bind(c::Channel, task::Task) + T = Task(() -> close_chnl_on_taskdone(task, c)) + _wait2(task, T) + 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 + lock(c) + try + isopen(c) || return + if istaskfailed(t) && task_result(t) isa Exception + close(c, TaskFailedException(t)) + return + end + close(c) + finally + unlock(c) + 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) +isempty(c::Channel) = isbuffered(c) ? isempty(c.data) : isempty(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..cc6ff5e --- /dev/null +++ b/base/char.jl @@ -0,0 +1,316 @@ +# 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 + +@inline -(x::AbstractChar, y::AbstractChar) = Int(x) - Int(y) +@inline -(x::T, y::Integer) where {T<:AbstractChar} = T(Int32(x) - Int32(y)) +@inline +(x::T, y::Integer) where {T<:AbstractChar} = T(Int32(x) + Int32(y)) +@inline +(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) + get(io, :compact, false) && return + if !ismalformed(c) + print(io, ": ") + if isoverlong(c) + print(io, "[overlong] ") + u = decode_overlong(c) + c = T(u) + else + u = codepoint(c) + end + h = uppercase(string(u, base = 16, pad = 4)) + 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..202d709 --- /dev/null +++ b/base/client.jl @@ -0,0 +1,514 @@ +# 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 = nothing +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 + +# deprecated function--preserved for DocTests.jl +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 && !(bt isa Vector{Any}) # ignore our sentinel value types + bt = stacktrace(bt) + # remove REPL-related frames from interactive printing + eval_ind = findlast(frame -> !frame.from_c && frame.func === :eval, bt) + eval_ind === nothing || deleteat!(bt, eval_ind:length(bt)) + end + return bt +end + +function display_error(io::IO, er, bt) + printstyled(io, "ERROR: "; bold=true, color=Base.error_color()) + bt = scrub_repl_backtrace(bt) + showerror(IOContext(io, :limit => true), er, bt, backtrace = bt!==nothing) + println(io) +end +function display_error(io::IO, stack::Vector) + printstyled(io, "ERROR: "; bold=true, color=Base.error_color()) + bt = Any[ (x[1], scrub_repl_backtrace(x[2])) for x in stack ] + show_exception_stack(IOContext(io, :limit => true), bt) +end +display_error(stack::Vector) = display_error(stderr, stack) +display_error(er, bt=nothing) = display_error(stderr, er, bt) + +function eval_user_input(errio, @nospecialize(ast), show_value::Bool) + errcount = 0 + lasterr = nothing + have_color = get(stdout, :color, false) + 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 = color_set ? (opts.color == 1) : nothing # --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 + elseif cmd == 'B' # --bug-report + # If we're doing a bug report, don't load anything else. We will + # spawn a child in which to execute these options. + let InteractiveUtils = load_InteractiveUtils() + InteractiveUtils.report_bug(arg) + end + return 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 + exit_on_sigint(true) + 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}() + +function load_InteractiveUtils() + # 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)) + return InteractiveUtils + end + catch ex + @warn "Failed to import InteractiveUtils into module Main" exception=(ex, catch_backtrace()) + end + return nothing + end + return getfield(Main, :InteractiveUtils) +end + +# 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_InteractiveUtils() + + 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 +using ..Base +include(mapexpr::Function, fname::AbstractString) = Base.include(mapexpr, Main, fname) +# We inline the definition of include from loading.jl/include_relative to get one-frame stacktraces +# for the common case of include(fname). Otherwise we would use: +# include(fname::AbstractString) = Base.include(Main, fname) +function include(fname::AbstractString) + mod = Main + isa(fname, String) || (fname = Base.convert(String, fname)::String) + path, prev = Base._include_dependency(mod, fname) + for callback in Base.include_callbacks # to preserve order, must come before Core.include + Base.invokelatest(callback, mod, path) + end + tls = Base.task_local_storage() + tls[:SOURCE_PATH] = path + local result + try + result = ccall(:jl_load, Any, (Any, Cstring), mod, path) + finally + if prev === nothing + Base.delete!(tls, :SOURCE_PATH) + else + tls[:SOURCE_PATH] = prev + end + end + return result +end +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([mapexpr::Function,] 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 +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. + +The optional first argument `mapexpr` can be used to transform the included code before +it is evaluated: for each parsed expression `expr` in `path`, the `include` function +actually evaluates `mapexpr(expr)`. If it is omitted, `mapexpr` defaults to [`identity`](@ref). + +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 === true + print(color_normal) + end +end diff --git a/base/cmd.jl b/base/cmd.jl new file mode 100644 index 0000000..4890af1 --- /dev/null +++ b/base/cmd.jl @@ -0,0 +1,390 @@ +# 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...) +shell_escape_winsomely(cmd::Cmd) = + shell_escape_winsomely(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..3e16c45 --- /dev/null +++ b/base/combinatorics.jl @@ -0,0 +1,343 @@ +# 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 + +@inline function _foldoneto(op, acc, ::Val{N}) where N + @assert N::Integer > 0 + if @generated + quote + acc_0 = acc + Base.Cartesian.@nexprs $N i -> acc_{i} = op(acc_{i-1}, i) + return $(Symbol(:acc_, N)) + end + else + for i in 1:N + acc = op(acc, i) + end + return acc + end +end + +""" + isperm(v) -> Bool + +Return `true` if `v` is a valid permutation. + +# Examples +```jldoctest +julia> isperm([1; 2]) +true + +julia> isperm([1; 3]) +false +``` +""" +isperm(A) = _isperm(A) + +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)) + +function isperm(P::Tuple) + valn = Val(length(P)) + _foldoneto(true, valn) do b,i + s = _foldoneto(false, valn) do s, j + s || P[j]==i + end + b&s + end +end + +isperm(P::Any16) = _isperm(P) + +# 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': ASCII/Unicode U+0062 (category Ll: Letter, lowercase) + 'd': ASCII/Unicode U+0064 (category Ll: Letter, lowercase) + 'c': ASCII/Unicode U+0063 (category Ll: Letter, lowercase) + 'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase) + +julia> B[invperm(v)] +4-element Array{Char,1}: + 'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase) + 'b': ASCII/Unicode U+0062 (category Ll: Letter, lowercase) + 'c': ASCII/Unicode U+0063 (category Ll: Letter, lowercase) + 'd': ASCII/Unicode U+0064 (category Ll: Letter, lowercase) +``` +""" +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 + +function invperm(P::Tuple) + valn = Val(length(P)) + ntuple(valn) do i + s = _foldoneto(nothing, valn) do s, j + s !== nothing && return s + P[j]==i && return j + nothing + end + s === nothing && throw(ArgumentError("argument is not a permutation")) + s + end +end + +invperm(P::Any16) = Tuple(invperm(collect(P))) + +#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..0011681 --- /dev/null +++ b/base/compiler/abstractinterpretation.jl @@ -0,0 +1,1350 @@ +# 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, min_valid[1], max_valid[1]) = + get!(sv.matching_methods_cache, sig_n) do + ms = _methods_by_ftype(sig_n, max_methods, sv.params.world, + min_valid, max_valid) + return (ms, min_valid[1], max_valid[1]) + end + xapplicable === false && return Any + append!(applicable, xapplicable) + end + else + (applicable, min_valid[1], max_valid[1]) = + get!(sv.matching_methods_cache, atype) do + ms = _methods_by_ftype(atype, max_methods, sv.params.world, + min_valid, max_valid) + return (ms, min_valid[1], max_valid[1]) + end + 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 + + if f !== nothing && napplicable == 1 && is_method_pure(applicable[1][3], applicable[1][1], applicable[1][2]) + val = pure_eval_call(f, argtypes) + if val !== false + return val + end + end + + 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) && !ismutable(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 + elseif arrty ⊑ Array + return Any + end + elseif istopfunction(f, :iterate) + itrty = argtypes[2] + if itrty isa Type && !issingletontype(itrty) + return Any + elseif itrty ⊑ Array + 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_ir_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_ir_flag_inferred, Bool, (Any,), cache_inf) + cache_src_inlineable = ccall(:jl_ir_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 === nothing && return Any # this is probably a bad generated function (unsound), but just ignore it + 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(itft), @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 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(itft, typ, vtypes, sv) + end +end + +# simulate iteration protocol on container type up to fixpoint +function abstract_iteration(@nospecialize(itft), @nospecialize(itertype), vtypes::VarTable, sv::InferenceState) + if !isdefined(Main, :Base) || !isdefined(Main.Base, :iterate) || !isconst(Main.Base, :iterate) + return Any[Vararg{Any}] + end + if itft === nothing + iteratef = getfield(Main.Base, :iterate) + itft = Const(iteratef) + elseif isa(itft, Const) + iteratef = itft.val + else + return Any[Vararg{Any}] + end + @assert !isvarargtype(itertype) + stateordonet = abstract_call_known(iteratef, nothing, Any[itft, 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_known(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_known(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(itft), @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]]) + if !isvarargtype(ti) + cti = precise_container_type(itft, ti, vtypes, sv) + else + cti = precise_container_type(itft, unwrapva(ti), vtypes, sv) + # We can't represent a repeating sequence of the same types, + # so tmerge everything together to get one type that represents + # everything. + argt = cti[end] + if isvarargtype(argt) + argt = unwrapva(argt) + end + for i in 1:(length(cti)-1) + argt = tmerge(argt, cti[i]) + end + cti = Any[Vararg{argt}] + end + 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 + lct = length(ct) + # truncate argument list at the first Vararg + for i = 1:lct-1 + if isvarargtype(ct[i]) + ct[i] = tuple_tail_elem(ct[i], ct[(i+1):lct]) + resize!(ct, i) + break + end + end + rt = abstract_call(nothing, ct, vtypes, sv, max_methods) + res = tmerge(res, rt) + if res === Any + break + end + end + return res +end + +function is_method_pure(method::Method, @nospecialize(sig), sparams::SimpleVector) + 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 + return true + end + return method.pure +end + +function pure_eval_call(@nospecialize(f), argtypes::Vector{Any}) + for i = 2:length(argtypes) + a = widenconditional(argtypes[i]) + if !(isa(a, Const) || isconstType(a)) + return false + end + 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 argtype_by_index(argtypes::Vector{Any}, i::Int) + n = length(argtypes) + if isvarargtype(argtypes[n]) + return i >= n ? unwrapva(argtypes[n]) : argtypes[i] + else + return i > n ? Bottom : argtypes[i] + end +end + +function argtype_tail(argtypes::Vector{Any}, i::Int) + n = length(argtypes) + if isvarargtype(argtypes[n]) && i > n + i = n + end + return argtypes[i:n] +end + +# call where the function is known exactly +function abstract_call_known(@nospecialize(f), fargs::Union{Nothing,Vector{Any}}, argtypes::Vector{Any}, vtypes::VarTable, sv::InferenceState, max_methods = sv.params.MAX_METHODS) + la = length(argtypes) + + if isa(f, Builtin) + if f === _apply + ft = argtype_by_index(argtypes, 2) + ft === Bottom && return Bottom + return abstract_apply(nothing, ft, argtype_tail(argtypes, 3), vtypes, sv, max_methods) + elseif f === _apply_iterate + itft = argtype_by_index(argtypes, 2) + ft = argtype_by_index(argtypes, 3) + (itft === Bottom || ft === Bottom) && return Bottom + return abstract_apply(itft, ft, argtype_tail(argtypes, 4), vtypes, sv, max_methods) + elseif f === ifelse && fargs isa Vector{Any} && la == 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}) && la == 3 && isa(argtypes[3], Const) && isa(argtypes[3].val, Int) && argtypes[2] ⊑ Tuple + cti = precise_container_type(nothing, 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 la == 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. + (la < 2 || la > 4) && return Union{} + n = argtypes[2] + ub_var = Const(Any) + lb_var = Const(Union{}) + if la == 4 + ub_var = argtypes[4] + lb_var = argtypes[3] + elseif la == 3 + ub_var = argtypes[3] + end + return typevar_tfunc(n, lb_var, ub_var) + elseif f === UnionAll + if la == 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 + return Type + elseif la == 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 la == 3 && istopfunction(f, :!==) + # mark !== as exactly a negated call to === + rty = abstract_call_known((===), 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 la == 3 && istopfunction(f, :(>:)) + # mark issupertype as a exact alias for issubtype + # swap T1 and T2 arguments and call <: + if fargs !== nothing && length(fargs) == 3 + fargs = Any[<:, fargs[3], fargs[2]] + else + fargs = nothing + end + argtypes = Any[typeof(<:), argtypes[3], argtypes[2]] + rty = abstract_call_known(<:, fargs, argtypes, vtypes, sv) + return rty + elseif la == 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 la == 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 la == 2 && istopfunction(f, :typename) + return typename_static(argtypes[2]) + elseif max_methods > 1 && istopfunction(f, :copyto!) + max_methods = 1 + elseif la == 3 && istopfunction(f, :typejoin) + val = pure_eval_call(f, argtypes) + return val === false ? Type : val + end + + atype = argtypes_to_type(argtypes) + return abstract_call_gf_by_type(f, argtypes, atype, sv, max_methods) +end + +# call where the function is any lattice element +function abstract_call(fargs::Union{Nothing,Vector{Any}}, argtypes::Vector{Any}, vtypes::VarTable, sv::InferenceState, + max_methods = sv.params.MAX_METHODS) + #print("call ", e.args[1], argtypes, "\n\n") + 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 + # 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, max_methods) + end + return abstract_call_known(f, fargs, argtypes, vtypes, sv, max_methods) +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_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 + ea = e.args + n = length(ea) + argtypes = Vector{Any}(undef, n) + @inbounds for i = 1:n + ai = abstract_eval(ea[i], vtypes, sv) + if ai === Bottom + return Bottom + end + argtypes[i] = ai + end + t = abstract_call(ea, 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] + if length(e.args) == 2 && isconcretetype(t) && !t.mutable + at = abstract_eval(e.args[2], vtypes, sv) + n = fieldcount(t) + if isa(at, Const) && isa(at.val, Tuple) && n == length(at.val) && + _all(i->at.val[i] isa fieldtype(t, i), 1:n) + t = Const(ccall(:jl_new_structt, Any, (Any, Any), t, at.val)) + elseif isa(at, PartialStruct) && at ⊑ Tuple && n == length(at.fields) && + _all(i->at.fields[i] ⊑ fieldtype(t, i), 1:n) + t = PartialStruct(t, at.fields) + end + end + 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 + +# 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 || hd == :code_coverage_effect + # these do not generate code + 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..039f28d --- /dev/null +++ b/base/compiler/compiler.jl @@ -0,0 +1,117 @@ +# 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, _apply_iterate, 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("iddict.jl") +include("idset.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..2420636 --- /dev/null +++ b/base/compiler/inferenceresult.jl @@ -0,0 +1,173 @@ +# 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] = argtype_by_index(given_argtypes, i) + end + if length(given_argtypes) >= nargs || !isvarargtype(given_argtypes[end]) + isva_given_argtypes[nargs] = tuple_tfunc(given_argtypes[nargs:end]) + else + isva_given_argtypes[nargs] = tuple_tfunc(given_argtypes[end:end]) + 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..8ac40f2 --- /dev/null +++ b/base/compiler/inferencestate.jl @@ -0,0 +1,294 @@ +# 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 + + # cached results of calling `_methods_by_ftype`, including `min_valid` and + # `max_valid`, to be used in inlining + matching_methods_cache::IdDict{Any, Tuple{Any, UInt, UInt}} + + # 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, + IdDict{Any, Tuple{Any, UInt, UInt}}()) + 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) + # typically, we expect that old ⊑ new (that output information only + # gets less precise with worse input information), but to actually + # guarantee convergence we need to use tmerge here to ensure that is true + frame.src.ssavaluetypes[ssa_id] = old === NOT_FOUND ? new : 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..b1cc75d --- /dev/null +++ b/base/compiler/optimize.jl @@ -0,0 +1,452 @@ +# 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 + # cached results of calling `_methods_by_ftype` from inference, including + # `min_valid` and `max_valid` + matching_methods_cache::IdDict{Any, Tuple{Any, UInt, UInt}} + 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, + frame.matching_methods_cache) + 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, + IdDict{Any, Tuple{Any, UInt, UInt}}()) + 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 + # 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.sqrt_llvm_fast || # 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{}) || isa(T, Const) || isconcretetype(widenconst(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 + if labelchangemap[end] == 0 && ssachangemap[end] == 0 + return + end + 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 === :(=) && el.args[2] isa Expr + el = el.args[2]::Expr + end + 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) + 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..f9b407f --- /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 [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..3353d57 --- /dev/null +++ b/base/compiler/ssair/driver.jl @@ -0,0 +1,155 @@ +# 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 convert_to_ircode(ci::CodeInfo, code::Vector{Any}, coverage::Bool, 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)) + labelmap = coverage ? fill(0, length(code)) : changemap + prevloc = zero(eltype(ci.codelocs)) + while idx <= length(code) + codeloc = ci.codelocs[idx] + if coverage && codeloc != prevloc && codeloc != 0 + # insert a side-effect instruction before the current instruction in the same basic block + insert!(code, idx, Expr(:code_coverage_effect)) + insert!(ci.codelocs, idx, codeloc) + insert!(ci.ssavaluetypes, idx, Nothing) + changemap[oldidx] += 1 + if oldidx < length(labelmap) + labelmap[oldidx + 1] += 1 + end + idx += 1 + prevloc = codeloc + end + if code[idx] isa Expr && ci.ssavaluetypes[idx] === Union{} + if !(idx < length(code) && isexpr(code[idx + 1], :unreachable)) + # insert unreachable in the same basic block after the current instruction (splitting it) + 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 + coverage && (labelmap[oldidx + 1] += 1) + end + idx += 1 + end + end + idx += 1 + oldidx += 1 + end + renumber_ir_elements!(code, changemap, labelmap) + + 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) + ir = IRCode(code, Any[], ci.codelocs, flags, cfg, collect(LineInfoNode, ci.linetable), sv.slottypes, meta, sv.sptypes) + return ir +end + +function slot2reg(ir::IRCode, ci::CodeInfo, nargs::Int, sv::OptimizationState) + # need `ci` for the slot metadata, IR for the code + @timeit "domtree 1" domtree = construct_domtree(ir.cfg) + defuse_insts = scan_slot_def_use(nargs, ci, ir.stmts) + @timeit "construct_ssa" ir = construct_ssa!(ci, ir, domtree, defuse_insts, nargs, sv.sptypes, sv.slottypes) # consumes `ir` + return ir +end + +function run_passes(ci::CodeInfo, nargs::Int, sv::OptimizationState) + preserve_coverage = coverage_enabled(sv.mod) + ir = convert_to_ircode(ci, copy_exprargs(ci.code), preserve_coverage, nargs, sv) + ir = slot2reg(ir, ci, 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..0c90b4d --- /dev/null +++ b/base/compiler/ssair/inlining.jl @@ -0,0 +1,1330 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +@nospecialize + +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(val, method::Method, sparams::Vector{Any}, metharg) = + new(val, method, sparams, metharg) +end + +struct DynamicCase + method::Method + sparams::Vector{Any} + metharg::Any + DynamicCase(method::Method, sparams::Vector{Any}, 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, atype, cases::Vector{Pair{Any, Any}}) = + new(idx, fully_covered, atype, cases, Int[]) +end +isinvoke(inl::UnionSplit) = false + +@specialize + +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}, arg_start::Int) + new_argexprs = Any[argexprs[arg_start]] + new_atypes = Any[atypes[arg_start]] + # loop over original arguments and flatten any known iterators + for i in (arg_start+1):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 && invoke_data === 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 + if sv.params.inlining + add_backedge!(mi, sv) + return ConstantCase(src, method, Any[methsp...], metharg) + else + return spec_lambda(atype_unlimited, sv, invoke_data) + end + end + if src === nothing + return spec_lambda(atype_unlimited, sv, invoke_data) + end + + src_inferred = ccall(:jl_ir_flag_inferred, Bool, (Any,), src) + src_inlineable = ccall(:jl_ir_flag_inlineable, Bool, (Any,), src) + + if !(src_inferred && src_inlineable && sv.params.inlining) + 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_ir, 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 || sig.f === Core._apply_iterate + arg_start = sig.f === Core._apply ? 2 : 3 + atypes = sig.atypes + if arg_start > length(atypes) + return nothing + end + # Try to figure out the signature of the function being called + # and if rewrite_apply_exprargs can deal with this form + for i = (arg_start + 1):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[arg_start] + if length(atypes) == arg_start+1 && ft isa Const && ft.val === Core.tuple && atypes[arg_start+1] ⊑ Tuple + # rewrite `((t::Tuple)...,)` to `t` + ir.stmts[idx] = stmt.args[arg_start+1] + return nothing + end + stmt.args, atypes = rewrite_apply_exprargs!(ir, idx, stmt.args, atypes, arg_start) + 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 + + # 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, params) || 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: Retrieve matching methods from cache (or compute them) + (meth, min_valid, max_valid) = get(sv.matching_methods_cache, sig.atype) do + # World age does not need to be taken into account in the cache + # because it is forwarded from type inference through `sv.params` + # in the case that the cache is nonempty, so it should be unchanged + # The max number of methods should be the same as in inference most + # of the time, and should not affect correctness otherwise. + min_val = UInt[typemin(UInt)] + max_val = UInt[typemax(UInt)] + ms = _methods_by_ftype(sig.atype, sv.params.MAX_METHODS, + sv.params.world, min_val, max_val) + return (ms, min_val[1], max_val[1]) + end + if meth === false || length(meth) == 0 + # No applicable method, or too many applicable methods + continue + end + update_valid_age!(min_valid, max_valid, 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, 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, params::Params) + typ = ir.types[idx] + f, ft, atypes = sig.f, sig.ft, sig.atypes + if params.inlining && 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 params.inlining && 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..1fa189d --- /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, Order.Forward)) +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..2e6a55d --- /dev/null +++ b/base/compiler/ssair/passes.jl @@ -0,0 +1,1155 @@ +# 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, GlobalRef) + mod, name = leaf.mod, leaf.name + if isdefined(mod, name) && isconst(mod, name) + leaf = getfield(mod, name) + else + return nothing + end + elseif isa(leaf, Union{Argument, Expr}) + return nothing + end + !ismutable(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..6a6ac89 --- /dev/null +++ b/base/compiler/ssair/queries.jl @@ -0,0 +1,87 @@ +# 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, + 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..8f54b40 --- /dev/null +++ b/base/compiler/ssair/slot2ssa.jl @@ -0,0 +1,872 @@ +# 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 in 1:length(code) + stmt = code[idx] + 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, ir::IRCode, domtree::DomTree, defuse, nargs::Int, sptypes::Vector{Any}, + slottypes::Vector{Any}) + code = ir.stmts + 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..7363e70 --- /dev/null +++ b/base/compiler/ssair/verify.jl @@ -0,0 +1,221 @@ +# 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)) + # At the moment, we allow GC preserve tokens outside the standard domination notion + #@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) + if stmt.head === :(=) + if stmt.args[1] isa SSAValue + @verify_error "SSAValue as assignment LHS" + error() + end + elseif stmt.head === :gc_preserve_end + # We allow gc_preserve_end tokens to span across try/catch + # blocks, which isn't allowed for regular SSA values, so + # we skip the validation below. + continue + 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..15fb81e --- /dev/null +++ b/base/compiler/tfuncs.jl @@ -0,0 +1,1556 @@ +# 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, 0) # usually used as not_int(::Bool) to negate a condition +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) +add_tfunc(sqrt_llvm_fast, 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 !ismutable(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 + end + if isa(x, Union) + return sizeof_nothrow(x.a) && sizeof_nothrow(x.b) + end + t, exact = instanceof_tfunc(x) + if !exact + # Could always be bottom at runtime, which throws + return false + end + if t !== Bottom + t === DataType && return true + x = t + x = unwrap_unionall(x) + if isa(x, Union) + isinline, sz, _ = uniontype_layout(x) + return isinline + end + isa(x, DataType) || return false + x.layout == C_NULL && return false + (datatype_nfields(x) == 0 && !datatype_pointerfree(x)) && return false + return true + else + x = widenconst(x) + x === DataType && return false + return isconcretetype(x) || isprimitivetype(x) + end +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]) + if isa(x, Union) + return tmerge(sizeof_tfunc(x.a), sizeof_tfunc(x.b)) + end + # Core.sizeof operates on either a type or a value. First check which + # case we're in. + t, exact = instanceof_tfunc(x) + if t !== Bottom + # The value corresponding to `x` at runtime could be a type. + # Normalize the query to ask about that type. + x = unwrap_unionall(t) + if isa(x, Union) + isinline, sz, _ = uniontype_layout(x) + return isinline ? Const(Int(sz)) : (exact ? Bottom : Int) + end + isa(x, DataType) || return Int + (isconcretetype(x) || isprimitivetype(x)) && return _const_sizeof(x) + else + x = widenconst(x) + x !== DataType && isconcretetype(x) && return _const_sizeof(x) + isprimitivetype(x) && return _const_sizeof(x) + end + 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) +function pointer_eltype(@nospecialize(ptr)) + a = widenconst(ptr) + 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 +add_tfunc(pointerref, 3, 3, + function (@nospecialize(a), @nospecialize(i), @nospecialize(align)) + return pointer_eltype(a) + 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) && isdefined(sv, 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(nv,Symbol) || isa(nv,Int)) + return Bottom + end + 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(sv, SimpleVector) || !ismutable(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 + +function intrinsic_nothrow(f::IntrinsicFunction, argtypes::Array{Any, 1}) + # First check that we have the correct number of arguments + iidx = Int(reinterpret(Int32, f::IntrinsicFunction)) + 1 + if iidx < 1 || iidx > length(T_IFUNC) + # invalid intrinsic + return false + end + tf = T_IFUNC[iidx] + tf = tf::Tuple{Int, Int, Any} + if !(tf[1] <= length(argtypes) <= tf[2]) + # wrong # of args + return false + end + # TODO: We could do better for cglobal + f === Intrinsics.cglobal && return false + # TODO: We can't know for sure, but the user should have a way to assert + # that it won't + f === Intrinsics.llvmcall && 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 + if !isprimitivetype(widenconst(argtypes[1])) || + (widenconst(argtypes[1]) !== widenconst(argtypes[2])) + return false + end + den_val = argtypes[2].val + den_val !== zero(typeof(den_val)) || return false + f !== Intrinsics.checked_sdiv_int && return true + # 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 + if f === Intrinsics.pointerset + eT = pointer_eltype(argtypes[1]) + isprimitivetype(eT) || return false + return argtypes[2] ⊑ eT && argtypes[3] ⊑ Int && argtypes[4] ⊑ Int + end + if f === Intrinsics.arraylen + return argtypes[1] ⊑ Array + end + if f === Intrinsics.bitcast + ty = instanceof_tfunc(argtypes[1])[1] + xty = widenconst(argtypes[2]) + return isprimitivetype(ty) && isprimitivetype(xty) && ty.size === xty.size + end + if f in (Intrinsics.sext_int, Intrinsics.zext_int, Intrinsics.trunc_int, + Intrinsics.fptoui, Intrinsics.fptosi, Intrinsics.uitofp, + Intrinsics.sitofp, Intrinsics.fptrunc, Intrinsics.fpext) + # If !isexact, `ty` may be Union{} at runtime even if we have + # isprimitivetype(ty). + ty, isexact = instanceof_tfunc(argtypes[1]) + xty = widenconst(argtypes[2]) + return isexact && isprimitivetype(ty) && isprimitivetype(xty) + end + # The remaining intrinsics are math/bits/comparison intrinsics. They work on all + # primitive types of the same type. + isshift = f == shl_int || f == lshr_int || f == ashr_int + argtype1 = widenconst(argtypes[1]) + isprimitivetype(argtype1) || return false + for i = 2:length(argtypes) + argtype = widenconst(argtypes[i]) + if isshift ? !isprimitivetype(argtype) : argtype !== argtype1 + return false + end + 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 + rt = abstract_call(nothing, argtypes_vec, vtypes, sv, -1) + 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..5091ea0 --- /dev/null +++ b/base/compiler/typeinfer.jl @@ -0,0 +1,645 @@ +# 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) + + # 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_ir, 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 = Core.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_ir(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..7abd0c1 --- /dev/null +++ b/base/compiler/typelattice.jl @@ -0,0 +1,328 @@ +# 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, PartialTypeVar) && b === TypeVar + return true + 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..cd88ac0 --- /dev/null +++ b/base/compiler/typelimits.jl @@ -0,0 +1,508 @@ +# 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 # this may fail if t contains a typevar in invariant and multiple times + # in covariant position and r looses the occurence in invariant position (see #36407) + if !(t <: r) # ideally, this should never happen + # widen to minimum complexity to obtain a valid result + r = _limit_type_size(t, Any, source, 1, allowed_tuplelen) + t <: r || (r = Any) # final escape hatch + end + #@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 + 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 + # TODO: need to check that the UnionAll bounds on t are limited enough too + 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 + # TODO: need to check that the UnionAll bounds on t are limited enough too + 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 + +function issimpleenoughtype(@nospecialize t) + return unionlen(t) <= MAX_TYPEUNION_LENGTH && unioncomplexity(t) <= MAX_TYPEUNION_COMPLEXITY +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 === Union{} && return typeb + typeb === Union{} && return typea + suba = typea ⊑ typeb + suba && issimpleenoughtype(typeb) && return typeb + subb = typeb ⊑ typea + suba && subb && return typea + subb && issimpleenoughtype(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{} + typenames[i] = Any.name + 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{} + typenames[i] = Any.name + types[j] = widen + break + end + end + end + end + u = Union{types...} + # don't let type unions get too big, if the above didn't reduce it enough + if issimpleenoughtype(u) + return u + end + # don't let the slow widening of Tuple cause the whole type to grow too fast + for i in 1:length(types) + if typenames[i] === Tuple.name + widen = unwrap_unionall(types[i]) + if isa(widen, DataType) && !isvatuple(widen) + widen = NTuple{length(widen.parameters), Any} + else + widen = Tuple + end + types[i] = widen + u = Union{types...} + if issimpleenoughtype(u) + return u + end + break + end + 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]} + p[i] = issimpleenoughtype(ui) ? ui : Any + 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..d6eb730 --- /dev/null +++ b/base/compiler/typeutils.jl @@ -0,0 +1,178 @@ +# 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)} + elseif a isa DataType + if b isa DataType + if a.name === b.name === Tuple.name && length(a.types) == length(b.types) + ta = switchtupleunion(a) + if length(ta) > 1 + return typesubtract(Union{ta...}, b) + end + end + end + 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}) + t = init + for x in ct + # FIXME: this is broken: it violates subtyping relations and creates invalid types with free typevars + t = tmerge(t, tvar_extent(unwrapva(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) + return unioncomplexity(u.a) + unioncomplexity(u.b) + 1 +end +function unioncomplexity(t::DataType) + t.name === Tuple.name || isvarargtype(t) || return 0 + c = 0 + for ti in t.parameters + c = max(c, unioncomplexity(ti)) + 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..84bfd07 --- /dev/null +++ b/base/compiler/utilities.jl @@ -0,0 +1,248 @@ +# 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)) + if x isa Type || x isa Symbol + return true + end + 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_ir, 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 + # check cached specializations + # for an existing result stored there + return ccall(:jl_specializations_lookup, Any, (Any, Any), method, atypes) + 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 # +########### + +is_root_module(m::Module) = false + +inlining_enabled() = (JLOptions().can_inline == 1) +function coverage_enabled(m::Module) + ccall(:jl_generating_output, Cint, ()) == 0 || return false # don't alter caches + cov = JLOptions().code_coverage + if cov == 1 + m = moduleroot(m) + m === Core && return false + isdefined(Main, :Base) && m === Main.Base && return false + return true + elseif cov == 2 + return true + end + return false +end +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..ad12086 --- /dev/null +++ b/base/compiler/validation.jl @@ -0,0 +1,237 @@ +# 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, + :code_coverage_effect => 0:0, + :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 || head === :code_coverage_effect + 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..49ac018 --- /dev/null +++ b/base/complex.jl @@ -0,0 +1,1030 @@ +# 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(C::Type{<:Complex}) = fieldtype(C, 1) + +""" + 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) + print(io, compact ? "-" : " - ") + if isa(i,Signed) && !isa(i,BigInt) && i == typemin(typeof(i)) + show(io, -widen(i)) + else + show(io, -i) + end + else + print(io, compact ? "+" : " + ") + show(io, i) + end + 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..24e636c --- /dev/null +++ b/base/condition.jl @@ -0,0 +1,168 @@ +# 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} + +lock(c::GenericCondition{AlwaysLockedST}) = + throw(ArgumentError("`Condition` is not thread-safe. Please use `Threads.Condition` instead for multi-threaded code.")) +unlock(c::GenericCondition{AlwaysLockedST}) = + throw(ArgumentError("`Condition` is not thread-safe. Please use `Threads.Condition` instead for multi-threaded code.")) 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..0100aff --- /dev/null +++ b/base/deepcopy.jl @@ -0,0 +1,128 @@ +# 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. + +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 + nf = nfields(x) + if T.mutable + if haskey(stackdict, x) + return stackdict[x] + end + y = ccall(:jl_new_struct_uninit, Any, (Any,), T) + stackdict[x] = y + for i in 1:nf + if isdefined(x, i) + xi = getfield(x, i) + xi = deepcopy_internal(xi, stackdict)::typeof(xi) + ccall(:jl_set_nth_field, Cvoid, (Any, Csize_t, Any), y, i-1, xi) + end + end + elseif nf == 0 || isbitstype(T) + y = x + else + flds = Vector{Any}(undef, nf) + for i in 1:nf + if isdefined(x, i) + xi = getfield(x, i) + xi = deepcopy_internal(xi, stackdict)::typeof(xi) + flds[i] = xi + else + nf = i - 1 # rest of tail must be undefined values + break + end + end + y = ccall(:jl_new_structv, Any, (Any, Ptr{Any}, UInt32), T, flds, nf) + 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)::typeof(xi) + 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..aa946cf --- /dev/null +++ b/base/deprecated.jl @@ -0,0 +1,212 @@ +# 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] + +Deprecate method `old` and specify the replacement call `new`. Prevent `@deprecate` from +exporting `old` by setting `ex` to `false`. `@deprecate` defines a new method with the same +signature as `old`. + +!!! compat "Julia 1.5" + As of Julia 1.5, functions defined by `@deprecate` do not print warning when `julia` + is run without the `--depwarn=yes` flag set, as the default value of `--depwarn` option + is `no`. The warnings are printed from tests run by `Pkg.test()`. + +# 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; force::Bool=false) + opts = JLOptions() + if opts.depwarn == 2 + throw(ErrorException(msg)) + end + deplevel = force || 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 + for ip in bt + lkups = StackTraces.lookup(ip) + for lkup in lkups + if lkup == StackTraces.UNKNOWN || lkup.from_c + continue + end + if found + return ip, lkup + 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 C_NULL, StackTraces.UNKNOWN +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 1.0 deprecations + +@deprecate one(i::CartesianIndex) oneunit(i) +@deprecate one(I::Type{CartesianIndex{N}}) where {N} oneunit(I) + +@deprecate BigFloat(x, prec::Int) BigFloat(x; precision=prec) +@deprecate BigFloat(x, prec::Int, rounding::RoundingMode) BigFloat(x, rounding; precision=prec) +@deprecate BigFloat(x::Real, prec::Int) BigFloat(x; precision=prec) +@deprecate BigFloat(x::Real, prec::Int, rounding::RoundingMode) BigFloat(x, rounding; precision=prec) + +# END 1.0 deprecations + +# BEGIN 1.5 deprecations + +""" + isimmutable(v) -> Bool +!!! warning + Consider using `!ismutable(v)` instead, as `isimmutable(v)` will be replaced by `!ismutable(v)` in a future release. (Since Julia 1.5) +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)) = !ismutable(x) +export isimmutable +# Note isimmutable is not @deprecated out of performance concerns + +macro get!(h, key0, default) + f, l = __source__.file, __source__.line + @warn "`@get!(dict, key, default)` at $f:$l is deprecated, use `get!(()->default, dict, key)` instead." + return quote + get!(()->$(esc(default)), $(esc(h)), $(esc(key0))) + end +end + +pointer(V::SubArray{<:Any,<:Any,<:Array,<:Tuple{Vararg{RangeIndex}}}, is::Tuple) = pointer(V, CartesianIndex(is)) + +# END 1.5 deprecations diff --git a/base/dict.jl b/base/dict.jl new file mode 100644 index 0000000..6a0f4ae --- /dev/null +++ b/base/dict.jl @@ -0,0 +1,791 @@ +# 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)[1]) + 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}() +``` +""" +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!(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 + + +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, if any, 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 + +julia> delete!(d, "b") # d is left unchanged +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 propagated 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 `ImmutableDict` for a `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]) +ImmutableDict(t::ImmutableDict{K,V}, KV::Pair, rest::Pair...) where {K,V} = + ImmutableDict(ImmutableDict(t, KV), rest...) +ImmutableDict(KV::Pair, rest::Pair...) = ImmutableDict(ImmutableDict(KV), rest...) + +function in(key_value::Pair, dict::ImmutableDict, valcmp=(==)) + key, value = key_value + while isdefined(dict, :parent) + if isequal(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) + isequal(dict.key, key) && return true + dict = dict.parent + end + return false +end + +function getindex(dict::ImmutableDict, key) + while isdefined(dict, :parent) + isequal(dict.key, key) && return dict.value + dict = dict.parent + end + throw(KeyError(key)) +end +function get(dict::ImmutableDict, key, default) + while isdefined(dict, :parent) + isequal(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/div.jl b/base/div.jl new file mode 100644 index 0000000..1923da1 --- /dev/null +++ b/base/div.jl @@ -0,0 +1,280 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# Div is truncating by default + +""" + div(x, y, r::RoundingMode=RoundToZero) + +The quotient from Euclidean division. Computes x/y, rounded to an integer according +to the rounding mode `r`. In other words, the quantity + + round(x/y,r) + +without any intermediate rounding. + +See also: [`fld`](@ref), [`cld`](@ref) which are special cases of this function + +# Examples: +```jldoctest +julia> div(4, 3, RoundDown) # Matches fld(4, 3) +1 +julia> div(4, 3, RoundUp) # Matches cld(4, 3) +2 +julia> div(5, 2, RoundNearest) +2 +julia> div(5, 2, RoundNearestTiesAway) +3 +julia> div(-5, 2, RoundNearest) +-2 +julia> div(-5, 2, RoundNearestTiesAway) +-3 +julia> div(-5, 2, RoundNearestTiesUp) +-2 +``` +""" +div(x, y, r::RoundingMode) + +div(a, b) = div(a, b, RoundToZero) + +""" + rem(x, y, r::RoundingMode=RoundToZero) + +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, r::RoundingMode) + +# TODO: Make these primitive and have the two-argument version call these +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, y, r::RoundingMode{:Nearest}) = x - y*div(x, y, r) +rem(x::Integer, y::Integer, r::RoundingMode{:Nearest}) = divrem(x, y, r)[2] + +""" + fld(x, y) + +Largest integer less than or equal to `x/y`. Equivalent to `div(x, y, RoundDown)`. + +See also: [`div`](@ref) + +# Examples +```jldoctest +julia> fld(7.3,5.5) +1.0 +``` +""" +fld(a, b) = div(a, b, RoundDown) + +""" + cld(x, y) + +Smallest integer larger than or equal to `x/y`. Equivalent to `div(x, y, RoundUp)`. + +See also: [`div`](@ref) + +# Examples +```jldoctest +julia> cld(5.5,2.2) +3.0 +``` +""" +cld(a, b) = div(a, b, RoundUp) + +# divrem +""" + divrem(x, y, r::RoundingMode=RoundToZero) + +The quotient and remainder from Euclidean division. +Equivalent to `(div(x,y,r), rem(x,y,r))`. Equivalently, with the default +value of `r`, this call is equivalent to `(x÷y, x%y)`. + +# Examples +```jldoctest +julia> divrem(3,7) +(0, 3) + +julia> divrem(7,3) +(2, 1) +``` +""" +divrem(x, y) = divrem(x, y, RoundToZero) +function divrem(a, b, r::RoundingMode) + if r === RoundToZero + # For compat. Remove in 2.0. + (div(a, b), rem(a, b)) + elseif r === RoundDown + # For compat. Remove in 2.0. + (fld(a, b), mod(a, b)) + else + (div(a, b, r), rem(a, b, r)) + end +end +function divrem(x::Integer, y::Integer, rnd::typeof(RoundNearest)) + (q, r) = divrem(x, y) + if x >= 0 + if y >= 0 + r >= (y÷2) + (isodd(y) | iseven(q)) ? (q+true, r-y) : (q, r) + else + r >= -(y÷2) + (isodd(y) | iseven(q)) ? (q-true, r+y) : (q, r) + end + else + if y >= 0 + r <= -signed(y÷2) - (isodd(y) | iseven(q)) ? (q-true, r+y) : (q, r) + else + r <= (y÷2) - (isodd(y) | iseven(q)) ? (q+true, r-y) : (q, r) + end + end +end +function divrem(x::Integer, y::Integer, rnd:: typeof(RoundNearestTiesAway)) + (q, r) = divrem(x, y) + if x >= 0 + if y >= 0 + r >= (y÷2) + isodd(y) ? (q+true, r-y) : (q, r) + else + r >= -(y÷2) + isodd(y) ? (q-true, r+y) : (q, r) + end + else + if y >= 0 + r <= -signed(y÷2) - isodd(y) ? (q-true, r+y) : (q, r) + else + r <= (y÷2) - isodd(y) ? (q+true, r-y) : (q, r) + end + end +end +function divrem(x::Integer, y::Integer, rnd::typeof(RoundNearestTiesUp)) + (q, r) = divrem(x, y) + if x >= 0 + if y >= 0 + r >= (y÷2) + isodd(y) ? (q+true, r-y) : (q, r) + else + r >= -(y÷2) + true ? (q-true, r+y) : (q, r) + end + else + if y >= 0 + r <= -signed(y÷2) - true ? (q-true, r+y) : (q, r) + else + r <= (y÷2) - isodd(y) ? (q+true, r-y) : (q, r) + end + end +end + +""" + fldmod(x, y) + +The floored quotient and modulus after division. A convenience wrapper for +`divrem(x, y, RoundDown)`. Equivalent to `(fld(x,y), mod(x,y))`. +""" +fldmod(x,y) = divrem(x, y, RoundDown) + +# We definite generic rounding methods for other rounding modes in terms of +# RoundToZero. +function div(x::Signed, y::Unsigned, ::typeof(RoundDown)) + (q, r) = divrem(x, y) + q - (signbit(x) & (r != 0)) +end +function div(x::Unsigned, y::Signed, ::typeof(RoundDown)) + (q, r) = divrem(x, y) + q - (signbit(y) & (r != 0)) +end + +function div(x::Signed, y::Unsigned, ::typeof(RoundUp)) + (q, r) = divrem(x, y) + q + (!signbit(x) & (r != 0)) +end +function div(x::Unsigned, y::Signed, ::typeof(RoundUp)) + (q, r) = divrem(x, y) + q + (!signbit(y) & (r != 0)) +end + +function div(x::Integer, y::Integer, rnd::Union{typeof(RoundNearest), + typeof(RoundNearestTiesAway), + typeof(RoundNearestTiesUp)}) + divrem(x, y, rnd)[1] +end + +# For bootstrapping purposes, we define div for integers directly. Provide the +# generic signature also +div(a::T, b::T, ::typeof(RoundToZero)) where {T<:Union{BitSigned, BitUnsigned64}} = div(a, b) +div(a::Bool, b::Bool, r::RoundingMode) = div(a, b) +# Prevent ambiguities +for rm in (RoundUp, RoundDown, RoundToZero) + @eval div(a::Bool, b::Bool, r::$(typeof(rm))) = div(a, b) +end +function div(x::Bool, y::Bool, rnd::Union{typeof(RoundNearest), + typeof(RoundNearestTiesAway), + typeof(RoundNearestTiesUp)}) + div(x, y) +end +fld(a::T, b::T) where {T<:Union{Integer,AbstractFloat}} = div(a, b, RoundDown) +cld(a::T, b::T) where {T<:Union{Integer,AbstractFloat}} = div(a, b, RoundUp) +div(a::Int128, b::Int128, ::typeof(RoundToZero)) = div(a, b) +div(a::UInt128, b::UInt128, ::typeof(RoundToZero)) = div(a, b) +rem(a::Int128, b::Int128, ::typeof(RoundToZero)) = rem(a, b) +rem(a::UInt128, b::UInt128, ::typeof(RoundToZero)) = rem(a, b) + +# These are kept for compatibility with external packages overriding fld/cld. +# In 2.0, packages should extend div(a,b,r) instead, in which case, these can +# be removed. +fld(x::Real, y::Real) = div(promote(x,y)..., RoundDown) +cld(x::Real, y::Real) = div(promote(x,y)..., RoundUp) +fld(x::Signed, y::Unsigned) = div(x, y, RoundDown) +fld(x::Unsigned, y::Signed) = div(x, y, RoundDown) +cld(x::Signed, y::Unsigned) = div(x, y, RoundUp) +cld(x::Unsigned, y::Signed) = div(x, y, RoundUp) +fld(x::T, y::T) where {T<:Real} = throw(MethodError(div, (x, y, RoundDown))) +cld(x::T, y::T) where {T<:Real} = throw(MethodError(div, (x, y, RoundUp))) + +# Promotion +function div(x::Real, y::Real, r::RoundingMode) + typeof(x) === typeof(y) && throw(MethodError(div, (x, y, r))) + if r === RoundToZero + # For compat. Remove in 2.0. + div(promote(x, y)...) + else + div(promote(x, y)..., r) + end +end + +# Integers +# fld(x,y) == div(x,y) - ((x>=0) != (y>=0) && rem(x,y) != 0 ? 1 : 0) +div(x::T, y::T, ::typeof(RoundDown)) where {T<:Unsigned} = div(x,y) +function div(x::T, y::T, ::typeof(RoundDown)) where T<:Integer + d = div(x, y, RoundToZero) + 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 div(x::T, y::T, ::typeof(RoundUp)) where T<:Unsigned + d = div(x, y, RoundToZero) + return d + (d * y != x) +end +function div(x::T, y::T, ::typeof(RoundUp)) where T<:Integer + d = div(x, y, RoundToZero) + return d + (((x > 0) == (y > 0)) & (d * y != x)) +end + +# Real +# 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, r::RoundingMode) where {T<:AbstractFloat} = convert(T,round((x-rem(x,y,r))/y)) +rem(x::T, y::T, ::typeof(RoundUp)) where {T<:AbstractFloat} = convert(T,x-y*ceil(x/y)) diff --git a/base/docs/Docs.jl b/base/docs/Docs.jl new file mode 100644 index 0000000..0d8665d --- /dev/null +++ b/base/docs/Docs.jl @@ -0,0 +1,623 @@ +# 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. +The macro has special parsing so that the documented object may occur on the next line: + + @doc "blah" + function foo() ... + +By default, documentation is written as Markdown, but any object can be used as +the first argument. + +## 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]))}) + if isexpr(expr, :meta) && length(expr.args) == 2 + a1 = expr.args[1] + if a1 === :nospecialize || a1 === :specialize + return argtype(expr.args[2]) + end + end + 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 = callargs(def) + if isempty(args) || all(validcall, args) + objectdoc(__source__, __module__, str, nothing, def, signature(def)) + else + docerror(def) + end +end +callargs(ex::Expr) = isexpr(ex, :where) ? callargs(ex.args[1]) : + isexpr(ex, :call) ? ex.args[2:end] : error("Invalid expression to callargs: $ex") +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 + +# iscallexpr checks if an expression is a :call expression. The call expression may be +# also part of a :where expression, so it unwraps the :where layers until it reaches the +# "actual" expression +iscallexpr(ex::Expr) = isexpr(ex, :where) ? iscallexpr(ex.args[1]) : isexpr(ex, :call) +iscallexpr(ex) = false + +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(...) + # + # Including if the "call" expression is wrapped in "where" expression(s) (#32960), i.e. + # + # f(::T) where T + # f(::T, ::U) where T where U + # + 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) : + iscallexpr(x) ? 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..fd8240c --- /dev/null +++ b/base/docs/basedocs.jl @@ -0,0 +1,2512 @@ +# 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"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. Macros can be defined for +variable number of arguments (varargs), but do not accept keyword arguments. + +# Examples +```jldoctest +julia> macro sayhello(name) + return :( println("Hello, ", \$name, "!") ) + end +@sayhello (macro with 1 method) + +julia> @sayhello "Charlie" +Hello, Charlie! + +julia> macro saylots(x...) + return :( println("Say: ", \$(x...)) ) + end +@saylots (macro with 1 method) + +julia> @saylots "hey " "there " "friend" +Say: hey there friend +``` +""" +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"=" + +""" + .= + +Perform broadcasted assignment. The right-side argument is expanded as in +[`broadcast`](@ref) and then assigned into the left-side argument in-place. +Fuses with other dotted operators in the same expression; i.e. the whole +assignment expression is converted into a single loop. + +`A .= B` is similar to `broadcast!(identity, A, B)`. + +# Examples +```jldoctest +julia> A = zeros(4, 4); B = [1, 2, 3, 4]; + +julia> A .= B +4×4 Array{Float64,2}: + 1.0 1.0 1.0 1.0 + 2.0 2.0 2.0 2.0 + 3.0 3.0 3.0 3.0 + 4.0 4.0 4.0 4.0 + +julia> A +4×4 Array{Float64,2}: + 1.0 1.0 1.0 1.0 + 2.0 2.0 2.0 2.0 + 3.0 3.0 3.0 3.0 + 4.0 4.0 4.0 4.0 +``` +""" +kw".=" + +""" + . + +The dot operator is used to access fields or properties of objects and access +variables defined inside modules. + +In general, `a.b` calls `getproperty(a, :b)` (see [`getproperty`](@ref Base.getproperty)). + +# Examples +```jldoctest +julia> z = 1 + 2im; z.im +2 + +julia> Iterators.product +product (generic function with 1 method) +``` +""" +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" + +""" + {} + +Curly braces are used to specify [type parameters](@ref man-parametric-types). + +Type parameters allow a single type declaration to introduce a whole family of +new types — one for each possible combination of parameter values. For example, +the [`Set`](@ref) type describes many possible types of sets; it uses one type +parameter to describe the type of the elements it contains. The specific _parameterized_ +types `Set{Float64}` and `Set{Int64}` describe two _concrete_ types: both are +subtypes ([`<:`](@ref)) of `Set`, but the former has `Float64` elements and the latter +has `Int64` elements. +""" +kw"{", kw"{}", kw"}" + +""" + [] + +Square braces are used for [indexing](@ref man-array-indexing), [indexed assignment](@ref man-indexed-assignment), +[array literals](@ref man-array-literals), and [array comprehensions](@ref man-comprehensions). +""" +kw"[", kw"[]", kw"]" + +""" + () + +Parentheses are used to group expressions, call functions, and construct [tuples](@ref Tuple) and [named tuples](@ref NamedTuple). +""" +kw"(", kw"()", kw")" + +""" + # + +The number sign (or hash) character is used to begin a single-line comment. +""" +kw"#" + +""" + #= =# + +A multi-line comment begins with `#=` and ends with `=#`, and may be nested. +""" +kw"#=", kw"=#" + +""" + ; + +Semicolons are used as statement separators and mark the beginning of keyword arguments in function declarations or calls. +""" +kw";" + +""" + 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 + +""" + \$ + +Interpolation operator for interpolating into e.g. [strings](@ref string-interpolation) +and [expressions](@ref man-expression-interpolation). + +# Examples +```jldoctest +julia> name = "Joe" +"Joe" + +julia> "My name is \$name." +"My name is Joe." +``` +""" +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" + +""" + a ? b : c + +Short form for conditionals; read "if `a`, evaluate `b` otherwise evaluate `c`". +Also known as the [ternary operator](https://en.wikipedia.org/wiki/%3F:). + +This syntax is equivalent to `if a; b else c end`, but is often used to +emphasize the value `b`-or-`c` which is being used as part of a larger +expression, rather than the side effects that evaluating `b` or `c` may have. + +See the manual section on [control flow](@ref man-conditional-evaluation) for more details. + +# Examples +``` +julia> x = 1; y = 2; + +julia> println(x > y ? "x is larger" : "y is larger") +y is larger +``` +""" +kw"?", kw"?:" + +""" + 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 man-inner-constructor-methods) +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 + +""" + Core.Type{T} + +`Core.Type` is an abstract type which has all type objects as its instances. +The only instance of the singleton type `Core.Type{T}` is the object +`T`. + +# Examples +```jldoctest +julia> isa(Type{Float64}, Type) +true + +julia> isa(Float64, Type) +true + +julia> isa(Real, Type{Float64}) +false + +julia> isa(Real, Type{Real}) +true +``` +""" +Core.Type + +""" + DataType <: Type{T} + +`DataType` represents explicitly declared types that have names, explicitly +declared supertypes, and, optionally, parameters. Every concrete value in the +system is an instance of some `DataType`. + +# Examples +```jldoctest +julia> typeof(Real) +DataType + +julia> typeof(Int) +DataType + +julia> struct Point + x::Int + y + end + +julia> typeof(Point) +DataType +``` +""" +Core.DataType + +""" + 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). + +Note that, in Julia script started without `-i` (interactive) option, +`InterruptException` is not thrown by default. Calling +[`Base.exit_on_sigint(false)`](@ref Base.exit_on_sigint) in the script +can recover the behavior of the REPL. Alternatively, a Julia script +can be started with + +```sh +julia -e "include(popfirst!(ARGS))" script.jl +``` + +to let `InterruptException` be thrown by CTRL+C during the execution. +""" +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). + +Be careful when using `invoke` for functions that you don't write. What definition is used +for given `argtypes` is an implementation detail unless the function is explicitly states +that calling with certain `argtypes` is a part of public API. For example, the change +between `f1` and `f2` in the example below is usually considered compatible because the +change is invisible by the caller with a normal (non-`invoke`) call. However, the change is +visible if you use `invoke`. + +# Examples +```jldoctest +julia> f(x::Real) = x^2; + +julia> f(x::Integer) = 1 + invoke(f, Tuple{Real}, x); + +julia> f(2) +5 + +julia> f1(::Integer) = Integer + f1(::Real) = Real; + +julia> f2(x::Real) = _f2(x) + _f2(::Integer) = Integer + _f2(_) = Real; + +julia> f1(1) +Integer + +julia> f2(1) +Integer + +julia> invoke(f1, Tuple{Real}, 1) +Real + +julia> invoke(f2, Tuple{Real}, 1) +Integer +``` +""" +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` is a kind of number: `false` is numerically +equal to `0` and `true` is numerically equal to `1`. +Moreover, `false` acts as a multiplicative "strong zero": + +```jldoctest +julia> false == 0 +true + +julia> true == 1 +true + +julia> 0 * NaN +NaN + +julia> false * NaN +0.0 +``` +""" +Bool + +for (bit, sign, exp, frac) in ((16, 1, 5, 10), (32, 1, 8, 23), (64, 1, 11, 52)) + @eval begin + """ + Float$($bit) <: AbstractFloat + + $($bit)-bit floating point number type (IEEE 754 standard). + + Binary format: $($sign) sign, $($exp) exponent, $($frac) fraction bits. + """ + $(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) + getfield(value, i::Int) + +Extract a field from a composite `value` by name or position. +See also [`getproperty`](@ref Base.getproperty) and [`fieldnames`](@ref). + +# Examples +```jldoctest +julia> a = 1//2 +1//2 + +julia> getfield(a, :num) +1 + +julia> a.num +1 + +julia> getfield(a, 1) +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 a value of type 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 a value of type 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 + +""" + 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) + +""" + 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. +""" +NamedTuple{names,T}(args::Tuple) + +""" + NamedTuple{names}(nt::NamedTuple) + +Construct a named tuple by selecting fields in `names` (a tuple of Symbols) from +another named tuple. +""" +NamedTuple{names}(nt::NamedTuple) + +""" + 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 a value of type 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} + +A hard-coded [`Union`](@ref) of common array types that follow the [strided array interface](@ref man-interface-strided-arrays), +with elements of type `T` and `N` dimensions. + +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..d96154f --- /dev/null +++ b/base/docs/bindings.jl @@ -0,0 +1,47 @@ +# 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(r::GlobalRef) = r.mod, quot(r.name) +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..0b9c6ba --- /dev/null +++ b/base/download.jl @@ -0,0 +1,104 @@ +# 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::AbstractString) + 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("busybox") !== nothing + try + run(`busybox 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, busybox 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..36565c8 --- /dev/null +++ b/base/error.jl @@ -0,0 +1,303 @@ +# 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() + +Rethrow the current exception from within a `catch` block. The rethrown +exception will continue propagation as if it had not been caught. + +!!! note + The alternative form `rethrow(e)` allows you to associate an alternative + exception object `e` with the current backtrace. However this misrepresents + the program state at the time of the error so you're encouraged to instead + throw a new exception using `throw(e)`. In Julia 1.1 and above, using + `throw(e)` will preserve the root cause exception on the stack, as + described in [`catch_stack`](@ref). +""" +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 + mod::Union{Module,Nothing} +end + +# convert dual arrays (raw bt buffer, array of GC managed values) 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) # See also jl_bt_is_native + # native frame + push!(ret, ip) + i += 1 + continue + end + # Extended backtrace entry + entry_metadata = reinterpret(UInt, bt[i+1]) + njlvalues = entry_metadata & 0x7 + nuintvals = (entry_metadata >> 3) & 0x7 + tag = (entry_metadata >> 6) & 0xf + header = entry_metadata >> 10 + if tag == 1 # JL_BT_INTERP_FRAME_TAG + code = bt2[j] + mod = njlvalues == 2 ? bt2[j+1] : nothing + push!(ret, InterpreterIP(code, header, mod)) + else + # Tags we don't know about are an error + throw(ArgumentError("Unexpected extended backtrace entry tag $tag at bt[$i]")) + end + # See jl_bt_entry_size + j += njlvalues + i += Int(2 + njlvalues + nuintvals) + end + ret +end + +""" + backtrace() + +Get a backtrace object for the current program point. +""" +function backtrace() + @_noinline_meta + # skip frame for backtrace(). Note that for this to work properly, + # backtrace() itself must not be interpreted nor inlined. + skip = 1 + bt1, bt2 = ccall(:jl_backtrace_from_here, Ref{SimpleVector}, (Cint, Cint), false, skip) + return _reformat_bt(bt1::Vector{Ptr{Cvoid}}, bt2::Vector{Any}) +end + +""" + catch_backtrace() + +Get the backtrace of the current exception, for use within `catch` blocks. +""" +function catch_backtrace() + bt, bt2 = ccall(:jl_get_backtrace, Ref{SimpleVector}, ()) + return _reformat_bt(bt::Vector{Ptr{Cvoid}}, bt2::Vector{Any}) +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[, errno::Cint=Libc.errno()]) + systemerror(sysfunc, iftrue::Bool) + +Raises a `SystemError` for `errno` with the descriptive string `sysfunc` if `iftrue` is `true` +""" +systemerror(p, b::Bool; extrainfo=nothing) = b ? systemerror(p, extrainfo=extrainfo) : nothing +systemerror(p, errno::Cint=Libc.errno(); extrainfo=nothing) = throw(Main.Base.SystemError(string(p), errno, extrainfo)) + +## system errors from Windows API functions +struct WindowsErrorInfo + errnum::UInt32 + extrainfo +end +""" + windowserror(sysfunc[, code::UInt32=Libc.GetLastError()]) + windowserror(sysfunc, iftrue::Bool) + +Like [`systemerror`](@ref), but for Windows API functions that use [`GetLastError`](@ref Base.Libc.GetLastError) to +return an error code instead of setting [`errno`](@ref Base.Libc.errno). +""" +windowserror(p, b::Bool; extrainfo=nothing) = b ? windowserror(p, extrainfo=extrainfo) : nothing +windowserror(p, code::UInt32=Libc.GetLastError(); extrainfo=nothing) = throw(Main.Base.SystemError(string(p), 0, WindowsErrorInfo(code, extrainfo))) + + +## 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..d611d11 --- /dev/null +++ b/base/errorshow.jl @@ -0,0 +1,750 @@ +# 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) + +show_index(io::IO, x::Any) = show(io, x) +show_index(io::IO, x::Slice) = show_index(io, x.indices) +show_index(io::IO, x::LogicalIndex) = show_index(io, x.mask) +show_index(io::IO, x::OneTo) = print(io, "1:", x.stop) +show_index(io::IO, x::Colon) = print(io, ':') + + +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 ex.i isa AbstractRange + print(io, ex.i) + elseif ex.i isa AbstractString + show(io, ex.i) + else + for (i, x) in enumerate(ex.i) + i > 1 && print(io, ", ") + show_index(io, x) + end + end + print(io, ']') + end + end + Experimental.show_error_hints(io, ex) +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 = ("a value of type $(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 + Experimental.show_error_hints(io, ex) +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 + Experimental.show_error_hints(io, ex) + 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, ')') + Experimental.show_error_hints(io, ex) +end + +typesof(args...) = Tuple{Any[ Core.Typeof(a) for a in args ]...} + +function print_with_compare(io::IO, @nospecialize(a::DataType), @nospecialize(b::DataType), color::Symbol) + if a.name === b.name + Base.show_type_name(io, a.name) + n = length(a.parameters) + print(io, '{') + for i = 1:n + if i > length(b.parameters) + printstyled(io, a.parameters[i], color=color) + else + print_with_compare(io::IO, a.parameters[i], b.parameters[i], color) + end + i < n && print(io, ',') + end + print(io, '}') + else + printstyled(io, a; color=color) + end +end + +function print_with_compare(io::IO, @nospecialize(a), @nospecialize(b), color::Symbol) + if a === b + print(io, a) + else + printstyled(io, a; color=color) + end +end + +function show_convert_error(io::IO, ex::MethodError, @nospecialize(arg_types_param)) + # 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_one_line = isa(T, DataType) && isa(arg_types_param[2], DataType) && T.name != arg_types_param[2].name + printstyled(io, "Cannot `convert` an object of type ") + print_one_line || printstyled(io, "\n ") + print_with_compare(io, arg_types_param[2], T, :light_green) + printstyled(io, " to an object of type ") + print_one_line || printstyled(io, "\n ") + print_with_compare(io, T, arg_types_param[2], :light_red) + end +end + +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 endswith(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 + show_convert_error(io, ex, arg_types_param) + 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 + # catch the two common cases of element-wise addition and subtraction + if (f === Base.:+ || f === Base.:-) && length(arg_types_param) == 2 + # we need one array of numbers and one number, in any order + if any(x -> x <: AbstractArray{<:Number}, arg_types_param) && + any(x -> x <: Number, arg_types_param) + + nouns = Dict( + Base.:+ => "addition", + Base.:- => "subtraction", + ) + varnames = ("scalar", "array") + first, second = arg_types_param[1] <: Number ? varnames : reverse(varnames) + print(io, "\nFor element-wise $(nouns[f]), use broadcasting with dot syntax: $first .$f $second") + end + 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) + print(io, "\nYou may have intended to import ") + show_unquoted(io, Expr(:., :Base, QuoteNode(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() + print(io, "\nThe 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 + Experimental.show_error_hints(io, ex, arg_types_param, kwargs) + 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 + if all(m->morespecific(sigfix, m.sig), meth) + print(io, "\nPossible fix, define\n ") + Base.show_tuple_as_call(io, :function, sigfix) + else + println(io) + print(io, "To resolve the ambiguity, try making one of the methods more specific, or ") + print(io, "adding a new method more specific than any of the existing applicable methods.") + end + 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 = kwarg_decl(method) + if !isempty(kwords) + print(iob, "; ") + join(iob, 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 + print(io, "\nClosest 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}) + # t is a pre-processed backtrace (ref #12856) + 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 is_kw_sorter_name(name::Symbol) + sn = string(name) + return !startswith(sn, '#') && endswith(sn, "##kw") +end + +function process_backtrace(t::Vector, limit::Int=typemax(Int); skipC = true) + n = 0 + last_frame = StackTraces.UNKNOWN + count = 0 + ret = Any[] + for i in eachindex(t) + lkups = t[i] + if lkups isa StackFrame + lkups = [lkups] + else + lkups = StackTraces.lookup(lkups) + end + for lkup in lkups + if lkup === StackTraces.UNKNOWN + continue + end + + if (lkup.from_c && skipC) || is_kw_sorter_name(lkup.func) + 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 + count > limit && break + 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 + +# Defined here rather than error.jl for bootstrap ordering +function show(io::IO, ip::InterpreterIP) + print(io, typeof(ip)) + if ip.code isa Core.CodeInfo + print(io, " in top-level CodeInfo for $(ip.mod) at statement $(Int(ip.stmt))") + else + print(io, " in $(ip.code) at statement $(Int(ip.stmt))") + end +end diff --git a/base/essentials.jl b/base/essentials.jl new file mode 100644 index 0000000..e5870bd --- /dev/null +++ b/base/essentials.jl @@ -0,0 +1,858 @@ +# 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. +An `AbstractDict{K, V}` should be an iterator of `Pair{K, V}`. +""" +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 + +function iterate 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 are similar to the methods we currently use but cnvt_all might work better: +#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.0f-45 3.0f-45 4.0f-45 6.0f-45 7.0f-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 + +""" + peek(stream[, T=UInt8]) + +Read and return a value of type `T` from a stream without advancing the current position +in the stream. + +# Examples + +```jldoctest +julia> b = IOBuffer("julia"); + +julia> peek(b) +0x6a + +julia> position(b) +0 + +julia> peek(b, Char) +'j': ASCII/Unicode U+006A (category Ll: Letter, lowercase) +``` + +!!! compat "Julia 1.5" + The method which accepts a type requires Julia 1.5 or later. +""" +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 + +# 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..014f9f8 --- /dev/null +++ b/base/experimental.jl @@ -0,0 +1,215 @@ +# 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 + +using Base: Threads, sync_varname + +""" + 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 + + +function sync_end(c::Channel{Any}) + if !isready(c) + # there must be at least one item to begin with + close(c) + return + end + nremaining::Int = 0 + while true + event = take!(c) + if event === :__completion__ + nremaining -= 1 + if nremaining == 0 + break + end + else + nremaining += 1 + schedule(Task(()->begin + try + wait(event) + put!(c, :__completion__) + catch e + close(c, e) + end + end)) + end + end + close(c) + nothing +end + +""" + Experimental.@sync + +Wait until all lexically-enclosed uses of `@async`, `@spawn`, `@spawnat` and `@distributed` +are complete, or at least one of them has errored. The first exception is immediately +rethrown. It is the responsibility of the user to cancel any still-running operations +during error handling. + +!!! Note + This interface is experimental and subject to change or removal without notice. +""" +macro sync(block) + var = esc(sync_varname) + quote + let $var = Channel(Inf) + v = $(esc(block)) + sync_end($var) + v + end + end +end + +""" + Experimental.@optlevel n::Int + +Set the optimization level (equivalent to the `-O` command line argument) +for code in the current module. Submodules inherit the setting of their +parent module. + +Supported values are 0, 1, 2, and 3. + +The effective optimization level is the minimum of that specified on the +command line and in per-module settings. +""" +macro optlevel(n::Int) + return Expr(:meta, :optlevel, n) +end + +# UI features for errors + +""" + Experimental.register_error_hint(handler, exceptiontype) + +Register a "hinting" function `handler(io, exception)` that can +suggest potential ways for users to circumvent errors. `handler` +should examine `exception` to see whether the conditions appropriate +for a hint are met, and if so generate output to `io`. +Packages should call `register_error_hint` from within their +`__init__` function. + +For specific exception types, `handler` is required to accept additional arguments: + +- `MethodError`: provide `handler(io, exc::MethodError, argtypes, kwargs)`, + which splits the combined arguments into positional and keyword arguments. + +When issuing a hint, the output should typically start with `\\n`. + +If you define custom exception types, your `showerror` method can +support hints by calling [`Experimental.show_error_hints`](@ref). + +# Example + +``` +julia> module Hinter + + only_int(x::Int) = 1 + any_number(x::Number) = 2 + + function __init__() + Base.Experimental.register_error_hint(MethodError) do io, exc, argtypes, kwargs + if exc.f == only_int + # Color is not necessary, this is just to show it's possible. + print(io, "\\nDid you mean to call ") + printstyled(io, "`any_number`?", color=:cyan) + end + end + end + + end +``` + +Then if you call `Hinter.only_int` on something that isn't an `Int` (thereby triggering a `MethodError`), it issues the hint: + +``` +julia> Hinter.only_int(1.0) +ERROR: MethodError: no method matching only_int(::Float64) +Did you mean to call `any_number`? +Closest candidates are: + ... +``` + +!!! compat "Julia 1.5" + Custom error hints are available as of Julia 1.5. +!!! warning + This interface is experimental and subject to change or removal without notice. + To insulate yourself against changes, consider putting any registrations inside an + `if isdefined(Base.Experimental, :register_error_hint) ... end` block. +""" +function register_error_hint(handler, exct::Type) + list = get!(()->[], _hint_handlers, exct) + push!(list, handler) + return nothing +end + +const _hint_handlers = IdDict{Type,Vector{Any}}() + +""" + Experimental.show_error_hints(io, ex, args...) + +Invoke all handlers from [`Experimental.register_error_hint`](@ref) for the particular +exception type `typeof(ex)`. `args` must contain any other arguments expected by +the handler for that type. + +!!! compat "Julia 1.5" + Custom error hints are available as of Julia 1.5. +!!! warning + This interface is experimental and subject to change or removal without notice. +""" +function show_error_hints(io, ex, args...) + hinters = get!(()->[], _hint_handlers, typeof(ex)) + for handler in hinters + try + Base.invokelatest(handler, io, ex, args...) + catch err + tn = typeof(handler).name + @error "Hint-handler $handler for $(typeof(ex)) in $(tn.module) caused an error" + end + end +end + +end diff --git a/base/exports.jl b/base/exports.jl new file mode 100644 index 0000000..fbd704e --- /dev/null +++ b/base/exports.jl @@ -0,0 +1,1000 @@ +# 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, + evalpoly, + abs, + abs2, + acos, + acosd, + acosh, + acot, + acotd, + acoth, + acsc, + acscd, + acsch, + angle, + asec, + asecd, + asech, + asin, + asind, + asinh, + atan, + atand, + atanh, + big, + binomial, + bitreverse, + bitrotate, + 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 + contains, + eachmatch, + endswith, + findall, + findfirst, + findlast, + findmax, + findmin, + findmin!, + findmax!, + findnext, + findprev, + match, + occursin, + searchsorted, + searchsortedfirst, + searchsortedlast, + startswith, + +# linear algebra + var"'", # to enable syntax a' for adjoint + adjoint, + transpose, + kron, + +# bitarrays + falses, + trues, + +# dequeues + append!, + insert!, + pop!, + popat!, + prepend!, + push!, + resize!, + popfirst!, + pushfirst!, + +# collections + all!, + all, + allunique, + any!, + any, + firstindex, + collect, + count!, + count, + delete!, + deleteat!, + eltype, + empty!, + empty, + lastindex, + filter!, + filter, + foldl, + foldr, + foreach, + get, + get!, + getindex, + getkey, + haskey, + in, + intersect!, + intersect, + isdisjoint, + isempty, + issubset, + issetequal, + keys, + keytype, + length, + map!, + map, + mapfoldl, + mapfoldr, + mapreduce, + merge!, + mergewith!, + merge, + mergewith, + 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, + only, + +# object identity and equality + copy, + deepcopy, + hash, + identity, + isbits, + isequal, + ismutable, + 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, + pkgdir, + 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, + peek, + 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, + @ccall, + 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 + @NamedTuple, + + # 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..4e7dd79 --- /dev/null +++ b/base/expr.jl @@ -0,0 +1,432 @@ +# 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) + cnew.edges = cnew.edges === nothing ? nothing : copy(cnew.edges) + 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 + +Give a hint to the compiler that it should not inline 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 + +If the function is trivial (for example returning a constant) it might get inlined anyway. +``` +""" +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. +This macro is intended for internal compiler use and may be subject to changes. +""" +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) + end + esc(ex) +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) || ex.head === :-> + 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..8d36e6e --- /dev/null +++ b/base/fastmath.jl @@ -0,0 +1,383 @@ +# 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. +# fast: Fast - Allow algebraically equivalent transformations that may +# dramatically change results in floating point (e.g. +# reassociate). This flag implies all the others. + +module FastMath + +export @fastmath + +import Core.Intrinsics: sqrt_llvm_fast, 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_fast(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..66640a2 --- /dev/null +++ b/base/file.jl @@ -0,0 +1,1016 @@ +# 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) + req = Libc.malloc(_sizeof_uv_fs) + try + ret = ccall(:uv_fs_mkdir, Cint, + (Ptr{Cvoid}, Ptr{Cvoid}, Cstring, Cint, Ptr{Cvoid}), + C_NULL, req, path, checkmode(mode), C_NULL) + if ret < 0 + ccall(:uv_fs_req_cleanup, Cvoid, (Ptr{Cvoid},), req) + uv_error("mkdir", ret) + end + ccall(:uv_fs_req_cleanup, Cvoid, (Ptr{Cvoid},), req) + return path + finally + Libc.free(req) + end +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, IOError) || !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(lstat(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. + +If the file does not exist a new file is created. + +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) + # each path should only be inserted here once, but if there + # is a collision, let !asap win over asap: if any user might + # still be using the path, don't delete it until process exit + TEMP_CLEANUP[path] = get(TEMP_CLEANUP, path, true) & 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 + try + 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) + catch ex + @warn "temp cleanup" _group=:file exception=(ex, catch_backtrace()) + end + 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 + +# generate a random string from random bytes +function _rand_string() + nchars = 10 + A = Vector{UInt8}(undef, nchars) + windowserror("SystemFunction036 (RtlGenRandom)", 0 == ccall( + (:SystemFunction036, :Advapi32), stdcall, UInt8, (Ptr{Cvoid}, UInt32), + A, sizeof(A))) + + slug = Base.StringVector(10) + chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + for i = 1:nchars + slug[i] = chars[(A[i] % length(chars)) + 1] + end + return name = String(slug) +end + +function tempname(parent::AbstractString=tempdir(); cleanup::Bool=true) + isdir(parent) || throw(ArgumentError("$(repr(parent)) is not a directory")) + name = _rand_string() + filename = joinpath(parent, temp_prefix * name) + @assert !ispath(filename) + cleanup && temp_cleanup_later(filename) + return filename +end + +else # !windows + +# Obtain a temporary filename. +function tempname(parent::AbstractString=tempdir(); cleanup::Bool=true) + isdir(parent) || throw(ArgumentError("$(repr(parent)) is not a directory")) + p = ccall(:tempnam, Cstring, (Cstring, Cstring), parent, temp_prefix) + systemerror(:tempnam, p == C_NULL) + s = unsafe_string(p) + Libc.free(p) + cleanup && temp_cleanup_later(s) + 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(parent=tempdir(); cleanup=true) -> String + +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 due to +the very remote posibility of two simultaneous calls to `tempname` generating +the same file name. The name is guaranteed to differ from all files already +existing at the time of the call to `tempname`. + +When called with no arguments, the temporary name will be an absolute path to a +temporary name in the system temporary directory as given by `tempdir()`. If a +`parent` directory argument is given, the temporary path will be in that +directory instead. + +The `cleanup` option controls whether the process attempts to delete the +returned path automatically when the process exits. Note that the `tempname` +function does not create any file or directory at the returned location, so +there is nothing to cleanup unless you create a file or directory there. If +you do and `clean` is `true` it will be deleted upon process termination. + +!!! compat "Julia 1.4" + The `parent` and `cleanup` arguments were added in 1.4. Prior to Julia 1.4 + the path `tempname` would never be cleaned up at process termination. + +!!! 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) + ispath(tmp_path) && 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 + ispath(tmpdir) && 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=pwd(); + join::Bool = false, + sort::Bool = true, + ) -> Vector{String} + +Return the names in the directory `dir` or the current working directory if not +given. When `join` is false, `readdir` returns just the names in the directory +as is; when `join` is true, it returns `joinpath(dir, name)` for each `name` so +that the returned strings are full paths. If you want to get absolute paths +back, call `readdir` with an absolute directory path and `join` set to true. + +By default, `readdir` sorts the list of names it returns. If you want to skip +sorting the names and get them in the order that the file system lists them, +you can use `readir(dir, sort=false)` to opt out of sorting. + +!!! compat "Julia 1.4" + The `join` and `sort` keyword arguments require at least Julia 1.4. + +# Examples +```julia-repl +julia> cd("/home/JuliaUser/dev/julia") + +julia> readdir() +30-element Array{String,1}: + ".appveyor.yml" + ".git" + ".gitattributes" + ⋮ + "ui" + "usr" + "usr-staging" + +julia> readdir(join=true) +30-element Array{String,1}: + "/home/JuliaUser/dev/julia/.appveyor.yml" + "/home/JuliaUser/dev/julia/.git" + "/home/JuliaUser/dev/julia/.gitattributes" + ⋮ + "/home/JuliaUser/dev/julia/ui" + "/home/JuliaUser/dev/julia/usr" + "/home/JuliaUser/dev/julia/usr-staging" + +julia> readdir("base") +145-element Array{String,1}: + ".gitignore" + "Base.jl" + "Enums.jl" + ⋮ + "version_git.sh" + "views.jl" + "weakkeydict.jl" + +julia> readdir("base", join=true) +145-element Array{String,1}: + "base/.gitignore" + "base/Base.jl" + "base/Enums.jl" + ⋮ + "base/version_git.sh" + "base/views.jl" + "base/weakkeydict.jl"``` + +julia> readdir(abspath("base"), join=true) +145-element Array{String,1}: + "/home/JuliaUser/dev/julia/base/.gitignore" + "/home/JuliaUser/dev/julia/base/Base.jl" + "/home/JuliaUser/dev/julia/base/Enums.jl" + ⋮ + "/home/JuliaUser/dev/julia/base/version_git.sh" + "/home/JuliaUser/dev/julia/base/views.jl" + "/home/JuliaUser/dev/julia/base/weakkeydict.jl" +``` +""" +function readdir(dir::AbstractString; join::Bool=false, sort::Bool=true) + # 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, dir, 0, C_NULL) + err < 0 && throw(SystemError("unable to read directory $dir", -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) + name = unsafe_string(ent[].name) + push!(entries, join ? joinpath(dir, name) : name) + end + + # Clean up the request string + ccall(:uv_fs_req_cleanup, Cvoid, (Ptr{UInt8},), uv_readdir_req) + + # sort entries unless opted out + sort && sort!(entries) + + return entries +end +readdir(; join::Bool=false, sort::Bool=true) = + readdir(join ? pwd() : ".", join=join, sort=sort) + +""" + 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 + path = joinpath(root, name) + + # If we're not following symlinks, then treat all symlinks as files + if (!follow_symlinks && islink(path)) || !isdir(path) + push!(files, name) + else + push!(dirs, 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; force::Bool=false) + err = ccall(:jl_fs_rename, Int32, (Cstring, Cstring), src, dst) + # on error, default to cp && rm + if err < 0 + cp(src, dst; force=force, 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..42b298e --- /dev/null +++ b/base/float.jl @@ -0,0 +1,926 @@ +# 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 number value is a NaN, an indeterminate value which is neither an infinity +nor a finite number ("not a number"). +""" +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 significand. +""" +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 = Float64) + +Return the smallest positive normal number representable by the floating-point +type `T`. + +# Examples +```jldoctest +julia> floatmin(Float16) +Float16(6.104e-5) + +julia> floatmin(Float32) +1.1754944f-38 + +julia> floatmin() +2.2250738585072014e-308 +``` +""" +floatmin(x::T) where {T<:AbstractFloat} = floatmin(T) + +""" + floatmax(T = Float64) + +Return the largest finite number representable by the floating-point type `T`. + +# Examples +```jldoctest +julia> floatmax(Float16) +Float16(6.55e4) + +julia> floatmax(Float32) +3.4028235f38 + +julia> floatmax() +1.7976931348623157e308 +``` +""" +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) + @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 + +# 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..f0afe55 --- /dev/null +++ b/base/floatfuncs.jl @@ -0,0 +1,345 @@ +# 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) + +function round(::Type{T}, x::AbstractFloat, r::RoundingMode) where {T<:Integer} + r != RoundToZero && (x = round(x,r)) + trunc(T, x) +end + +# 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) + 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 + isfinite(x) || return float(x) + _round_sigdigits(x, r, sigdigits, base === nothing ? 10 : base) + end + else + if sigdigits === nothing + isfinite(x) || return float(x) + _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 + +""" + isapprox(x; kwargs...) / ≈(x; kwargs...) + +Create a function that compares its argument to `x` using `≈`, i.e. a function equivalent to `y -> y ≈ x`. + +The keyword arguments supported here are the same as those in the 2-argument `isapprox`. +""" +isapprox(y; kwargs...) = x -> isapprox(x, y; kwargs...) + +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..5d6d8fc --- /dev/null +++ b/base/gcutils.jl @@ -0,0 +1,165 @@ +# 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`. +Using the `@async` macro (to defer context switching to outside of the finalizer) or +`ccall` to directly invoke IO functions in C may be helpful for debugging purposes. + +# Examples +```julia +finalizer(my_mutable_struct) do x + @async println("Finalizing \$x.") +end + +finalizer(my_mutable_struct) do x + ccall(:jl_safe_printf, Cvoid, (Cstring, Cstring), "Finalizing %s.", repr(x)) +end +``` +""" +function finalizer(@nospecialize(f), @nospecialize(o)) + if !ismutable(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 !ismutable(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 + +# mirrored from julia.h +const GC_AUTO = 0 +const GC_FULL = 1 +const GC_INCREMENTAL = 2 + +""" + GC.gc([full=true]) + +Perform garbage collection. The argument `full` determines the kind of +collection: A full collection (default) sweeps all objects, which makes the +next GC scan much slower, while an incremental collection may only sweep +so-called young objects. + +!!! warning + Excessive use will likely lead to poor performance. +""" +gc(full::Bool=true) = + ccall(:jl_gc_collect, Cvoid, (Cint,), full ? GC_FULL : GC_INCREMENTAL) + +""" + 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 + +Mark the objects `x1, x2, ...` as being *in use* during the evaluation of the +expression `expr`. This is only required in unsafe code where `expr` +*implicitly uses* memory or other resources owned by one of the `x`s. + +*Implicit use* of `x` covers any indirect use of resources logically owned by +`x` which the compiler cannot see. Some examples: +* Accessing memory of an object directly via a `Ptr` +* Passing a pointer to `x` to `ccall` +* Using resources of `x` which would be cleaned up in the finalizer. + +`@preserve` should generally not have any performance impact in typical use +cases where it briefly extends object lifetime. In implementation, `@preserve` +has effects such as protecting dynamically allocated objects from garbage +collection. + +# Examples + +When loading from a pointer with `unsafe_load`, the underlying object is +implicitly used, for example `x` is implicitly used by `unsafe_load(p)` in the +following: + +```jldoctest +julia> let + x = Ref{Int}(101) + p = Base.unsafe_convert(Ptr{Int}, x) + GC.@preserve x unsafe_load(p) + end +101 +``` + +When passing pointers to `ccall`, the pointed-to object is implicitly used and +should be preserved. (Note however that you should normally just pass `x` +directly to `ccall` which counts as an explicit use.) + +```jldoctest +julia> let + x = "Hello" + p = pointer(x) + Int(GC.@preserve x @ccall strlen(p::Cstring)::Csize_t) + # Preferred alternative + Int(@ccall strlen(x::Cstring)::Csize_t) + end +5 +``` +""" +macro preserve(args...) + syms = args[1:end-1] + for x in syms + isa(x, Symbol) || error("Preserved variable must be a symbol") + end + esc(Expr(:gc_preserve, args[end], syms...)) +end + +""" + GC.safepoint() + +Inserts a point in the program where garbage collection may run. +This can be useful in rare cases in multi-threaded programs where some threads +are allocating memory (and hence may need to run GC) but other threads are doing +only simple operations (no allocation, task switches, or I/O). +Calling this function periodically in non-allocating threads allows garbage +collection to run. + +!!! compat "Julia 1.4" + This function is available as of Julia 1.4. +""" +safepoint() = ccall(:jl_gc_safepoint, Cvoid, ()) + +end # module GC diff --git a/base/generator.jl b/base/generator.jl new file mode 100644 index 0000000..7cd075d --- /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 for constructing an instance of this type is `f(x) for x in iter [if cond(x)::Bool] `. +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..8001cad --- /dev/null +++ b/base/gmp.jl @@ -0,0 +1,746 @@ +# 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, cld, 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, + sign, 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 + +julia> BigInt(10)^19 +10000000000000000000 +``` +""" +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, :cdiv_q => 0, + :fdiv_r => 0, :tdiv_r => 0, :cdiv_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), + (: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 + +for (r, f) in ((RoundToZero, :tdiv_q), + (RoundDown, :fdiv_q), + (RoundUp, :cdiv_q)) + @eval div(x::BigInt, y::BigInt, ::typeof($r)) = MPZ.$f(x, y) +end + +# For compat only. Remove in 2.0. +div(x::BigInt, y::BigInt) = div(x, y, RoundToZero) +fld(x::BigInt, y::BigInt) = div(x, y, RoundDown) +cld(x::BigInt, y::BigInt) = div(x, y, RoundUp) + +/(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 +function sign(x::BigInt) + isneg(x) && return -one(x) + ispos(x) && return one(x) + return x +end + +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..a311c15 --- /dev/null +++ b/base/grisu/grisu.jl @@ -0,0 +1,216 @@ +# 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 + +# 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..ec67075 --- /dev/null +++ b/base/hashing2.jl @@ -0,0 +1,243 @@ +# 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 + +# this condition is true most (all?) of the time, and in this case we can define +# an optimized version of the above hash_integer(::Integer, ::UInt) method for BigInt +if GMP.Limb === UInt + # used e.g. for Rational{BigInt} + function hash_integer(n::BigInt, h::UInt) + GC.@preserve n begin + 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 + end +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 + +## streamlined hashing for BigInt, by avoiding allocation from shifts ## + +if GMP.Limb === UInt + _divLimb(n) = UInt === UInt64 ? n >>> 6 : n >>> 5 + _modLimb(n) = UInt === UInt64 ? n & 63 : n & 31 + + function hash(x::BigInt, h::UInt) + GC.@preserve x begin + sz = x.size + sz == 0 && return hash(0, h) + ptr = Ptr{UInt}(x.d) + if sz == 1 + return hash(unsafe_load(ptr), h) + elseif sz == -1 + limb = unsafe_load(ptr) + limb <= typemin(Int) % UInt && return hash(-(limb % Int), h) + end + pow = trailing_zeros(x) + nd = ndigits0z(x, 2) + idx = _divLimb(pow) + 1 + shift = _modLimb(pow) % UInt + upshift = GMP.BITS_PER_LIMB - shift + asz = abs(sz) + if shift == 0 + limb = unsafe_load(ptr, idx) + else + limb1 = unsafe_load(ptr, idx) + limb2 = idx < asz ? unsafe_load(ptr, idx+1) : UInt(0) + limb = limb2 << upshift | limb1 >> shift + end + if nd <= 1024 && nd - pow <= 53 + return hash(ldexp(flipsign(Float64(limb), sz), pow), h) + end + h = hash_integer(1, h) + h = hash_integer(pow, h) + h ⊻= hash_uint(flipsign(limb, sz) ⊻ h) + for idx = idx+1:asz + if shift == 0 + limb = unsafe_load(ptr, idx) + else + limb1 = limb2 + if idx == asz + limb = limb1 >> shift + limb == 0 && break # don't hash leading zeros + else + limb2 = unsafe_load(ptr, idx+1) + limb = limb2 << upshift | limb1 >> shift + end + end + h ⊻= hash_uint(limb ⊻ h) + end + return h + end + end +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 + ccall(memhash, UInt, (Ptr{UInt8}, Csize_t, UInt32), s, sizeof(s), h % UInt32) + h +end +hash(s::AbstractString, h::UInt) = hash(String(s), h) diff --git a/base/iddict.jl b/base/iddict.jl new file mode 100644 index 0000000..64ed85d --- /dev/null +++ b/base/iddict.jl @@ -0,0 +1,162 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" + 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) + +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) diff --git a/base/idset.jl b/base/idset.jl new file mode 100644 index 0000000..6812c4f --- /dev/null +++ b/base/idset.jl @@ -0,0 +1,35 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# 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 diff --git a/base/indices.jl b/base/indices.jl new file mode 100644 index 0000000..2cc822b --- /dev/null +++ b/base/indices.jl @@ -0,0 +1,489 @@ +# 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 index to describe the position in the array +(even if it's a multidimensional array) and column-major +ordering is used to efficiently access the elements. This means that +requesting [`eachindex`](@ref) from an array that is `IndexLinear` will return +a simple one-dimensional range, even if it is multidimensional. + +A custom array that reports its `IndexStyle` as `IndexLinear` only needs +to implement indexing (and indexed assignment) with a single `Int` index; +all other indexing expressions — including multidimensional accesses — will +be recomputed to the linear index. For example, if `A` were a `2×3` custom +matrix with linear indexing, and we referenced `A[1, 3]`, this would be +recomputed to the equivalent linear index and call `A[5]` 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. This is the default +for new custom [`AbstractArray`](@ref) subtypes. + +A Cartesian indexing style uses multiple integer indices to describe the position in +a multidimensional array, with exactly one index per dimension. This means that +requesting [`eachindex`](@ref) from an array that is `IndexCartesian` will return +a range of [`CartesianIndices`](@ref). + +A `N`-dimensional custom array that reports its `IndexStyle` as `IndexCartesian` needs +to implement indexing (and indexed assignment) with exactly `N` `Int` indices; +all other indexing expressions — including linear indexing — will +be recomputed to the equivalent Cartesian location. For example, if `A` were a `2×3` custom +matrix with cartesian indexing, and we referenced `A[5]`, this would be +recomputed to the equivalent Cartesian index and call `A[1, 3]` since `5 = 2*1 + 3`. + +It is significantly more expensive to compute Cartesian indices from a linear index than it is +to go the other way. The former operation requires division — a very costly operation — whereas +the latter only uses multiplication and addition and is essentially free. This asymmetry means it +is far more costly to use linear indexing with an `IndexCartesian` array than it is to use +Cartesian indexing with an `IndexLinear` array. + +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 only 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) +recompute 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: a has dims $a, b has dims $b")) + 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: a has dims $a, b has dims $b")) + 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: a has dims $a, b has dims $b")) + 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: a has dims $a, b has dims $b, mismatch at $i")) + end + end + for i=length(b)+1:length(a) + if a[i] != 1 + throw(DimensionMismatch("dimensions must match: a has dims $a, must have singleton at dim $i")) + 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: a has dims $a, b has dims $b, mismatch at $i")) + end + end + for i=length(b)+1:length(a) + if a[i] != 1:1 + throw(DimensionMismatch("dimensions must match: a has dims $a, must have singleton at dim $i")) + 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..85a3d57 --- /dev/null +++ b/base/initdefs.jl @@ -0,0 +1,350 @@ +# 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. + +Exit hooks are allowed to call `exit(n)`, in which case Julia will exit with +exit code `n` (instead of the original exit code). If more than one exit hook +calls `exit(n)`, then Julia will exit with the exit code corresponding to the +last called exit hook that calls `exit(n)`. (Because exit hooks are called in +LIFO order, "last called" is equivalent to "first registered".) +""" +atexit(f::Function) = (pushfirst!(atexit_hooks, f); nothing) + +function _atexit() + while !isempty(atexit_hooks) + f = popfirst!(atexit_hooks) + try + f() + catch ex + showerror(stderr, ex) + Base.show_backtrace(stderr, catch_backtrace()) + 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..a0f8490 --- /dev/null +++ b/base/int.jl @@ -0,0 +1,958 @@ +# 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)...} + +# >> this use of `unsigned` is defined somewhere else << the docstring should migrate there +""" + unsigned(T::Integer) + +Convert an integer bitstype to the unsigned type of the same size. +# Examples +```jldoctest +julia> unsigned(Int16) +UInt16 +julia> unsigned(UInt64) +UInt64 +``` +""" unsigned + +""" + signed(T::Integer) + +Convert an integer bitstype to the signed type of the same size. +# Examples +```jldoctest +julia> signed(UInt16) +Int16 +julia> signed(UInt64) +Int64 +``` +""" +signed(::Type{UInt8}) = Int8 +signed(::Type{UInt16}) = Int16 +signed(::Type{UInt32}) = Int32 +signed(::Type{UInt64}) = Int64 +signed(::Type{UInt128}) = Int128 +signed(::Type{T}) where {T<:Signed} = T + +## 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` if `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) + +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) = x % typeof(convert(Unsigned, zero(x))) +unsigned(x::BitSigned) = reinterpret(typeof(convert(Unsigned, 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) = x % typeof(convert(Signed, zero(x))) +signed(x::BitUnsigned) = reinterpret(typeof(convert(Signed, zero(x))), 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))) + +function divrem(x::BitSigned, y::Unsigned) + q, r = divrem(unsigned(abs(x)), y) + flipsign(signed(q), x), flipsign(signed(r), x) +end + +function divrem(x::Unsigned, y::BitSigned) + q, r = divrem(x, unsigned(abs(y))) + unsigned(flipsign(signed(q), y)), r +end + + +""" + 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) + +# 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) + +## 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`. Add parentheses for +function application form: `(&)(x, y)`. + +# 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`. + +(See also [`ntoh`](@ref) and [`hton`](@ref) to convert between the current native byte order and big-endian order.) + +# 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) = ctpop_int(x) % Int + +""" + 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) = ctlz_int(x) % Int + +""" + 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) = cttz_int(x) % Int + +""" + 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 + +## integer bitwise rotations ## + +""" + bitrotate(x::Base.BitInteger, k::Integer) + +`bitrotate(x, k)` implements bitwise rotation. +It returns the value of `x` with its bits rotated left `k` times. +A negative value of `k` will rotate to the right instead. + +!!! compat "Julia 1.5" + This function requires Julia 1.5 or later. + +```jldoctest +julia> bitrotate(UInt8(114), 2) +0xc9 + +julia> bitstring(bitrotate(0b01110010, 2)) +"11001001" + +julia> bitstring(bitrotate(0b01110010, -2)) +"10011100" + +julia> bitstring(bitrotate(0b01110010, 8)) +"01110010" +``` +""" +bitrotate(x::T, k::Integer) where {T <: BitInteger} = + (x << ((sizeof(T) << 3 - 1) & k)) | (x >>> ((sizeof(T) << 3 - 1) & -k)) + +# @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::Signed, ::Type{Unsigned}) = x % unsigned(typeof(x)) +rem(x::Unsigned, ::Type{Signed}) = x % signed(typeof(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 + +const _mask1_uint128 = (UInt128(0x5555555555555555) << 64) | UInt128(0x5555555555555555) +const _mask2_uint128 = (UInt128(0x3333333333333333) << 64) | UInt128(0x3333333333333333) +const _mask4_uint128 = (UInt128(0x0f0f0f0f0f0f0f0f) << 64) | UInt128(0x0f0f0f0f0f0f0f0f) + +""" + bitreverse(x) + +Reverse the order of bits in integer `x`. `x` must have a fixed bit width, +e.g. be an `Int16` or `Int32`. + +!!! compat "Julia 1.5" + This function requires Julia 1.5 or later. + +# Examples +```jldoctest +julia> bitreverse(0x8080808080808080) +0x0101010101010101 + +julia> reverse(bitstring(0xa06e)) == bitstring(bitreverse(0xa06e)) +true +``` +""" +function bitreverse(x::BitInteger) + # TODO: consider using llvm.bitreverse intrinsic + z = unsigned(x) + mask1 = _mask1_uint128 % typeof(z) + mask2 = _mask2_uint128 % typeof(z) + mask4 = _mask4_uint128 % typeof(z) + z = ((z & mask1) << 1) | ((z >> 1) & mask1) + z = ((z & mask2) << 2) | ((z >> 2) & mask2) + z = ((z & mask4) << 4) | ((z >> 4) & mask4) + return bswap(z) % typeof(x) +end diff --git a/base/intfuncs.jl b/base/intfuncs.jl new file mode 100644 index 0000000..b99bbb6 --- /dev/null +++ b/base/intfuncs.jl @@ -0,0 +1,960 @@ +# 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). +The arguments may be integer and rational numbers. + +!!! compat "Julia 1.4" + Rational arguments require Julia 1.4 or later. + +# Examples +```jldoctest +julia> gcd(6,9) +3 + +julia> gcd(6,-9) +3 + +julia> gcd(6,0) +6 + +julia> gcd(0,0) +0 + +julia> gcd(1//3,2//3) +1//3 + +julia> gcd(1//3,-2//3) +1//3 + +julia> gcd(1//3,2) +1//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<:BitInteger + a == 0 && return checked_abs(b) + b == 0 && return checked_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. +The arguments may be integer and rational numbers. + +!!! compat "Julia 1.4" + Rational arguments require Julia 1.4 or later. + +# Examples +```jldoctest +julia> lcm(2,3) +6 + +julia> lcm(-2,3) +6 + +julia> lcm(0,3) +0 + +julia> lcm(0,0) +0 + +julia> lcm(1//3,2//3) +2//3 + +julia> lcm(1//3,-2//3) +2//3 + +julia> lcm(1//3,2) +2//1 +``` +""" +function lcm(a::T, b::T) where T<:Integer + # explicit a==0 test is to handle case of lcm(0,0) correctly + # explicit b==0 test is to handle case of lcm(typemin(T),0) correctly + if a == 0 || b == 0 + return zero(a) + else + return checked_abs(checked_mul(a, div(b, gcd(b,a)))) + end +end + +gcd(a::Union{Integer,Rational}) = a +lcm(a::Union{Integer,Rational}) = a +gcd(a::Unsigned, b::Signed) = gcd(promote(a, abs(b))...) +gcd(a::Signed, b::Unsigned) = gcd(promote(abs(a), b)...) +gcd(a::Real, b::Real) = gcd(promote(a,b)...) +lcm(a::Real, b::Real) = lcm(promote(a,b)...) +gcd(a::Real, b::Real, c::Real...) = gcd(a, gcd(b, c...)) +lcm(a::Real, b::Real, c::Real...) = lcm(a, lcm(b, c...)) +gcd(a::T, b::T) where T<:Real = throw(MethodError(gcd, (a,b))) +lcm(a::T, b::T) where T<:Real = throw(MethodError(lcm, (a,b))) + +gcd(abc::AbstractArray{<:Real}) = reduce(gcd, abc; init=zero(eltype(abc))) +lcm(abc::AbstractArray{<:Real}) = 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)``. + +The arguments may be integer and rational numbers. + +!!! compat "Julia 1.4" + Rational arguments require Julia 1.4 or later. + +# 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::U, b::V) where {U<:Integer, V<:Integer} + T = promote_type(U, V) + # a0, b0 = a, b + s0, s1 = oneunit(T), zero(T) + t0, t1 = s1, s0 + # The loop invariant is: s0*a0 + t0*b0 == a + x = a % T + y = b % T + while y != 0 + q = div(x, y) + x, y = y, rem(x, y) + s0, s1 = s1, s0 - q*s1 + t0, t1 = t1, t0 - q*t1 + end + x < 0 ? (-x, -s0, -t0) : (x, s0, t0) +end +gcdx(a::Real, b::Real) = gcdx(promote(a,b)...) +gcdx(a::T, b::T) where T<:Real = throw(MethodError(gcdx, (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::Integer, m::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 + +# ^ 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 coefficient](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..911498f --- /dev/null +++ b/base/io.jl @@ -0,0 +1,1138 @@ +# 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}`. +""" +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. + +Note that Julia does not convert the endianness for you. Use [`ntoh`](@ref) or +[`ltoh`](@ref) for this purpose. + + 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`). + +The endianness of the written value depends on the endianness of the host system. +Convert to/from a fixed endianness when writing/reading (e.g. using [`htol`](@ref) and +[`ltoh`](@ref)) to get results that are consistent across platforms. + +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 +Consistent serialization: +```jldoctest +julia> fname = tempname(); # random temporary filename + +julia> open(fname,"w") do f + # Make sure we write 64bit integer in little-endian byte order + write(f,htol(Int64(42))) + end +8 + +julia> open(fname,"r") do f + # Convert back to host byte order and host integer type + Int(ltoh(read(f,Int64))) + end +42 +``` + +Merging write calls: +```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, ::Type{T}) where T + mark(s) + try read(s, T) + finally + reset(s) + end +end + +peek(s) = peek(s, UInt8) + +# 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) +readbytes!(io::AbstractPipe, target::AbstractVector{UInt8}, n=length(target)) = readbytes!(pipe_reader(io), target, n) + +for f in ( + # peek/mark interface + :mark, :unmark, :reset, :ismarked, + # Simple reader functions + :readavailable, :isreadable) + @eval $(f)(io::AbstractPipe) = $(f)(pipe_reader(io)) +end +peek(io::AbstractPipe, ::Type{T}) where {T} = peek(pipe_reader(io), T) + +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::AbstractArray) + read!(filename::AbstractString, array::AbstractArray) + +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 = elsize(a) + 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), elsz) + end + return elsz * length(a) + elseif N <= 1 + return unsafe_write(s, pointer(a, 1), colsz) + else + for colstart in CartesianIndices((1, size(a)[2:end]...)) + unsafe_write(s, pointer(a, colstart), 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::AbstractArray{T}) where T + if isbitstype(T) && (a isa Array || a isa FastContiguousSubArray{T,<:Any,<:Array{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..4120aad --- /dev/null +++ b/base/iobuffer.jl @@ -0,0 +1,526 @@ +# 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 peek(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 + return x +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}}) + x = peek(from, T) + from.ptr += sizeof(T) + 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, ::Type{UInt8}) + 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..448721e --- /dev/null +++ b/base/iostream.jl @@ -0,0 +1,534 @@ +# 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 + _dolock::Bool + + IOStream(name::AbstractString, buf::Array{UInt8,1}) = new(pointer(buf), buf, name, -1, ReentrantLock(), true) +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, ")") + +macro _lock_ios(s, expr) + s = esc(s) + quote + l = ($s)._dolock + temp = ($s).lock + l && lock(temp) + val = $(esc(expr)) + l && unlock(temp) + val + end +end + +""" + 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)) + +isopen(s::IOStream) = ccall(:ios_isopen, Cint, (Ptr{Cvoid},), s.ios) != 0 + +function close(s::IOStream) + bad = @_lock_ios s ccall(:ios_close, Cint, (Ptr{Cvoid},), s.ios) != 0 + systemerror("close", bad) +end + +function flush(s::IOStream) + sigatomic_begin() + bad = @_lock_ios s 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_ios s 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_ios s 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_ios s 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_ios s 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_ios s 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_ios s _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; lock = true, 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. + +The `lock` keyword argument controls whether operations will be locked for +safe multi-threaded access. + +!!! compat "Julia 1.5" + The `lock` argument is available as of Julia 1.5. +""" +function open(fname::AbstractString; lock = true, + 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("")) + if !lock + s._dolock = false + end + 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]; lock = true) -> 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` | + +The `lock` keyword argument controls whether operations will be locked for +safe multi-threaded access. + +# 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") +``` + +!!! compat "Julia 1.5" + The `lock` argument is available as of Julia 1.5. +""" +function open(fname::AbstractString, mode::AbstractString; lock = true) + mode == "r" ? open(fname, lock = lock, read = true) : + mode == "r+" ? open(fname, lock = lock, read = true, write = true) : + mode == "w" ? open(fname, lock = lock, truncate = true) : + mode == "w+" ? open(fname, lock = lock, truncate = true, read = true) : + mode == "a" ? open(fname, lock = lock, append = true) : + mode == "a+" ? open(fname, lock = lock, 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_ios s 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_ios s 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_ios s 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_ios s 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_ios s 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_ios s ccall(:jl_take_buffer, Vector{UInt8}, (Ptr{Cvoid},), s.ios) + +function readuntil(s::IOStream, delim::UInt8; keep::Bool=false) + @_lock_ios s 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_ios s ccall(:jl_readuntil, Ref{String}, (Ptr{Cvoid}, UInt8, UInt8, UInt8), s.ios, delim, 1, !keep) +end + +function readline(s::IOStream; keep::Bool=false) + @_lock_ios s 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_ios s 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_ios s 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, ::Type{UInt8}) + b = @_lock_ios s ccall(:ios_peekc, Cint, (Ptr{Cvoid},), s.ios) + if b == -1 + throw(EOFError()) + end + return b % UInt8 +end diff --git a/base/irrationals.jl b/base/irrationals.jl new file mode 100644 index 0000000..e199404 --- /dev/null +++ b/base/irrationals.jl @@ -0,0 +1,208 @@ +# 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, which is automatically rounded to the correct precision in +arithmetic operations with other numeric quantities. + +Subtypes `MyIrrational <: AbstractIrrational` should implement at least `==(::MyIrrational, ::MyIrrational)`, +`hash(x::MyIrrational, h::UInt)`, and `convert(::Type{F}, x::MyIrrational) where {F <: Union{BigFloat,Float32,Float64}}`. + +If a subtype is used to represent values that may occasionally be rational (e.g. a square-root type that represents `√n` +for integers `n` will give a rational result when `n` is a perfect square), then it should also implement +`isinteger`, `iszero`, `isone`, and `==` with `Real` values (since all of these default to `false` for +`AbstractIrrational` types), as well as defining [`hash`](@ref) to equal that of the corresponding `Rational`. +""" +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} + if get(io, :compact, false) + print(io, sym) + else + print(io, sym, " = ", string(float(x))[1:15], "...") + end +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(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) +zip(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) = mapreduce(size, _zip_promote_shape, z.is) +axes(z::Zip) = mapreduce(axes, _zip_promote_shape, z.is) +_zip_promote_shape((a,)::Tuple{OneTo}, (b,)::Tuple{OneTo}) = (intersect(a, b),) +_zip_promote_shape((m,)::Tuple{Integer},(n,)::Tuple{Integer}) = (min(m,n),) +_zip_promote_shape(a, b) = promote_shape(a, b) +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)) + +# Accumulate -- partial reductions of a function over an iterator + +struct Accumulate{F,I,T} + f::F + itr::I + init::T +end + +""" + Iterators.accumulate(f, itr; [init]) + +Given a 2-argument function `f` and an iterator `itr`, return a new +iterator that successively applies `f` to the previous value and the +next element of `itr`. + +This is effectively a lazy version of [`Base.accumulate`](@ref). + +!!! compat "Julia 1.5" + Keyword argument `init` is added in Julia 1.5. + +# Examples +```jldoctest +julia> f = Iterators.accumulate(+, [1,2,3,4]); + +julia> foreach(println, f) +1 +3 +6 +10 + +julia> f = Iterators.accumulate(+, [1,2,3]; init = 100); + +julia> foreach(println, f) +101 +103 +106 +``` +""" +accumulate(f, itr; init = Base._InitialValue()) = Accumulate(f, itr, init) + +function iterate(itr::Accumulate) + state = iterate(itr.itr) + if state === nothing + return nothing + end + val = Base.BottomRF(itr.f)(itr.init, state[1]) + return (val, (val, state[2])) +end + +function iterate(itr::Accumulate, state) + nxt = iterate(itr.itr, state[2]) + if nxt === nothing + return nothing + end + val = itr.f(state[1], nxt[1]) + return (val, (val, nxt[2])) +end + +length(itr::Accumulate) = length(itr.itr) +size(itr::Accumulate) = size(itr.itr) + +IteratorSize(::Type{<:Accumulate{F,I}}) where {F,I} = IteratorSize(I) +IteratorEltype(::Type{<:Accumulate}) = EltypeUnknown() + +# 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': ASCII/Unicode U+0062 (category Ll: Letter, lowercase) + 'c': ASCII/Unicode U+0063 (category Ll: Letter, lowercase) +``` +""" +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) + + +# takewhile + +struct TakeWhile{I,P<:Function} + pred::P + xs::I +end + +""" + takewhile(pred, iter) + +An iterator that generates element from `iter` as long as predicate `pred` is true, +afterwards, drops every element. + +!!! compat "Julia 1.4" + This function requires at least Julia 1.4. + +# Examples + +```jldoctest +julia> s = collect(1:5) +5-element Array{Int64,1}: + 1 + 2 + 3 + 4 + 5 + +julia> collect(Iterators.takewhile(<(3),s)) +2-element Array{Int64,1}: + 1 + 2 +``` +""" +takewhile(pred,xs) = TakeWhile(pred,xs) + +function iterate(ibl::TakeWhile, itr...) + y = iterate(ibl.xs,itr...) + y === nothing && return nothing + ibl.pred(y[1]) || return nothing + y +end + +IteratorSize(::Type{<:TakeWhile}) = SizeUnknown() +eltype(::Type{TakeWhile{I,P}}) where {I,P} = eltype(I) +IteratorEltype(::Type{TakeWhile{I,P}}) where {I,P} = IteratorEltype(I) + + +# dropwhile + +struct DropWhile{I,P<:Function} + pred::P + xs::I +end + +""" + dropwhile(pred, iter) + +An iterator that drops element from `iter` as long as predicate `pred` is true, +afterwards, returns every element. + +!!! compat "Julia 1.4" + This function requires at least Julia 1.4. + +# Examples + +```jldoctest +julia> s = collect(1:5) +5-element Array{Int64,1}: + 1 + 2 + 3 + 4 + 5 + +julia> collect(Iterators.dropwhile(<(3),s)) +3-element Array{Int64,1}: + 3 + 4 + 5 +``` +""" +dropwhile(pred,itr) = DropWhile(pred,itr) + +iterate(ibl::DropWhile,itr) = iterate(ibl.xs, itr) +function iterate(ibl::DropWhile) + y = iterate(ibl.xs) + while y !== nothing + ibl.pred(y[1]) || break + y = iterate(ibl.xs,y[2]) + end + y +end + +IteratorSize(::Type{<:DropWhile}) = SizeUnknown() +eltype(::Type{DropWhile{I,P}}) where {I,P} = eltype(I) +IteratorEltype(::Type{DropWhile{I,P}}) where {I,P} = IteratorEltype(I) + + +# 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{SubArray{Int64,1,Array{Int64,1},Tuple{UnitRange{Int64}},true},1}: + [1, 2] + [3, 4] + [5] +``` +""" +function partition(c, n::Integer) + n < 1 && throw(ArgumentError("cannot create partitions of length $n")) + return PartitionIterator(c, Int(n)) +end + +struct PartitionIterator{T} + c::T + n::Int +end +# Partitions are explicitly a linear indexing operation, so reshape to 1-d immediately +PartitionIterator(A::AbstractArray, n::Int) = PartitionIterator(vec(A), n) +PartitionIterator(v::AbstractVector, n::Int) = PartitionIterator{typeof(v)}(v, n) + +eltype(::Type{PartitionIterator{T}}) where {T} = Vector{eltype(T)} +# Arrays use a generic `view`-of-a-`vec`, so we cannot exactly predict what we'll get back +eltype(::Type{PartitionIterator{T}}) where {T<:AbstractArray} = AbstractVector{eltype(T)} +# But for some common implementations in Base we know the answer exactly +eltype(::Type{PartitionIterator{T}}) where {T<:Vector} = SubArray{eltype(T), 1, T, Tuple{UnitRange{Int}}, true} + +IteratorEltype(::Type{<:PartitionIterator{T}}) where {T} = IteratorEltype(T) +IteratorEltype(::Type{<:PartitionIterator{T}}) where {T<:AbstractArray} = EltypeUnknown() +IteratorEltype(::Type{<:PartitionIterator{T}}) where {T<:Vector} = IteratorEltype(T) + +partition_iteratorsize(::HasShape) = HasLength() +partition_iteratorsize(isz) = isz +function IteratorSize(::Type{PartitionIterator{T}}) where {T} + partition_iteratorsize(IteratorSize(T)) +end + +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{<:AbstractRange}, state=1) + state > length(itr.c) && return nothing + r = min(state + itr.n - 1, length(itr.c)) + return @inbounds itr.c[state:r], r + 1 +end + +function iterate(itr::PartitionIterator{<:AbstractArray}, state=1) + state > length(itr.c) && return nothing + r = min(state + itr.n - 1, length(itr.c)) + return @inbounds 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`](@ref) in a [`for`](@ref) 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': ASCII/Unicode U+0062 (category Ll: Letter, lowercase) + 'c': ASCII/Unicode U+0063 (category Ll: Letter, lowercase) + 'd': ASCII/Unicode U+0064 (category Ll: Letter, lowercase) + +julia> collect(a) +2-element Array{Char,1}: + 'e': ASCII/Unicode U+0065 (category Ll: Letter, lowercase) + 'f': ASCII/Unicode U+0066 (category Ll: Letter, lowercase) +``` + +```jldoctest +julia> a = Iterators.Stateful([1,1,1,2,3,4]); + +julia> for x in a; x == 1 || break; end + +julia> 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 + +""" + only(x) + +Returns the one and only element of collection `x`, and throws an `ArgumentError` if the +collection has zero or multiple elements. + +See also: [`first`](@ref), [`last`](@ref). + +!!! compat "Julia 1.4" + This method requires at least Julia 1.4. +""" +@propagate_inbounds function only(x) + i = iterate(x) + @boundscheck if i === nothing + throw(ArgumentError("Collection is empty, must contain exactly 1 element")) + end + (ret, state) = i + @boundscheck if iterate(x, state) !== nothing + throw(ArgumentError("Collection has multiple elements, must contain exactly 1 element")) + end + return ret +end + +# Collections of known size +only(x::Ref) = x[] +only(x::Number) = x +only(x::Char) = x +only(x::Tuple{Any}) = x[1] +only(x::Tuple) = throw( + ArgumentError("Tuple contains $(length(x)) elements, must contain exactly 1 element") +) +only(a::AbstractArray{<:Any, 0}) = @inbounds return a[] +only(x::NamedTuple{<:Any, <:Tuple{Any}}) = first(x) +only(x::NamedTuple) = throw( + ArgumentError("NamedTuple contains $(length(x)) elements, must contain exactly 1 element") +) + +end diff --git a/base/libc.jl b/base/libc.jl new file mode 100644 index 0000000..379c76d --- /dev/null +++ b/base/libc.jl @@ -0,0 +1,403 @@ +# 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, ()) + + FormatMessage(e) = FormatMessage(UInt32(e)) + function FormatMessage(e::UInt32=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, (UInt32, Ptr{Cvoid}, UInt32, UInt32, Ptr{Ptr{UInt16}}, UInt32, 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, ()) +@static if Sys.iswindows() + # Windows RAND_MAX is 2^15-1 + rand(::Type{UInt32}) = ((rand() % UInt32) << 17) ⊻ ((rand() % UInt32) << 8) ⊻ (rand() % UInt32) +else + # RAND_MAX is at least 2^15-1 in theory, but we assume 2^16-1 + # on non-Windows systems (in practice, it's 2^31-1) + rand(::Type{UInt32}) = ((rand() % UInt32) << 16) ⊻ (rand() % UInt32) +end +rand(::Type{Float64}) = rand(UInt32) * 2.0^-32 + +""" + srand([seed]) + +Interface to the C `srand(seed)` function. +""" +srand(seed=floor(Int, time()) % Cuint) = ccall(:srand, Cvoid, (Cuint,), seed) + +end # module diff --git a/base/libuv.jl b/base/libuv.jl new file mode 100644 index 0000000..2ed665c --- /dev/null +++ b/base/libuv.jl @@ -0,0 +1,147 @@ +# 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() = ccall(:jl_global_event_loop, Ptr{Cvoid}, ()) + +function process_events() + return ccall(:jl_process_events, Int32, ()) +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 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..f63bb6b --- /dev/null +++ b/base/loading.jl @@ -0,0 +1,1536 @@ +# 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") + +function dummy_uuid(project_file::String) + project_path = try + realpath(project_file) + catch + project_file + end + return uuid5(ns_dummy_uuid, project_path) +end + +## 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 the `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 + +""" + pkgdir(m::Module) + + Return the root directory of the package that imported module `m`, + or `nothing` if `m` was not imported from a package. + """ +function pkgdir(m::Module) + rootmodule = Base.moduleroot(m) + path = pathof(rootmodule) + path === nothing && return nothing + return dirname(dirname(path)) +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 + +## 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} + M = M::Module + 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(__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([mapexpr::Function,] m::Module, code::AbstractString, filename::AbstractString="string") + +Like [`include`](@ref), except reads code from the given string rather than from a file. + +The optional first argument `mapexpr` can be used to transform the included code before +it is evaluated: for each parsed expression `expr` in `code`, the `include_string` function +actually evaluates `mapexpr(expr)`. If it is omitted, `mapexpr` defaults to [`identity`](@ref). +""" +function include_string(mapexpr::Function, m::Module, txt_::AbstractString, fname::AbstractString="string") + txt = String(txt_) + if mapexpr === identity + ccall(:jl_load_file_string, Any, (Ptr{UInt8}, Csize_t, Cstring, Any), + txt, sizeof(txt), String(fname), m) + else + ccall(:jl_load_rewrite_file_string, Any, (Ptr{UInt8}, Csize_t, Cstring, Any, Any), + txt, sizeof(txt), String(fname), m, mapexpr) + end +end + +include_string(m::Module, txt::AbstractString, fname::AbstractString="string") = + include_string(identity, m, txt, fname) + +function source_path(default::Union{AbstractString,Nothing}="") + s = current_task().storage + if s !== nothing && haskey(s::IdDict{Any,Any}, :SOURCE_PATH) + return s[:SOURCE_PATH]::Union{Nothing,String} + end + return default +end + +function source_dir() + p = source_path(nothing) + return p === nothing ? pwd() : dirname(p) +end + +""" + Base.include([mapexpr::Function,] [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 +definition of `include` omitting the `m` argument, 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. + +The optional first argument `mapexpr` can be used to transform the included code before +it is evaluated: for each parsed expression `expr` in `path`, the `include` function +actually evaluates `mapexpr(expr)`. If it is omitted, `mapexpr` defaults to [`identity`](@ref). +""" +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(mapexpr::Function, x) = $(Expr(:top, :include))(mapexpr, __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 === nothing ? "auto" : 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 + crc = _crc32c(something(Base.active_project(), "")) + crc = _crc32c(unsafe_string(JLOptions().image_file), crc) + crc = _crc32c(unsafe_string(JLOptions().julia_bin), crc) + project_precompile_slug = slug(crc, 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) + cachepath = dirname(cachefile) + # prune the directory with cache files + if pkg.uuid !== nothing + 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" + + # create a temporary file in `cachepath` directory, write the cache in it, + # write the checksum, _and then_ atomically move the file to `cachefile`. + tmppath, tmpio = mktemp(cachepath) + local p + try + close(tmpio) + p = create_expr_cache(path, tmppath, concrete_deps, pkg.uuid) + if success(p) + # append checksum to the end of the .ji file: + open(tmppath, "a+") do f + write(f, _crc32c(seekstart(f))) + end + # inherit permission from the source file + chmod(tmppath, filemode(path) & 0o777) + + # this is atomic according to POSIX: + rename(tmppath, cachefile; force=true) + return cachefile + end + finally + rm(tmppath, force=true) + end + if p.exitcode == 125 + return PrecompilableError() + else + error("Failed to precompile $pkg to $cachefile.") + end +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::Symbol) +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::Symbol)) + return isempty(_dirname) ? pwd() : abspath(_dirname) +end diff --git a/base/lock.jl b/base/lock.jl new file mode 100644 index 0000000..b518766 --- /dev/null +++ b/base/lock.jl @@ -0,0 +1,340 @@ +# 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 + +""" + lock(f::Function, lock) + +Acquire the `lock`, execute `f` with the `lock` held, and release the `lock` when `f` +returns. If the lock is already locked by a different task/thread, wait for it to become +available. + +When this function returns, the `lock` has been released, so the caller should +not attempt to `unlock` it. +""" +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). + + To call [`wait`](@ref) or [`notify`](@ref) on a `Threads.Condition`, you must first call + [`lock`](@ref) on it. When `wait` is called, the lock is atomically released during + blocking, and will be reacquired before `wait` returns. Therefore idiomatic use + of a `Threads.Condition` `c` looks like the following: + + ``` + lock(c) + try + while !thing_we_are_waiting_for + wait(c) + end + finally + unlock(c) + end + ``` + + !!! 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..2a492c1 --- /dev/null +++ b/base/locks-mt.jl @@ -0,0 +1,90 @@ +# 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 +########################################## + +""" + SpinLock() + +Create a non-reentrant, test-and-test-and-set spin lock. +Recursive use will result in a deadlock. +This kind of lock should only be used around code that takes little time +to execute and does not block (e.g. perform I/O). +In general, [`ReentrantLock`](@ref) should be used instead. + +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, different +synchronization approaches should be considered. +""" +mutable struct SpinLock <: AbstractLock + handle::Int + SpinLock() = new(0) +end + +import Base.Sys.WORD_SIZE + +@eval _xchg!(x::SpinLock, v::Int) = + llvmcall($""" + %ptr = inttoptr i$WORD_SIZE %0 to i$WORD_SIZE* + %rv = atomicrmw xchg i$WORD_SIZE* %ptr, i$WORD_SIZE %1 acq_rel + ret i$WORD_SIZE %rv + """, Int, Tuple{Ptr{Int}, Int}, unsafe_convert(Ptr{Int}, pointer_from_objref(x)), v) + +@eval _get(x::SpinLock) = + llvmcall($""" + %ptr = inttoptr i$WORD_SIZE %0 to i$WORD_SIZE* + %rv = load atomic i$WORD_SIZE, i$WORD_SIZE* %ptr acquire, align $(gc_alignment(Int)) + ret i$WORD_SIZE %rv + """, Int, Tuple{Ptr{Int}}, unsafe_convert(Ptr{Int}, pointer_from_objref(x))) + +@eval _set!(x::SpinLock, v::Int) = + llvmcall($""" + %ptr = inttoptr i$WORD_SIZE %0 to i$WORD_SIZE* + store atomic i$WORD_SIZE %1, i$WORD_SIZE* %ptr release, align $(gc_alignment(Int)) + ret void + """, Cvoid, Tuple{Ptr{Int}, Int}, unsafe_convert(Ptr{Int}, pointer_from_objref(x)), v) + +# 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 : Base.concurrency_violation() + +function lock(l::SpinLock) + while true + if _get(l) == 0 + p = _xchg!(l, 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 _get(l) == 0 + return _xchg!(l, 1) == 0 + end + return false +end + +function unlock(l::SpinLock) + _set!(l, 0) + ccall(:jl_cpu_wake, Cvoid, ()) + return +end + +function islocked(l::SpinLock) + return _get(l) != 0 +end diff --git a/base/logging.jl b/base/logging.jl new file mode 100644 index 0000000..7e41e63 --- /dev/null +++ b/base/logging.jl @@ -0,0 +1,573 @@ +# 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 value (typically a +[`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 + + +# Prevent invalidation when packages define custom loggers +# Using invoke in combination with @nospecialize eliminates backedges to these methods +function _invoked_shouldlog(logger, level, _module, group, id) + @nospecialize + invoke(shouldlog, Tuple{typeof(logger), typeof(level), typeof(_module), typeof(group), typeof(id)}, logger, level, _module, group, id) +end + +_invoked_min_enabled_level(@nospecialize(logger)) = invoke(min_enabled_level, Tuple{typeof(logger)}, logger) + + +""" + 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::Symbol), + __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 + +default_group(file) = Symbol(splitext(basename(file))[1]) + +# 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(default_group(file)) + else + # memoized run-time execution + ref = Ref{Symbol}() + :(isassigned($ref) ? $ref[] + : $ref[] = default_group(something($file, ""))) + 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 _invoked_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(_invoked_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) + @nospecialize + 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_include::Vector{Symbol} = Symbol[], + _debug_groups_exclude::Vector{Symbol} = 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_include) + empty!(_debug_groups_exclude) + for g in split(debug, ',') + if !isempty(g) + if startswith(g, "!") + if !isempty(g[2:end]) + push!(_debug_groups_exclude, Symbol(g[2:end])) + end + else + push!(_debug_groups_include, Symbol(g)) + end + end + end + unique!(_debug_groups_include) + unique!(_debug_groups_exclude) + end + + if !(:all in _debug_groups_exclude) && (:all in _debug_groups_include || !isempty(_debug_groups_exclude)) + if isempty(_debug_groups_exclude) + return true + elseif isa(group, Symbol) && group in _debug_groups_exclude + return false + elseif isa(_module, Module) && (nameof(_module) in _debug_groups_exclude || nameof(Base.moduleroot(_module)) in _debug_groups_exclude) + return false + else + return true + end + else + if isempty(_debug_groups_include) + return false + elseif isa(group, Symbol) && group in _debug_groups_include + return true + elseif isa(_module, Module) && (nameof(_module) in _debug_groups_include || nameof(Base.moduleroot(_module)) in _debug_groups_include) + return true + else + return false + 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(@nospecialize(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..3730f5c --- /dev/null +++ b/base/math.jl @@ -0,0 +1,1201 @@ +# 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, 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, exponent_bias, + exponent_max, exponent_raw_max + +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 + +# 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(x, T)::T + +Clamp `x` between `typemin(T)` and `typemax(T)` and convert the result to type `T`. + +# Examples +```jldoctest +julia> clamp(200, Int8) +127 +julia> clamp(-200, Int8) +-128 +``` +""" +clamp(x, ::Type{T}) where {T<:Integer} = clamp(x, typemin(T), typemax(T)) % T + + +""" + 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 + + +""" + evalpoly(x, p) + +Evaluate the polynomial ``\\sum_k x^{k-1} p[k]`` for the coefficients `p[1]`, `p[2]`, ...; +that is, the coefficients are given in ascending order by power of `x`. +Loops are unrolled at compile time if the number of coefficients is statically known, i.e. +when `p` is a `Tuple`. +This function generates efficient code using Horner's method if `x` is real, or using +a Goertzel-like [^DK62] algorithm if `x` is complex. + +[^DK62]: Donald Knuth, Art of Computer Programming, Volume 2: Seminumerical Algorithms, Sec. 4.6.4. + +!!! compat "Julia 1.4" + This function requires Julia 1.4 or later. + +# Example +```jldoctest +julia> evalpoly(2, (1, 2, 3)) +17 +``` +""" +function evalpoly(x, p::Tuple) + if @generated + N = length(p.parameters) + ex = :(p[end]) + for i in N-1:-1:1 + ex = :(muladd(x, $ex, p[$i])) + end + ex + else + _evalpoly(x, p) + end +end + +evalpoly(x, p::AbstractVector) = _evalpoly(x, p) + +function _evalpoly(x, p) + N = length(p) + ex = p[end] + for i in N-1:-1:1 + ex = muladd(x, ex, p[i]) + end + ex +end + +function evalpoly(z::Complex, p::Tuple) + if @generated + N = length(p.parameters) + a = :(p[end]) + b = :(p[end-1]) + as = [] + for i in N-2:-1:1 + ai = Symbol("a", i) + push!(as, :($ai = $a)) + a = :(muladd(r, $ai, $b)) + b = :(muladd(-s, $ai, p[$i])) + end + ai = :a0 + push!(as, :($ai = $a)) + C = Expr(:block, + :(x = real(z)), + :(y = imag(z)), + :(r = x + x), + :(s = muladd(x, x, y*y)), + as..., + :(muladd($ai, z, $b))) + else + _evalpoly(z, p) + end +end +evalpoly(z::Complex, p::Tuple{<:Any}) = p[1] + + +evalpoly(z::Complex, p::AbstractVector) = _evalpoly(z, p) + +function _evalpoly(z::Complex, p) + length(p) == 1 && return p[1] + N = length(p) + a = p[end] + b = p[end-1] + + x = real(z) + y = imag(z) + r = 2x + s = muladd(x, x, y*y) + for i in N-2:-1:1 + ai = a + a = muladd(r, ai, b) + b = muladd(-s, ai, p[i]) + end + ai = a + muladd(ai, z, b) +end + +""" + @horner(x, p...) + +Evaluate `p[1] + x * (p[2] + x * (....))`, i.e. a polynomial via Horner's rule. +""" +macro horner(x, p...) + xesc, pesc = esc(x), esc.(p) + :(invoke(evalpoly, Tuple{Any, Tuple}, $xesc, ($(pesc...),))) +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 z^{k-1} c[k]`` 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...) + zesc, pesc = esc(z), esc.(p) + :(evalpoly($zesc, ($(pesc...),))) +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(abs(x), abs(y)) +hypot(x::T, y::T) where {T<:Real} = hypot(float(x), float(y)) +function hypot(x::T, y::T) where {T<:Number} + if !iszero(x) + z = y/x + z2 = z*z + + abs(x) * sqrt(oneunit(z2) + z2) + else + abs(y) + end +end + +function hypot(x::T, y::T) where T<:AbstractFloat + # Return Inf if either or both inputs is Inf (Compliance with IEEE754) + if isinf(x) || isinf(y) + return 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 + return h*scale +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::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) = isinf(x) ? (flipsign(zero(x), x), 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 function ^(x::Float64, y::Integer) + y == -1 && return inv(x) + y == 0 && return one(x) + y == 1 && return x + y == 2 && return x*x + y == 3 && return x*x*x + ccall("llvm.pow.f64", llvmcall, Float64, (Float64, Float64), x, Float64(y)) +end +@inline function ^(x::Float32, y::Integer) + y == -1 && return inv(x) + y == 0 && return one(x) + y == 1 && return x + y == 2 && return x*x + y == 3 && return x*x*x + ccall("llvm.pow.f32", llvmcall, Float32, (Float32, Float32), x, Float32(y)) +end +@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(ax) + + 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π`. + +!!! note + Depending on the format of the input value, the closest representable value to 2π may + be less than 2π. For example, the expression `mod2pi(2π)` will not return `0`, because + the intermediate value of `2*π` is a `Float64` and `2*Float64(π) < 2*big(π)`. See + [`rem2pi`](@ref) for more refined control of this behavior. + +# 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..6f9c46f --- /dev/null +++ b/base/methodshow.jl @@ -0,0 +1,431 @@ +# 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("") + +# NOTE: second argument is deprecated and is no longer used +function kwarg_decl(m::Method, kwtype = nothing) + mt = get_methodtable(m) + if isdefined(mt, :kwsorter) + kwtype = typeof(mt.kwsorter) + 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 + end + return Any[] +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.) +const methodloc_callback = Ref{Union{Function, Nothing}}(nothing) + +# This function does the method location updating +function updated_methodloc(m::Method)::Tuple{String, Int32} + file, line = m.file, m.line + if methodloc_callback[] !== nothing + try + file, line = invokelatest(methodloc_callback[], m) + catch + end + end + # The file defining Base.Sys gets included after this file is included so make sure + # this function is valid even in this intermediary state + if isdefined(@__MODULE__, :Sys) && Sys.BUILD_STDLIB_PATH != Sys.STDLIB + # BUILD_STDLIB_PATH gets defined in sysinfo.jl + file = replace(string(file), normpath(Sys.BUILD_STDLIB_PATH) => normpath(Sys.STDLIB)) + end + return string(file), line +end + +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 = updated_methodloc(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) + 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]], + ", ", ", ") + kwargs = kwarg_decl(m) + if !isempty(kwargs) + print(io, "; ") + join(io, kwargs, ", ", ", ") + end + print(io, ")") + show_method_params(io, tv) + print(io, " in ", m.module) + if line > 0 + file, line = updated_methodloc(m) + 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 + 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) + 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) + 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]], ", ", ", ") + kwargs = kwarg_decl(m) + if !isempty(kwargs) + print(io, "; ") + join(io, kwargs, ", ", ", ") + print(io, "") + end + print(io, ")") + if !isempty(tv) + print(io,"") + show_method_params(io, tv) + print(io,"") + end + print(io, " in ", m.module) + if line > 0 + file, line = updated_methodloc(m) + 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*"") + print(io, "
    ") + for meth in ms + print(io, "
  • ") + show(io, mime, meth) + 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 = updated_methodloc(m) + 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..403a109 --- /dev/null +++ b/base/missing.jl @@ -0,0 +1,417 @@ +# 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 (:(+), :(-), :(*), :(/), :(^), :(mod), :(rem)) + @eval begin + # Scalar with missing + ($f)(::Missing, ::Missing) = missing + ($f)(::Missing, ::Number) = missing + ($f)(::Number, ::Missing) = missing + end +end + +div(::Missing, ::Missing, r::RoundingMode) = missing +div(::Missing, ::Number, r::RoundingMode) = missing +div(::Number, ::Missing, r::RoundingMode) = missing + +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{Tr}, r::RoundingMode=RoundNearest) where {T>:Missing,Tr} = 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]) +skipmissing(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 + +function show(io::IO, s::SkipMissing) + print(io, "skipmissing(") + show(io, s.x) + print(io, ')') +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::eltype(itr) = 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`. + +See also [`something`](@ref). + +# 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..f6b8c3b --- /dev/null +++ b/base/mpfr.jl @@ -0,0 +1,1046 @@ +# 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, sign, 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[]) + precision < 1 && throw(DomainError(precision, "`precision` cannot be less than 1.")) + 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 +_unchecked_cast(T, x::BigFloat, r::RoundingMode) = _unchecked_cast(T, x, convert(MPFRRoundingMode, r)) + +function _unchecked_cast(::Type{Int64}, x::BigFloat, r::MPFRRoundingMode) + ccall((:__gmpfr_mpfr_get_sj,:libmpfr), Cintmax_t, (Ref{BigFloat}, MPFRRoundingMode), x, r) +end + +function _unchecked_cast(::Type{UInt64}, x::BigFloat, r::MPFRRoundingMode) + ccall((:__gmpfr_mpfr_get_uj,:libmpfr), Cuintmax_t, (Ref{BigFloat}, MPFRRoundingMode), x, r) +end + +function _unchecked_cast(::Type{BigInt}, x::BigFloat, r::MPFRRoundingMode) + z = BigInt() + ccall((:mpfr_get_z, :libmpfr), Int32, (Ref{BigInt}, Ref{BigFloat}, MPFRRoundingMode), z, x, r) + return z +end + +function _unchecked_cast(::Type{T}, x::BigFloat, r::MPFRRoundingMode) where T<:Union{Signed, Unsigned} + CT = T <: Signed ? Int64 : UInt64 + typemax(T) < typemax(CT) ? _unchecked_cast(CT, x, r) : _unchecked_cast(BigInt, x, r) +end + +function round(::Type{T}, x::BigFloat, r::Union{RoundingMode, MPFRRoundingMode}) where T<:Union{Signed, Unsigned} + clear_flags() + res = _unchecked_cast(T, x, r) + if had_range_exception() || !(typemin(T) <= res <= typemax(T)) + throw(InexactError(:round, T, x)) + end + return unsafe_trunc(T, res) +end +round(::Type{BigInt}, x::BigFloat, r::Union{RoundingMode, MPFRRoundingMode}) = _unchecked_cast(BigInt, x, r) +round(::Type{T}, x::BigFloat, r::RoundingMode) where T<:Union{Signed, Unsigned} = + invoke(round, Tuple{Type{<:Union{Signed, Unsigned}}, BigFloat, Union{RoundingMode, MPFRRoundingMode}}, T, x, r) +round(::Type{BigInt}, x::BigFloat, r::RoundingMode) = + invoke(round, Tuple{Type{BigInt}, BigFloat, Union{RoundingMode, MPFRRoundingMode}}, BigInt, x, r) +round(::Type{<:Integer}, x::BigFloat, r::RoundingMode) = throw(MethodError(round, (Integer, x, r))) + + +unsafe_trunc(::Type{T}, x::BigFloat) where {T<:Integer} = unsafe_trunc(T, _unchecked_cast(T, x, RoundToZero)) +unsafe_trunc(::Type{BigInt}, x::BigFloat) = _unchecked_cast(BigInt, x, RoundToZero) + +# TODO: Ideally the base fallbacks for these would already exist +for (f, rnd) in zip((:trunc, :floor, :ceil, :round), + (RoundToZero, RoundDown, RoundUp, :(ROUNDING_MODE[]))) + @eval $f(::Type{T}, x::BigFloat) where T<:Union{Unsigned, Signed, BigInt} = round(T, x, $rnd) + @eval $f(::Type{Integer}, x::BigFloat) = $f(BigInt, x) +end + +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) + 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 sign(x::BigFloat) + c = cmp(x, 0) + (c == 0 || isnan(x)) && return x + return c < 0 ? -one(x) : one(x) +end + +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) + pc = Ref{Ptr{UInt8}}() + n = ccall((:mpfr_asprintf,:libmpfr), Cint, + (Ptr{Ptr{UInt8}}, Ptr{UInt8}, Ref{BigFloat}...), + pc, fmt, x) + p = pc[] + # convert comma decimal separator to dot + for i = 1:n + if unsafe_load(p, i) == UInt8(',') + unsafe_store!(p, '.', i) + break + end + end + str = unsafe_string(p) + ccall((:mpfr_free_str, :libmpfr), Cvoid, (Ptr{UInt8},), p) + return str +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, ()) + +check_exponent_err(ret) = ret == 0 || throw(ArgumentError("Invalid MPFR exponent range")) +set_emax!(x) = check_exponent_err(ccall((:mpfr_set_emax, :libmpfr), Cint, (Clong,), x)) +set_emin!(x) = check_exponent_err(ccall((:mpfr_set_emin, :libmpfr), Cint, (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 + +# flags +clear_flags() = ccall((:mpfr_clear_flags, :libmpfr), Cvoid, ()) +had_underflow() = ccall((:mpfr_underflow_p, :libmpfr), Cint, ()) != 0 +had_overflow() = ccall((:mpfr_underflow_p, :libmpfr), Cint, ()) != 0 +had_nan() = ccall((:mpfr_nanflag_p, :libmpfr), Cint, ()) != 0 +had_inexact_exception() = ccall((:mpfr_inexflag_p, :libmpfr), Cint, ()) != 0 +had_range_exception() = ccall((:mpfr_erangeflag_p, :libmpfr), Cint, ()) != 0 + +end #module diff --git a/base/multidimensional.jl b/base/multidimensional.jl new file mode 100644 index 0000000..b16a203 --- /dev/null +++ b/base/multidimensional.jl @@ -0,0 +1,1789 @@ +# 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, to_indices + + import .Base: +, -, *, (:) + import .Base: simd_outer_range, simd_inner_length, simd_index, setindex + using .Base: IndexLinear, IndexCartesian, AbstractCartesianIndex, fill_to_length, tail, + ReshapedArray, ReshapedArrayLF, OneTo + using .Base.Iterators: Reverse, PartitionIterator + + 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 + + Base.setindex(x::CartesianIndex,i,j) = CartesianIndex(Base.setindex(Tuple(x),i,j)) + + # 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 + + Base._ind2sub(t::Tuple, ind::CartesianIndex) = Tuple(ind) + + # 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)) + + # equality + Base.:(==)(a::CartesianIndices{N}, b::CartesianIndices{N}) where N = + all(map(==, a.indices, b.indices)) + Base.:(==)(a::CartesianIndices, b::CartesianIndices) = false + + # 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)) + + # When used as indices themselves, CartesianIndices can simply become its tuple of ranges + @inline to_indices(A, inds, I::Tuple{CartesianIndices, Vararg{Any}}) = + to_indices(A, inds, (I[1].indices..., tail(I)...)) + # but preserve CartesianIndices{0} as they consume a dimension. + @inline to_indices(A, inds, I::Tuple{CartesianIndices{0},Vararg{Any}}) = + (first(I), to_indices(A, inds, tail(I))...) + + @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) + + # Views of reshaped CartesianIndices are used for partitions — ensure these are fast + const CartesianPartition{T<:CartesianIndex, P<:CartesianIndices, R<:ReshapedArray{T,1,P}} = SubArray{T,1,R,Tuple{UnitRange{Int}},false} + eltype(::Type{PartitionIterator{T}}) where {T<:ReshapedArrayLF} = SubArray{eltype(T), 1, T, Tuple{UnitRange{Int}}, true} + eltype(::Type{PartitionIterator{T}}) where {T<:ReshapedArray} = SubArray{eltype(T), 1, T, Tuple{UnitRange{Int}}, false} + Iterators.IteratorEltype(::Type{<:PartitionIterator{T}}) where {T<:ReshapedArray} = Iterators.IteratorEltype(T) + + eltype(::Type{PartitionIterator{T}}) where {T<:OneTo} = UnitRange{eltype(T)} + eltype(::Type{PartitionIterator{T}}) where {T<:Union{UnitRange, StepRange, StepRangeLen, LinRange}} = T + Iterators.IteratorEltype(::Type{<:PartitionIterator{T}}) where {T<:Union{OneTo, UnitRange, StepRange, StepRangeLen, LinRange}} = Iterators.IteratorEltype(T) + + + @inline function iterate(iter::CartesianPartition) + isempty(iter) && return nothing + f = first(iter) + return (f, (f, 1)) + end + @inline function iterate(iter::CartesianPartition, (state, n)) + n >= length(iter) && return nothing + I = IteratorsMD.inc(state.I, first(iter.parent.parent).I, last(iter.parent.parent).I) + return I, (I, n+1) + end + + @inline function simd_outer_range(iter::CartesianPartition) + # In general, the Cartesian Partition might start and stop in the middle of the outer + # dimensions — thus the outer range of a CartesianPartition is itself a + # CartesianPartition. + t = tail(iter.parent.parent.indices) + ci = CartesianIndices(t) + li = LinearIndices(t) + return @inbounds view(ci, li[tail(iter[1].I)...]:li[tail(iter[end].I)...]) + end + function simd_outer_range(iter::CartesianPartition{CartesianIndex{2}}) + # But for two-dimensional Partitions the above is just a simple one-dimensional range + # over the second dimension; we don't need to worry about non-rectangular staggers in + # higher dimensions. + return @inbounds CartesianIndices((iter[1][2]:iter[end][2],)) + end + @inline function simd_inner_length(iter::CartesianPartition, I::CartesianIndex) + inner = iter.parent.parent.indices[1] + @inbounds fi = iter[1].I + @inbounds li = iter[end].I + inner_start = I.I == tail(fi) ? fi[1] : first(inner) + inner_end = I.I == tail(li) ? li[1] : last(inner) + return inner_end - inner_start + 1 + end + @inline function simd_index(iter::CartesianPartition, Ilast::CartesianIndex, I1::Int) + # I1 is the 0-based distance from the first dimension's offest + offset = first(iter.parent.parent.indices[1]) # (this is 1 for 1-based arrays) + # In the first column we need to also add in the iter's starting point (branchlessly) + f = @inbounds iter[1] + startoffset = (Ilast.I == tail(f.I))*(f[1] - 1) + CartesianIndex((I1 + offset + startoffset, Ilast.I...)) + end +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 +checkindex(::Type{Bool}, inds::Tuple, I::CartesianIndices) = all(checkindex.(Bool, inds, I.indices)) + +# 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,collect(r)) +print_array(io::IO, X::LogicalIndex) = print_array(io, collect(X)) +# 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 +function diff(r::AbstractRange{T}; dims::Integer=1) where {T} + dims == 1 || throw(ArgumentError("dimension $dims out of range (1:1)")) + return T[@inbounds r[i+1] - r[i] for i in firstindex(r):lastindex(r)-1] +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 + +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, map(Integer, (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}})::typeof(dest) + 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}})::typeof(dest) + 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..4729849 --- /dev/null +++ b/base/multimedia.jl @@ -0,0 +1,417 @@ +# 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::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`. + +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 default MIME type is `MIME"text/plain"`. There is a fallback definition for `text/plain` +output that calls `show` with 2 arguments, so it is not always necessary to add a method +for that case. If a type benefits from custom human-readable output though, +`show(::IO, ::MIME"text/plain", ::T)` should be defined. For example, the `Day` type uses +`1 day` as the output for the `text/plain` MIME type, and `Day(1)` as the output of 2-argument `show`. + +Container types generally implement 3-argument `show` by calling `show(io, MIME"text/plain"(), x)` +for elements `x`, with `:compact => true` set in an [`IOContext`](@ref) passed as the first argument. +""" +show(stream::IO, 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). + +To customize how instances of a type are displayed, overload [`show`](@ref) rather than `display`, +as explained in the manual section on [custom pretty-printing](@ref man-custom-pretty-printing). +""" +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..f315be3 --- /dev/null +++ b/base/namedtuple.jl @@ -0,0 +1,383 @@ +# 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. + +The [`@NamedTuple`](@ref) macro can be used for conveniently declaring `NamedTuple` types. + +# 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) +``` + +As in keyword arguments, identifiers and dot expressions imply names: + +```jldoctest +julia> x = 0 +0 + +julia> t = (; x) +(x = 0,) + +julia> (; t.x) +(x = 0,) +``` + +!!! compat "Julia 1.5" + Implicit names from identifiers and dot expressions are available as of Julia 1.5. +""" +Core.NamedTuple + +if nameof(@__MODULE__) === :Base + +@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 + +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) +front(t::NamedTuple{names}) where names = NamedTuple{front(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 + +""" + setindex(nt::NamedTuple, val, key::Symbol) + +Constructs a new `NamedTuple` with the key `key` set to `val`. +If `key` is already in the keys of `nt`, `val` replaces the old value. + +```jldoctest +julia> nt = (a = 3,) +(a = 3,) + +julia> Base.setindex(nt, 33, :b) +(a = 3, b = 33) + +julia> Base.setindex(nt, 4, :a) +(a = 4,) + +julia> Base.setindex(nt, "a", :a) +(a = "a",) +``` +""" +function setindex(nt::NamedTuple, v, idx::Symbol) + merge(nt, (; idx => v)) +end + +""" + @NamedTuple{key1::Type1, key2::Type2, ...} + @NamedTuple begin key1::Type1; key2::Type2; ...; end + +This macro gives a more convenient syntax for declaring `NamedTuple` types. It returns a `NamedTuple` +type with the given keys and types, equivalent to `NamedTuple{(:key1, :key2, ...), Tuple{Type1,Type2,...}}`. +If the `::Type` declaration is omitted, it is taken to be `Any`. The `begin ... end` form allows the +declarations to be split across multiple lines (similar to a `struct` declaration), but is otherwise +equivalent. + +For example, the tuple `(a=3.1, b="hello")` has a type `NamedTuple{(:a, :b),Tuple{Float64,String}}`, which +can also be declared via `@NamedTuple` as: + +```jldoctest +julia> @NamedTuple{a::Float64, b::String} +NamedTuple{(:a, :b),Tuple{Float64,String}} + +julia> @NamedTuple begin + a::Float64 + b::String + end +NamedTuple{(:a, :b),Tuple{Float64,String}} +``` + +!!! compat "Julia 1.5" + This macro is available as of Julia 1.5. +""" +macro NamedTuple(ex) + Meta.isexpr(ex, :braces) || Meta.isexpr(ex, :block) || + throw(ArgumentError("@NamedTuple expects {...} or begin...end")) + decls = filter(e -> !(e isa LineNumberNode), ex.args) + all(e -> e isa Symbol || Meta.isexpr(e, :(::)), decls) || + throw(ArgumentError("@NamedTuple must contain a sequence of name or name::type expressions")) + vars = [QuoteNode(e isa Symbol ? e : e.args[1]) for e in decls] + types = [esc(e isa Symbol ? :Any : e.args[2]) for e in decls] + return :(NamedTuple{($(vars...),), Tuple{$(types...)}}) +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..9b615c9 --- /dev/null +++ b/base/number.jl @@ -0,0 +1,320 @@ +# 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 + +""" + 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) = iszero(x) ? x/abs(oneunit(x)) : x/abs(x) +sign(x::Real) = ifelse(x < zero(x), oftype(one(x),-1), ifelse(x > zero(x), one(x), typeof(one(x))(x))) +sign(x::Unsigned) = ifelse(x > zero(x), 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..c304530 --- /dev/null +++ b/base/operators.jl @@ -0,0 +1,1178 @@ +# 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`. +""" +(>:)(@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). + +# Examples + ```jldoctest + julia> isless(1, 3) + true + + julia> isless("Red", "Blue") + false + ``` +""" +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 + +const var"'" = adjoint + +""" + \\(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) + +# 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 + +julia> 5.0 ÷ 2 +2.0 +``` +""" +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`. + +Function composition also works in prefix form: `∘(f, g)` is the same as `f ∘ g`. +The prefix form supports composition of multiple functions: `∘(f, g, h) = f ∘ g ∘ h` +and splatting `∘(fs...)` for composing an iterable collection of functions. + +!!! compat "Julia 1.4" + Multiple function composition requires at least Julia 1.4. + +!!! compat "Julia 1.5" + Composition of one function ∘(f) requires at least Julia 1.5. + +# Examples +```jldoctest +julia> map(uppercase∘first, ["apple", "banana", "carrot"]) +3-element Array{Char,1}: + 'A': ASCII/Unicode U+0041 (category Lu: Letter, uppercase) + 'B': ASCII/Unicode U+0042 (category Lu: Letter, uppercase) + 'C': ASCII/Unicode U+0043 (category Lu: Letter, uppercase) + +julia> fs = [ + x -> 2x + x -> x/2 + x -> x-1 + x -> x+1 + ]; + +julia> ∘(fs...)(3) +3.0 +``` +""" +function ∘ end +∘(f) = f +∘(f, g) = (x...)->f(g(x...)) +∘(f, g, h...) = ∘(f ∘ g, h...) + +""" + !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`. + +To determine whether an item is not in a given collection, see [`:∉`](@ref). +You may also negate the `in` by doing `!(a in b)` which is logically similar to "not in". + +When broadcasting with `in.(items, collection)` or `items .∈ collection`, both +`item` and `collection` are broadcasted over, which is often not what is intended. +For example, if both arguments are vectors (and the dimensions match), the result is +a vector indicating whether each value in collection `items` is `in` the value at the +corresponding position in `collection`. To get a vector indicating whether each value +in `items` is in `collection`, wrap `collection` in a tuple or a `Ref` like this: +`in.(items, Ref(collection))` or `items .∈ Ref(collection)`. + +# 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 + +julia> !(21 in a) +true + +julia> !(19 in a) +false + +julia> [1, 2] .∈ [2, 3] +2-element BitArray{1}: + 0 + 0 + +julia> [1, 2] .∈ ([2, 3],) +2-element BitArray{1}: + 0 + 1 +``` +""" +in, ∋ + +""" + ∉(item, collection) -> Bool + ∌(collection, item) -> Bool + +Negation of `∈` and `∋`, i.e. checks that `item` is not in `collection`. + +When broadcasting with `items .∉ collection`, both `item` and `collection` are +broadcasted over, which is often not what is intended. For example, if both arguments +are vectors (and the dimensions match), the result is a vector indicating whether +each value in collection `items` is not in the value at the corresponding position +in `collection`. To get a vector indicating whether each value in `items` is not in +`collection`, wrap `collection` in a tuple or a `Ref` like this: +`items .∉ Ref(collection)`. + +# Examples +```jldoctest +julia> 1 ∉ 2:4 +true + +julia> 1 ∉ 1:3 +false + +julia> [1, 2] .∉ [2, 3] +2-element BitArray{1}: + 1 + 1 + +julia> [1, 2] .∉ ([2, 3],) +2-element BitArray{1}: + 1 + 0 +``` +""" +∉, ∌ diff --git a/base/options.jl b/base/options.jl new file mode 100644 index 0000000..2054808 --- /dev/null +++ b/base/options.jl @@ -0,0 +1,85 @@ +# 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} + nthreads::Int32 + 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} + outputo::Ptr{UInt8} + outputasm::Ptr{UInt8} + outputji::Ptr{UInt8} + output_code_coverage::Ptr{UInt8} + incremental::Int8 + image_file_specified::Int8 + warn_scope::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..dc2f2c8 --- /dev/null +++ b/base/ordering.jl @@ -0,0 +1,103 @@ +# 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) +ReverseOrdering() = ReverseOrdering(ForwardOrdering()) + +const DirectOrdering = Union{ForwardOrdering,ReverseOrdering{ForwardOrdering}} + +const Forward = ForwardOrdering() +const Reverse = ReverseOrdering() + +struct By{T, O} <: Ordering + by::T + order::O +end + +# backwards compatibility with VERSION < v"1.5-" +By(by) = By(by, Forward) + +struct Lt{T} <: Ordering + lt::T +end + +struct Perm{O<:Ordering,V<:AbstractVector} <: Ordering + order::O + data::V +end + +ReverseOrdering(by::By) = By(by.by, ReverseOrdering(by.order)) +ReverseOrdering(perm::Perm) = Perm(ReverseOrdering(perm.order), perm.data) + +lt(o::ForwardOrdering, a, b) = isless(a,b) +lt(o::ReverseOrdering, a, b) = lt(o.fwd,b,a) +lt(o::By, a, b) = lt(o.order,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 + +_ord(lt::typeof(isless), by::typeof(identity), order::Ordering) = order +_ord(lt::typeof(isless), by, order::Ordering) = By(by, order) + +function _ord(lt, by, order::Ordering) + if order === Forward + return Lt((x, y) -> lt(by(x), by(y))) + elseif order === Reverse + return Lt((x, y) -> lt(by(y), by(x))) + else + error("Passing both lt= and order= arguments is ambiguous; please pass order=Forward or order=Reverse (or leave default)") + end +end + +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 + + +# This function is not in use anywhere in Base but we observed +# use in sorting-related packages (#34719). It's probably best to move +# this functionality to those packages in the future; let's remind/force +# ourselves to deprecate this in v2.0. +# The following clause means `if VERSION < v"2.0-"` but it also works during +# bootstrap. For the same reason, we need to write `Int32` instead of `Cint`. +if ccall(:jl_ver_major, Int32, ()) < 2 + 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) +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..ebad466 --- /dev/null +++ b/base/path.jl @@ -0,0 +1,522 @@ +# 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() -> String + +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::AbstractString) = occursin(path_absolute_re, path) +else + isabspath(path::AbstractString) = 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 + +# NOTE: deprecated in 1.4 +pathsep() = path_separator + +""" + 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" +``` +""" +splitpath(p::AbstractString) = splitpath(String(p)) + +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(path::AbstractString)::String = path + +if Sys.iswindows() + +function joinpath(path::AbstractString, paths::AbstractString...)::String + result_drive, result_path = splitdrive(path) + + local p_drive, p_path + for p in paths + p_drive, p_path = splitdrive(p) + + if startswith(p_path, ('\\', '/')) + # second path is absolute + if !isempty(p_drive) || !isempty(result_drive) + result_drive = p_drive + end + result_path = p_path + continue + elseif !isempty(p_drive) && p_drive != result_drive + if lowercase(p_drive) != lowercase(result_drive) + # different drives, ignore the first path entirely + result_drive = p_drive + result_path = p_path + continue + end + end + + # second path is relative to the first + if !isempty(result_path) && result_path[end] ∉ ('\\', '/') + result_path *= "\\" + end + + result_path = result_path * p_path + end + + # add separator between UNC and non-absolute path + if !isempty(p_path) && result_path[1] ∉ ('\\', '/') && !isempty(result_drive) && result_drive[end] != ':' + return result_drive * "\\" * result_path + end + + return result_drive * result_path +end + +else + +function joinpath(path::AbstractString, paths::AbstractString...)::String + for p in paths + if isabspath(p) + path = p + elseif isempty(path) || path[end] == '/' + path *= p + else + path *= "/" * p + end + end + return path +end + +end # os-test + +""" + joinpath(parts::AbstractString...) -> String + +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. + +Note on Windows since there is a current directory for each drive, `joinpath("c:", "foo")` +represents a path relative to the current directory on drive "c:" so this is equal to "c:foo", +not "c:\\foo". Furthermore, `joinpath` treats this as a non-absolute path and ignores the drive +letter casing, hence `joinpath("C:\\A","c:b") = "C:\\A\\b"`. + +# Examples +```jldoctest +julia> joinpath("/home/myuser", "example.jl") +"/home/myuser/example.jl" +``` +""" +joinpath + +""" + normpath(path::AbstractString) -> String + +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(path::AbstractString, paths::AbstractString...) -> String + +Convert a set of paths to a normalized path by joining them together and removing +"." and ".." entries. Equivalent to `normpath(joinpath(path, paths...))`. +""" +normpath(a::AbstractString, b::AbstractString...) = normpath(joinpath(a,b...)) + +""" + abspath(path::AbstractString) -> String + +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...) -> String + +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) -> String + +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 (: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..cc539d8 --- /dev/null +++ b/base/pcre.jl @@ -0,0 +1,237 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## low-level pcre2 interface ## + +module PCRE + +import ..RefValue + +# include($BUILDROOT/base/pcre_h.jl) +include(string(length(Core.ARGS) >= 2 ? Core.ARGS[2] : "", "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}, + (Csize_t, Csize_t, 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), Cint, + (Ptr{Cvoid}, UInt32, Ptr{Cvoid}), + regex, what, buf) + if ret != 0 + error(ret == ERROR_NULL ? "PCRE error: NULL regex object" : + ret == ERROR_BADMAGIC ? "PCRE error: invalid regex object" : + ret == ERROR_BADOPTION ? "PCRE error: invalid option flags" : + "PCRE error: unknown error ($ret)") + end + return buf[] +end + +function ovec_length(match_data) + n = ccall((:pcre2_get_ovector_count_8, PCRE_LIB), UInt32, + (Ptr{Cvoid},), match_data) + return 2Int(n) +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) + if !(pattern isa Union{String,SubString{String}}) + pattern = String(pattern) + end + 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, ncodeunits(pattern), options, errno, erroff, C_NULL) + if re_ptr == C_NULL + error("PCRE compilation error: $(err_message(errno[])) at offset $(erroff[])") + end + return re_ptr +end + +function jit_compile(regex::Ptr{Cvoid}) + errno = ccall((:pcre2_jit_compile_8, PCRE_LIB), Cint, + (Ptr{Cvoid}, UInt32), regex, JIT_COMPLETE) + 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::Integer) + buffer = Vector{UInt8}(undef, 1024) + ret = ccall((:pcre2_get_error_message_8, PCRE_LIB), Cint, + (Cint, Ptr{UInt8}, Csize_t), errno, buffer, length(buffer)) + ret == ERROR_BADDATA && error("PCRE error: invalid errno ($errno)") + # TODO: seems like there should be a better way to get this string + return GC.@preserve buffer unsafe_string(pointer(buffer)) +end + +function exec(re, subject, offset, options, match_data) + if !(subject isa Union{String,SubString{String}}) + subject = String(subject) + end + rc = ccall((:pcre2_match_8, PCRE_LIB), Cint, + (Ptr{Cvoid}, Ptr{UInt8}, Csize_t, Csize_t, UInt32, Ptr{Cvoid}, Ptr{Cvoid}), + re, subject, ncodeunits(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))") + return 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) + p = ccall((:pcre2_match_data_create_from_pattern_8, PCRE_LIB), + Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}), re, C_NULL) + p == C_NULL && error("PCRE error: could not allocate memory") + return p +end + +function substring_number_from_name(re, name) + n = ccall((:pcre2_substring_number_from_name_8, PCRE_LIB), Cint, + (Ptr{Cvoid}, Cstring), re, name) + n < 0 && error("PCRE error: $(err_message(n))") + return Int(n) +end + +function substring_length_bynumber(match_data, number) + s = RefValue{Csize_t}() + rc = ccall((:pcre2_substring_length_bynumber_8, PCRE_LIB), Cint, + (Ptr{Cvoid}, Cint, Ref{Csize_t}), match_data, number, s) + rc < 0 && error("PCRE error: $(err_message(rc))") + return 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))") + return 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 + return names +end + +end # module diff --git a/base/permuteddimsarray.jl b/base/permuteddimsarray.jl new file mode 100644 index 0000000..7ac87df --- /dev/null +++ b/base/permuteddimsarray.jl @@ -0,0 +1,263 @@ +# 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.similar(A::PermutedDimsArray, T::Type, dims::Base.Dims) = similar(parent(A), T, dims) + +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 +Base.elsize(::Type{<:PermutedDimsArray{<:Any, <:Any, <:Any, <:Any, P}}) where {P} = Base.elsize(P) + +@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..924a5c1 --- /dev/null +++ b/base/pointer.jl @@ -0,0 +1,164 @@ +# 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 + +unsigned(x::Ptr) = UInt(x) +signed(x::Ptr) = Int(x) diff --git a/base/process.jl b/base/process.jl new file mode 100644 index 0000000..e664d74 --- /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 + err = 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 err != 0 + ccall(:jl_forceclose_uv, Cvoid, (Ptr{Cvoid},), handle) # will call free on handle eventually + throw(_UVError("could not spawn " * repr(cmd), err)) + 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=Base.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..6436ee3 --- /dev/null +++ b/base/promotion.jl @@ -0,0 +1,413 @@ +# 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)...) + +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..24d9725 --- /dev/null +++ b/base/range.jl @@ -0,0 +1,1110 @@ +# 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. + +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. + +Special care is taken to ensure intermediate values are computed rationally. +To avoid this induced overhead, see the [`LinRange`](@ref) constructor. + +`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) + # Constant-fold-able indexing into tuples to functionally expose Base.tail and Base.front + function getindex(@nospecialize(t::Tuple), r::UnitRange) + @_inline_meta + r.start > r.stop && return () + if r.start == 1 + r.stop == length(t) && return t + r.stop == length(t)-1 && return front(t) + r.stop == length(t)-2 && return front(front(t)) + elseif r.stop == length(t) + r.start == 2 && return tail(t) + r.start == 3 && return tail(tail(t)) + end + return (eltype(t)[t[ri] for ri in r]...,) + 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 +``` + +Compared to using [`range`](@ref), directly constructing a `LinRange` should +have less overhead but won't try to correct for floating point errors: +```julia +julia> collect(range(-0.1, 0.3, length=5)) +5-element Array{Float64,1}: + -0.1 + 0.0 + 0.1 + 0.2 + 0.3 + +julia> collect(LinRange(-0.1, 0.3, 5)) +5-element Array{Float64,1}: + -0.1 + -1.3877787807814457e-17 + 0.09999999999999999 + 0.19999999999999998 + 0.3 +``` +""" +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) = r.step +step(r::StepRangeLen{T}) where {T<:AbstractFloat} = 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{T}, s::OrdinalRange{<:Integer}) where {T} + @_inline_meta + @boundscheck checkbounds(r, s) + vfirst = unsafe_getindex(r, first(s)) + vlast = unsafe_getindex(r, last(s)) + return LinRange{T}(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, ")") + +function ==(r::T, s::T) where {T<:AbstractRange} + isempty(r) && return isempty(s) + _has_length_one(r) && return _has_length_one(s) & (first(r) == first(s)) + (first(r) == first(s)) & (step(r) == step(s)) & (last(r) == last(s)) +end + +function ==(r::OrdinalRange, s::OrdinalRange) + isempty(r) && return isempty(s) + _has_length_one(r) && return _has_length_one(s) & (first(r) == first(s)) + (first(r) == first(s)) & (step(r) == step(s)) & (last(r) == last(s)) +end + +==(r::T, s::T) where {T<:Union{StepRangeLen,LinRange}} = + (isempty(r) & isempty(s)) | ((first(r) == first(s)) & (length(r) == length(s)) & (last(r) == last(s))) + +function ==(r::Union{StepRange{T},StepRangeLen{T,T}}, s::Union{StepRange{T},StepRangeLen{T,T}}) where {T} + isempty(r) && return isempty(s) + _has_length_one(r) && return _has_length_one(s) & (first(r) == first(s)) + (first(r) == first(s)) & (step(r) == step(s)) & (last(r) == last(s)) +end + +_has_length_one(r::OrdinalRange) = first(r) == last(r) +_has_length_one(r::AbstractRange) = isone(length(r)) + +function ==(r::AbstractRange, s::AbstractRange) + lr = length(r) + if lr != length(s) + return false + elseif iszero(lr) + return true + 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)) +union(r::OneTo, s::OneTo) = OneTo(max(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}) = + isempty(r) || 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: length of r1 is $r1l, length of r2 is $(length(r2))"))) + 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: length of r1 is $len, length of r2 is $(r2.len)"))) + 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: length of r1 is $len, length of r2 is $(length(r2))"))) + 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..0462554 --- /dev/null +++ b/base/rational.jl @@ -0,0 +1,487 @@ +# 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 + + # Unexported inner constructor of Rational that bypasses all checks + global unsafe_rational(::Type{T}, num, den) where {T} = new{T}(num, den) +end + +unsafe_rational(num::T, den::T) where {T<:Integer} = unsafe_rational(T, num, den) +unsafe_rational(num::Integer, den::Integer) = unsafe_rational(promote(num, den)...) + +@noinline __throw_rational_argerror_typemin(T) = throw(ArgumentError("invalid rational: denominator can't be typemin($T)")) +function checked_den(num::T, den::T) where T<:Integer + if signbit(den) + den = -den + signbit(den) && __throw_rational_argerror_typemin(T) + num = -num + end + return unsafe_rational(T, num, den) +end +checked_den(num::Integer, den::Integer) = checked_den(promote(num, den)...) + +@noinline __throw_rational_argerror_zero(T) = throw(ArgumentError("invalid rational: zero($T)//zero($T)")) +function Rational{T}(num::Integer, den::Integer) where T<:Integer + iszero(den) && iszero(num) && __throw_rational_argerror_zero(T) + num, den = divgcd(num, den) + return checked_den(T(num), T(den)) +end + +Rational(n::T, d::T) where {T<:Integer} = Rational{T}(n, d) +Rational(n::Integer, d::Integer) = Rational(promote(n, d)...) +Rational(n::Integer) = unsafe_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(promote(x.num, y)...) + checked_den(xn, checked_mul(x.den, yn)) +end +function //(x::Integer, y::Rational) + xn, yn = divgcd(promote(x, y.num)...) + checked_den(checked_mul(xn, y.den), yn) +end +function //(x::Rational, y::Rational) + xn,yn = divgcd(promote(x.num, y.num)...) + xd,yd = divgcd(promote(x.den, y.den)...) + checked_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 + +function Rational{T}(x::Rational) where T<:Integer + unsafe_rational(T, convert(T, x.num), convert(T, x.den)) +end +function Rational{T}(x::Integer) where T<:Integer + unsafe_rational(T, convert(T, x), one(T)) +end + +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) = unsafe_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)} + +@noinline __throw_negate_unsigned() = throw(OverflowError("cannot negate unsigned number")) + +""" + 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 + T<:Unsigned && x < 0 && __throw_negate_unsigned() + isnan(x) && return T(x)//one(T) + isinf(x) && return unsafe_rational(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) = unsafe_rational(copysign(x.num, y), x.den) +copysign(x::Rational, y::Rational) = unsafe_rational(copysign(x.num, y.num), x.den) + +abs(x::Rational) = Rational(abs(x.num), x.den) + +typemin(::Type{Rational{T}}) where {T<:Signed} = unsafe_rational(T, -one(T), zero(T)) +typemin(::Type{Rational{T}}) where {T<:Integer} = unsafe_rational(T, zero(T), one(T)) +typemax(::Type{Rational{T}}) where {T<:Integer} = unsafe_rational(T, one(T), zero(T)) + +isinteger(x::Rational) = x.den == 1 + ++(x::Rational) = unsafe_rational(+x.num, x.den) +-(x::Rational) = unsafe_rational(-x.num, x.den) + +function -(x::Rational{T}) where T<:BitSigned + x.num == typemin(T) && __throw_rational_numerator_typemin(T) + unsafe_rational(-x.num, x.den) +end +@noinline __throw_rational_numerator_typemin(T) = throw(OverflowError("rational numerator is typemin($T)")) + +function -(x::Rational{T}) where T<:Unsigned + x.num != zero(T) && __throw_negate_unsigned() + 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(promote(x.den, y.den)...) + Rational(($chop)(checked_mul(x.num,yd), checked_mul(y.num,xd)), checked_mul(x.den,yd)) + end + + function ($op)(x::Rational, y::Integer) + unsafe_rational(($chop)(x.num, checked_mul(x.den, y)), x.den) + end + end +end +for (op,chop) in ((:+,:checked_add), (:-,:checked_sub)) + @eval begin + function ($op)(y::Integer, x::Rational) + unsafe_rational(($chop)(checked_mul(x.den, y), x.num), x.den) + end + end +end +for (op,chop) in ((:rem,:rem), (:mod,:mod)) + @eval begin + function ($op)(y::Integer, x::Rational) + Rational(($chop)(checked_mul(x.den, y), x.num), x.den) + end + end +end + +function *(x::Rational, y::Rational) + xn, yd = divgcd(promote(x.num, y.den)...) + xd, yn = divgcd(promote(x.den, y.num)...) + unsafe_rational(checked_mul(xn, yn), checked_mul(xd, yd)) +end +function *(x::Rational, y::Integer) + xd, yn = divgcd(promote(x.den, y)...) + unsafe_rational(checked_mul(x.num, yn), xd) +end +function *(y::Integer, x::Rational) + yn, xd = divgcd(promote(y, x.den)...) + unsafe_rational(checked_mul(yn, x.num), xd) +end +/(x::Rational, y::Union{Rational, Integer, Complex{<:Union{Integer,Rational}}}) = x//y +/(x::Union{Integer, Complex{<:Union{Integer,Rational}}}, y::Rational) = x//y +inv(x::Rational{T}) where {T} = checked_den(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) + +function div(x::Rational, y::Integer, r::RoundingMode) + xn,yn = divgcd(x.num,y) + div(xn, checked_mul(x.den,yn), r) +end +function div(x::Integer, y::Rational, r::RoundingMode) + xn,yn = divgcd(x,y.num) + div(checked_mul(xn,y.den), yn, r) +end +function div(x::Rational, y::Rational, r::RoundingMode) + xn,yn = divgcd(x.num,y.num) + xd,yd = divgcd(x.den,y.den) + div(checked_mul(xn,yd), checked_mul(xd,yn), r) +end + +# For compatibility - to be removed in 2.0 when the generic fallbacks +# are removed from div.jl +div(x::T, y::T, r::RoundingMode) where {T<:Rational} = + invoke(div, Tuple{Rational, Rational, RoundingMode}, x, y, r) +for (S, T) in ((Rational, Integer), (Integer, Rational), (Rational, Rational)) + @eval begin + div(x::$S, y::$T) = div(x, y, RoundToZero) + fld(x::$S, y::$T) = div(x, y, RoundDown) + cld(x::$S, y::$T) = div(x, y, RoundUp) + end +end + +trunc(::Type{T}, x::Rational) where {T} = round(T, x, RoundToZero) +floor(::Type{T}, x::Rational) where {T} = round(T, x, RoundDown) +ceil(::Type{T}, x::Rational) where {T} = round(T, x, RoundUp) + +round(x::Rational, r::RoundingMode=RoundNearest) = round(typeof(x), x, r) + +function round(::Type{T}, x::Rational{Tr}, r::RoundingMode=RoundNearest) where {T,Tr} + if iszero(denominator(x)) && !(T <: Integer) + return convert(T, copysign(unsafe_rational(one(Tr), zero(Tr)), numerator(x))) + end + convert(T, div(numerator(x), denominator(x), r)) +end + +function round(::Type{T}, x::Rational{Bool}, ::RoundingMode=RoundNearest) where T + if denominator(x) == false && (T <: Integer) + throw(DivideError()) + end + convert(T, x) +end + +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) + +gcd(x::Rational, y::Rational) = unsafe_rational(gcd(x.num, y.num), lcm(x.den, y.den)) +lcm(x::Rational, y::Rational) = unsafe_rational(lcm(x.num, y.num), gcd(x.den, y.den)) +function gcdx(x::Rational, y::Rational) + c = gcd(x, y) + if iszero(c.num) + a, b = one(c.num), c.num + elseif iszero(c.den) + a = ifelse(iszero(x.den), one(c.den), c.den) + b = ifelse(iszero(y.den), one(c.den), c.den) + else + idiv(x, c) = div(x.num, c.num) * div(c.den, x.den) + _, a, b = gcdx(idiv(x, c), idiv(y, c)) + end + c, a, b +end diff --git a/base/reduce.jl b/base/reduce.jl new file mode 100644 index 0000000..598ce08 --- /dev/null +++ b/base/reduce.jl @@ -0,0 +1,888 @@ +# 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 + +abstract type AbstractBroadcasted end +const AbstractArrayOrBroadcasted = Union{AbstractArray, AbstractBroadcasted} + +""" + 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::F, op::OP, nt, itr) where {F,OP} + op′, itr′ = _xfadjoint(BottomRF(op), Generator(f, itr)) + return foldl_impl(op′, nt, itr′) +end + +function foldl_impl(op::OP, nt, itr) where {OP} + v = _foldl_impl(op, get(nt, :init, _InitialValue()), itr) + v isa _InitialValue && return reduce_empty_iter(op, itr) + return v +end + +function _foldl_impl(op::OP, init, itr) where {OP} + # Unroll the while loop once; if init is known, the call to op may + # be evaluated at compile time + y = iterate(itr) + y === nothing && return init + v = op(init, y[1]) + while true + y = iterate(itr, y[2]) + y === nothing && break + v = op(v, y[1]) + end + return v +end + +struct _InitialValue end + +""" + BottomRF(rf) -> rf′ + +"Bottom" reducing function. This is a thin wrapper around the `op` argument +passed to `foldl`-like functions for handling the initial invocation to call +[`reduce_first`](@ref). +""" +struct BottomRF{T} + rf::T +end + +@inline (op::BottomRF)(::_InitialValue, x) = reduce_first(op.rf, x) +@inline (op::BottomRF)(acc, x) = op.rf(acc, x) + +""" + MappingRF(f, rf) -> rf′ + +Create a mapping reducing function `rf′(acc, x) = rf(acc, f(x))`. +""" +struct MappingRF{F, T} + f::F + rf::T +end + +@inline (op::MappingRF)(acc, x) = op.rf(acc, op.f(x)) + +""" + FilteringRF(f, rf) -> rf′ + +Create a filtering reducing function `rf′(acc, x) = f(x) ? rf(acc, x) : acc`. +""" +struct FilteringRF{F, T} + f::F + rf::T +end + +@inline (op::FilteringRF)(acc, x) = op.f(x) ? op.rf(acc, x) : acc + +""" + FlatteningRF(rf) -> rf′ + +Create a flattening reducing function that is roughly equivalent to +`rf′(acc, x) = foldl(rf, x; init=acc)`. +""" +struct FlatteningRF{T} + rf::T +end + +@inline function (op::FlatteningRF)(acc, x) + op′, itr′ = _xfadjoint(op.rf, x) + return _foldl_impl(op′, acc, itr′) +end + +""" + _xfadjoint(op, itr) -> op′, itr′ + +Given a pair of reducing function `op` and an iterator `itr`, return a pair +`(op′, itr′)` of similar types. If the iterator `itr` is transformed by an +iterator transform `ixf` whose adjoint transducer `xf` is known, `op′ = xf(op)` +and `itr′ = ixf⁻¹(itr)` is returned. Otherwise, `op` and `itr` are returned +as-is. For example, transducer `rf -> MappingRF(f, rf)` is the adjoint of +iterator transform `itr -> Generator(f, itr)`. + +Nested iterator transforms are converted recursively. That is to say, +given `op` and + + itr = (ixf₁ ∘ ixf₂ ∘ ... ∘ ixfₙ)(itr′) + +what is returned is `itr′` and + + op′ = (xfₙ ∘ ... ∘ xf₂ ∘ xf₁)(op) +""" +_xfadjoint(op, itr) = (op, itr) +_xfadjoint(op, itr::Generator) = + if itr.f === identity + _xfadjoint(op, itr.iter) + else + _xfadjoint(MappingRF(itr.f, op), itr.iter) + end +_xfadjoint(op, itr::Filter) = + _xfadjoint(FilteringRF(itr.flt, op), itr.itr) +_xfadjoint(op, itr::Flatten) = + _xfadjoint(FlatteningRF(op), itr.it) + +""" + 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 + +function mapfoldr_impl(f, op, nt, itr) + op′, itr′ = _xfadjoint(BottomRF(FlipArgs(op)), Generator(f, itr)) + return foldl_impl(op′, nt, _reverse(itr′)) +end + +_reverse(itr) = Iterators.reverse(itr) +_reverse(itr::Tuple) = reverse(itr) #33235 + +struct FlipArgs{F} + f::F +end + +@inline (f::FlipArgs)(x, y) = f.f(y, x) + +""" + 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::AbstractArrayOrBroadcasted, + 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::AbstractArrayOrBroadcasted, 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, ::Type{T}) where {T} = _empty_reduce_error() +reduce_empty(::typeof(+), ::Type{Union{}}) = _empty_reduce_error() +reduce_empty(::typeof(+), ::Type{T}) where {T} = zero(T) +reduce_empty(::typeof(+), ::Type{Bool}) = zero(Int) +reduce_empty(::typeof(*), ::Type{Union{}}) = _empty_reduce_error() +reduce_empty(::typeof(*), ::Type{T}) where {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), ::Type{Union{}}) = _empty_reduce_error() +reduce_empty(::typeof(add_sum), ::Type{T}) where {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), ::Type{Union{}}) = _empty_reduce_error() +reduce_empty(::typeof(mul_prod), ::Type{T}) where {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) + +reduce_empty(op::BottomRF, ::Type{T}) where {T} = reduce_empty(op.rf, T) +reduce_empty(op::MappingRF, ::Type{T}) where {T} = mapreduce_empty(op.f, op.rf, T) +reduce_empty(op::FilteringRF, ::Type{T}) where {T} = reduce_empty(op.rf, T) +reduce_empty(op::FlipArgs, ::Type{T}) where {T} = reduce_empty(op.f, T) + +""" + 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)) + +# For backward compatibility: +mapreduce_empty_iter(f, op, itr, ItrEltype) = + reduce_empty_iter(MappingRF(f, op), itr, ItrEltype) + +@inline reduce_empty_iter(op, itr) = reduce_empty_iter(op, itr, IteratorEltype(itr)) +@inline reduce_empty_iter(op, itr, ::HasEltype) = reduce_empty(op, eltype(itr)) +reduce_empty_iter(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::AbstractArrayOrBroadcasted) = _mapreduce(f, op, IndexStyle(A), A) + +function _mapreduce(f, op, ::IndexLinear, A::AbstractArrayOrBroadcasted) + inds = LinearIndices(A) + n = length(inds) + if n == 0 + return mapreduce_empty_iter(f, op, A, IteratorEltype(A)) + 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::AbstractArrayOrBroadcasted) = 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::AbstractArrayOrBroadcasted, 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 + +_bool(f) = x->f(x)::Bool + +""" + 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 +``` +""" +count(itr) = count(identity, itr) + +count(f, itr) = _simple_count(f, itr) + +function _simple_count(pred, itr) + n = 0 + for x in itr + n += pred(x)::Bool + end + return n +end + +function count(::typeof(identity), x::Array{Bool}) + n = 0 + chunks = length(x) ÷ sizeof(UInt) + mask = 0x0101010101010101 % UInt + GC.@preserve x begin + ptr = Ptr{UInt}(pointer(x)) + for i in 1:chunks + n += count_ones(unsafe_load(ptr, i) & mask) + end + end + for i in sizeof(UInt)*chunks+1:length(x) + n += x[i] + end + return n +end diff --git a/base/reducedim.jl b/base/reducedim.jl new file mode 100644 index 0000000..82febb1 --- /dev/null +++ b/base/reducedim.jl @@ -0,0 +1,952 @@ +# 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}) = oftype(i, 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::AbstractArrayOrBroadcasted, 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)...)::typeof(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)...)::typeof(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::AbstractArrayOrBroadcasted, region, init, ::Type{R}) where {R} = fill!(similar(A,R,reduced_indices(A,region)), init) +reducedim_initarray(A::AbstractArrayOrBroadcasted, 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 initial 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::AbstractArrayOrBroadcasted, region) = reducedim_initarray(A, region, true) +reducedim_init(f, op::typeof(|), A::AbstractArrayOrBroadcasted, 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::AbstractArrayOrBroadcasted) = false +has_fast_linear_indexing(a::Array) = true +has_fast_linear_indexing(::Number) = true # for Broadcasted +has_fast_linear_indexing(bc::Broadcast.Broadcasted) = + all(has_fast_linear_indexing, bc.args) + +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::AbstractArrayOrBroadcasted) + 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::AbstractArrayOrBroadcasted) = + (_mapreducedim!(f, op, R, A); R) + +reducedim!(op, R::AbstractArray{RT}, A::AbstractArrayOrBroadcasted) 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::AbstractArrayOrBroadcasted; dims=:, kw...) = + _mapreduce_dim(f, op, kw.data, A, dims) +mapreduce(f, op, A::AbstractArrayOrBroadcasted...; kw...) = + reduce(op, map(f, A...); kw...) + +_mapreduce_dim(f, op, nt::NamedTuple{(:init,)}, A::AbstractArrayOrBroadcasted, ::Colon) = + mapfoldl(f, op, A; nt...) + +_mapreduce_dim(f, op, ::NamedTuple{()}, A::AbstractArrayOrBroadcasted, ::Colon) = + _mapreduce(f, op, IndexStyle(A), A) + +_mapreduce_dim(f, op, nt::NamedTuple{(:init,)}, A::AbstractArrayOrBroadcasted, dims) = + mapreducedim!(f, op, reducedim_initarray(A, dims, nt.init), A) + +_mapreduce_dim(f, op, ::NamedTuple{()}, A::AbstractArrayOrBroadcasted, 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 ##### + +""" + count([f=identity,] A::AbstractArray; dims=:) + +Count the number of elements in `A` for which `f` returns `true` over the given +dimensions. + +!!! compat "Julia 1.5" + `dims` keyword was added in Julia 1.5. + +# Examples +```jldoctest +julia> A = [1 2; 3 4] +2×2 Array{Int64,2}: + 1 2 + 3 4 + +julia> count(<=(2), A, dims=1) +1×2 Array{Int64,2}: + 1 1 + +julia> count(<=(2), A, dims=2) +2×1 Array{Int64,2}: + 2 + 0 +``` +""" +count(A::AbstractArrayOrBroadcasted; dims=:) = count(identity, A, dims=dims) +count(f, A::AbstractArrayOrBroadcasted; dims=:) = _count(f, A, dims) + +_count(f, A::AbstractArrayOrBroadcasted, dims::Colon) = _simple_count(f, A) +_count(f, A::AbstractArrayOrBroadcasted, dims) = mapreduce(_bool(f), add_sum, A, dims=dims, init=0) + +""" + count!([f=identity,] r, A) + +Count the number of elements in `A` for which `f` returns `true` over the +singleton dimensions of `r`, writing the result into `r` in-place. + +!!! compat "Julia 1.5" + inplace `count!` was added in Julia 1.5. + +# Examples +```jldoctest +julia> A = [1 2; 3 4] +2×2 Array{Int64,2}: + 1 2 + 3 4 + +julia> count!(<=(2), [1 1], A) +1×2 Array{Int64,2}: + 1 1 + +julia> count!(<=(2), [1; 1], A) +2-element Array{Int64,1}: + 2 + 0 +``` +""" +count!(r::AbstractArray, A::AbstractArrayOrBroadcasted; init::Bool=true) = count!(identity, r, A; init=init) +count!(f, r::AbstractArray, A::AbstractArrayOrBroadcasted; init::Bool=true) = + mapreducedim!(_bool(f), add_sum, initarray!(r, add_sum, init, A), A) + +""" + 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..08c243e --- /dev/null +++ b/base/reflection.jl @@ -0,0 +1,1383 @@ +# 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 + npointers::UInt32 + firstptr::Int32 + alignment::UInt16 + flags::UInt16 + # haspadding : 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) +end + +function uniontype_layout(T::Type) + sz = RefValue{Csize_t}(0) + algn = RefValue{Csize_t}(0) + isinline = ccall(:jl_islayout_inline, Cint, (Any, Ptr{Csize_t}, Ptr{Csize_t}), T, sz, algn) != 0 + (isinline, sz[], algn[]) +end + +# amount of total space taken by T when stored in a container +function aligned_sizeof(T) + @_pure_meta + if isbitsunion(T) + _, sz, al = uniontype_layout(T) + 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()) + flags = unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).flags + return flags & 1 == 1 +end + +""" + Base.datatype_nfields(dt::DataType) -> Bool + +Return the number of fields known to this datatype's layout. +Can be called on any `isconcretetype`. +""" +function datatype_nfields(dt::DataType) + @_pure_meta + dt.layout == C_NULL && throw(UndefRefError()) + return unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).nfields +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()) + npointers = unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).npointers + return npointers == 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()) + flags = unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).flags + return (flags >> 1) & 3 +end + +""" + ismutable(v) -> Bool + +Return `true` iff value `v` is mutable. 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> ismutable(1) +false + +julia> ismutable([1,2]) +true +``` + +!!! compat "Julia 1.5" + This function requires at least Julia 1.5. +""" +ismutable(@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)) + +morespecific(@nospecialize(a), @nospecialize(b)) = ccall(:jl_type_morespecific, Cint, (Any, Any), a, b) != 0 + +""" + 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_ir(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], [module]) + +Return the method table for `f`. + +If `types` is specified, return an array of methods whose types match. +If `module` is specified, return an array of methods defined in that module. +A list of modules can also be specified as an array. + +!!! compat "Julia 1.4" + At least Julia 1.4 is required for specifying a module. +""" +function methods(@nospecialize(f), @nospecialize(t), + @nospecialize(mod::Union{Module,AbstractArray{Module},Nothing}=nothing)) + if mod isa Module + mod = (mod,) + end + if isa(f, Core.Builtin) + throw(ArgumentError("argument is not a generic function")) + end + t = to_tuple_type(t) + world = typemax(UInt) + MethodList(Method[m[3] for m in _methods(f, t, -1, world) if mod === nothing || m[3].module in mod], + 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), + @nospecialize(mod::Union{Module,AbstractArray{Module},Nothing}=nothing)) + # return all matches + return methods(f, Tuple{Vararg{Any}}, mod) +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 2:2:length(e) + isassigned(e, i) && visit(f, e[i]) + end + end + if mc.arg1 !== nothing + e = mc.arg1::Vector{Any} + for i in 2:2: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_ir(m::Method) = isdefined(m, :source) ? _uncompressed_ir(m, m.source) : + isdefined(m, :generator) ? error("Method is @generated; try `code_lowered` instead.") : + error("Code for this Method is not available.") +_uncompressed_ir(m::Method, s::CodeInfo) = copy(s) +_uncompressed_ir(m::Method, s::Array{UInt8,1}) = ccall(:jl_uncompress_ir, Any, (Any, Ptr{Cvoid}, Any), m, C_NULL, s)::CodeInfo +_uncompressed_ir(ci::Core.CodeInstance, s::Array{UInt8,1}) = ccall(:jl_uncompress_ir, Any, (Any, Any, Any), ci.def.def::Method, ci, s)::CodeInfo +# for backwards compat +const uncompressed_ast = uncompressed_ir +const _uncompressed_ast = _uncompressed_ir + +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 + 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 + + function CodegenParams(; 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) + return new( + 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 +end + +const SLOT_USED = 0x8 +ast_slotflag(@nospecialize(code), i) = ccall(:jl_ir_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_ir_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 + +""" + 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 + +function nameof(f::Core.IntrinsicFunction) + name = ccall(:jl_intrinsic_name, Ptr{UInt8}, (Core.IntrinsicFunction,), f) + return ccall(:jl_symbol, Ref{Symbol}, (Ptr{UInt8},), 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) + 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..0a67a8b --- /dev/null +++ b/base/refpointer.jl @@ -0,0 +1,156 @@ +# 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 +`Ref` is sometimes used in broadcasting in order to treat the referenced values 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 allocatedinline(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) + +# pass NTuple{N,T} as Ptr{T}/Ref{T} +cconvert(::Type{Ref{T}}, t::NTuple{N,T}) where {N,T} = Ref{NTuple{N,T}}(t) +cconvert(::Type{Ref{T}}, r::Ref{NTuple{N,T}}) where {N,T} = r +unsafe_convert(::Type{Ref{T}}, r::Ref{NTuple{N,T}}) where {N,T} = + convert(Ptr{T}, unsafe_convert(Ptr{NTuple{N,T}}, r)) +unsafe_convert(::Type{Ptr{T}}, r::Ref{NTuple{N,T}}) where {N,T} = + convert(Ptr{T}, unsafe_convert(Ptr{NTuple{N,T}}, r)) +unsafe_convert(::Type{Ptr{T}}, r::Ptr{NTuple{N,T}}) where {N,T} = + convert(Ptr{T}, r) + +### + +getindex(b::RefArray) = b.x[b.i] +setindex!(b::RefArray, x) = (b.x[b.i] = x; b) + +### + +""" + LLVMPtr{T, AS} + +A pointer type that more closely resembles LLVM semantics: It includes the pointer address +space, and will be passed as an actual pointer instead of an integer. + +This type is mainly used to interface with code that has strict requirements about pointers, +e.g., intrinsics that are selected based on the address space, or back-ends that require +pointers to be identifiable by their types. +""" +Core.LLVMPtr diff --git a/base/refvalue.jl b/base/refvalue.jl new file mode 100644 index 0000000..6803ef8 --- /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 allocatedinline(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..1c94e24 --- /dev/null +++ b/base/regex.jl @@ -0,0 +1,729 @@ +# 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. + +# Examples +```jldoctest +julia> findall("a", "apple") +1-element Array{UnitRange{Int64},1}: + 1:1 + +julia> findall("nana", "banana") +1-element Array{UnitRange{Int64},1}: + 3:6 + +julia> findall("a", "banana") +3-element Array{UnitRange{Int64},1}: + 2:2 + 4:4 + 6:6 +``` +""" +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..c74efcc --- /dev/null +++ b/base/reinterpretarray.jl @@ -0,0 +1,328 @@ +# 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 + +reinterpret(::Type{T}, a::ReinterpretArray) where {T} = reinterpret(T, a.parent) + +# 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, ReshapedUnitRange, 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..2a50c32 --- /dev/null +++ b/base/reshapedarray.jl @@ -0,0 +1,293 @@ +# 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)) + +# Add a few handy specializations to further speed up views of reshaped ranges +const ReshapedUnitRange{T,N,A<:AbstractUnitRange} = ReshapedArray{T,N,A,Tuple{}} +viewindexing(I::Tuple{Slice, ReshapedUnitRange, Vararg{ScalarIndex}}) = IndexLinear() +viewindexing(I::Tuple{ReshapedRange, Vararg{ScalarIndex}}) = IndexLinear() +compute_stride1(s, inds, I::Tuple{ReshapedRange, Vararg{Any}}) = s*step(I[1].parent) +compute_offset1(parent::AbstractVector, stride1::Integer, I::Tuple{ReshapedRange}) = + (@_inline_meta; first(I[1]) - first(axes1(I[1]))*stride1) +substrides(strds::NTuple{N,Int}, I::Tuple{ReshapedUnitRange, Vararg{Any}}) where N = + (size_to_strides(strds[1], size(I[1])...)..., substrides(tail(strds), tail(I))...) +unsafe_convert(::Type{Ptr{T}}, V::SubArray{T,N,P,<:Tuple{Vararg{Union{RangeIndex,ReshapedUnitRange}}}}) where {T,N,P} = + unsafe_convert(Ptr{T}, V.parent) + (first_index(V)-1)*sizeof(T) 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/ryu/LICENSE.md b/base/ryu/LICENSE.md new file mode 100644 index 0000000..74c7186 --- /dev/null +++ b/base/ryu/LICENSE.md @@ -0,0 +1,25 @@ +The code in this directory (base/ryu) is a derivative based on the work in the https://github.com/ulfjack/ryu repository, which allows the use of the Boost software license, included below. + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/base/ryu/Ryu.jl b/base/ryu/Ryu.jl new file mode 100644 index 0000000..6260023 --- /dev/null +++ b/base/ryu/Ryu.jl @@ -0,0 +1,130 @@ +module Ryu + +import .Base: significand_bits, significand_mask, exponent_bits, exponent_mask, exponent_bias, exponent_max, uinttype + +include("utils.jl") +include("shortest.jl") +include("fixed.jl") +include("exp.jl") + +""" + Ryu.neededdigits(T) + +Number of digits necessary to represent type `T` in fixed-precision decimal. +""" +neededdigits(::Type{Float64}) = 309 + 17 +neededdigits(::Type{Float32}) = 39 + 9 + 2 +neededdigits(::Type{Float16}) = 9 + 5 + 9 + +""" + Ryu.writeshortest(x, plus=false, space=false, hash=true, precision=-1, expchar=UInt8('e'), padexp=false, decchar=UInt8('.'), typed=false, compact=false) + Ryu.writeshortest(buf::Vector{UInt8}, pos::Int, x, args...) + +Convert a float value `x` into its "shortest" decimal string, which can be parsed back to the same value. +This function allows achieving the `%g` printf format. +Note the 2nd method allows passing in a byte buffer and position directly; callers must ensure the buffer has sufficient room to hold the entire decimal string. + +Various options for the output format include: + * `plus`: for positive `x`, prefix decimal string with a `'+'` character + * `space`: for positive `x`, prefix decimal string with a `' '` character; overridden if `plus=true` + * `hash`: whether the decimal point should be written, even if no additional digits are needed for precision + * `precision`: minimum number of digits to be included in the decimal string; extra `'0'` characters will be added for padding if necessary + * `expchar`: character to use exponent component in scientific notation + * `padexp`: whether two digits should always be written, even for single-digit exponents (e.g. `e+1` becomes `e+01`) + * `decchar`: decimal point character to be used + * `typed`: whether additional type information should be printed for `Float16` / `Float32` + * `compact`: output will be limited to 6 significant digits +""" +function writeshortest(x::T, + plus::Bool=false, + space::Bool=false, + hash::Bool=true, + precision::Integer=-1, + expchar::UInt8=UInt8('e'), + padexp::Bool=false, + decchar::UInt8=UInt8('.'), + typed::Bool=false, + compact::Bool=false) where {T <: Base.IEEEFloat} + buf = Base.StringVector(neededdigits(T)) + pos = writeshortest(buf, 1, x, plus, space, hash, precision, expchar, padexp, decchar, typed, compact) + return String(resize!(buf, pos - 1)) +end + +""" + Ryu.writefixed(x, precision, plus=false, space=false, hash=false, decchar=UInt8('.'), trimtrailingzeros=false) + Ryu.writefixed(buf::Vector{UInt8}, pos::Int, x, args...) + +Convert a float value `x` into a "fixed" size decimal string of the provided precision. +This function allows achieving the `%f` printf format. +Note the 2nd method allows passing in a byte buffer and position directly; callers must ensure the buffer has sufficient room to hold the entire decimal string. + +Various options for the output format include: + * `plus`: for positive `x`, prefix decimal string with a `'+'` character + * `space`: for positive `x`, prefix decimal string with a `' '` character; overridden if `plus=true` + * `hash`: whether the decimal point should be written, even if no additional digits are needed for precision + * `precision`: minimum number of significant digits to be included in the decimal string; extra `'0'` characters will be added for padding if necessary + * `decchar`: decimal point character to be used + * `trimtrailingzeros`: whether trailing zeros should be removed +""" +function writefixed(x::T, + precision::Integer, + plus::Bool=false, + space::Bool=false, + hash::Bool=false, + decchar::UInt8=UInt8('.'), + trimtrailingzeros::Bool=false) where {T <: Base.IEEEFloat} + buf = Base.StringVector(precision + neededdigits(T)) + pos = writefixed(buf, 1, x, precision, plus, space, hash, decchar, trimtrailingzeros) + return String(resize!(buf, pos - 1)) +end + +""" + Ryu.writeexp(x, precision, plus=false, space=false, hash=false, expchar=UInt8('e'), decchar=UInt8('.'), trimtrailingzeros=false) + Ryu.writeexp(buf::Vector{UInt8}, pos::Int, x, args...) + +Convert a float value `x` into a scientific notation decimal string. +This function allows achieving the `%e` printf format. +Note the 2nd method allows passing in a byte buffer and position directly; callers must ensure the buffer has sufficient room to hold the entire decimal string. + +Various options for the output format include: + * `plus`: for positive `x`, prefix decimal string with a `'+'` character + * `space`: for positive `x`, prefix decimal string with a `' '` character; overridden if `plus=true` + * `hash`: whether the decimal point should be written, even if no additional digits are needed for precision + * `precision`: minimum number of significant digits to be included in the decimal string; extra `'0'` characters will be added for padding if necessary + * `expchar`: character to use exponent component in scientific notation + * `decchar`: decimal point character to be used + * `trimtrailingzeros`: whether trailing zeros should be removed +""" +function writeexp(x::T, + precision::Integer, + plus::Bool=false, + space::Bool=false, + hash::Bool=false, + expchar::UInt8=UInt8('e'), + decchar::UInt8=UInt8('.'), + trimtrailingzeros::Bool=false) where {T <: Base.IEEEFloat} + buf = Base.StringVector(precision + neededdigits(T)) + pos = writeexp(buf, 1, x, precision, plus, space, hash, expchar, decchar, trimtrailingzeros) + return String(resize!(buf, pos - 1)) +end + +function Base.show(io::IO, x::T, forceuntyped::Bool=false, fromprint::Bool=false) where {T <: Base.IEEEFloat} + compact = get(io, :compact, false) + buf = Base.StringVector(neededdigits(T)) + typed = !forceuntyped && !compact && get(io, :typeinfo, Any) != typeof(x) + pos = writeshortest(buf, 1, x, false, false, true, -1, + (x isa Float32 && !fromprint) ? UInt8('f') : UInt8('e'), false, UInt8('.'), typed, compact) + write(io, resize!(buf, pos - 1)) + return +end + +function Base.string(x::T) where {T <: Base.IEEEFloat} + buf = Base.StringVector(neededdigits(T)) + pos = writeshortest(buf, 1, x, false, false, true, -1, + UInt8('e'), false, UInt8('.'), false, false) + return String(resize!(buf, pos - 1)) +end + +Base.print(io::IO, x::Union{Float16, Float32}) = show(io, x, true, true) + +end # module diff --git a/base/ryu/exp.jl b/base/ryu/exp.jl new file mode 100644 index 0000000..a083296 --- /dev/null +++ b/base/ryu/exp.jl @@ -0,0 +1,264 @@ +@inline function writeexp(buf, pos, v::T, + precision=-1, plus=false, space=false, hash=false, + expchar=UInt8('e'), decchar=UInt8('.'), trimtrailingzeros=false) where {T <: Base.IEEEFloat} + @assert 0 < pos <= length(buf) + x = Float64(v) + neg = signbit(x) + # special cases + if x == 0 + if neg + buf[pos] = UInt8('-') + pos += 1 + elseif plus + buf[pos] = UInt8('+') + pos += 1 + elseif space + buf[pos] = UInt8(' ') + pos += 1 + end + buf[pos] = UInt8('0') + pos += 1 + if precision > 0 + buf[pos] = decchar + pos += 1 + for _ = 1:precision + buf[pos] = UInt8('0') + pos += 1 + end + elseif hash + buf[pos] = decchar + pos += 1 + end + buf[pos] = expchar + buf[pos + 1] = UInt8('+') + buf[pos + 2] = UInt8('0') + buf[pos + 3] = UInt8('0') + return pos + 4 + elseif isnan(x) + buf[pos] = UInt8('N') + buf[pos + 1] = UInt8('a') + buf[pos + 2] = UInt8('N') + return pos + 3 + elseif !isfinite(x) + if neg + buf[pos] = UInt8('-') + pos += 1 + elseif plus + buf[pos] = UInt8('+') + pos += 1 + elseif space + buf[pos] = UInt8(' ') + pos += 1 + end + buf[pos] = UInt8('I') + buf[pos + 1] = UInt8('n') + buf[pos + 2] = UInt8('f') + return pos + 3 + end + + bits = Core.bitcast(UInt64, x) + mant = bits & MANTISSA_MASK + exp = Int((bits >> 52) & EXP_MASK) + + if exp == 0 + e2 = 1 - 1023 - 52 + m2 = mant + else + e2 = exp - 1023 - 52 + m2 = (Int64(1) << 52) | mant + end + nonzero = false + precision += 1 + if neg + buf[pos] = UInt8('-') + pos += 1 + elseif plus + buf[pos] = UInt8('+') + pos += 1 + elseif space + buf[pos] = UInt8(' ') + pos += 1 + end + digits = 0 + printedDigits = 0 + availableDigits = 0 + e = 0 + if e2 >= -52 + idx = e2 < 0 ? 0 : indexforexp(e2) + p10bits = pow10bitsforindex(idx) + len = lengthforindex(idx) + i = len - 1 + while i >= 0 + j = p10bits - e2 + #=@inbounds=# mula, mulb, mulc = POW10_SPLIT[POW10_OFFSET[idx + 1] + i + 1] + digits = mulshiftmod1e9(m2 << 8, mula, mulb, mulc, j + 8) + if printedDigits != 0 + if printedDigits + 9 > precision + availableDigits = 9 + break + end + pos = append_nine_digits(digits, buf, pos) + printedDigits += 9 + elseif digits != 0 + availableDigits = decimallength(digits) + e = i * 9 + availableDigits - 1 + if availableDigits > precision + break + end + if precision > 1 + pos = append_d_digits(availableDigits, digits, buf, pos, decchar) + else + buf[pos] = UInt8('0') + digits + pos += 1 + if hash + buf[pos] = decchar + pos += 1 + end + end + printedDigits = availableDigits + availableDigits = 0 + end + i -= 1 + end + end + if e2 < 0 && availableDigits == 0 + idx = div(-e2, 16) + i = MIN_BLOCK_2[idx + 1] + while i < 200 + j = 120 + (-e2 - 16 * idx) + p = POW10_OFFSET_2[idx + 1] + i - MIN_BLOCK_2[idx + 1] + if p >= POW10_OFFSET_2[idx + 2] + digits = 0 + else + #=@inbounds=# mula, mulb, mulc = POW10_SPLIT_2[p + 1] + digits = mulshiftmod1e9(m2 << 8, mula, mulb, mulc, j + 8) + end + if printedDigits != 0 + if printedDigits + 9 > precision + availableDigits = 9 + break + end + pos = append_nine_digits(digits, buf, pos) + printedDigits += 9 + elseif digits != 0 + availableDigits = decimallength(digits) + e = -(i + 1) * 9 + availableDigits - 1 + if availableDigits > precision + break + end + if precision > 1 + pos = append_d_digits(availableDigits, digits, buf, pos, decchar) + else + buf[pos] = UInt8('0') + digits + pos += 1 + if hash + buf[pos] = decchar + pos += 1 + end + end + printedDigits = availableDigits + availableDigits = 0 + end + i += 1 + end + end + maximum = precision - printedDigits + if availableDigits == 0 + digits = 0 + end + lastDigit = 0 + if availableDigits > maximum + for k = 0:(availableDigits - maximum - 1) + lastDigit = digits % 10 + digits = div(digits, 10) + end + end + roundUp = 0 + if lastDigit != 5 + roundUp = lastDigit > 5 + else + rexp = precision - e + requiredTwos = -e2 - rexp + trailingZeros = requiredTwos <= 0 || + (requiredTwos < 60 && pow2(m2, requiredTwos)) + if rexp < 0 + requiredFives = -rexp + trailingZeros = trailingZeros & pow5(m2, requiredFives) + end + roundUp = trailingZeros ? 2 : 1 + end + if printedDigits != 0 + if digits == 0 + for _ = 1:maximum + buf[pos] = UInt8('0') + pos += 1 + end + else + pos = append_c_digits(maximum, digits, buf, pos) + end + else + if precision > 1 + pos = append_d_digits(maximum, digits, buf, pos, decchar) + else + buf[pos] = UInt8('0') + digits + pos += 1 + if hash + buf[pos] = decchar + pos += 1 + end + end + end + if roundUp != 0 + roundPos = pos + while true + roundPos -= 1 + if roundPos == 0 || buf[roundPos] == UInt8('-') + buf[roundPos + 1] = UInt8('1') + e += 1 + break + end + c = roundPos > 0 ? buf[roundPos] : 0x00 + if c == decchar + continue + elseif c == UInt8('9') + buf[roundPos] = UInt8('0') + roundUp = 1 + continue + else + if roundUp == 2 && UInt8(c) % 2 == 0 + break + end + buf[roundPos] = c + 1 + break + end + end + end + if trimtrailingzeros + while buf[pos - 1] == UInt8('0') + pos -= 1 + end + if buf[pos - 1] == decchar && !hash + pos -= 1 + end + end + buf[pos] = expchar + pos += 1 + if e < 0 + buf[pos] = UInt8('-') + pos += 1 + e = -e + else + buf[pos] = UInt8('+') + pos += 1 + end + if e >= 100 + c = e % 10 + unsafe_copyto!(buf, pos, DIGIT_TABLE, 2 * div(e, 10) + 1, 2) + buf[pos + 2] = UInt8('0') + c + pos += 3 + else + unsafe_copyto!(buf, pos, DIGIT_TABLE, 2 * e + 1, 2) + pos += 2 + end + return pos +end diff --git a/base/ryu/fixed.jl b/base/ryu/fixed.jl new file mode 100644 index 0000000..af2a2ab --- /dev/null +++ b/base/ryu/fixed.jl @@ -0,0 +1,210 @@ +@inline function writefixed(buf, pos, v::T, + precision=-1, plus=false, space=false, hash=false, + decchar=UInt8('.'), trimtrailingzeros=false) where {T <: Base.IEEEFloat} + @assert 0 < pos <= length(buf) + x = Float64(v) + neg = signbit(x) + # special cases + if x == 0 + if neg + buf[pos] = UInt8('-') + pos += 1 + elseif plus + buf[pos] = UInt8('+') + pos += 1 + elseif space + buf[pos] = UInt8(' ') + pos += 1 + end + buf[pos] = UInt8('0') + pos += 1 + if precision > 0 + buf[pos] = decchar + pos += 1 + if trimtrailingzeros + precision = 1 + end + for _ = 1:precision + buf[pos] = UInt8('0') + pos += 1 + end + elseif hash + buf[pos] = decchar + pos += 1 + end + return pos + elseif isnan(x) + buf[pos] = UInt8('N') + buf[pos + 1] = UInt8('a') + buf[pos + 2] = UInt8('N') + return pos + 3 + elseif !isfinite(x) + if neg + buf[pos] = UInt8('-') + pos += 1 + elseif plus + buf[pos] = UInt8('+') + pos += 1 + elseif space + buf[pos] = UInt8(' ') + pos += 1 + end + buf[pos] = UInt8('I') + buf[pos + 1] = UInt8('n') + buf[pos + 2] = UInt8('f') + return pos + 3 + end + + bits = Core.bitcast(UInt64, x) + mant = bits & MANTISSA_MASK + exp = Int((bits >> 52) & EXP_MASK) + + if exp == 0 + e2 = 1 - 1023 - 52 + m2 = mant + else + e2 = exp - 1023 - 52 + m2 = (Int64(1) << 52) | mant + end + nonzero = false + if neg + buf[pos] = UInt8('-') + pos += 1 + elseif plus + buf[pos] = UInt8('+') + pos += 1 + elseif space + buf[pos] = UInt8(' ') + pos += 1 + end + if e2 >= -52 + idx = e2 < 0 ? 0 : indexforexp(e2) + p10bits = pow10bitsforindex(idx) + len = lengthforindex(idx) + i = len - 1 + while i >= 0 + j = p10bits - e2 + #=@inbounds=# mula, mulb, mulc = POW10_SPLIT[POW10_OFFSET[idx + 1] + i + 1] + digits = mulshiftmod1e9(m2 << 8, mula, mulb, mulc, j + 8) + if nonzero + pos = append_nine_digits(digits, buf, pos) + elseif digits != 0 + olength = decimallength(digits) + pos = append_n_digits(olength, digits, buf, pos) + nonzero = true + end + i -= 1 + end + end + if !nonzero + buf[pos] = UInt8('0') + pos += 1 + end + if precision > 0 || hash + buf[pos] = decchar + pos += 1 + end + if e2 < 0 + idx = div(-e2, 16) + blocks = div(precision, 9) + 1 + roundUp = 0 + i = 0 + if blocks <= MIN_BLOCK_2[idx + 1] + i = blocks + for _ = 1:precision + buf[pos] = UInt8('0') + pos += 1 + end + elseif i < MIN_BLOCK_2[idx + 1] + i = MIN_BLOCK_2[idx + 1] + for _ = 1:(9 * i) + buf[pos] = UInt8('0') + pos += 1 + end + end + while i < blocks + j = 120 + (-e2 - 16 * idx) + p = POW10_OFFSET_2[idx + 1] + UInt32(i) - MIN_BLOCK_2[idx + 1] + if p >= POW10_OFFSET_2[idx + 2] + for _ = 1:(precision - 9 * i) + buf[pos] = UInt8('0') + pos += 1 + end + break + end + #=@inbounds=# mula, mulb, mulc = POW10_SPLIT_2[p + 1] + digits = mulshiftmod1e9(m2 << 8, mula, mulb, mulc, j + 8) + if i < blocks - 1 + pos = append_nine_digits(digits, buf, pos) + else + maximum = precision - 9 * i + lastDigit = 0 + k = 0 + while k < 9 - maximum + # global digits, lastDigit, k + lastDigit = digits % 10 + digits = div(digits, 10) + k += 1 + end + if lastDigit != 5 + roundUp = lastDigit > 5 + else + requiredTwos = -e2 - precision - 1 + trailingZeros = requiredTwos <= 0 || (requiredTwos < 60 && pow2(m2, requiredTwos)) + roundUp = trailingZeros ? 2 : 1 + end + if maximum > 0 + pos = append_c_digits(maximum, digits, buf, pos) + end + break + end + i += 1 + end + if roundUp != 0 + roundPos = pos + dotPos = 1 + while true + roundPos -= 1 + if roundPos == 0 || (buf[roundPos] == UInt8('-')) + buf[roundPos + 1] = UInt8('1') + if dotPos > 1 + buf[dotPos] = UInt8('0') + buf[dotPos + 1] = decchar + end + buf[pos] = UInt8('0') + pos += 1 + break + end + c = roundPos > 0 ? buf[roundPos] : 0x00 + if c == decchar + dotPos = roundPos + continue + elseif c == UInt8('9') + buf[roundPos] = UInt8('0') + roundUp = 1 + continue + else + if roundUp == 2 && UInt8(c) % 2 == 0 + break + end + buf[roundPos] = c + 1 + break + end + end + end + else + for _ = 1:precision + buf[pos] = UInt8('0') + pos += 1 + end + end + if trimtrailingzeros + while buf[pos - 1] == UInt8('0') + pos -= 1 + end + if buf[pos - 1] == decchar && !hash + pos -= 1 + end + end + return pos +end diff --git a/base/ryu/shortest.jl b/base/ryu/shortest.jl new file mode 100644 index 0000000..e1463ca --- /dev/null +++ b/base/ryu/shortest.jl @@ -0,0 +1,496 @@ +""" + b, e10 = reduce_shortest(f[, maxsignif]) + +Reduce to shortest decimal representation where `abs(f) == b * 10^e10` and `b` is an +integer. If a `maxsignif` argument is provided, then `b < maxsignif`. +""" +@inline function reduce_shortest(f::T, maxsignif=nothing) where {T} + U = uinttype(T) + uf = reinterpret(U, f) + m = uf & significand_mask(T) + e = ((uf & exponent_mask(T)) >> significand_bits(T)) % Int + + ## Step 1 + # mf * 2^ef == f + mf = (one(U) << significand_bits(T)) | m + ef = e - exponent_bias(T) - significand_bits(T) + f_isinteger = mf & ((one(U) << -ef) - one(U)) == 0 + + if ef > 0 || ef < -Base.significand_bits(T) || !f_isinteger + # fixup subnormals + if e == 0 + ef = 1 - exponent_bias(T) - significand_bits(T) + mf = m + end + + ## Step 2 + # u * 2^e2 == (f + prevfloat(f))/2 + # v * 2^e2 == f + # w * 2^e2 == (f + nextfloat(f))/2 + e2 = ef - 2 + mf_iseven = iseven(mf) # trailing bit of significand is zero + + v = U(4) * mf + w = v + U(2) + u_shift_half = m == 0 && e > 1 # if first element of binade, other than first normal one + u = v - U(2) + u_shift_half + + ## Step 3 + # a == floor(u * 2^e2 / 10^e10), exact if a_allzero + # b == floor(v * 2^e2 / 10^e10), exact if b_allzero + # c == floor(w * 2^e2 / 10^e10) + a_allzero = false + b_allzero = false + b_lastdigit = 0x00 + if e2 >= 0 + q = log10pow2(e2) - (T == Float64 ? (e2 > 3) : 0) + e10 = q + k = pow5_inv_bitcount(T) + pow5bits(q) - 1 + i = -e2 + q + k + a, b, c = mulshiftinvsplit(T, u, v, w, q, i) + if T == Float32 || T == Float16 + if q != 0 && div(c - 1, 10) <= div(a, 10) + l = pow5_inv_bitcount(T) + pow5bits(q - 1) - 1 + mul = pow5invsplit_lookup(T, q-1) + b_lastdigit = (mulshift(v, mul, -e2 + q - 1 + l) % 10) % UInt8 + end + end + if q <= qinvbound(T) + if ((v % UInt32) - 5 * div(v, 5)) == 0 + b_allzero = pow5(v, q) + elseif mf_iseven + a_allzero = pow5(u, q) + else + c -= pow5(w, q) + end + end + else + q = log10pow5(-e2) - (T == Float64 ? (-e2 > 1) : 0) + e10 = q + e2 + i = -e2 - q + k = pow5bits(i) - pow5_bitcount(T) + j = q - k + a, b, c = mulshiftsplit(T, u, v, w, i, j) + if T == Float32 || T == Float16 + if q != 0 && div(c - 1, 10) <= div(a, 10) + j = q - 1 - (pow5bits(i + 1) - pow5_bitcount(T)) + mul = pow5split_lookup(T, i+1) + b_lastdigit = (mulshift(v, mul, j) % 10) % UInt8 + end + end + if q <= 1 + b_allzero = true + if mf_iseven + a_allzero = !u_shift_half + else + c -= 1 + end + elseif q < qbound(T) + b_allzero = pow2(v, q - (T != Float64)) + end + end + + ## Step 4: reduction + if a_allzero || b_allzero + # a) slow loop + while true + c_div10 = div(c, 10) + a_div10 = div(a, 10) + if c_div10 <= a_div10 + break + end + a_mod10 = (a % UInt32) - UInt32(10) * (a_div10 % UInt32) + b_div10 = div(b, 10) + b_mod10 = (b % UInt32) - UInt32(10) * (b_div10 % UInt32) + a_allzero &= a_mod10 == 0 + b_allzero &= b_lastdigit == 0 + b_lastdigit = b_mod10 % UInt8 + b = b_div10 + c = c_div10 + a = a_div10 + e10 += 1 + end + if a_allzero + while true + a_div10 = div(a, 10) + a_mod10 = (a % UInt32) - UInt32(10) * (a_div10 % UInt32) + if a_mod10 != 0 && (maxsignif === nothing || b < maxsignif) + break + end + c_div10 = div(c, 10) + b_div10 = div(b, 10) + b_mod10 = (b % UInt32) - UInt32(10) * (b_div10 % UInt32) + b_allzero &= b_lastdigit == 0 + b_lastdigit = b_mod10 % UInt8 + b = b_div10 + c = c_div10 + a = a_div10 + e10 += 1 + end + end + if b_allzero && b_lastdigit == 5 && iseven(b) + b_lastdigit = UInt8(4) + end + roundup = (b == a && (!mf_iseven || !a_allzero)) || b_lastdigit >= 5 + else + # b) specialized for common case (99% Float64, 96% Float32) + roundup = b_lastdigit >= 5 + c_div100 = div(c, 100) + a_div100 = div(a, 100) + if c_div100 > a_div100 + b_div100 = div(b, 100) + b_mod100 = (b % UInt32) - UInt32(100) * (b_div100 % UInt32) + roundup = b_mod100 >= 50 + b = b_div100 + c = c_div100 + a = a_div100 + e10 += 2 + end + while true + c_div10 = div(c, 10) + a_div10 = div(a, 10) + if c_div10 <= a_div10 + break + end + b_div10 = div(b, 10) + b_mod10 = (b % UInt32) - UInt32(10) * (b_div10 % UInt32) + roundup = b_mod10 >= 5 + b = b_div10 + c = c_div10 + a = a_div10 + e10 += 1 + end + roundup = (b == a || roundup) + end + if maxsignif !== nothing && b > maxsignif + # reduce to max significant digits + while true + b_div10 = div(b, 10) + b_mod10 = (b % UInt32) - UInt32(10) * (b_div10 % UInt32) + if b <= maxsignif + break + end + b = b_div10 + roundup = (b_allzero && iseven(b)) ? b_mod10 > 5 : b_mod10 >= 5 + b_allzero &= b_mod10 == 0 + e10 += 1 + end + b = b + roundup + + # remove trailing zeros + while true + b_div10 = div(b, 10) + b_mod10 = (b % UInt32) - UInt32(10) * (b_div10 % UInt32) + if b_mod10 != 0 + break + end + b = b_div10 + e10 += 1 + end + else + b = b + roundup + end + else + # c) specialized f an integer < 2^53 + b = mf >> -ef + e10 = 0 + + if maxsignif !== nothing && b > maxsignif + b_allzero = true + # reduce to max significant digits + while true + b_div10 = div(b, 10) + b_mod10 = (b % UInt32) - UInt32(10) * (b_div10 % UInt32) + if b <= maxsignif + break + end + b = b_div10 + roundup = (b_allzero && iseven(b)) ? b_mod10 > 5 : b_mod10 >= 5 + b_allzero &= b_mod10 == 0 + e10 += 1 + end + b = b + roundup + end + while true + b_div10 = div(b, 10) + b_mod10 = (b % UInt32) - UInt32(10) * (b_div10 % UInt32) + if b_mod10 != 0 + break + end + b = b_div10 + e10 += 1 + end + end + return b, e10 +end + + +@inline function writeshortest(buf::Vector{UInt8}, pos, x::T, + plus=false, space=false, hash=true, + precision=-1, expchar=UInt8('e'), padexp=false, decchar=UInt8('.'), + typed=false, compact=false) where {T} + @assert 0 < pos <= length(buf) + neg = signbit(x) + # special cases + if x == 0 + if neg + buf[pos] = UInt8('-') + pos += 1 + elseif plus + buf[pos] = UInt8('+') + pos += 1 + elseif space + buf[pos] = UInt8(' ') + pos += 1 + end + buf[pos] = UInt8('0') + pos += 1 + if hash + buf[pos] = decchar + pos += 1 + end + if precision == -1 + buf[pos] = UInt8('0') + pos += 1 + if typed && x isa Float32 + buf[pos] = UInt8('f') + buf[pos + 1] = UInt8('0') + pos += 2 + end + return pos + end + while precision > 1 + buf[pos] = UInt8('0') + pos += 1 + precision -= 1 + end + if typed && x isa Float32 + buf[pos] = UInt8('f') + buf[pos + 1] = UInt8('0') + pos += 2 + end + return pos + elseif isnan(x) + buf[pos] = UInt8('N') + buf[pos + 1] = UInt8('a') + buf[pos + 2] = UInt8('N') + if typed + if x isa Float32 + buf[pos + 3] = UInt8('3') + buf[pos + 4] = UInt8('2') + elseif x isa Float16 + buf[pos + 3] = UInt8('1') + buf[pos + 4] = UInt8('6') + end + end + return pos + 3 + (typed && x isa Union{Float32, Float16} ? 2 : 0) + elseif !isfinite(x) + if neg + buf[pos] = UInt8('-') + end + buf[pos + neg] = UInt8('I') + buf[pos + neg + 1] = UInt8('n') + buf[pos + neg + 2] = UInt8('f') + if typed + if x isa Float32 + buf[pos + neg + 3] = UInt8('3') + buf[pos + neg + 4] = UInt8('2') + elseif x isa Float16 + buf[pos + neg + 3] = UInt8('1') + buf[pos + neg + 4] = UInt8('6') + end + end + return pos + neg + 3 + (typed && x isa Union{Float32, Float16} ? 2 : 0) + end + + output, nexp = reduce_shortest(x, compact ? 999_999 : nothing) + + if typed && x isa Float16 + buf[pos] = UInt8('F') + buf[pos + 1] = UInt8('l') + buf[pos + 2] = UInt8('o') + buf[pos + 3] = UInt8('a') + buf[pos + 4] = UInt8('t') + buf[pos + 5] = UInt8('1') + buf[pos + 6] = UInt8('6') + buf[pos + 7] = UInt8('(') + pos += 8 + end + if neg + buf[pos] = UInt8('-') + pos += 1 + elseif plus + buf[pos] = UInt8('+') + pos += 1 + elseif space + buf[pos] = UInt8(' ') + pos += 1 + end + + olength = decimallength(output) + exp_form = true + pt = nexp + olength + if -4 < pt <= (precision == -1 ? (T == Float16 ? 3 : 6) : precision) && + !(pt >= olength && abs(mod(x + 0.05, 10^(pt - olength)) - 0.05) > 0.05) + exp_form = false + if pt <= 0 + buf[pos] = UInt8('0') + pos += 1 + buf[pos] = decchar + pos += 1 + for _ = 1:abs(pt) + buf[pos] = UInt8('0') + pos += 1 + end + # elseif pt >= olength + # nothing to do at this point + # else + # nothing to do at this point + end + else + pos += 1 + end + i = 0 + ptr = pointer(buf) + ptr2 = pointer(DIGIT_TABLE) + if (output >> 32) != 0 + q = output ÷ 100000000 + output2 = (output % UInt32) - UInt32(100000000) * (q % UInt32) + output = q + + c = output2 % UInt32(10000) + output2 = div(output2, UInt32(10000)) + d = output2 % UInt32(10000) + c0 = (c % 100) << 1 + c1 = (c ÷ 100) << 1 + d0 = (d % 100) << 1 + d1 = (d ÷ 100) << 1 + memcpy(ptr, pos + olength - 2, ptr2, c0 + 1, 2) + memcpy(ptr, pos + olength - 4, ptr2, c1 + 1, 2) + memcpy(ptr, pos + olength - 6, ptr2, d0 + 1, 2) + memcpy(ptr, pos + olength - 8, ptr2, d1 + 1, 2) + i += 8 + end + output2 = output % UInt32 + while output2 >= 10000 + c = output2 % UInt32(10000) + output2 = div(output2, UInt32(10000)) + c0 = (c % 100) << 1 + c1 = (c ÷ 100) << 1 + memcpy(ptr, pos + olength - i - 2, ptr2, c0 + 1, 2) + memcpy(ptr, pos + olength - i - 4, ptr2, c1 + 1, 2) + i += 4 + end + if output2 >= 100 + c = (output2 % UInt32(100)) << 1 + output2 = div(output2, UInt32(100)) + memcpy(ptr, pos + olength - i - 2, ptr2, c + 1, 2) + i += 2 + end + if output2 >= 10 + c = output2 << 1 + buf[pos + 1] = DIGIT_TABLE[c + 2] + buf[pos - exp_form] = DIGIT_TABLE[c + 1] + else + buf[pos - exp_form] = UInt8('0') + (output2 % UInt8) + end + + if !exp_form + if pt <= 0 + pos += olength + precision -= olength + while hash && precision > 0 + buf[pos] = UInt8('0') + pos += 1 + precision -= 1 + end + elseif pt >= olength + pos += olength + precision -= olength + for _ = 1:nexp + buf[pos] = UInt8('0') + pos += 1 + precision -= 1 + end + if hash + buf[pos] = decchar + pos += 1 + if precision < 0 + buf[pos] = UInt8('0') + pos += 1 + end + while precision > 0 + buf[pos] = UInt8('0') + pos += 1 + precision -= 1 + end + end + else + pointoff = olength - abs(nexp) + memmove(ptr, pos + pointoff + 1, ptr, pos + pointoff, olength - pointoff + 1) + buf[pos + pointoff] = decchar + pos += olength + 1 + precision -= olength + while hash && precision > 0 + buf[pos] = UInt8('0') + pos += 1 + precision -= 1 + end + end + if typed && x isa Float32 + buf[pos] = UInt8('f') + buf[pos + 1] = UInt8('0') + pos += 2 + end + else + if olength > 1 || hash + buf[pos] = decchar + pos += olength + precision -= olength + end + if hash && olength == 1 + buf[pos] = UInt8('0') + pos += 1 + end + while hash && precision > 0 + buf[pos] = UInt8('0') + pos += 1 + precision -= 1 + end + + buf[pos] = expchar + pos += 1 + exp2 = nexp + olength - 1 + if exp2 < 0 + buf[pos] = UInt8('-') + pos += 1 + exp2 = -exp2 + elseif padexp + buf[pos] = UInt8('+') + pos += 1 + end + + if exp2 >= 100 + c = exp2 % 10 + memcpy(ptr, pos, ptr2, 2 * div(exp2, 10) + 1, 2) + buf[pos + 2] = UInt8('0') + (c % UInt8) + pos += 3 + elseif exp2 >= 10 + memcpy(ptr, pos, ptr2, 2 * exp2 + 1, 2) + pos += 2 + else + if padexp + buf[pos] = UInt8('0') + pos += 1 + end + buf[pos] = UInt8('0') + (exp2 % UInt8) + pos += 1 + end + end + if typed && x isa Float16 + buf[pos] = UInt8(')') + pos += 1 + end + + return pos +end diff --git a/base/ryu/utils.jl b/base/ryu/utils.jl new file mode 100644 index 0000000..49b0856 --- /dev/null +++ b/base/ryu/utils.jl @@ -0,0 +1,410 @@ +const MANTISSA_MASK = Base.significand_mask(Float64) +const EXP_MASK = Base.exponent_mask(Float64) >> Base.significand_bits(Float64) + +memcpy(d, doff, s, soff, n) = (ccall(:memcpy, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), d + doff - 1, s + soff - 1, n); nothing) +memmove(d, doff, s, soff, n) = (ccall(:memmove, Ptr{Cvoid}, (Ptr{Cvoid}, Ptr{Cvoid}, Csize_t), d + doff - 1, s + soff - 1, n); nothing) + +# Note: these are smaller than the values given in Figure 4 from the paper +# see https://github.com/ulfjack/ryu/issues/119 +pow5_bitcount(::Type{Float16}) = 30 +pow5_bitcount(::Type{Float32}) = 61 +pow5_bitcount(::Type{Float64}) = 121 + +pow5_inv_bitcount(::Type{Float16}) = 30 +pow5_inv_bitcount(::Type{Float32}) = 59 +pow5_inv_bitcount(::Type{Float64}) = 122 + +qinvbound(::Type{Float16}) = 4 +qinvbound(::Type{Float32}) = 9 +qinvbound(::Type{Float64}) = 21 + +qbound(::Type{Float16}) = 15 +qbound(::Type{Float32}) = 31 +qbound(::Type{Float64}) = 63 + +""" + Ryu.log10pow2(e::Integer) + +Computes `floor(log10(2^e))`. This is valid for all `e < 1651`. +""" +log10pow2(e) = (e * 78913) >> 18 + + +""" + Ryu.log10pow5(e::Integer) + +Computes `floor(log10(5^e))`. This is valid for all `e < 2621`. +""" +log10pow5(e) = (e * 732923) >> 20 + +""" + Ryu.pow5bits(e) + +Computes `e == 0 ? 1 : ceil(log2(5^e))`. This is valid for `e < 3529` (if performend in `Int32` arithmetic). +""" +pow5bits(e) = ((e * 1217359) >> 19) + 1 + +"""" + Ryu.mulshift(m::U, mula, j) where {U<:Unsigned} + +Compute `(m * mul) >> j`, where `j >= 8*sizeof(U)`. The type of the results is the larger of `U` or `UInt32`. +""" +@inline function mulshift(m::U, mul, j) where {U<:Unsigned} + W = widen(U) + nbits = 8*sizeof(U) + return ((((W(m) * (mul % U)) >> nbits) + W(m) * (mul >> nbits)) >> (j - nbits)) % promote_type(U,UInt32) +end + +indexforexp(e) = div(e + 15, 16) +pow10bitsforindex(idx) = 16 * idx + 120 +lengthforindex(idx) = div(((Int64(16 * idx) * 1292913986) >> 32) + 1 + 16 + 8, 9) + +""" + Ryu.pow5(x, p) + +Return `true` if `5^p` is a divisor of `x`. +""" +@inline function pow5(x, p) + count = 0 + while true + q = div(x, 5) + r = x - 5 * q + r != 0 && return count >= p + x = q + count += 1 + end +end + +""" + Ryu.pow2(x, p) + +Return `true` if `2^p` is a divisor of `x`. In other words, if the trailing `p` bits of `x` are zero. +""" +pow2(x, p) = (x & ((Int64(1) << p) - 1)) == 0 + +""" + Ryu.decimallength(v) + +The number of decimal digits of the integer `v`. +""" +@inline function decimallength(v) + v >= 10000000000000000 && return 17 + v >= 1000000000000000 && return 16 + v >= 100000000000000 && return 15 + v >= 10000000000000 && return 14 + v >= 1000000000000 && return 13 + v >= 100000000000 && return 12 + v >= 10000000000 && return 11 + v >= 1000000000 && return 10 + v >= 100000000 && return 9 + v >= 10000000 && return 8 + v >= 1000000 && return 7 + v >= 100000 && return 6 + v >= 10000 && return 5 + v >= 1000 && return 4 + v >= 100 && return 3 + v >= 10 && return 2 + return 1 +end +@inline function decimallength(v::UInt32) + v >= 100000000 && return 9 + v >= 10000000 && return 8 + v >= 1000000 && return 7 + v >= 100000 && return 6 + v >= 10000 && return 5 + v >= 1000 && return 4 + v >= 100 && return 3 + v >= 10 && return 2 + return 1 +end +@inline function decimallength(v::UInt16) + v >= 10000 && return 5 + v >= 1000 && return 4 + v >= 100 && return 3 + v >= 10 && return 2 + return 1 +end + +@inline function mulshiftinvsplit(::Type{T}, mv, mp, mm, i, j) where {T} + mul = pow5invsplit_lookup(T, i) + vr = mulshift(mv, mul, j) + vp = mulshift(mp, mul, j) + vm = mulshift(mm, mul, j) + return vr, vp, vm +end + +@inline function mulshiftsplit(::Type{T}, mv, mp, mm, i, j) where {T} + mul = pow5split_lookup(T, i) + vr = mulshift(mv, mul, j) + vp = mulshift(mp, mul, j) + vm = mulshift(mm, mul, j) + return vr, vp, vm +end + +""" + Ryu.umul256(a::UInt128, bHi::UInt64, bLo::UInt64)::Tuple{UInt128, UInt128} + +Compute `p = a*b` where `b = bLo + bHi<<64`, returning the result as `pLo, pHi` where `p = pLo + pHi<<128`. +""" +@inline function umul256(a, bHi, bLo) + aLo = a % UInt64 + aHi = (a >> 64) % UInt64 + + b00 = UInt128(aLo) * bLo + b01 = UInt128(aLo) * bHi + b10 = UInt128(aHi) * bLo + b11 = UInt128(aHi) * bHi + + b00Lo = b00 % UInt64 + b00Hi = (b00 >> 64) % UInt64 + + mid1 = b10 + b00Hi + mid1Lo = mid1 % UInt64 + mid1Hi = (mid1 >> 64) % UInt64 + + mid2 = b01 + mid1Lo + mid2Lo = mid2 % UInt64 + mid2Hi = (mid2 >> 64) % UInt64 + + pHi = b11 + mid1Hi + mid2Hi + pLo = (UInt128(mid2Lo) << 64) | b00Lo + return pLo, pHi +end + +""" + Ryu.umul256_hi(a::UInt128, bHi::UInt64, bLo::UInt64)::UInt128 + +Compute `pHi = (a*b)>>128` where `b = bLo + bHi<<64`. +""" +@inline umul256_hi(a, bHi, bLo) = umul256(a, bHi, bLo)[2] + +""" + Ryu.mulshiftmod1e9(m, mula, mulb, mulc, j)::UInt32 + +Compute `(m * mul) >> j % 10^9` where `mul = mula + mulb<<64 + mulc<<128`, and `j >= 128`. +""" +@inline function mulshiftmod1e9(m, mula, mulb, mulc, j) + b0 = UInt128(m) * mula + b1 = UInt128(m) * mulb + b2 = UInt128(m) * mulc + mid = b1 + ((b0 >> 64) % UInt64) + s1 = b2 + ((mid >> 64) % UInt64) + v = s1 >> (j - 128) + multiplied = umul256_hi(v, 0x89705F4136B4A597, 0x31680A88F8953031) + shifted = (multiplied >> 29) % UInt32 + return (v % UInt32) - UInt32(1000000000) * shifted +end + +@inline function append_n_digits(olength, digits, buf, pos) + i = 0 + while digits >= 10000 + c = digits % 10000 + digits = div(digits, 10000) + c0 = (c % 100) << 1 + c1 = div(c, 100) << 1 + unsafe_copyto!(buf, pos + olength - i - 2, DIGIT_TABLE, c0 + 1, 2) + unsafe_copyto!(buf, pos + olength - i - 4, DIGIT_TABLE, c1 + 1, 2) + i += 4 + end + if digits >= 100 + c = (digits % 100) << 1 + digits = div(digits, 100) + unsafe_copyto!(buf, pos + olength - i - 2, DIGIT_TABLE, c + 1, 2) + i += 2 + end + if digits >= 10 + c = digits << 1 + unsafe_copyto!(buf, pos + olength - i - 2, DIGIT_TABLE, c + 1, 2) + i += 2 + else + buf[pos] = UInt8('0') + digits + i += 1 + end + return pos + i +end + +@inline function append_d_digits(olength, digits, buf, pos, decchar) + i = 0 + while digits >= 10000 + c = digits % 10000 + digits = div(digits, 10000) + c0 = (c % 100) << 1 + c1 = div(c, 100) << 1 + unsafe_copyto!(buf, pos + olength + 1 - i - 2, DIGIT_TABLE, c0 + 1, 2) + unsafe_copyto!(buf, pos + olength + 1 - i - 4, DIGIT_TABLE, c1 + 1, 2) + i += 4 + end + if digits >= 100 + c = (digits % 100) << 1 + digits = div(digits, 100) + unsafe_copyto!(buf, pos + olength + 1 - i - 2, DIGIT_TABLE, c + 1, 2) + i += 2 + end + if digits >= 10 + c = digits << 1 + buf[pos] = DIGIT_TABLE[c + 1] + buf[pos + 1] = decchar + buf[pos + 2] = DIGIT_TABLE[c + 2] + i += 3 + else + buf[pos] = UInt8('0') + digits + buf[pos + 1] = decchar + i += 2 + end + return pos + i +end + +@inline function append_c_digits(count, digits, buf, pos) + i = 0 + while i < count - 1 + c = (digits % 100) << 1 + digits = div(digits, 100) + unsafe_copyto!(buf, pos + count - i - 2, DIGIT_TABLE, c + 1, 2) + i += 2 + end + if i < count + buf[pos + count - i - 1] = UInt8('0') + (digits % 10) + i += 1 + end + return pos + i +end + +@inline function append_nine_digits(digits, buf, pos) + if digits == 0 + for _ = 1:9 + buf[pos] = UInt8('0') + pos += 1 + end + return pos + end + i = 0 + while i < 5 + c = digits % 10000 + digits = div(digits, 10000) + c0 = (c % 100) << 1 + c1 = div(c, 100) << 1 + unsafe_copyto!(buf, pos + 7 - i, DIGIT_TABLE, c0 + 1, 2) + unsafe_copyto!(buf, pos + 5 - i, DIGIT_TABLE, c1 + 1, 2) + i += 4 + end + buf[pos] = UInt8('0') + digits + i += 1 + return pos + i +end + +const BIG_MASK = (big(1) << 64) - 1 + +const POW10_SPLIT = collect(Iterators.flatten(map(0:63) do idx + pow10bits = pow10bitsforindex(idx) + map(0:lengthforindex(idx)-1) do i + v = (div(big(1) << pow10bits, big(10)^(9 * i)) + 1) % ((big(10)^9) << 136) + return (UInt64(v & BIG_MASK), UInt64((v >> 64) & BIG_MASK), UInt64((v >> 128) & BIG_MASK)) + end +end)) + +function generateinversetables() + POW10_OFFSET_2 = Vector{UInt16}(undef, 68 + 1) + MIN_BLOCK_2 = fill(0xff, 68 + 1) + POW10_SPLIT_2 = Tuple{UInt64, UInt64, UInt64}[] + lowerCutoff = big(1) << (54 + 8) + for idx = 0:67 + POW10_OFFSET_2[idx + 1] = length(POW10_SPLIT_2) + i = 0 + while true + v = ((big(10)^(9 * (i + 1)) >> (-(120 - 16 * idx))) % (big(10)^9) << (120 + 16)) + if MIN_BLOCK_2[idx + 1] == 0xff && ((v * lowerCutoff) >> 128) == 0 + i += 1 + continue + end + if MIN_BLOCK_2[idx + 1] == 0xff + MIN_BLOCK_2[idx + 1] = i + end + v == 0 && break + push!(POW10_SPLIT_2, ((v & BIG_MASK) % UInt64, ((v >> 64) & BIG_MASK) % UInt64, ((v >> 128) & BIG_MASK) % UInt64)) + i += 1 + end + end + POW10_OFFSET_2[end] = length(POW10_SPLIT_2) + MIN_BLOCK_2[end] = 0x00 + + return POW10_OFFSET_2, MIN_BLOCK_2, POW10_SPLIT_2 +end + +const POW10_OFFSET_2, MIN_BLOCK_2, POW10_SPLIT_2 = generateinversetables() + +""" + Ryu.pow5invsplit(T, i) + +Compute `floor(2^k/5^i)+1`, where `k = pow5bits(i) - 1 + pow5_inv_bitcount(T)`. The result +is an unsigned integer twice as wide as `T` (i.e. a `UInt128` if `T == Float64`), with +`pow5_inv_bitcount(T)` significant bits. +""" +function pow5invsplit(::Type{T}, i) where {T<:AbstractFloat} + W = widen(uinttype(T)) + pow = big(5)^i + inv = div(big(1) << (ndigits(pow, base=2) - 1 + pow5_inv_bitcount(T)), pow) + 1 + return W(inv) +end + +""" + Ryu.pow5invsplit_lookup(T, i) + +[`pow5invsplit`](@ref) computed via lookup table. +""" +function pow5invsplit_lookup end +for T in (Float64, Float32, Float16) + e2_max = exponent_max(T) - precision(T) - 2 + i_max = log10pow2(e2_max) + table = [pow5invsplit(T, i) for i = 0:i_max] + @eval pow5invsplit_lookup(::Type{$T}, i) = @inbounds($table[i+1]) +end + + +""" + Ryu.pow5split(T, i) + +Compute `floor(5^i/2^k)`, where `k = pow5bits(i) - pow5_bitcount(T)`. The result is an +unsigned integer twice as wide as `T` (i.e. a `UInt128` if `T == Float64`), with +`pow5_bitcount(T)` significant bits. +""" +function pow5split(::Type{T}, i) where {T<:AbstractFloat} + W = widen(uinttype(T)) + pow = big(5)^i + return W(pow >> (ndigits(pow, base=2) - pow5_bitcount(T))) +end + +""" + Ryu.pow5split_lookup(T, i) + +[`pow5split`](@ref) computed via lookup table. +""" +function pow5split_lookup end +for T in (Float64, Float32, Float16) + e2_min = 1 - exponent_bias(T) - significand_bits(T) - 2 + i_max = 1 - e2_min - log10pow5(-e2_min) + table = [pow5split(T, i) for i = 0:i_max] + @eval pow5split_lookup(::Type{$T}, i) = @inbounds($table[i+1]) +end + +const DIGIT_TABLE = UInt8[ + '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9', + '1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9', + '2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9', + '3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9', + '4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9', + '5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9', + '6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9', + '7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9', + '8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9', + '9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9' +] + +const POW10_OFFSET = UInt16[ + 0, 2, 5, 8, 12, 16, 21, 26, 32, 39, + 46, 54, 62, 71, 80, 90, 100, 111, 122, 134, + 146, 159, 173, 187, 202, 217, 233, 249, 266, 283, + 301, 319, 338, 357, 377, 397, 418, 440, 462, 485, + 508, 532, 556, 581, 606, 632, 658, 685, 712, 740, + 769, 798, 828, 858, 889, 920, 952, 984, 1017, 1050, + 1084, 1118, 1153, 1188 +] diff --git a/base/secretbuffer.jl b/base/secretbuffer.jl new file mode 100644 index 0000000..02a133b --- /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), Int(ccall(:strlen, Csize_t, (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, ::Type{UInt8}) + 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) && @async @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..6b74da6 --- /dev/null +++ b/base/set.jl @@ -0,0 +1,664 @@ +# 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) + if isempty(s) + if get(io, :typeinfo, Any) == typeof(s) + print(io, "Set()") + else + show(io, typeof(s)) + print(io, "()") + end + else + print(io, "Set(") + show_vector(io, s) + print(io, ')') + end +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) + +copymutable(s::Set{T}) where {T} = Set{T}(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(::Union{AbstractSet,AbstractDict}) = true + +allunique(r::AbstractRange) = !iszero(step(r)) || 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{Int64} with 3 elements: + 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{Int64} with 2 elements: + 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} + count == 0 && return res + 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 + c == count && break + end + 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 + c == count && break + end + 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 + if res === A # for optimization only + for i in eachindex(A) + @inbounds Ai = A[i] + y = new(Ai) + @inbounds A[i] = y + end + else + for i in eachindex(A) + @inbounds Ai = A[i] + y = new(Ai) + @inbounds res[i] = y + end + 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..fcb8b2d --- /dev/null +++ b/base/shell.jl @@ -0,0 +1,307 @@ +# 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 + +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 + error("parsing command `$str`: special characters \"$special\" must be quoted in commands") + 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...) + + +function print_shell_escaped_winsomely(io::IO, args::AbstractString...) + first = true + for arg in args + first || write(io, ' ') + first = false + # Quote any arg that contains a whitespace (' ' or '\t') or a double quote mark '"'. + # It's also valid to quote an arg with just a whitespace, + # but the following may be 'safer', and both implementations are valid anyways. + quotes = any(c -> c in (' ', '\t', '"'), arg) || isempty(arg) + quotes && write(io, '"') + backslashes = 0 + for c in arg + if c == '\\' + backslashes += 1 + else + # escape all backslashes and the following double quote + c == '"' && (backslashes = backslashes * 2 + 1) + for j = 1:backslashes + # backslashes aren't special here + write(io, '\\') + end + backslashes = 0 + write(io, c) + end + end + # escape all backslashes, letting the terminating double quote we add below to then be interpreted as a special char + quotes && (backslashes *= 2) + for j = 1:backslashes + write(io, '\\') + end + quotes && write(io, '"') + end + return nothing +end + + +""" + shell_escaped_winsomely(args::Union{Cmd,AbstractString...})::String + +Convert the collection of strings `args` into single string suitable for passing as the argument +string for a Windows command line. Windows passes the entire command line as a single string to +the application (unlike POSIX systems, where the list of arguments are passed separately). +Many Windows API applications (including julia.exe), use the conventions of the [Microsoft C +runtime](https://docs.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments) to +split that command line into a list of strings. This function implements the inverse of such a +C runtime command-line parser. It joins command-line arguments to be passed to a Windows console +application into a command line, escaping or quoting meta characters such as space, +double quotes and backslash where needed. This may be useful in concert with the `windows_verbatim` +flag to [`Cmd`](@ref) when constructing process pipelines. + +# Example +```jldoctest +julia> println(shell_escaped_winsomely("A B\\", "C")) +"A B\\" C +""" +shell_escape_winsomely(args::AbstractString...) = + sprint(print_shell_escaped_winsomely, args..., sizehint=(sum(length, args)) + 3*length(args)) diff --git a/base/show.jl b/base/show.jl new file mode 100644 index 0000000..12e7453 --- /dev/null +++ b/base/show.jl @@ -0,0 +1,2305 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +function show(io::IO, ::MIME"text/plain", u::UndefInitializer) + show(io, u) + get(io, :compact, false) && return + print(io, ": array initializer with undefined values") +end + +# 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) + isempty(r) && return show(io, r) + # 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) + println(io, ":") + print_range(io, r) +end + +function show(io::IO, ::MIME"text/plain", f::Function) + get(io, :compact, false) && return show(io, f) + ft = typeof(f) + mt = ft.name.mt + if isa(f, Core.IntrinsicFunction) + print(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}) + isempty(iter) && get(io, :compact, false) && return show(io, iter) + 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} + isempty(t) && return show(io, t) + # 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 ") + if i == rows < length(t) + print(io, rpad("⋮", keylen), " => ⋮") + break + end + + 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 summary(io::IO, t::AbstractSet) + n = length(t) + showarg(io, t, true) + print(io, " with ", n, (n==1 ? " element" : " elements")) +end + +function show(io::IO, ::MIME"text/plain", t::AbstractSet{T}) where T + isempty(t) && return show(io, t) + # show more descriptively, with one line per value + recur_io = IOContext(io, :SHOWN_SET => t) + limit::Bool = get(io, :limit, false) + + 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 -= 2 # Subtract the width of prefix " " + cols < 4 && (cols = 4) # Minimum widths of 4 for value + rows -= 1 # Subtract the summary + else + rows = cols = typemax(Int) + end + + for (i, v) in enumerate(t) + print(io, "\n ") + if i == rows < length(t) + print(io, rpad("⋮", 2)) + break + end + + if limit + str = sprint(show, v, context=recur_io, sizehint=0) + print(io, _truncate_at_width_or_chars(str, cols, "\r\n")) + 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 values should be printed more compactly, e.g. + that numbers should be printed with fewer digits. This is set when printing array + elements. `:compact` output should not contain line breaks. + - `: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([io::IO = stdout], x) + +Write a text representation of a value `x` to the output stream `io`. New types `T` +should overload `show(io::IO, x::T)`. The representation used by `show` generally +includes Julia-specific formatting and type information, and should be parseable +Julia code when possible. + +[`repr`](@ref) returns the output of `show` as a string. + +To customize human-readable text output for objects of type `T`, define +`show(io::IO, ::MIME"text/plain", ::T)` instead. Checking the `:compact` +[`IOContext`](@ref) property of `io` in such methods is recommended, +since some containers show their elements by calling this method with +`:compact => true`. + +See also [`print`](@ref), which writes un-decorated representations. + +# Examples +```jldoctest +julia> show("Hello World!") +"Hello World!" +julia> print("Hello World!") +Hello World! +``` +""" +show(io::IO, @nospecialize(x)) = show_default(io, x) + +show(x) = show(stdout::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, f::Core.IntrinsicFunction) + if !get(io, :compact, false) + print(io, "Core.Intrinsics.") + end + print(io, nameof(f)) +end + +print(io::IO, f::Core.IntrinsicFunction) = print(io, nameof(f)) + +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 + if x.a isa DataType && Core.Compiler.typename(x.a) === Core.Compiler.typename(DenseArray) + T, N = x.a.parameters + if x == StridedArray{T,N} + print(io, "StridedArray") + show_delim_array(io, (T,N), '{', ',', '}', false) + return + elseif x == StridedVecOrMat{T} + print(io, "StridedVecOrMat") + show_delim_array(io, (T,), '{', ',', '}', false) + return + elseif StridedArray{T,N} <: x + print(io, "Union") + show_delim_array(io, vcat(StridedArray{T,N}, uniontypes(Core.Compiler.typesubtract(x, StridedArray{T,N}))), '{', ',', '}', false) + return + end + end + 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, 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. +# IOContext(io, :unquote_fallback => false) tells show_unquoted to treat any +# Expr whose head is :$ as if it is inside a quote, preventing fallback to the +# "unhandled" case: this is used by print/string to be lawful to Rule 1 above. +# On the countrary, show/repr have to follow Rule 2, requiring any Expr whose +# head is :$ and which is not inside a quote to fallback to the "unhandled" case: +# this is behavior is triggered by IOContext(io, :unquote_fallback => true) +print( io::IO, ex::ExprNode) = (show_unquoted(IOContext(io, :unquote_fallback => false), ex, 0, -1); nothing) +show( io::IO, ex::ExprNode) = show_unquoted_quote_expr(IOContext(io, :unquote_fallback => true), ex, 0, -1, 0) +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) +show_unquoted(io::IO, ex, indent::Int, prec::Int, ::Int) = show_unquoted(io, ex, indent, prec) + +## 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 + (s == "true" || s == "false") && 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, 12, 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 === :<| || s === :|| + return :right + elseif operator_precedence(s) in (0, prec_comparison) || s in (:+, :++, :*) + return :none + end + return :left +end + +is_expr(@nospecialize(ex), head::Symbol) = isa(ex, Expr) && (ex.head === head) +is_expr(@nospecialize(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, quote_level::Int) + print(io, head) + if !isempty(args) + print(io, ' ') + if head === :elseif + show_list(io, args, " ", indent, 0, quote_level) + else + show_list(io, args, ", ", indent, 0, quote_level) + end + end + + ind = head === :module || head === :baremodule ? indent : indent + indent_width + exs = (is_expr(body, :block) || is_expr(body, :quote)) ? body.args : Any[body] + for ex in exs + print(io, '\n', " "^ind) + show_unquoted(io, ex, ind, -1, quote_level) + end + print(io, '\n', " "^indent) +end +show_block(io::IO,head, block,i::Int, quote_level::Int) = show_block(io,head, [], block,i, quote_level) +function show_block(io::IO, head, arg, block, i::Int, quote_level::Int) + if is_expr(arg, :block) || is_expr(arg, :quote) + show_block(io, head, arg.args, block, i, quote_level) + else + show_block(io, head, Any[arg], block, i, quote_level) + end +end + +# show an indented list +function show_list(io::IO, items, sep, indent::Int, prec::Int=0, quote_level::Int=0, enclose_operators::Bool=false, + kw::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, '(') + if kw && is_expr(item, :kw, 2) + show_unquoted(io, Expr(:(=), item.args[1], item.args[2]), indent, parens ? 0 : prec, quote_level) + elseif kw && is_expr(item, :(=), 2) + show_unquoted_expr_fallback(io, item, indent, quote_level) + else + show_unquoted(io, item, indent, parens ? 0 : prec, quote_level) + end + 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, quote_level=0, encl_ops=false, kw::Bool=false) + print(io, op) + show_list(io, items, sep, indent, prec, quote_level, encl_ops, kw) + print(io, cl) +end + +# show a normal (non-operator) function call, e.g. f(x, y) or A[z] +# kw: `=` expressions are parsed with head `kw` in this context +function show_call(io::IO, head, func, func_args, indent, quote_level, kw::Bool) + 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, 0, quote_level) + else + print(io, '(') + show_unquoted(io, func, indent, 0, quote_level) + 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, 0, quote_level, false, kw) + print(io, "; ") + show_list(io, func_args[1].args, ", ", indent, 0, quote_level, false, kw) + print(io, cl) + else + show_enclosed_list(io, op, func_args, ", ", cl, indent, 0, quote_level, false, kw) + 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) && sym !== Symbol("'")) + 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, allow_macroname=false) +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) +show_unquoted(io::IO, ex::GlobalRef, ::Int, ::Int) = show_globalref(io, ex) + +function show_globalref(io::IO, ex::GlobalRef; allow_macroname=false) + 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=allow_macroname) + 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, 0) + else + print(io, "\$(QuoteNode(") + # QuoteNode does not allows for interpolation, so if ex.value is an + # Expr it should be shown with quote_level equal to zero. + # Calling show(io, ex.value) like this implicitly enforce that. + show(io, ex.value) + print(io, "))") + end +end + +function show_unquoted_quote_expr(io::IO, @nospecialize(value), indent::Int, prec::Int, quote_level::Int) + if isa(value, Symbol) && !(value in quoted_syms) + s = string(value) + if isidentifier(s) || (isoperator(value) && value !== Symbol("'")) + print(io, ":") + print(io, value) + else + print(io, "Symbol(", repr(s), ")") + end + else + if isa(value,Expr) && value.head === :block + show_block(IOContext(io, beginsym=>false), "quote", value, indent, quote_level) + print(io, "end") + else + print(io, ":(") + show_unquoted(io, value, indent+2, -1, quote_level) # +2 for `:(` + print(io, ")") + end + end +end + +function show_generator(io, ex, indent, quote_level) + 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, 0, quote_level) + for r in ranges + print(io, " for ") + show_list(io, r, ", ", indent, 0, quote_level) + end + else + show_unquoted(io, ex.args[1], indent, 0, quote_level) + print(io, " for ") + show_list(io, ex.args[2:end], ", ", indent, 0, quote_level) + end +end + +function valid_import_path(@nospecialize ex) + return is_expr(ex, :(.)) && length((ex::Expr).args) > 0 && all(a->isa(a,Symbol), (ex::Expr).args) +end + +function show_import_path(io::IO, ex, quote_level) + if !isa(ex, Expr) + show_unquoted(io, ex) + elseif ex.head === :(:) + show_import_path(io, ex.args[1], quote_level) + print(io, ": ") + for i = 2:length(ex.args) + if i > 2 + print(io, ", ") + end + show_import_path(io, ex.args[i], quote_level) + 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, 0, 0, quote_level) + end +end + +# Wrap symbols for macro names to allow them to be printed literally +function allow_macroname(ex) + if (ex isa Symbol && first(string(ex)) == '@') || + ex isa GlobalRef || + (is_expr(ex, :(.)) && length(ex.args) == 2 && + (is_expr(ex.args[2], :quote) || ex.args[2] isa QuoteNode)) + return Expr(:macroname, ex) + else + ex + end +end + +function is_core_macro(arg, macro_name::AbstractString) + arg === GlobalRef(Core, Symbol(macro_name)) +end + +# symbol for IOContext flag signaling whether "begin" is treated +# as an ordinary symbol, which is true in indexing expressions. +const beginsym = gensym(:beginsym) + +function show_unquoted_expr_fallback(io::IO, ex::Expr, indent::Int, quote_level::Int) + print(io, "\$(Expr(") + show(io, ex.head) + for arg in ex.args + print(io, ", ") + show(io, arg) + end + print(io, "))") +end + +# TODO: implement interpolated strings +function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int, quote_level::Int = 0) + 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)) && !is_expr(item, :(.)) + parens && print(io, '(') + show_unquoted(io, item, indent, 0, quote_level) + 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, 0, quote_level) + 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, quote_level, true) + else + show_list(io, args, head_, indent, func_prec, quote_level, true) + end + + elseif head === :tuple + print(io, "(") + if nargs > 0 && is_expr(args[1], :parameters) + show_list(io, args[2:end], ", ", indent, 0, quote_level) + nargs == 2 && print(io, ',') + print(io, ";") + if !isempty(args[1].args) + print(io, " ") + end + show_list(io, args[1].args, ", ", indent, 0, quote_level, false, true) + else + show_list(io, args, ", ", indent, 0, quote_level) + nargs == 1 && print(io, ',') + end + print(io, ")") + + # list-like forms, e.g. "[1, 2, 3]" + elseif haskey(expr_parens, head) || # :vcat etc. + head === :typed_vcat || head === :typed_hcat + # print the type and defer to the untyped case + if head === :typed_vcat || head === :typed_hcat + show_unquoted(io, args[1], indent, prec, quote_level) + if head === :typed_vcat + head = :vcat + else + head = :hcat + end + args = args[2:end] + nargs = nargs - 1 + end + 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, 0, quote_level) + if nargs == 1 && head === :vcat + print(io, ';') + 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] + + # :kw exprs are only parsed inside parenthesized calls + if any(a->is_expr(a, :kw), func_args) || (!isempty(func_args) && is_expr(func_args[1], :parameters)) + show_call(io, head, func, func_args, indent, quote_level, true) + + # scalar multiplication (i.e. "100x") + elseif (func === :* && + length(func_args) == 2 && isa(func_args[1], Union{Int, Int64, Float32, Float64}) && + isa(func_args[2], Symbol) && + !in(string(func_args[2]::Symbol)[1], ('e', 'E', 'f', (func_args[1] == 0 && func_args[1] isa Integer ? + # don't juxtapose 0 with b, o, x + ('b', 'o', 'x') : ())...))) + if func_prec <= prec + show_enclosed_list(io, '(', func_args, "", ')', indent, func_prec, quote_level) + else + show_list(io, func_args, "", indent, func_prec, quote_level) + end + + # unary operator (i.e. "!z") + elseif isa(func,Symbol) && func in uni_ops && length(func_args) == 1 + show_unquoted(io, func, indent, 0, quote_level) + 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, quote_level) + 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, quote_level, true) + else + show_list(io, func_args, sep, indent, func_prec, quote_level, true) + end + elseif na == 1 + # 1-argument call to normally-binary operator + op, cl = expr_calls[head] + print(io, "(") + show_unquoted(io, func, indent, 0, quote_level) + print(io, ")") + show_enclosed_list(io, op, func_args, ", ", cl, indent, 0, quote_level) + else + show_call(io, head, func, func_args, indent, quote_level, true) + end + + # normal function (i.e. "f(x,y)") + else + show_call(io, head, func, func_args, indent, quote_level, true) + end + + # new expr + elseif head === :new || head === :splatnew + show_enclosed_list(io, "%$head(", args, ", ", ")", indent, 0, quote_level) + + # 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(head == :ref ? IOContext(io, beginsym=>true) : io, head, args[1], funcargslike, indent, quote_level, head !== :curly) + + # comprehensions + elseif head === :typed_comprehension && nargs == 2 + show_unquoted(io, args[1], indent, 0, quote_level) + print(io, '[') + show_generator(io, args[2], indent, quote_level) + print(io, ']') + + elseif head === :comprehension && nargs == 1 + print(io, '[') + show_generator(io, args[1], indent, quote_level) + print(io, ']') + + elseif (head === :generator && nargs >= 2) || (head === :flatten && nargs == 1) + print(io, '(') + show_generator(io, ex, indent, quote_level) + print(io, ')') + + elseif head === :filter && nargs == 2 + show_unquoted(io, args[2], indent, 0, quote_level) + print(io, " if ") + show_unquoted(io, args[1], indent, 0, quote_level) + + # 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, quote_level) + else + show_list(io, args, " ", indent, comp_prec, quote_level) + 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(IOContext(io, beginsym=>false), head, Expr(:calldecl, args[1].args...), args[2], indent, quote_level) + print(io, "end") + + elseif (head === :function || head === :macro) && nargs == 1 + print(io, head, ' ') + show_unquoted(IOContext(io, beginsym=>false), args[1]) + print(io, " end") + + elseif head === :do && nargs == 2 + iob = IOContext(io, beginsym=>false) + show_unquoted(iob, args[1], indent, -1, quote_level) + print(io, " do ") + show_list(iob, args[2].args[1].args, ", ", 0, 0, quote_level) + for stmt in args[2].args[2].args + print(io, '\n', " "^(indent + indent_width)) + show_unquoted(iob, stmt, indent + indent_width, -1, quote_level) + end + print(io, '\n', " "^indent) + print(io, "end") + + # block with argument + elseif head in (:for,:while,:function,:macro,:if,:elseif,:let) && nargs==2 + if is_expr(args[2], :block) + show_block(IOContext(io, beginsym=>false), head, args[1], args[2], indent, quote_level) + else + show_block(IOContext(io, beginsym=>false), head, args[1], Expr(:block, args[2]), indent, quote_level) + end + print(io, "end") + + elseif (head === :if || head === :elseif) && nargs == 3 + iob = IOContext(io, beginsym=>false) + show_block(iob, head, args[1], args[2], indent, quote_level) + if isa(args[3],Expr) && args[3].head === :elseif + show_unquoted(iob, args[3], indent, prec, quote_level) + else + show_block(iob, "else", args[3], indent, quote_level) + print(io, "end") + end + + elseif head === :module && nargs==3 && isa(args[1],Bool) + show_block(IOContext(io, beginsym=>false), args[1] ? :module : :baremodule, args[2], args[3], indent, quote_level) + print(io, "end") + + # type declaration + elseif head === :struct && nargs==3 + show_block(IOContext(io, beginsym=>false), args[1] ? Symbol("mutable struct") : Symbol("struct"), args[2], args[3], indent, quote_level) + print(io, "end") + + elseif head === :primitive && nargs == 2 + print(io, "primitive type ") + show_list(io, args, ' ', indent, 0, quote_level) + print(io, " end") + + elseif head === :abstract && nargs == 1 + print(io, "abstract type ") + show_list(IOContext(io, beginsym=>false), args, ' ', indent, 0, quote_level) + 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, 0, quote_level) + + # var-arg declaration or expansion + # (i.e. "function f(L...) end" or "f(B...)") + elseif head === :(...) && nargs == 1 + show_unquoted(io, args[1], indent, 0, quote_level) + 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, 0, quote_level) + + elseif head === :export + print(io, head, ' ') + show_list(io, allow_macroname.(args), ", ", indent) + + elseif head === :macrocall && nargs >= 2 + # handle some special syntaxes + # `a b c` + if is_core_macro(args[1], "@cmd") + print(io, "`", args[3], "`") + # 11111111111111111111, 0xfffffffffffffffff, 1111...many digits... + elseif is_core_macro(args[1], "@int128_str") || + is_core_macro(args[1], "@uint128_str") || + is_core_macro(args[1], "@big_str") + print(io, args[3]) + # x"y" and x"y"z + elseif isa(args[1], Symbol) && nargs >= 3 && isa(args[3], String) && + startswith(string(args[1]::Symbol), "@") && + endswith(string(args[1]::Symbol), "_str") + s = string(args[1]::Symbol) + print(io, s[2:prevind(s,end,4)], "\"") + escape_raw_string(io, args[3]) + print(io, "\"") + if nargs == 4 + print(io, args[4]) + end + # general case + else + # 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, quote_level, false) + 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, 0, quote_level) + end + end + + elseif head === :macroname && nargs == 1 + arg1 = args[1] + if arg1 isa Symbol + show_sym(io, arg1, allow_macroname=true) + elseif arg1 isa GlobalRef + show_globalref(io, arg1, allow_macroname=true) + elseif is_expr(arg1, :(.)) && length(arg1.args) == 2 + m = arg1.args[1] + if m isa Symbol || m isa GlobalRef || is_expr(m, :(.), 2) + show_unquoted(io, m) + else + print(io, "(") + show_unquoted(io, m) + print(io, ")") + end + print(io, '.') + if is_expr(arg1.args[2], :quote) + mname = arg1.args[2].args[1] + else + mname = arg1.args[2].value + end + if mname isa Symbol + show_sym(io, mname, allow_macroname=true) + else + show_unquoted(io, mname) + end + else + show_unquoted(io, arg1) + end + + elseif head === :line && 1 <= nargs <= 2 + show_linenumber(io, args...) + + elseif head === :try && 3 <= nargs <= 4 + iob = IOContext(io, beginsym=>false) + show_block(iob, "try", args[1], indent, quote_level) + if is_expr(args[3], :block) + show_block(iob, "catch", args[2] === false ? Any[] : args[2], args[3], indent, quote_level) + end + if nargs >= 4 && is_expr(args[4], :block) + show_block(iob, "finally", Any[], args[4], indent, quote_level) + end + print(io, "end") + + elseif head === :block + # print as (...; ...; ...;) inside indexing expression + if get(io, beginsym, false) + print(io, '(') + ind = indent + indent_width + for i = 1:length(ex.args) + if i > 1 + # if there was only a comment before the first semicolon, the expression would get parsed as a NamedTuple + if !(i == 2 && ex.args[1] isa LineNumberNode) + print(io, ';') + end + print(io, "\n", ' '^ind) + end + show_unquoted(io, ex.args[i], ind, -1, quote_level) + end + if length(ex.args) < 2 + print(io, isempty(ex.args) ? ";;)" : ";)") + else + print(io, ')') + end + else + show_block(io, "begin", ex, indent, quote_level) + print(io, "end") + end + + elseif head === :quote && nargs == 1 && isa(args[1], Symbol) + show_unquoted_quote_expr(IOContext(io, beginsym=>false), args[1]::Symbol, indent, 0, quote_level+1) + elseif head === :quote && !get(io, :unquote_fallback, true) + if nargs == 1 && is_expr(args[1], :block) + show_block(IOContext(io, beginsym=>false), "quote", Expr(:quote, args[1].args...), indent, + quote_level+1) + print(io, "end") + elseif nargs == 1 + print(io, ":(") + show_unquoted(IOContext(io, beginsym=>false), args[1], indent+2, 0, quote_level+1) + print(io, ")") + else + show_block(IOContext(io, beginsym=>false), "quote", ex, indent, quote_level+1) + print(io, "end") + end + + elseif head === :gotoifnot && nargs == 2 && isa(args[2], Int) + print(io, "unless ") + show_unquoted(io, args[1], indent, 0, quote_level) + 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 === :string + print(io, '"') + for x in args + if !isa(x,AbstractString) + print(io, "\$(") + if isa(x,Symbol) && !(x in quoted_syms) + show_sym(io, x) + else + show_unquoted(io, x, 0, 0, quote_level) + end + print(io, ")") + else + escape_string(io, x, "\"\$") + end + end + print(io, '"') + + elseif (head === :& || head === :$) && nargs == 1 + if head === :$ + quote_level -= 1 + end + if head === :$ && get(io, :unquote_fallback, true) + unhandled = true + else + print(io, head) + a1 = args[1] + parens = (isa(a1,Expr) && !in(a1.head, (:tuple, :$, :vect, :braces))) || + (isa(a1,Symbol) && isoperator(a1)) + parens && print(io, "(") + show_unquoted(io, a1, 0, 0, quote_level) + parens && print(io, ")") + end + + # transpose + elseif head === Symbol('\'') && nargs == 1 + if isa(args[1], Symbol) + show_unquoted(io, args[1], 0, 0, quote_level) + else + print(io, "(") + show_unquoted(io, args[1], 0, 0, quote_level) + 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(:(::)), quote_level) + print(io, " where ") + if nargs == 2 + show_unquoted(io, args[2], indent, 1, quote_level) + else + print(io, "{") + show_list(io, args[2:end], ", ", indent, 0, quote_level) + print(io, "}") + end + parens && print(io, ")") + + elseif (head === :import || head === :using) && + ((nargs == 1 && (valid_import_path(args[1]) || + (is_expr(args[1], :(:)) && + length((args[1]::Expr).args) > 1 && + all(valid_import_path, (args[1]::Expr).args)))) || + all(valid_import_path, args)) + print(io, head) + print(io, ' ') + first = true + for a in args + if !first + print(io, ", ") + end + first = false + show_import_path(io, a, quote_level) + 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 + show_unquoted_expr_fallback(io, ex, indent, quote_level) + end + nothing +end + +demangle_function_name(name::Symbol) = Symbol(demangle_function_name(string(name))) +function demangle_function_name(name::AbstractString) + demangle = split(name, '#') + # kw sorters and impl methods use the name scheme `f#...` + if length(demangle) >= 2 && demangle[1] != "" + return demangle[1] + end + return name +end + +function show_tuple_as_call(io::IO, name::Symbol, sig::Type, demangle=false, kwargs=nothing) + # 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, demangle ? demangle_function_name(name) : name, "(...)", color=color) + return + end + tv = Any[] + env_io = io + while isa(sig, UnionAll) + push!(tv, sig.var) + env_io = IOContext(env_io, :unionall_env => sig.var) + sig = sig.body + end + sig = sig.parameters + with_output_color(color, env_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, (demangle ? demangle_function_name : identity)(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(env_io, "::", sig[i]) + end + if kwargs !== nothing + print(io, "; ") + first = true + for (k, t) in kwargs + first || print(io, ", ") + first = false + print(io, k, "::") + show(io, t) + end + end + printstyled(io, ")", color=print_style) + show_method_params(io, tv) + 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 + show_unquoted(io, tv.name) + print(io, ">:") + show_bound(io, lb) + else + show_bound(io, lb) + print(io, "<:") + show_unquoted(io, tv.name) + end + else + show_unquoted(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) + if x === Union{} + show(io, x) + return + end + 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 + if x.name === NamedTuple_typename && !(x.parameters[1] isa Tuple) + # named tuple type with unknown field names + return + 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(io, 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(stdout, 42)` yields (2, 0)" +alignment(io::IO, x::Integer) = (length(sprint(show, x, context=io, sizehint=0)), 0) +"`alignment(stdout, 4.23)` yields (1, 3) for `4` and `.23`" +function alignment(io::IO, x::Real) + m = match(r"^(.*?)((?:[\.eEfF].*)?)$", 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(stdout, 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"^(.*[^ef][\+\-])(.*)$", 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 + +# printing iterators from Base.Iterators + +function show(io::IO, e::Iterators.Enumerate) + print(io, "enumerate(") + show(io, e.itr) + print(io, ')') +end +show(io::IO, z::Iterators.Zip) = show_delim_array(io, z.is, "zip(", ',', ')', false) + +# 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..fbbc3ae --- /dev/null +++ b/base/some.jl @@ -0,0 +1,97 @@ +# 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. + +See also [`coalesce`](@ref). + +# 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..ff8edf1 --- /dev/null +++ b/base/sort.jl @@ -0,0 +1,1210 @@ +# 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, keytype + +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{Integer,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{Integer,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{Integer,OrdinalRange}; kws...) = + partialsort!(copymutable(v), k; kws...) + +# This implementation of `midpoint` is performance-optimized but safe +# only if `lo <= hi`. +midpoint(lo::T, hi::T) where T<:Integer = lo + ((hi - lo) >>> 0x01) +midpoint(lo::Integer, hi::Integer) = midpoint(promote(lo, hi)...) + +# 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)::keytype(v) where T<:Integer + u = T(1) + lo = lo - u + hi = hi + u + @inbounds while lo < hi - u + m = midpoint(lo, hi) + 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)::keytype(v) where T<:Integer + u = T(1) + lo = lo - u + hi = hi + u + @inbounds while lo < hi - u + m = midpoint(lo, hi) + 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)::UnitRange{keytype(v)} where T<:Integer + u = T(1) + lo = ilo - u + hi = ihi + u + @inbounds while lo < hi - u + m = midpoint(lo, hi) + 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)::keytype(a) + 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)::keytype(a) + 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)::keytype(a) + require_one_based_indexing(a) + h = step(a) + if h == 0 + lt(o, x, first(a)) ? 0 : length(a) + elseif h > 0 && x < first(a) + firstindex(a) - 1 + elseif h > 0 && x >= last(a) + lastindex(a) + elseif h < 0 && x > first(a) + firstindex(a) - 1 + elseif h < 0 && x <= last(a) + lastindex(a) + else + if o isa ForwardOrdering + fld(floor(Integer, x) - first(a), h) + 1 + else + fld(ceil(Integer, x) - first(a), h) + 1 + end + end +end + +function searchsortedfirst(a::AbstractRange{<:Integer}, x::Real, o::DirectOrdering)::keytype(a) + require_one_based_indexing(a) + h = step(a) + if h == 0 + lt(o, first(a), x) ? length(a)+1 : 1 + elseif h > 0 && x <= first(a) + firstindex(a) + elseif h > 0 && x > last(a) + lastindex(a) + 1 + elseif h < 0 && x >= first(a) + firstindex(a) + elseif h < 0 && x < last(a) + lastindex(a) + 1 + else + if o isa ForwardOrdering + -fld(floor(Integer, -x) + Signed(first(a)), h) + 1 + else + -fld(ceil(Integer, -x) + Signed(first(a)), h) + 1 + end + end +end + +function searchsortedfirst(a::AbstractRange{<:Integer}, x::Unsigned, o::DirectOrdering)::keytype(a) + 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)::keytype(a) + 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{Integer,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{Integer,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::Integer, hi::Integer, ::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::Integer, hi::Integer, o::Ordering) + @inbounds begin + mi = midpoint(lo, hi) + + # 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::Integer, hi::Integer, 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::Integer, hi::Integer, 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::Integer, hi::Integer, 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 = midpoint(lo, hi) + (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::Integer, hi::Integer, a::PartialQuickSort{<:Integer}, + 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::Integer, hi::Integer, 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`](@ref) is used by default for numeric arrays while +[`MergeSort`](@ref) is used for other arrays. You can specify an algorithm to use via the `alg` +keyword (see [Sorting Algorithms](@ref) 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 || ordr === Reverse) && 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, ordr === Reverse ? reverse : identity) + end + end + end + sort!(v, alg, ordr) +end + +# sort! for vectors of few unique integers +function sort_int_range!(x::AbstractVector{<:Integer}, rangelen, minval, maybereverse) + offs = 1 - minval + + where = fill(0, rangelen) + @inbounds for i = eachindex(x) + where[x[i] + offs] += 1 + end + + idx = firstindex(x) + @inbounds for i = maybereverse(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` the same size as +`v`, which is used to store (a permutation of) the indices of `v`. + +If the index vector `ix` is initialized with the indices of `v` (or a permutation thereof), `initialized` should be set to +`true`. + +If `initialized` is `false` (the default), then `ix` is initialized to contain the indices of `v`. + +If `initialized` is `true`, but `ix` does not contain (a permutation of) the indices of `v`, the behavior of +`partialsortperm!` is undefined. + +(Typically, the indices of `v` will be `1:length(v)`, although if `v` has an alternative array type +with non-one-based indices, such as an `OffsetArray`, `ix` must also be an `OffsetArray` with the same +indices, and must contain as values (a permutation of) these same indices.) + +Upon return, `ix` is guaranteed to have the indices `k` in their sorted positions, such that + +```julia +partialsortperm!(ix, v, k); +v[ix[k]] == partialsort(v, k) +``` + +The return value is the `k`th element of `ix` if `k` is an integer, or view into `ix` if `k` is +a range. + +# Examples +```jldoctest +julia> v = [3, 1, 2, 1]; + +julia> ix = Vector{Int}(undef, 4); + +julia> partialsortperm!(ix, v, 1) +2 + +julia> ix = [1:4;]; + +julia> partialsortperm!(ix, v, 2:3, initialized=true) +2-element view(::Array{Int64,1}, 2:3) with eltype Int64: + 4 + 3 +``` + """ +function partialsortperm!(ix::AbstractVector{<:Integer}, v::AbstractVector, + k::Union{Integer, OrdinalRange}; + lt::Function=isless, + by::Function=identity, + rev::Union{Bool,Nothing}=nothing, + order::Ordering=Forward, + initialized::Bool=false) + if axes(ix,1) != axes(v,1) + throw(ArgumentError("The index vector is used as a workspace and must have the " * + "same length/indices as the source vector, $(axes(ix,1)) != $(axes(v,1))")) + end + 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 + ax = axes(v, 1) + p = similar(Vector{eltype(ax)}, ax) + for (i,ind) in zip(eachindex(p), ax) + 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 length/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::Integer) = isnan(o.order,o.data[i]) + +function nans2left!(v::AbstractVector, o::Ordering, lo::Integer=first(axes(v,1)), hi::Integer=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::Integer=first(axes(v,1)), hi::Integer=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{<:Integer}, o::Perm{<:ForwardOrdering}) = nans2right!(v,o) +nans2end!(v::AbstractVector{<:Integer}, 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::Integer) = 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..23b518a --- /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 division 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..7242eb8 --- /dev/null +++ b/base/special/rem_pio2.jl @@ -0,0 +1,328 @@ +# 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 + raw_exponent = ((u & exponent_mask(Float64)) >> significand_bits(Float64)) % Int + k = raw_exponent - 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::Union{Float32, Float64}) + +Calculate `x` divided by `π/2` accurately for arbitrarily large `x`. +Returns a pair `(k, r)`, where `k` is the quadrant of the result +(multiple of π/2) and `r` is the remainder, such that ``k * π/2 = x - r``. +The remainder is given as a double-double pair. +`k` is positive if `x > 0` and is negative if `x ≤ 0`. +""" +@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..e63449d --- /dev/null +++ b/base/stacktraces.jl @@ -0,0 +1,289 @@ +# 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 +import Core: CodeInfo, MethodInstance + +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{MethodInstance, 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::Ptr{Cvoid}) -> 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, false) + pointer = convert(UInt64, pointer) + isempty(infos) && return [StackFrame(empty_sym, empty_sym, -1, nothing, true, false, pointer)] # this is equal to UNKNOWN + res = Vector{StackFrame}(undef, length(infos)) + for i in 1:length(infos) + info = infos[i] + @assert(length(info) == 6) + res[i] = StackFrame(info[1], info[2], info[3], info[4], info[5], info[6], pointer) + end + return res +end + +const top_level_scope_sym = Symbol("top-level scope") + +function lookup(ip::Base.InterpreterIP) + if ip.code isa 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 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 + +""" + 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 = StackTrace() + for ip in trace + for frame in lookup(ip) + # Skip frames that come from C calls. + if c_funcs || !frame.from_c + push!(stack, frame) + end + end + end + return stack +end + +function stacktrace(c_funcs::Bool=false) + stack = stacktrace(backtrace(), c_funcs) + # Remove frame for this function (and any functions called by this function). + remove_frames!(stack, :stacktrace) + # also remove all of the non-Julia functions that led up to this point (if that list is non-empty) + c_funcs && deleteat!(stack, 1:(something(findfirst(frame -> !frame.from_c, stack), 1) - 1)) + return stack +end + +""" + 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) + deleteat!(stack, 1:something(findlast(frame -> frame.func == name, stack), 0)) + return stack +end + +function remove_frames!(stack::StackTrace, names::Vector{Symbol}) + deleteat!(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 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 + print(io, "ip:0x", string(frame.pointer, base=16)) + 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, Base.demangle_function_name(string(frame.func)), color=color) + end + elseif frame.linfo isa MethodInstance + def = frame.linfo.def + if isa(def, Method) + sig = frame.linfo.specTypes + if def.nkw > 0 + # rearrange call kw_impl(kw_args..., func, pos_args...) to func(pos_args...) + kwarg_types = Any[ fieldtype(sig, i) for i = 2:(1+def.nkw) ] + uw = Base.unwrap_unionall(sig) + pos_sig = Base.rewrap_unionall(Tuple{uw.parameters[(def.nkw+2):end]...}, sig) + kwnames = Base.method_argnames(def)[2:(def.nkw+1)] + for i = 1:length(kwnames) + str = string(kwnames[i]) + if endswith(str, "...") + kwnames[i] = Symbol(str[1:end-3]) + end + end + Base.show_tuple_as_call(io, def.name, pos_sig, true, zip(kwnames, kwarg_types)) + else + Base.show_tuple_as_call(io, def.name, sig, true) + end + else + Base.show(io, frame.linfo) + end + elseif frame.linfo isa 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 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..cd277b5 --- /dev/null +++ b/base/stream.jl @@ -0,0 +1,1303 @@ +# 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) +# +- DevNull (not exported) +# +- Filesystem.File +# +- LibuvStream (not exported) +# . +- PipeEndpoint (not exported) +# . +- TCPSocket +# . +- TTY (not exported) +# . +- UDPSocket +# . +- BufferStream (FIXME: 2.0) +# +- 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 precede 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, io.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 precede 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) + # A workaround for #34620 and #26687 (this still has the TOCTOU problem). + check_open(io) + + 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 + +### 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 = min(length(buffer.data), buffer.maxsize) - 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, ::Type{T}) where T + mark(s) + try read(s, T) + 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 + readerror::Any + is_open::Bool + buffer_writes::Bool + lock::ReentrantLock # advisory lock + + BufferStream() = new(PipeBuffer(), Threads.Condition(), nothing, 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..67368bc --- /dev/null +++ b/base/strings/basic.jl @@ -0,0 +1,753 @@ +# 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)`. + +# Examples +```jldoctest +julia> ncodeunits("The Julia Language") +18 + +julia> ncodeunits("∫eˣ") +6 + +julia> ncodeunits('∫'), ncodeunits('e'), ncodeunits('ˣ') +(3, 1, 2) +``` + +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)`. + +# Examples +```jldoctest +julia> a = codeunit("Hello", 2) +0x65 + +julia> typeof(a) +UInt8 +``` + +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)) +isempty(s::AbstractString) = iszero(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 ## + +function map(f, s::AbstractString) + out = StringVector(max(4, sizeof(s)÷sizeof(codeunit(s)))) + index = UInt(1) + 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")) + index + 3 > length(out) && resize!(out, unsigned(2 * length(out))) + index += __unsafe_string!(out, convert(Char, c′), index) + end + resize!(out, index-1) + sizehint!(out, index-1) + return String(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`. + +# Examples +```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`. + +# Examples +```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. + +# Examples +```jldoctest +julia> codeunits("Juλia") +6-element Base.CodeUnits{UInt8,String}: + 0x4a + 0x75 + 0xce + 0xbb + 0x69 + 0x61 +``` +""" +codeunits(s::AbstractString) = CodeUnits(s) diff --git a/base/strings/io.jl b/base/strings/io.jl new file mode 100644 index 0000000..22a8d10 --- /dev/null +++ b/base/strings/io.jl @@ -0,0 +1,698 @@ +# 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 + +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 BMP code points (`\\u` with 1-4 trailing hex digits) + - All Unicode code points (`\\U` with 1-8 trailing hex digits; max value = 0010ffff) + - 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 + +""" + escape_raw_string(s::AbstractString) + escape_raw_string(io, s::AbstractString) + +Escape a string in the manner used for parsing raw string literals. +For each double-quote (`"`) character in input string `s`, this +function counts the number _n_ of preceeding backslash (`\\`) characters, +and then increases there the number of backslashes from _n_ to 2_n_+1 +(even for _n_ = 0). It also doubles a sequence of backslashes at the end +of the string. + +This escaping convention is used in raw strings and other non-standard +string literals. (It also happens to be the escaping convention +expected by the Microsoft C/C++ compiler runtime when it parses a +command-line string into the argv[] array.) + +See also: [`escape_string`](@ref) +""" +function escape_raw_string(io, str::AbstractString) + escapes = 0 + for c in str + if c == '\\' + escapes += 1 + else + if c == '"' + # if one or more backslashes are followed by + # a double quote then escape all backslashes + # and the double quote + escapes = escapes * 2 + 1 + end + while escapes > 0 + write(io, '\\') + escapes -= 1 + end + escapes = 0 + write(io, c) + end + end + # also escape any trailing backslashes, + # so they do not affect the closing quote + while escapes > 0 + write(io, '\\') + write(io, '\\') + escapes -= 1 + end +end +escape_raw_string(str::AbstractString) = sprint(escape_raw_string, str; + sizehint = lastindex(str) + 2) + +## 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..9d2fdf7 --- /dev/null +++ b/base/strings/search.jl @@ -0,0 +1,535 @@ +# 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) + i = Int(i) + z = ncodeunits(s) + 1 + 1 ≤ i ≤ z || throw(BoundsError(s, i)) + @inbounds i == z || isvalid(s, i) || string_index_err(s, i) + e = lastindex(s) + while i <= e + testf(@inbounds s[i]) && return i + i = @inbounds nextind(s, i) + 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, Int(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) + i = Int(i) + z = ncodeunits(s) + 1 + 0 ≤ i ≤ z || throw(BoundsError(s, i)) + i == z && return nothing + @inbounds i == 0 || isvalid(s, i) || string_index_err(s, i) + while i >= 1 + testf(@inbounds s[i]) && return i + i = @inbounds prevind(s, i) + end + return nothing +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, Int(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 +``` + +See also: [`contains`](@ref). +""" +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..56b603f --- /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) +@pure 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) + +@pure ncodeunits(s::String) = Core.sizeof(s) +@pure 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..d98dab5 --- /dev/null +++ b/base/strings/substring.jl @@ -0,0 +1,216 @@ +# 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 == 0 && return "" + 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..3569130 --- /dev/null +++ b/base/strings/util.jl @@ -0,0 +1,704 @@ +# 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 + +""" + contains(haystack::AbstractString, needle) + +Return `true` if `haystack` contains `needle`. +This is the same as `occursin(needle, haystack)`, but is provided for consistency with +`startswith(haystack, needle)` and `endswith(haystack, needle)`. + +# Examples +```jldoctest +julia> contains("JuliaLang is pretty cool!", "Julia") +true + +julia> contains("JuliaLang is pretty cool!", 'a') +true + +julia> contains("aba", r"a.a") +true + +julia> contains("abba", r"a.a") +false +``` + +!!! compat "Julia 1.5" + The `contains` function requires at least Julia 1.5. +""" +contains(haystack::AbstractString, needle) = occursin(needle, haystack) + +""" + endswith(suffix) + +Create a function that checks whether its argument ends with `suffix`, i.e. +a function equivalent to `y -> endswith(y, suffix)`. + +The returned function is of type `Base.Fix2{typeof(endswith)}`, which can be +used to implement specialized methods. + +!!! compat "Julia 1.5" + The single argument `endswith(suffix)` requires at least Julia 1.5. + +""" +endswith(s) = Base.Fix2(endswith, s) + +""" + startswith(prefix) + +Create a function that checks whether its argument starts with `prefix`, i.e. +a function equivalent to `y -> startswith(y, prefix)`. + +The returned function is of type `Base.Fix2{typeof(startswith)}`, which can be +used to implement specialized methods. + +!!! compat "Julia 1.5" + The single argument `startswith(prefix)` requires at least Julia 1.5. + +""" +startswith(s) = Base.Fix2(startswith, s) + +""" + contains(needle) + +Create a function that checks whether its argument contains `needle`, i.e. +a function equivalent to `haystack -> contains(haystack, needle)`. + +The returned function is of type `Base.Fix2{typeof(contains)}`, which can be +used to implement specialized methods. +""" +contains(needle) = Base.Fix2(contains, needle) + +""" + 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) -> SubString + +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) -> SubString + lstrip(str::AbstractString, chars) -> SubString + +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) -> SubString + rstrip(str::AbstractString, chars) -> SubString + +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) -> SubString + strip(str::AbstractString, chars) -> SubString + +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 = findfirst(splitter,str) + if !isnothing(r) + 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 = findnext(splitter,str,k) + isnothing(r) && 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..3fbb1a9 --- /dev/null +++ b/base/subarray.jl @@ -0,0 +1,432 @@ +# 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 + +""" + SubArray{T,N,P,I,L} <: AbstractArray{T,N} + +`N`-dimensional view into a parent array (of type `P`) with an element type `T`, restricted by a tuple of indices (of type `I`). `L` is true for types that support fast linear indexing, and `false` otherwise. + +Construct `SubArray`s using the [`view`](@ref) function. +""" +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 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)) +sizeof(V::SubArray{<:Any,<:Any,<:Array}) = length(V) * elsize(V.parent) + +elsize(::Type{<:SubArray{<:Any,<:Any,P}}) where {P<:Array} = elsize(P) + +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 + +# Ranges implement getindex to return recomputed ranges; use that for views, too (when possible) +function view(r1::OneTo, r2::OneTo) + @_propagate_inbounds_meta + getindex(r1, r2) +end +function view(r1::AbstractUnitRange, r2::AbstractUnitRange{<:Integer}) + @_propagate_inbounds_meta + getindex(r1, r2) +end +function view(r1::AbstractUnitRange, r2::StepRange{<:Integer}) + @_propagate_inbounds_meta + getindex(r1, r2) +end +function view(r1::StepRange, r2::AbstractRange{<:Integer}) + @_propagate_inbounds_meta + getindex(r1, r2) +end +function view(r1::StepRangeLen, r2::OrdinalRange{<:Integer}) + @_propagate_inbounds_meta + getindex(r1, r2) +end +function view(r1::LinRange, r2::OrdinalRange{<:Integer}) + @_propagate_inbounds_meta + getindex(r1, r2) +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]) - stride1*first(axes1(I[1]))) +# If the result is one-dimensional and it's a Colon, then linear +# indexing uses the indices along the given dimension. +# If the result is one-dimensional and it's a range, then linear +# indexing might be offset if the index itself is offset +# 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{Slice}, I::Tuple) = + (@_inline_meta; compute_linindex(parent, I) - stride1*first(axes(parent, dims[1]))) # index-preserving case +compute_offset1(parent, stride1::Integer, dims, inds::Tuple{AbstractRange}, I::Tuple) = + (@_inline_meta; compute_linindex(parent, I) - stride1*first(axes1(inds[1]))) # potentially index-offsetting 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() = () + +function unsafe_convert(::Type{Ptr{T}}, V::SubArray{T,N,P,<:Tuple{Vararg{RangeIndex}}}) where {T,N,P} + return unsafe_convert(Ptr{T}, V.parent) + _memory_offset(V.parent, map(first, V.indices)...) +end + +pointer(V::FastSubArray, i::Int) = pointer(V.parent, V.offset1 + V.stride1*i) +pointer(V::FastContiguousSubArray, i::Int) = pointer(V.parent, V.offset1 + i) + +function pointer(V::SubArray{<:Any,<:Any,<:Array,<:Tuple{Vararg{RangeIndex}}}, is::AbstractCartesianIndex{N}) where {N} + index = first_index(V) + strds = strides(V) + for d = 1:N + 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 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..6cb6b12 --- /dev/null +++ b/base/sysimg.jl @@ -0,0 +1,105 @@ +# 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, + :Printf, + :Markdown, + :LibGit2, + :Logging, + :Sockets, + :Profile, + :Dates, + :DelimitedFiles, + :Random, + :UUIDs, + :Future, + :LinearAlgebra, + :SparseArrays, + :SuiteSparse, + :Distributed, + :SharedArrays, + :Pkg, + :Test, + :REPL, + :Statistics, + ] + + maxlen = reduce(max, textwidth.(string.(stdlibs)); init=0) + + # use a temp module to avoid leaving the type of this closure in Main + m = Module() + GC.@preserve m begin + print_time = @eval m (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 +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 + +# Set up Main module +import Base.MainInclude: eval, include + +Base.@eval Base let + ccall(:jl_clear_implicit_imports, Cvoid, (Any,), Main) + tot_time_userimg = @elapsed (isfile("userimg.jl") && include(Main, "userimg.jl")) + + tot_time_base = (end_base_include - start_base_include) * 10.0^(-9) + tot_time = tot_time_base + tot_time_stdlib[] + tot_time_userimg + + println("Sysimage built. Summary:") + print("Total ─────── "); time_print(tot_time * 10^9); print(" \n"); + print("Base: ─────── "); time_print(tot_time_base * 10^9); print(" "); show(IOContext(stdout, :compact=>true), (tot_time_base / tot_time) * 100); println("%") + print("Stdlibs: ──── "); time_print(tot_time_stdlib[] * 10^9); print(" "); show(IOContext(stdout, :compact=>true), (tot_time_stdlib[] / tot_time) * 100); println("%") + if isfile("userimg.jl") + print("Userimg: ──── "); time_print(tot_time_userimg * 10^9); print(" "); show(IOContext(stdout, :compact=>true), (tot_time_userimg / tot_time) * 100); println("%") + end + + empty!(LOAD_PATH) + empty!(DEPOT_PATH) +end diff --git a/base/sysinfo.jl b/base/sysinfo.jl new file mode 100644 index 0000000..d4b1c55 --- /dev/null +++ b/base/sysinfo.jl @@ -0,0 +1,521 @@ +# 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 +# In case STDLIB change after julia is built, the variable below can be used +# to update cached method locations to updated ones. +const BUILD_STDLIB_PATH = STDLIB + +# 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 bytes. +""" +free_memory() = ccall(:uv_get_free_memory, UInt64, ()) + +""" + Sys.total_memory() + +Get the total memory in RAM (including that which is currently used) in bytes. +""" +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..b717a31 --- /dev/null +++ b/base/task.jl @@ -0,0 +1,723 @@ +# 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 + +# have `waiter` wait for `t` +function _wait2(t::Task, waiter::Task) + if !istaskdone(t) + lock(t.donenotify) + if !istaskdone(t) + push!(t.donenotify.waitq, waiter) + unlock(t.donenotify) + return nothing + else + unlock(t.donenotify) + end + end + schedule(waiter) + 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(c::Channel{Any}) + local c_ex + while isready(c) + r = take!(c) + if isa(r, Task) + _wait(r) + if istaskfailed(r) + if !@isdefined(c_ex) + c_ex = CompositeException() + end + push!(c_ex, TaskFailedException(r)) + end + else + try + wait(r) + catch e + if !@isdefined(c_ex) + c_ex = CompositeException() + end + push!(c_ex, e) + end + end + end + close(c) + if @isdefined(c_ex) + 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 = Channel(Inf) + 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. + +Values can be interpolated into `@async` via `\$`, which copies the value directly into the +constructed underlying closure. This allows you to insert the _value_ of a variable, +isolating the aysnchronous code from changes to the variable's value in the current task. + +!!! compat "Julia 1.4" + Interpolating values via `\$` is available as of Julia 1.4. +""" +macro async(expr) + letargs = Base._lift_one_interp!(expr) + + thunk = esc(:(()->($expr))) + var = esc(sync_varname) + quote + let $(letargs...) + local task = Task($thunk) + if $(Expr(:islocal, var)) + put!($var, task) + end + schedule(task) + task + end + end +end + +# Capture interpolated variables in $() and move them to let-block +function _lift_one_interp!(e) + letargs = Any[] # store the new gensymed arguments + _lift_one_interp_helper(e, false, letargs) # Start out _not_ in a quote context (false) + letargs +end +_lift_one_interp_helper(v, _, _) = v +function _lift_one_interp_helper(expr::Expr, in_quote_context, letargs) + if expr.head === :$ + if in_quote_context # This $ is simply interpolating out of the quote + # Now, we're out of the quote, so any _further_ $ is ours. + in_quote_context = false + else + newarg = gensym() + push!(letargs, :($(esc(newarg)) = $(esc(expr.args[1])))) + return newarg # Don't recurse into the lifted $() exprs + end + elseif expr.head === :quote + in_quote_context = true # Don't try to lift $ directly out of quotes + elseif expr.head === :macrocall + return expr # Don't recur into macro calls, since some other macros use $ + end + for (i,e) in enumerate(expr.args) + expr.args[i] = _lift_one_interp_helper(e, in_quote_context, letargs) + end + expr +end + + +# add a wait-able object to the sync pool +macro sync_add(expr) + var = esc(sync_varname) + quote + local ref = $(esc(expr)) + put!($var, ref) + ref + end +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 + + 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 + +@inline set_next_task(t::Task) = ccall(:jl_set_next_task, Cvoid, (Any,), t) + +""" + 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()) + set_next_task(t) + return try_yieldto(ensure_rescheduled) +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 + set_next_task(t) + return try_yieldto(identity) +end + +function try_yieldto(undo) + try + ccall(:jl_switch, Cvoid, ()) + catch + undo(ccall(:jl_get_next_task, Ref{Task}, ())) + 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 poptask(W::StickyWorkqueue) + task = trypoptask(W) + if !(task isa Task) + task = ccall(:jl_task_get_next, Ref{Task}, (Any, Any), trypoptask, W) + end + set_next_task(task) + nothing +end + +function wait() + W = Workqueues[Threads.threadid()] + poptask(W) + result = try_yieldto(ensure_rescheduled) + 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..aa12204 --- /dev/null +++ b/base/threadingconstructs.jl @@ -0,0 +1,182 @@ +# 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()`](@ref). +""" +nthreads() = Int(unsafe_load(cglobal(:jl_n_threads, Cint))) + +function threading_run(func) + ccall(:jl_enter_threaded_region, Cvoid, ()) + n = nthreads() + tasks = Vector{Task}(undef, n) + for i = 1:n + t = Task(func) + t.sticky = true + ccall(:jl_set_task_tid, Cvoid, (Any, Cint), t, i-1) + tasks[i] = t + schedule(t) + end + try + for i = 1:n + wait(tasks[i]) + end + finally + ccall(:jl_exit_threaded_region, Cvoid, ()) + end +end + +function _threadsfor(iter, lbody, schedule) + 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 = firstindex(r) + ((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)) = @inbounds r[i] + $(esc(lbody)) + end + end + end + if threadid() != 1 || ccall(:jl_in_threaded_region, Cint, ()) != 0 + $(if schedule === :static + :(error("`@threads :static` can only be used from thread 1 and not nested")) + else + # only use threads when called from thread 1, outside @threads + :(Base.invokelatest(threadsfor_fun, true)) + end) + else + threading_run(threadsfor_fun) + end + nothing + end +end + +""" + Threads.@threads [schedule] for ... end + +A macro to parallelize a `for` loop to run with multiple threads. Splits the iteration +space among multiple tasks and runs those tasks on threads according to a scheduling +policy. +A barrier is placed at the end of the loop which waits for all tasks to finish +execution. + +The `schedule` argument can be used to request a particular scheduling policy. +The only currently supported value is `:static`, which creates one task per thread +and divides the iterations equally among them. Specifying `:static` is an error +if used from inside another `@threads` loop or from a thread other than 1. + +The default schedule (used when no `schedule` argument is present) is subject to change. + +!!! compat "Julia 1.5" + The `schedule` argument is available as of Julia 1.5. +""" +macro threads(args...) + na = length(args) + if na == 2 + sched, ex = args + if sched isa QuoteNode + sched = sched.value + elseif sched isa Symbol + # for now only allow quoted symbols + sched = nothing + end + if sched !== :static + throw(ArgumentError("unsupported schedule argument in @threads")) + end + elseif na == 1 + sched = :default + ex = args[1] + else + throw(ArgumentError("wrong number of arguments in @threads")) + end + if !(isa(ex, Expr) && ex.head === :for) + throw(ArgumentError("@threads requires a `for` loop expression")) + end + if !(ex.args[1] isa Expr && ex.args[1].head === :(=)) + throw(ArgumentError("nested outer loops are not currently supported by @threads")) + end + return _threadsfor(ex.args[1], ex.args[2], sched) +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. + +Values can be interpolated into `@spawn` via `\$`, which copies the value directly into the +constructed underlying closure. This allows you to insert the _value_ of a variable, +isolating the aysnchronous code from changes to the variable's value in the current task. + +!!! note + See the manual chapter on threading for important caveats. + +!!! compat "Julia 1.3" + This macro is available as of Julia 1.3. + +!!! compat "Julia 1.4" + Interpolating values via `\$` is available as of Julia 1.4. +""" +macro spawn(expr) + letargs = Base._lift_one_interp!(expr) + + thunk = esc(:(()->($expr))) + var = esc(Base.sync_varname) + quote + let $(letargs...) + local task = Task($thunk) + task.sticky = false + if $(Expr(:islocal, var)) + put!($var, task) + end + schedule(task) + task + end + end +end diff --git a/base/threads.jl b/base/threads.jl new file mode 100644 index 0000000..2b68c71 --- /dev/null +++ b/base/threads.jl @@ -0,0 +1,35 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" +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/timing.jl b/base/timing.jl new file mode 100644 index 0000000..ac19e9a --- /dev/null +++ b/base/timing.jl @@ -0,0 +1,315 @@ +# 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 ::Int64 + realloc ::Int64 + poolalloc ::Int64 + bigalloc ::Int64 + freecall ::Int64 + total_time ::Int64 + total_allocd ::Int64 # GC internal + since_sweep ::Int64 # 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 + 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, + new.malloc - old.malloc, + new.realloc - old.realloc, + new.poolalloc - old.poolalloc, + new.bigalloc - old.bigalloc, + new.freecall - old.freecall, + 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, ()) + +""" + Base.gc_live_bytes() + +Return the total size (in bytes) of objects currently in memory. +This is computed as the total size of live objects after +the last garbage collection, plus the number of bytes allocated +since then. +""" +function gc_live_bytes() + num = gc_num() + Int(ccall(:jl_gc_live_bytes, Int64, ())) + num.allocd + num.deferred_alloc +end + +# 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) # also used by InteractiveUtils + bytes, mb = prettyprint_getunits(bytes, length(_mem_units), Int64(1024)) + if mb == 1 + return string(Int(bytes), " ", _mem_units[mb], bytes==1 ? "" : "s") + else + return string(Ryu.writefixed(Float64(bytes), 3), " ", _mem_units[mb]) + end +end + +function time_print(elapsedtime, bytes=0, gctime=0, allocs=0) + timestr = Ryu.writefixed(Float64(elapsedtime/1e9), 6) + length(timestr) < 10 && print(" "^(10 - length(timestr))) + print(timestr, " seconds") + if bytes != 0 || allocs != 0 + allocs, ma = prettyprint_getunits(allocs, length(_cnt_units), Int64(1000)) + if ma == 1 + print(" (", Int(allocs), _cnt_units[ma], allocs==1 ? " allocation: " : " allocations: ") + else + print(" (", Ryu.writefixed(Float64(allocs), 2), _cnt_units[ma], " allocations: ") + end + print(format_bytes(bytes)) + end + if gctime > 0 + print(", ", Ryu.writefixed(Float64(100*gctime/elapsedtime), 2), "% gc time") + end + if bytes != 0 || allocs != 0 + print(")") + end + nothing +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). + +!!! note + For more serious benchmarking, consider the `@btime` macro from the BenchmarkTools.jl + package which among other things evaluates the function multiple times in order to + reduce noise. + +```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 + while false; end # compiler heuristic: compile this block (alter this if the heuristic changes) + 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 + while false; end # compiler heuristic: compile this block (alter this if the heuristic changes) + 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 + while false; end # compiler heuristic: compile this block (alter this if the heuristic changes) + local t0 = time_ns() + $(esc(ex)) + (time_ns() - t0) / 1e9 + end +end + +# total number of bytes allocated so far +gc_bytes(b::Ref{Int64}) = ccall(:jl_gc_get_total_bytes, Cvoid, (Ptr{Int64},), b) +# NOTE: gc_bytes() is deprecated +function gc_bytes() + b = Ref{Int64}() + gc_bytes(b) + b[] +end + +""" + @allocated + +A macro to evaluate an expression, discarding the resulting value, instead returning the +total number of bytes allocated during evaluation of the expression. + +See also [`@time`](@ref), [`@timev`](@ref), [`@timed`](@ref), +and [`@elapsed`](@ref). + +```julia-repl +julia> @allocated rand(10^6) +8000080 +``` +""" +macro allocated(ex) + quote + while false; end # compiler heuristic: compile this block (alter this if the heuristic changes) + local b0 = Ref{Int64}(0) + local b1 = Ref{Int64}(0) + gc_bytes(b0) + $(esc(ex)) + gc_bytes(b1) + b1[] - b0[] + 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> stats = @timed rand(10^6); + +julia> stats.time +0.006634834 + +julia> stats.bytes +8000256 + +julia> stats.gctime +0.0055765 + +julia> propertynames(stats.gcstats) +(:allocd, :malloc, :realloc, :poolalloc, :bigalloc, :freecall, :total_time, :pause, :full_sweep) + +julia> stats.gcstats.total_time +5576500 +``` + +!!! compat "Julia 1.5" + The return type of this macro was changed from `Tuple` to `NamedTuple` in Julia 1.5. +""" +macro timed(ex) + quote + while false; end # compiler heuristic: compile this block (alter this if the heuristic changes) + local stats = gc_num() + local elapsedtime = time_ns() + local val = $(esc(ex)) + elapsedtime = time_ns() - elapsedtime + local diff = GC_Diff(gc_num(), stats) + (value=val, time=elapsedtime/1e9, bytes=diff.allocd, gctime=diff.total_time/1e9, gcstats=diff) + end +end diff --git a/base/traits.jl b/base/traits.jl new file mode 100644 index 0000000..69be1c9 --- /dev/null +++ b/base/traits.jl @@ -0,0 +1,60 @@ +# 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() +OrderStyle(::Type{Union{}}) = Ordered() + +# 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/ttyhascolor.jl b/base/ttyhascolor.jl new file mode 100644 index 0000000..800dcc3 --- /dev/null +++ b/base/ttyhascolor.jl @@ -0,0 +1,27 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +if Sys.iswindows() + ttyhascolor(term_type = nothing) = true +else + function ttyhascolor(term_type = get(ENV, "TERM", "")) + startswith(term_type, "xterm") && return true + try + @static if Sys.KERNEL === :FreeBSD + return success(`tput AF 0`) + else + return success(`tput setaf 0`) + end + catch e + return false + end + end +end +function get_have_color() + global have_color + have_color === nothing && (have_color = ttyhascolor()) + return have_color::Bool +end +in(key_value::Pair{Symbol,Bool}, ::TTY) = key_value.first === :color && key_value.second === get_have_color() +haskey(::TTY, key::Symbol) = key === :color +getindex(::TTY, key::Symbol) = key === :color ? get_have_color() : throw(KeyError(key)) +get(::TTY, key::Symbol, default) = key === :color ? get_have_color() : default \ No newline at end of file diff --git a/base/tuple.jl b/base/tuple.jl new file mode 100644 index 0000000..3a5806d --- /dev/null +++ b/base/tuple.jl @@ -0,0 +1,428 @@ +# 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}) = (eltype(t)[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(c::Tuple, v, i::Integer) + +Creates a new tuple similar to `x` with the value at index `i` set to `v`. +Throws a `BoundsError` when out of bounds. + +# Examples +```jldoctest +julia> Base.setindex((1, 2, 6), 2, 3) == (1, 2, 2) +true +``` +""" +function setindex(x::Tuple, v, i::Integer) + @boundscheck 1 <= i <= length(x) || throw(BoundsError(x, i)) + @_inline_meta + _setindex(v, i, x...) +end + +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 + +_foldl_impl(op, init, itr::Tuple) = afoldl(op, init, itr...) + +# 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 + +Tuple(x::Ref) = tuple(getindex(x)) # faster than iterator for one element +Tuple(x::Array{T,0}) where {T} = tuple(getindex(x)) + +(::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 + +## filter ## + +filter(f, xs::Tuple) = afoldl((ys, x) -> f(x) ? (ys..., x) : ys, (), xs...) + +# use Array for long tuples +filter(f, t::Any16) = Tuple(filter(f, collect(t))) + +## 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..4fac090 --- /dev/null +++ b/base/twiceprecision.jl @@ -0,0 +1,731 @@ +# 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) + +function zero(::Type{TwicePrecision{T}}) where {T} + z = zero(T) + TwicePrecision{T}(z, z) +end + +# 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 + +step(r::StepRangeLen{T,TwicePrecision{T},TwicePrecision{T}}) where {T<:AbstractFloat} = T(r.step) +step(r::StepRangeLen{T,TwicePrecision{T},TwicePrecision{T}}) where {T} = T(r.step) + +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) + W = widen(Int) + Δn = W(sp[1]) * W(sp[2]) - W(sn[1]) * W(sn[2]) + s = r.step * Δn + # Add in contributions of ref + ref = r.ref * l + convert(eltype(r), s + ref) +end +function sum(r::StepRangeLen{<:Any,<:TwicePrecision,<:TwicePrecision}) + 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..2f34507 --- /dev/null +++ b/base/util.jl @@ -0,0 +1,542 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## 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`, `--track-allocation`, `--color`, `--startup-file`, 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. + +!!! compat "Julia 1.5" + The flags `--color` and `--startup-file` were added in Julia 1.5. +""" +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 == 1 + "yes" + elseif opts.depwarn == 2 + "error" + else + "" # default = "no" + 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 == 1 + push!(addflags, "--track-allocation=user") + elseif opts.malloc_log == 2 + push!(addflags, "--track-allocation=all") + end + if opts.color == 1 + push!(addflags, "--color=yes") + elseif opts.color == 2 + push!(addflags, "--color=no") + end + if opts.startupfile == 2 + push!(addflags, "--startup-file=no") + 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, revise=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 `revise` is `true`, the `Revise` package is used to load any modifications to `Base` or +to the standard libraries before running the tests. +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::Bool=false, + revise::Bool=false, + seed::Union{BitInteger,Nothing}=nothing) + if isa(tests,AbstractString) + tests = split(tests) + end + exit_on_error && push!(tests, "--exit-on-error") + revise && push!(tests, "--revise") + 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)) + nothing + 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..c8d7549 --- /dev/null +++ b/base/version.jl @@ -0,0 +1,302 @@ +# 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 = if endswith(libllvm_version_string, "jl") + # strip the "jl" SONAME suffix (JuliaLang/julia#33058) + # (LLVM does never report a prerelease version anyway) + VersionNumber(libllvm_version_string[1:end-2]) +else + VersionNumber(libllvm_version_string) +end + +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..955f604 --- /dev/null +++ b/base/views.jl @@ -0,0 +1,228 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" + replace_ref_begin_end!(ex) + +Recursively replace occurrences of the symbols `:begin` and `:end` in a "ref" expression +(i.e. `A[...]`) `ex` with the appropriate function calls (`firstindex` or `lastindex`). +Replacement uses the closest enclosing ref, so + + A[B[end]] + +should transform to + + A[B[lastindex(B)]] + +""" +replace_ref_begin_end!(ex) = replace_ref_begin_end_!(ex, nothing)[1] +# replace_ref_begin_end_!(ex,withex) returns (new ex, whether withex was used) +function replace_ref_begin_end_!(ex, withex) + used_withex = false + if isa(ex,Symbol) + if ex === :begin + withex === nothing && error("Invalid use of begin") + return withex[1], true + elseif ex === :end + withex === nothing && error("Invalid use of end") + return withex[2], true + end + elseif isa(ex,Expr) + if ex.head === :ref + ex.args[1], used_withex = replace_ref_begin_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_begin_end_!(ex.args[2], (:($firstindex($S)),:($lastindex($S)))) + else + n = 1 + J = lastindex(ex.args) + for j = 2:J + exj, used = replace_ref_begin_end_!(ex.args[j], (:($firstindex($S)),:($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_begin_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. + +!!! compat "Julia 1.5" + Using `begin` in an indexing expression to refer to the first index requires at least + Julia 1.5. + +# 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_begin_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::Union{Number,AbstractCartesianIndex}...) = 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 +# firstindex and lastindex in replace_ref_begin_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`](@ref) 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. + +!!! compat "Julia 1.5" + Using `begin` in an indexing expression to refer to the first index requires at least + Julia 1.5. + +# 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_begin_end!(x))) +end diff --git a/base/weakkeydict.jl b/base/weakkeydict.jl new file mode 100644 index 0000000..f21097d --- /dev/null +++ b/base/weakkeydict.jl @@ -0,0 +1,124 @@ +# 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 + +sizehint!(d::WeakKeyDict, newsz) = sizehint!(d.ht, newsz) +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); 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..574cf24 --- /dev/null +++ b/contrib/README.md @@ -0,0 +1,34 @@ +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 | +|[ mac/ ](https://github.com/JuliaLang/julia/blob/master/contrib/mac/) | Mac install files | +|[ relative_path.py ](https://github.com/JuliaLang/julia/blob/master/contrib/relative_path.py) | Convert absolute paths into relative paths | +|[ 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..ce52881 --- /dev/null +++ b/contrib/add_license_to_files.jl @@ -0,0 +1,199 @@ +#!/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", + "../base/ryu", + "../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..897a1a0 --- /dev/null +++ b/contrib/fixup-libgfortran.sh @@ -0,0 +1,163 @@ +#!/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 "$*" >&2; } +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 + ${PATCHELF} --print-needed "$lib_path" | grep "$2" | 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" ] || [ "$(dirname "$1")" = "." ]; 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)" + if [ -z "$LIBGFORTRAN_PATH" ]; then continue; fi + + # 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_DIR="$(find_shlib_dir "$LIBGFORTRAN_PATH")" + LIBGFORTRAN_DIRS="$LIBGFORTRAN_DIRS $LIBGFORTRAN_DIR" + + # Save the SONAME + LIBGFORTRAN_SONAME="$(basename "$LIBGFORTRAN_PATH")" + LIBGFORTRAN_SONAMES="$LIBGFORTRAN_SONAMES $LIBGFORTRAN_SONAME" + + + # Now that we've (maybe) found a libgfortran, ask _it_ for things like libgcc_s and libquadmath: + LIBGCC_PATH="$(find_shlib "${LIBGFORTRAN_DIR}/${LIBGFORTRAN_SONAME}" libgcc_s)" + LIBQUADMATH_PATH="$(find_shlib "${LIBGFORTRAN_DIR}/${LIBGFORTRAN_SONAME}" libquadmath)" + + if [ ! -z "$LIBGCC_PATH" ]; then + LIBGFORTRAN_DIRS="$LIBGFORTRAN_DIRS $(find_shlib_dir $LIBGCC_PATH)" + LIBGCC_SONAMES="$LIBGCC_SONAMES $(basename "$LIBGCC_PATH")" + fi + if [ ! -z "$LIBQUADMATH_PATH" ]; then + LIBGFORTRAN_DIRS="$LIBGFORTRAN_DIRS $(find_shlib_dir $LIBQUADMATH_PATH)" + LIBQUADMATH_SONAMES="$LIBQUADMATH_SONAMES $(basename "$LIBQUADMATH_PATH")" + fi + 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 + debug "Rewriting identity of ${private_libdir}/${soname} to @rpath/${soname}" + 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" + chmod 755 "$lib_path" + 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..be9ad9f --- /dev/null +++ b/contrib/generate_precompile.jl @@ -0,0 +1,205 @@ +# 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" + +hardcoded_precompile_statements = """ +precompile(Tuple{typeof(Base.stale_cachefile), String, String}) +precompile(Tuple{typeof(push!), Set{Module}, Module}) +precompile(Tuple{typeof(push!), Set{Method}, Method}) +precompile(Tuple{typeof(push!), Set{Base.PkgId}, Base.PkgId}) +precompile(Tuple{typeof(setindex!), Dict{String,Base.PkgId}, Base.PkgId, String}) +""" + +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")) + +Distributed = get(Base.loaded_modules, + Base.PkgId(Base.UUID("8ba89e20-285c-5b6f-9357-94700520ee1b"), "Distributed"), + nothing) +if Distributed !== nothing + precompile_script *= """ + using Distributed + addprocs(2) + pmap(x->iseven(x) ? 1 : 0, 1:4) + @distributed (+) for i = 1:100 Int(rand(Bool)) end + """ +end + +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_ns() + 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 the precompile file + statements = Set{String}() + for statement in eachline(precompile_file_h) + # Main should be completely clean + occursin("Main.", statement) && continue + push!(statements, statement) + end + + for statement in split(hardcoded_precompile_statements, '\n') + 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 > 1500 + end + + print(" $(length(statements)) generated in ") + tot_time = time_ns() - start_time + Base.time_print(tot_time) + print(" (overhead "); Base.time_print(tot_time - (include_time * 1e9)); 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/mac/app/.gitignore b/contrib/mac/app/.gitignore new file mode 100644 index 0000000..5af425f --- /dev/null +++ b/contrib/mac/app/.gitignore @@ -0,0 +1,4 @@ +julia/ +dmg/ +*.dmg +notarize-*.xml diff --git a/contrib/mac/app/Entitlements.plist b/contrib/mac/app/Entitlements.plist new file mode 100644 index 0000000..b84dccb --- /dev/null +++ b/contrib/mac/app/Entitlements.plist @@ -0,0 +1,24 @@ + + + + + com.apple.security.automation.apple-events + + com.apple.security.cs.get-task-allow + + com.apple.security.cs.allow-dyld-environment-variables + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.debugger + + com.apple.security.cs.disable-library-validation + + com.apple.security.device.audio-input + + com.apple.security.device.camera + + + diff --git a/contrib/mac/app/Makefile b/contrib/mac/app/Makefile new file mode 100644 index 0000000..665b4f1 --- /dev/null +++ b/contrib/mac/app/Makefile @@ -0,0 +1,99 @@ +# 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:=© $(shell date '+%Y') 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"; \ + MACHO_FILES=$$(find "$@" -type f -perm -755 | cut -d: -f1); \ + for f in $${MACHO_FILES}; do \ + echo "Codesigning $${f}..."; \ + codesign -s "$$MACOS_CODESIGN_IDENTITY" --option=runtime --entitlements Entitlements.plist -vvv --timestamp --deep --force "$${f}"; \ + done; \ + codesign -s "$$MACOS_CODESIGN_IDENTITY" --option=runtime --entitlements Entitlements.plist -vvv --timestamp --deep --force "$@"; \ + 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 notarize-*.xml +else + rm -rf dmg *.dmg notarize-*.xml +endif + +notarize-upload-$(DMG_NAME).xml: $(DMG_NAME) + @# Upload the `.dmg` for notarization + xcrun altool --notarize-app --primary-bundle-id org.julialang.launcherapp --username "$$APPLEID" --password "$$APPLEID_PASSWORD" -itc_provider A427R7F42H --file "$(DMG_NAME)" --output-format xml > "$@" + @# Sleep for a few seconds so that we don't immediately error out when we request the UUID from Apple + @sleep 5 + + +notarize-check: notarize-upload-$(DMG_NAME).xml + @# We wait in a while loop for notarization to complete + ./notarize_check.sh "$<" + +# This is the top-level notarization target. Note that this is still a somewhat manual +# process; things can go wrong, and so if it fails, you may need to inspect the `.xml` +# files to see what went wrong, but in general you can just run `make notarize` and it +# should upload, notarize, staple, and re-package the .dmg for you. +# Note that for this to work, you need to have exported `APPLEID`, `APPLEID_PASSWORD` +# and `MACOS_CODESIGN_IDENTITY` to have signed the `.app` in the first place. +notarize: notarize-check + @# Delete old .dmg file + rm -f $(DMG_NAME) + @# Staple the .app + xcrun stapler staple dmg/$(APP_NAME) + @# re-build the .dmg + $(MAKE) $(DMG_NAME) + + +.PHONY: clean all notarize-upload notarize-check 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 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 +MrDih+A5&S+h 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 ukEn!eH9TmY?{`1}>H0KSvPze~/dev/null + if [[ $? != 0 ]]; then + sed -n -E "s/${SED_PATTERN}/\1/p" "${PLIST_FILE}" 2>/dev/null | head -1 + fi +} + +# Continually probe and ask if Apple is done notarizing our precious binary bits +function wait_until_completed() +{ + UUID="$1" + PLIST_FILE="$2" + echo "Waiting until UUID ${UUID} is done processing...." + + while true; do + xcrun altool --notarization-info "${UUID}" --username "${APPLEID}" --password "${APPLEID_PASSWORD}" --output-format xml > "${PLIST_FILE}" + STATUS=$(/usr/libexec/PlistBuddy -c "print notarization-info:Status" "${PLIST_FILE}" 2>/dev/null) + + # Process loop exit conditions + if [[ ${STATUS} == "success" ]]; then + echo "Notarization finished" + return 0 + elif [[ ${STATUS} == "in progress" ]]; then + echo -n "." + sleep 10 + continue + elif [[ ${STATUS} == "invalid" ]]; then + echo "invalid! Looks like something got borked:" + /usr/libexec/PlistBuddy -c "print notarization-info:LogFileURL" "${PLIST_FILE}" 2>/dev/null + exit 1 + else + echo "Notarization failed with status ${STATUS}" + exit 1 + fi + done +} + +if [[ "$#" != 1 ]]; then + echo "Usage: $0 notarize-upload-.xml" + exit 1 +fi + +# Get input parameters +UPLOAD_PLIST_FILE="$1" +SUFFIX="${UPLOAD_PLIST_FILE#"notarize-upload-"}" +SUFFIX="${SUFFIX%".xml"}" + +# Extract UUID from uploaded plist file +UUID=$(extract_uuid "${UPLOAD_PLIST_FILE}") +if [[ -z "${UUID}" ]]; then + echo "ERROR: Could not extract UUID value from ${UPLOAD_PLIST_FILE}" >&2 + exit 1 +fi + +# Wait until the UUID is done processing +wait_until_completed "${UUID}" "notarize-check-${SUFFIX}.xml" diff --git a/contrib/mac/app/renotarize_dmg.sh b/contrib/mac/app/renotarize_dmg.sh new file mode 100755 index 0000000..3942126 --- /dev/null +++ b/contrib/mac/app/renotarize_dmg.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# We need a URL +if [[ -z "$1" ]]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +# You need to define these in your environment +if [[ -z "${APPLEID}" ]] || [[ -z "${APPLEID_PASSWORD}" ]]; then + echo "You must define APPLEID and APPLEID_PASSWORD in your environment!" >&2 + exit 1 +fi + +# Download .dmg +curl -L "$1" -O + +# Unpack dmg into our `dmg` folder +rm -rf dmg + +# Copy app over to our `dmg` folder +for j in /Volumes/Julia-*; do hdiutil detach "${j}"; done +hdiutil mount "$(basename "$1")" +cp -Ra /Volumes/Julia-* dmg + +# Override some important Makefile variables +DMG_NAME=$(basename "$1") +APP_NAME=$(basename dmg/*.app) +VOL_NAME=$(basename /Volumes/Julia-*) +# Unmount everything again +for j in /Volumes/Julia-*; do hdiutil detach "${j}"; done + +# Run notarization +make notarize "DMG_NAME=${DMG_NAME}" "APP_NAME=${APP_NAME}" "VOL_NAME=${VOL_NAME}" diff --git a/contrib/mac/app/startup.applescript b/contrib/mac/app/startup.applescript new file mode 100644 index 0000000..f02830a --- /dev/null +++ b/contrib/mac/app/startup.applescript @@ -0,0 +1,5 @@ +set RootPath to POSIX path of (path to me) +tell application id "com.apple.terminal" + do script ("exec '" & RootPath & "Contents/Resources/julia/bin/julia'") + activate +end tell diff --git a/contrib/mac/framework/Julia.h b/contrib/mac/framework/Julia.h new file mode 100644 index 0000000..7e70489 --- /dev/null +++ b/contrib/mac/framework/Julia.h @@ -0,0 +1,6 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + +#include +#include +#include +#include diff --git a/contrib/mac/framework/Makefile b/contrib/mac/framework/Makefile new file mode 100644 index 0000000..4449436 --- /dev/null +++ b/contrib/mac/framework/Makefile @@ -0,0 +1,188 @@ +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 -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..42ef146 --- /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 + occurred 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..4cd03be72cb8a5968c2be496e211f186a303932f GIT binary patch literal 5865 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 literal 0 HcmV?d00001 diff --git a/contrib/mac/frameworkapp/JuliaLauncher/Assets.xcassets/AppIcon.appiconset/128@2x.png b/contrib/mac/frameworkapp/JuliaLauncher/Assets.xcassets/AppIcon.appiconset/128@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..f391207782d2e1a5837ca3a29a8b02dc3678a15a GIT binary patch literal 12858 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 W|G$HP&)^Es0VFT20xg#`4f#Jvh}RMT literal 0 HcmV?d00001 diff --git a/contrib/mac/frameworkapp/JuliaLauncher/Assets.xcassets/AppIcon.appiconset/16.png b/contrib/mac/frameworkapp/JuliaLauncher/Assets.xcassets/AppIcon.appiconset/16.png new file mode 100644 index 0000000000000000000000000000000000000000..50f46aa80179f41c734ce7177141b39a7cd08cb4 GIT binary patch literal 703 zcmV;w0zmzVP)Px%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*L6z3(M!jeUw<8_n*iv@TiSDciFe(@I)yydQgKv05y;S-n{F zng4pGUAQnYv1G9zLX1h9vGc!anXT%iZU{bv5Zn@QDGS6E& zIFiSi0#*i}oxx|N^SHS!AKFw}VXHWnG(aOOD{fmC7R(D4UU?5IonL!NAnVasbh_cJ z%VH`?*Vn5QPqH$2jkh}H^(LciL_{HuRX*t846-lsAEom?D`$0meTtHzmKu?I=+nGP zC8m+pteW1&;o3~W%!I)(JuSPDI}lHqlYX2Y)753a5!~<9&FJSwh6j@9sTWIVX|0SkT6DK#`C3?S^CoZ+OZcT=W~SLHUxMabU`h9t9@Ol+e~ zK_*ztu>jm=I7w6mEh|%lg8f&j;3&j_y*u~hJ3z852pr}Y>wkC;*_ zl|~tts}u?qDUL#GDH>Og78ez+KPV+m4ktZ7B0Yx(*3OJJ;H6qn}6R zykhPo9L;s!FnN4SeQyKzIqcB;f$puln%>x)gYQ1P_{rJee50b`j2vEp4yaGbD&w4; z-g=K{Pfp(U^O0p>pO=TML1XqDbanOKzr}UaIwuF`^_v~tk>P;5W4ni~P4=y@s_L-* zKCh!_?D_;`Z$Z>hkbVBGeQtM4@55k7rhNeXbrfMu7=DM_ndlYacp_|e7ot3tiXr%g z7C%e%A^5sHU-30w@PJcy%iylxVDYp;zb4xD-Cgmt-^ZzH!fD zrcCxxVOB+DV0%e80k_F+73!hN&aW%BCm^~N7WbX)tEihP>J}KtHeA1bz(e~C`u@&T z|H#x>aw-)|IY)&6cw#&;ZWvED4-W#y3+v;J_43(=!C*0%z}@F4{|Y1}$I@vR{y%Ui Ry+Z~CK#+e3j`KY!`yZqYdPx8P 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..90f2436656d61a4d7262251c90fc7bbe2807bf25 GIT binary patch literal 13006 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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..6f8f3ce4a3e737eda62b00d91bcef0040d9ca7bb GIT binary patch literal 28597 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 bSCDmvaPj{QUr|TiLIw~M6^FSWnL+;>LrsjH 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..540cd13bfea801e12515f995f7743537e6b3cca2 GIT binary patch literal 2743 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 literal 0 HcmV?d00001 diff --git a/contrib/mac/frameworkapp/JuliaLauncher/Assets.xcassets/AppIcon.appiconset/512.png b/contrib/mac/frameworkapp/JuliaLauncher/Assets.xcassets/AppIcon.appiconset/512.png new file mode 100644 index 0000000000000000000000000000000000000000..18950b3cea7a997250c676f2fa0d08445c393c85 GIT binary patch literal 28591 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 D2ROZK literal 0 HcmV?d00001 diff --git a/contrib/mac/frameworkapp/JuliaLauncher/Assets.xcassets/AppIcon.appiconset/512@2x.png b/contrib/mac/frameworkapp/JuliaLauncher/Assets.xcassets/AppIcon.appiconset/512@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..77e99dcf4bee06738f499219ed910e0f40f158aa GIT binary patch literal 63825 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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..ad73204 --- /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: {} [] []".format(sys.argv[0])) + sys.exit(1) + +arch_mapping = { + 'x86_64': '(x86_|amd)64', + 'i686': "i\\d86", + 'aarch64': "aarch64", + 'armv7l': "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", +} +libgfortran_version_mapping = { + 'blank_libgfortran': "", + 'libgfortran3': "-libgfortran3", + 'libgfortran4': "-libgfortran4", + 'libgfortran5': "-libgfortran5", +} +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(libgfortran_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) +libgfortran_version = get_field(m, libgfortran_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_libgfortran", "") + 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 +# "-libgfortran5" tag at the end of the triplet, but only if it has otherwise +# not been specified +if libgfortran_version == "blank_libgfortran": + if len(sys.argv) >= 3: + libgfortran_version = { + "4": "libgfortran3", + "5": "libgfortran3", + "6": "libgfortran3", + "7": "libgfortran4", + "8": "libgfortran5", + "9": "libgfortran5", + "10": "libgfortran5", + }[list(filter(lambda x: re.match("\d+\.\d+(\.\d+)?", x), sys.argv[2].split()))[-1].split('.')[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(libgfortran_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..e76b2dc --- /dev/null +++ b/contrib/refresh_bb_tarballs.sh @@ -0,0 +1,67 @@ +#!/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 armv7l-linux-gnueabihf powerpc64le-linux-gnu i686-linux-musl x86_64-linux-musl aarch64-linux-musl armv7l-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 suitesparse openlibm" +BB_GCC_EXPANDED_PROJECTS="openblas" +BB_CXX_EXPANDED_PROJECTS="llvm" + +# 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 + case "${BB_CXX_EXPANDED_PROJECTS}" in + *${1}*) BB_CXX_EXPANDED_PROJECTS="${1}" ;; + *) BB_CXX_EXPANDED_PROJECTS="" ;; + esac +fi + +# Get "contrib/" directory path +CONTRIB_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) + +# Get the source hash for each project +for proj in ${BB_PROJECTS}; do + PROJ="$(echo ${proj} | tr [a-z] [A-Z])" + make -C "${CONTRIB_DIR}/../deps" USE_BINARYBUILDER_${PROJ}=0 DEPS_GIT=0 extract-${proj} +done + +# 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 libgfortran in libgfortran3 libgfortran4 libgfortran5; do + make -C "${CONTRIB_DIR}/../deps" USE_BINARYBUILDER_${PROJ}=1 ${PROJ}_BB_TRIPLET=${triplet}-${libgfortran} BB_TRIPLET_CXXABI=${triplet} distclean-${proj} + make -C "${CONTRIB_DIR}/../deps" USE_BINARYBUILDER_${PROJ}=1 ${PROJ}_BB_TRIPLET=${triplet}-${libgfortran} BB_TRIPLET_CXXABI=${triplet} install-${proj} + done + done + + for proj in ${BB_CXX_EXPANDED_PROJECTS}; do + PROJ="$(echo ${proj} | tr [a-z] [A-Z])" + for cxx in cxx03 cxx11; do + make -C "${CONTRIB_DIR}/../deps" USE_BINARYBUILDER_${PROJ}=1 ${PROJ}_BB_TRIPLET=${triplet}-${cxx} BB_TRIPLET_CXXABI=${triplet} distclean-${proj} + make -C "${CONTRIB_DIR}/../deps" USE_BINARYBUILDER_${PROJ}=1 ${PROJ}_BB_TRIPLET=${triplet}-${cxx} BB_TRIPLET_CXXABI=${triplet} install-${proj} + done + done +done diff --git a/contrib/relative_path.py b/contrib/relative_path.py new file mode 100755 index 0000000..b9d3d1e --- /dev/null +++ b/contrib/relative_path.py @@ -0,0 +1,10 @@ +import sys, os +if len(sys.argv) != 3: + sys.stderr.write("\nrelative_path.py - incomplete arguments: %s\n"%(sys.argv)) + sys.exit(1) + +# We always use `/` as the path separator, no matter what OS we're running on, since our +# shells and whatnot during the build are all POSIX shells/cygwin. We rely on the build +# system itself to canonicalize to `\` when it needs to, and deal with the shell escaping +# and whatnot at the latest possible moment. +sys.stdout.write(os.path.relpath(sys.argv[2], sys.argv[1]).replace(os.path.sep, '/')) \ No newline at end of file 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..90e231f --- /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: pcre unwind gmp mpfr patchelf libuv curl +# custom Makefile rules: openlibm dsfmt suitesparse-wrapper suitesparse lapack openblas utf8proc objconv osxunwind libwhich +# CMake libs: llvm libgit2 libssh2 mbedtls +# +# downloadable via git: llvm-svn, libuv, libopenlibm, utf8proc, libgit2, libssh2 +# +# 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 + +# unlist targets that have not been converted to use the staged-install +DEP_LIBS_STAGED := $(filter-out suitesparse-wrapper,$(DEP_LIBS)) +ifneq ($(USE_BINARYBUILDER_LIBUNWIND),1) +DEP_LIBS_STAGED := $(filter-out osxunwind,$(DEP_LIBS)) +endif + + +## 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)) +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 + +include $(SRCDIR)/tools/uninstallers.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..fc8b612 --- /dev/null +++ b/deps/SuiteSparse_wrapper.c @@ -0,0 +1,47 @@ +/* + SuiteSparse_wrapper.c: Changes made to this file in the Julia repo + in deps/SuiteSparse_wrapper.c should be also made in + Yggdrasil/S/SuiteSparse and vice versa. +*/ + +#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..be23034 --- /dev/null +++ b/deps/Versions.make @@ -0,0 +1,44 @@ +LLVM_VER = 9.0.1 +LLVM_BB_REL = 8 +PCRE_VER = 10.31 +PCRE_BB_REL = 0 +DSFMT_VER = 2.2.3 +DSFMT_BB_REL = 0 +OPENBLAS_VER = 0.3.9 +OPENBLAS_BB_REL = 4 +LAPACK_VER = 3.9.0 +SUITESPARSE_VER = 5.4.0 +SUITESPARSE_BB_REL = 6 +OPENLIBM_VER = 0.7.0 +OPENLIBM_BB_REL = 0 +UNWIND_VER = 1.3.1 +UNWIND_BB_REL = 4 +OSXUNWIND_VER = 0.0.6 +OSXUNWIND_BB_REL = 0 +GMP_VER = 6.1.2 +GMP_BB_REL = 4 +MPFR_BB_REL = 2 +MPFR_VER = 4.1.0 +MPFR_BB_REL = 1 +PATCHELF_VER = 0.9 +MBEDTLS_VER = 2.16.0 +MBEDTLS_BB_REL = 1 +LIBSSH2_VER = 1.9.0 +LIBSSH2_BB_REL = 1 +CURL_VER = 7.66.0 +CURL_BB_REL = 1 +LIBGIT2_VER = 0.28.2 +LIBGIT2_BB_REL = 1 +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 = 10 +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 := 2020-07-22 diff --git a/deps/blas.mk b/deps/blas.mk new file mode 100644 index 0000000..8753ad9 --- /dev/null +++ b/deps/blas.mk @@ -0,0 +1,230 @@ +## 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) $(USE_BLAS_FFLAGS) +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-fix-initialization-to-zero-arm64.patch-applied: $(BUILDDIR)/$(OPENBLAS_SRC_DIR)/source-extracted + cd $(BUILDDIR)/$(OPENBLAS_SRC_DIR) && \ + patch -p1 -f < $(SRCDIR)/patches/openblas-fix-initialization-to-zero-arm64.patch + echo 1 > $@ + +$(BUILDDIR)/$(OPENBLAS_SRC_DIR)/openblas-winexit.patch-applied: $(BUILDDIR)/$(OPENBLAS_SRC_DIR)/openblas-fix-initialization-to-zero-arm64.patch-applied + cd $(BUILDDIR)/$(OPENBLAS_SRC_DIR) && \ + patch -p1 -f < $(SRCDIR)/patches/openblas-winexit.patch + echo 1 > $@ + +$(BUILDDIR)/$(OPENBLAS_SRC_DIR)/openblas-ofast-power.patch-applied: $(BUILDDIR)/$(OPENBLAS_SRC_DIR)/openblas-winexit.patch-applied + cd $(BUILDDIR)/$(OPENBLAS_SRC_DIR) && \ + patch -p1 -f < $(SRCDIR)/patches/openblas-ofast-power.patch + echo 1 > $@ + +$(BUILDDIR)/$(OPENBLAS_SRC_DIR)/build-configured: $(BUILDDIR)/$(OPENBLAS_SRC_DIR)/openblas-ofast-power.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/JuliaBinaryWrappers/OpenBLAS_jll.jl/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-4.aarch64-linux-gnu.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.aarch64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..a33c13e --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.aarch64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +fb8136c2a92d37edcd0ba4ae22352b5c diff --git a/deps/checksums/GMP.v6.1.2-4.aarch64-linux-gnu.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.aarch64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..7a530e1 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.aarch64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +9622464c9054371ee1621efe87801259b900e80457aa5cf39d4323e51dec56b7b3527c6fcf9ae74058f2b4db8034a204f71b9a28c02b3975239064261db5c361 diff --git a/deps/checksums/GMP.v6.1.2-4.aarch64-linux-musl.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.aarch64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..e0793c2 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.aarch64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +f7d1f68ba15b3a5e9996cb349d4d5c58 diff --git a/deps/checksums/GMP.v6.1.2-4.aarch64-linux-musl.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.aarch64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..f7483c5 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.aarch64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +921723c1e71cc80e1dd9f35f4142ca16be030ab1bd7a3c78c73a63be2d0ed80a342e28376bbc074d7203c97bec6e593b386f41ab25fbf22b8942d9d6818edbea diff --git a/deps/checksums/GMP.v6.1.2-4.armv7l-linux-gnueabihf.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.armv7l-linux-gnueabihf.tar.gz/md5 new file mode 100644 index 0000000..862c586 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.armv7l-linux-gnueabihf.tar.gz/md5 @@ -0,0 +1 @@ +b7210d1ffa5e517ef0bada8669303fab diff --git a/deps/checksums/GMP.v6.1.2-4.armv7l-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.armv7l-linux-gnueabihf.tar.gz/sha512 new file mode 100644 index 0000000..86f9c96 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.armv7l-linux-gnueabihf.tar.gz/sha512 @@ -0,0 +1 @@ +77be741f35248a4c4fda80d5efb3b7b1b4ca3fd4f0fed9e305794b89bbb88c7baf5d1d57747bb8e0047377bf1ccbb00cba1a61df17d1c58df6a9901ca42eb593 diff --git a/deps/checksums/GMP.v6.1.2-4.armv7l-linux-musleabihf.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.armv7l-linux-musleabihf.tar.gz/md5 new file mode 100644 index 0000000..fb8e567 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.armv7l-linux-musleabihf.tar.gz/md5 @@ -0,0 +1 @@ +cbd71c0e90e4c92381c79045d9310752 diff --git a/deps/checksums/GMP.v6.1.2-4.armv7l-linux-musleabihf.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.armv7l-linux-musleabihf.tar.gz/sha512 new file mode 100644 index 0000000..c6b1d6e --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.armv7l-linux-musleabihf.tar.gz/sha512 @@ -0,0 +1 @@ +1a8a622746ad1bc7c6576476d7217e383c72771a8b2b3a3c74e5e9d3b0daec1920f85f556339c95857c16ebeb5d5c79669c900cb4b30f68969b9b57146f53b4c diff --git a/deps/checksums/GMP.v6.1.2-4.i686-linux-gnu.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.i686-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..c044213 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.i686-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +96228c26a324e616715ff5116129efbf diff --git a/deps/checksums/GMP.v6.1.2-4.i686-linux-gnu.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.i686-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..0a98d76 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.i686-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +dda1eed06658ee175868c80dac0dcc4848a2b179fa6226e5899de13492205e6d509180c65d533a132b46fdbcbcaa49a85df627a637336f5b14291ea747350d84 diff --git a/deps/checksums/GMP.v6.1.2-4.i686-linux-musl.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.i686-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..d4061b6 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.i686-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +bcb20e336ed586893a2bd1664bd4af96 diff --git a/deps/checksums/GMP.v6.1.2-4.i686-linux-musl.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.i686-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..06cdc0b --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.i686-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +e0cfabe7d2b331c3cc693ca50311319737785a087fc84b15f1862c6a60d036dfffed6f4e9a865d0b83fc374cba3b625face165505fd45de78f674bdafb34fbd2 diff --git a/deps/checksums/GMP.v6.1.2-4.i686-w64-mingw32.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.i686-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..37b7334 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.i686-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +df89ac295cf9a17e16357e433fd917a9 diff --git a/deps/checksums/GMP.v6.1.2-4.i686-w64-mingw32.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.i686-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..72d168d --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.i686-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +856cbaf79376e470ec5be0913a37f4ff06827bb6d06fceb462007375bd8d2b430ff8de938d7f3807a6612ba20d57cddfbb7fa52c94d828d62c678f75c1f5cb31 diff --git a/deps/checksums/GMP.v6.1.2-4.powerpc64le-linux-gnu.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.powerpc64le-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..7b2febc --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.powerpc64le-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +5b232584c835a70ff5d43cfc57ec3438 diff --git a/deps/checksums/GMP.v6.1.2-4.powerpc64le-linux-gnu.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.powerpc64le-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..ce283f5 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.powerpc64le-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +9ce5e5686bfcb14ca1ae7589cba6beec49215ffa2fe9b179c6afd5300d9da7b5c31133888a29cba923e4d399322b018ffb19d5a2bc096c1577a9e7bc7b8b33de diff --git a/deps/checksums/GMP.v6.1.2-4.x86_64-apple-darwin14.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.x86_64-apple-darwin14.tar.gz/md5 new file mode 100644 index 0000000..364fe0a --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.x86_64-apple-darwin14.tar.gz/md5 @@ -0,0 +1 @@ +a0d321bba6e59a449c92088fa693c969 diff --git a/deps/checksums/GMP.v6.1.2-4.x86_64-apple-darwin14.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.x86_64-apple-darwin14.tar.gz/sha512 new file mode 100644 index 0000000..bb0ddb6 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.x86_64-apple-darwin14.tar.gz/sha512 @@ -0,0 +1 @@ +2b9ae682eecf5b5513bf7bc06872bd2bb79ac0d09d1a032217e8727854429e57bb415c5e6a6a04fd7da94cbcb45e3ad8a8578e7b9cf3ba1949db82dd8a9eb6ff diff --git a/deps/checksums/GMP.v6.1.2-4.x86_64-linux-gnu.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.x86_64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..bb7bbc3 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.x86_64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +a6fabb3bc108b0eef63c5543d1f13b7d diff --git a/deps/checksums/GMP.v6.1.2-4.x86_64-linux-gnu.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.x86_64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..c6cb48d --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.x86_64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +cf1fd3a1072f4a5ed3f71db9bf6cab2fa54fab8ae55fcc7bb2d6496e106a6a9a1844f9d8b8d7fdacfcd51e811e168fb99605ebbe1e87e61f978449ad6a280e24 diff --git a/deps/checksums/GMP.v6.1.2-4.x86_64-linux-musl.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.x86_64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..2164b31 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.x86_64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +ba1b090802d2636f460ee71253825a4c diff --git a/deps/checksums/GMP.v6.1.2-4.x86_64-linux-musl.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.x86_64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..9f14c65 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.x86_64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +6320d81bc7d27848606aa7063cf29e1c088f22272db28600a27238a0f933c6695b917ecb54670c93473dbdb7141c7ee33e65b8d717edd0f400966d5a03d5bd86 diff --git a/deps/checksums/GMP.v6.1.2-4.x86_64-unknown-freebsd11.1.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.x86_64-unknown-freebsd11.1.tar.gz/md5 new file mode 100644 index 0000000..65452b8 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.x86_64-unknown-freebsd11.1.tar.gz/md5 @@ -0,0 +1 @@ +deb35cf6f3aa221c814a9cb9b77a99d9 diff --git a/deps/checksums/GMP.v6.1.2-4.x86_64-unknown-freebsd11.1.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.x86_64-unknown-freebsd11.1.tar.gz/sha512 new file mode 100644 index 0000000..e92eb52 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.x86_64-unknown-freebsd11.1.tar.gz/sha512 @@ -0,0 +1 @@ +d80954af671b6c24d8f0988c8cca8e89499dbb7206c8d0cd5c655688df9cc3a6b2e6ca8dbafb86ffaa42707be3e6da44e82c04d8173f7543514dae8dab4d5162 diff --git a/deps/checksums/GMP.v6.1.2-4.x86_64-w64-mingw32.tar.gz/md5 b/deps/checksums/GMP.v6.1.2-4.x86_64-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..9d25f75 --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.x86_64-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +51fb1b1a43cec0ec216c61d17cd637cd diff --git a/deps/checksums/GMP.v6.1.2-4.x86_64-w64-mingw32.tar.gz/sha512 b/deps/checksums/GMP.v6.1.2-4.x86_64-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..7521ace --- /dev/null +++ b/deps/checksums/GMP.v6.1.2-4.x86_64-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +9a702da6de85f3a81262858a2299222664417d424d89b1704eca702661f77e27cd07b7fca8fbd77c58521ef9caa83287c05956fbe97d9047272a4cb6912fc80d diff --git a/deps/checksums/LLVM_full.v9.0.1-8.aarch64-linux-gnu-cxx03.tar.gz/md5 b/deps/checksums/LLVM_full.v9.0.1-8.aarch64-linux-gnu-cxx03.tar.gz/md5 new file mode 100644 index 0000000..0216ba0 --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.aarch64-linux-gnu-cxx03.tar.gz/md5 @@ -0,0 +1 @@ +e8024bf7b5a2bf8d60ed833c48d64c30 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.aarch64-linux-gnu-cxx03.tar.gz/sha512 b/deps/checksums/LLVM_full.v9.0.1-8.aarch64-linux-gnu-cxx03.tar.gz/sha512 new file mode 100644 index 0000000..dc0eec0 --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.aarch64-linux-gnu-cxx03.tar.gz/sha512 @@ -0,0 +1 @@ +85bee115865a9f015ad30e8c4a24092b3910c22e10e9cf0e4fa7592fc35a102d3d1b864dfd112e0c1c4a83ade9196c7480704fa0893de3dd03e9f7a96427911f diff --git a/deps/checksums/LLVM_full.v9.0.1-8.aarch64-linux-gnu-cxx11.tar.gz/md5 b/deps/checksums/LLVM_full.v9.0.1-8.aarch64-linux-gnu-cxx11.tar.gz/md5 new file mode 100644 index 0000000..b3f46c5 --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.aarch64-linux-gnu-cxx11.tar.gz/md5 @@ -0,0 +1 @@ +d3a606fe6e14db4a84dca5681b10cfeb diff --git a/deps/checksums/LLVM_full.v9.0.1-8.aarch64-linux-gnu-cxx11.tar.gz/sha512 b/deps/checksums/LLVM_full.v9.0.1-8.aarch64-linux-gnu-cxx11.tar.gz/sha512 new file mode 100644 index 0000000..ce33af2 --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.aarch64-linux-gnu-cxx11.tar.gz/sha512 @@ -0,0 +1 @@ +4d4a0680327b475e3c6fc8f9b8bb48e89c3a4e5b3584e8681a82b0849539645e820181af4eb2317b15c424eaccd7f51a75fd62486bee3121e7f3918593f4f948 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.aarch64-linux-musl-cxx03.tar.gz/md5 b/deps/checksums/LLVM_full.v9.0.1-8.aarch64-linux-musl-cxx03.tar.gz/md5 new file mode 100644 index 0000000..db92076 --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.aarch64-linux-musl-cxx03.tar.gz/md5 @@ -0,0 +1 @@ +0947966926ca83089aad09d86815b6fa diff --git a/deps/checksums/LLVM_full.v9.0.1-8.aarch64-linux-musl-cxx03.tar.gz/sha512 b/deps/checksums/LLVM_full.v9.0.1-8.aarch64-linux-musl-cxx03.tar.gz/sha512 new file mode 100644 index 0000000..ae66869 --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.aarch64-linux-musl-cxx03.tar.gz/sha512 @@ -0,0 +1 @@ +73120f8d846905394d2d9f3a29208afcc8880c8984df88f7afefd1cf14825c721656162bab90332b319a2dad82c7a9f32ce648a458cd5e815bde6d7ffda04d39 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.aarch64-linux-musl-cxx11.tar.gz/md5 b/deps/checksums/LLVM_full.v9.0.1-8.aarch64-linux-musl-cxx11.tar.gz/md5 new file mode 100644 index 0000000..9f8704f --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.aarch64-linux-musl-cxx11.tar.gz/md5 @@ -0,0 +1 @@ +06469513d1fab1038cc24d2b009eeb97 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.aarch64-linux-musl-cxx11.tar.gz/sha512 b/deps/checksums/LLVM_full.v9.0.1-8.aarch64-linux-musl-cxx11.tar.gz/sha512 new file mode 100644 index 0000000..60fba06 --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.aarch64-linux-musl-cxx11.tar.gz/sha512 @@ -0,0 +1 @@ +d3fa4ff76895ae15abbf6359a045b7942a0d92051b97ac30f38038cd25e9092f745fe75df07cf73015308ba72da85047096264bc26e7f7ebf2c5fe6044b2f5d5 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.armv7l-linux-gnueabihf-cxx03.tar.gz/md5 b/deps/checksums/LLVM_full.v9.0.1-8.armv7l-linux-gnueabihf-cxx03.tar.gz/md5 new file mode 100644 index 0000000..0a9e93e --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.armv7l-linux-gnueabihf-cxx03.tar.gz/md5 @@ -0,0 +1 @@ +7a94bfae0f955b820b860d8da494f109 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.armv7l-linux-gnueabihf-cxx03.tar.gz/sha512 b/deps/checksums/LLVM_full.v9.0.1-8.armv7l-linux-gnueabihf-cxx03.tar.gz/sha512 new file mode 100644 index 0000000..360cd16 --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.armv7l-linux-gnueabihf-cxx03.tar.gz/sha512 @@ -0,0 +1 @@ +9a119958026e60b269e820ad852801b977a43c82026e21bc10f0c38bd0873d8a0ca4260aa4352696103414b8f44f64f8ab89ab4dc350f3c6b3a1ebd55e3b4a1f diff --git a/deps/checksums/LLVM_full.v9.0.1-8.armv7l-linux-gnueabihf-cxx11.tar.gz/md5 b/deps/checksums/LLVM_full.v9.0.1-8.armv7l-linux-gnueabihf-cxx11.tar.gz/md5 new file mode 100644 index 0000000..0b6e2f5 --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.armv7l-linux-gnueabihf-cxx11.tar.gz/md5 @@ -0,0 +1 @@ +39a99af902efcf6df48b1921fe205794 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.armv7l-linux-gnueabihf-cxx11.tar.gz/sha512 b/deps/checksums/LLVM_full.v9.0.1-8.armv7l-linux-gnueabihf-cxx11.tar.gz/sha512 new file mode 100644 index 0000000..74ca802 --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.armv7l-linux-gnueabihf-cxx11.tar.gz/sha512 @@ -0,0 +1 @@ +6181f9d053c739984e25e91015cefcc26b2bbb82ef5379cd79744c708b5ea2cd5cd4ac2bed0b35db72a49b86635c9c5057523421a1194541b5391f16c6a56f1c diff --git a/deps/checksums/LLVM_full.v9.0.1-8.armv7l-linux-musleabihf-cxx03.tar.gz/md5 b/deps/checksums/LLVM_full.v9.0.1-8.armv7l-linux-musleabihf-cxx03.tar.gz/md5 new file mode 100644 index 0000000..5e1a722 --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.armv7l-linux-musleabihf-cxx03.tar.gz/md5 @@ -0,0 +1 @@ +61dc0291bbf721935c63ae575d1245e7 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.armv7l-linux-musleabihf-cxx03.tar.gz/sha512 b/deps/checksums/LLVM_full.v9.0.1-8.armv7l-linux-musleabihf-cxx03.tar.gz/sha512 new file mode 100644 index 0000000..644457c --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.armv7l-linux-musleabihf-cxx03.tar.gz/sha512 @@ -0,0 +1 @@ +b3c80406388987d401dd924d5b294a6976108f05df00beb856784abe7e4be4b610bf1e0cc4ddee252fe992861bc1aaaf41a466f8ae0b94d9ea7354a45d6418d8 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.armv7l-linux-musleabihf-cxx11.tar.gz/md5 b/deps/checksums/LLVM_full.v9.0.1-8.armv7l-linux-musleabihf-cxx11.tar.gz/md5 new file mode 100644 index 0000000..17b6f9c --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.armv7l-linux-musleabihf-cxx11.tar.gz/md5 @@ -0,0 +1 @@ +5d67b7e54d0851534841a3968568eb07 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.armv7l-linux-musleabihf-cxx11.tar.gz/sha512 b/deps/checksums/LLVM_full.v9.0.1-8.armv7l-linux-musleabihf-cxx11.tar.gz/sha512 new file mode 100644 index 0000000..289b2df --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.armv7l-linux-musleabihf-cxx11.tar.gz/sha512 @@ -0,0 +1 @@ +d5d6cac18ac68f2122eb2624f6b3288b5341c2f9a18fca94dcb0d87fc028c468b553c0c14ab4e139fe313c29b15b48bed601aa896e2408a144d844803730e381 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.i686-linux-gnu-cxx03.tar.gz/md5 b/deps/checksums/LLVM_full.v9.0.1-8.i686-linux-gnu-cxx03.tar.gz/md5 new file mode 100644 index 0000000..7a22e29 --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.i686-linux-gnu-cxx03.tar.gz/md5 @@ -0,0 +1 @@ +622df46f071bd3459c530c6dc00243e9 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.i686-linux-gnu-cxx03.tar.gz/sha512 b/deps/checksums/LLVM_full.v9.0.1-8.i686-linux-gnu-cxx03.tar.gz/sha512 new file mode 100644 index 0000000..ddb1f65 --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.i686-linux-gnu-cxx03.tar.gz/sha512 @@ -0,0 +1 @@ +1d873a4f344c70d84d7b31273c3e9dbcbe54edf338817ed4f5799712f4569ba3cb965d98d15f684ea1718ee3718d0e7b0acc4bb48bbcd2964fce4e1468a92ec2 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.i686-linux-gnu-cxx11.tar.gz/md5 b/deps/checksums/LLVM_full.v9.0.1-8.i686-linux-gnu-cxx11.tar.gz/md5 new file mode 100644 index 0000000..56b61e6 --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.i686-linux-gnu-cxx11.tar.gz/md5 @@ -0,0 +1 @@ +732d2cf660dad8c2166f337275eff3dd diff --git a/deps/checksums/LLVM_full.v9.0.1-8.i686-linux-gnu-cxx11.tar.gz/sha512 b/deps/checksums/LLVM_full.v9.0.1-8.i686-linux-gnu-cxx11.tar.gz/sha512 new file mode 100644 index 0000000..960ecaa --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.i686-linux-gnu-cxx11.tar.gz/sha512 @@ -0,0 +1 @@ +a2808a77f3aee834c1aed4bbcb102037bc3f5c4dbaa50b5c0aec8e1627b281cea6d6225e92a60c94c722873e8520a7c926f34fc3a11e4689dc82b2b7cc2a2b43 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.i686-linux-musl-cxx03.tar.gz/md5 b/deps/checksums/LLVM_full.v9.0.1-8.i686-linux-musl-cxx03.tar.gz/md5 new file mode 100644 index 0000000..c220b19 --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.i686-linux-musl-cxx03.tar.gz/md5 @@ -0,0 +1 @@ +2253fa3009616c7a30f1dd758a3d1427 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.i686-linux-musl-cxx03.tar.gz/sha512 b/deps/checksums/LLVM_full.v9.0.1-8.i686-linux-musl-cxx03.tar.gz/sha512 new file mode 100644 index 0000000..878ac94 --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.i686-linux-musl-cxx03.tar.gz/sha512 @@ -0,0 +1 @@ +1c1b15443d809a6dd08fceb5b8cafbbfa2bcb1a105196d3ca92b43f3fcb454db1e7f31920bef67b52059f5d5ed2d7673f7c609b9dbdca7c5cbd1c1a7ded6c7b7 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.i686-linux-musl-cxx11.tar.gz/md5 b/deps/checksums/LLVM_full.v9.0.1-8.i686-linux-musl-cxx11.tar.gz/md5 new file mode 100644 index 0000000..f5b1322 --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.i686-linux-musl-cxx11.tar.gz/md5 @@ -0,0 +1 @@ +2cc4df8d06d65b24ece512e3d5e51372 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.i686-linux-musl-cxx11.tar.gz/sha512 b/deps/checksums/LLVM_full.v9.0.1-8.i686-linux-musl-cxx11.tar.gz/sha512 new file mode 100644 index 0000000..1f93d74 --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.i686-linux-musl-cxx11.tar.gz/sha512 @@ -0,0 +1 @@ +e12d86b21234c840718592eb3325c97ea7194fe14ffd149249b53d90914064e0749b27359d772475d032becf74047172bc598674bf4d20bc4bd14aad60d033d0 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.i686-w64-mingw32-cxx03.tar.gz/md5 b/deps/checksums/LLVM_full.v9.0.1-8.i686-w64-mingw32-cxx03.tar.gz/md5 new file mode 100644 index 0000000..b4cccec --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.i686-w64-mingw32-cxx03.tar.gz/md5 @@ -0,0 +1 @@ +6723ced2c9ba27ca863a2b7f20c27b54 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.i686-w64-mingw32-cxx03.tar.gz/sha512 b/deps/checksums/LLVM_full.v9.0.1-8.i686-w64-mingw32-cxx03.tar.gz/sha512 new file mode 100644 index 0000000..e6ea04f --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.i686-w64-mingw32-cxx03.tar.gz/sha512 @@ -0,0 +1 @@ +6c9004560ffd7f1bc56d86e32fa19aec5856117409f0bdc1dc56f2902fb388df98947b58c84c35e806a3776dd33b2e7cb9c740cc6a73605caa68affe1864368b diff --git a/deps/checksums/LLVM_full.v9.0.1-8.i686-w64-mingw32-cxx11.tar.gz/md5 b/deps/checksums/LLVM_full.v9.0.1-8.i686-w64-mingw32-cxx11.tar.gz/md5 new file mode 100644 index 0000000..1784fb3 --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.i686-w64-mingw32-cxx11.tar.gz/md5 @@ -0,0 +1 @@ +078ff5d48e7363352c1cdec44d2af350 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.i686-w64-mingw32-cxx11.tar.gz/sha512 b/deps/checksums/LLVM_full.v9.0.1-8.i686-w64-mingw32-cxx11.tar.gz/sha512 new file mode 100644 index 0000000..dfbaaf8 --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.i686-w64-mingw32-cxx11.tar.gz/sha512 @@ -0,0 +1 @@ +a1b5d27c9b7f20e74d321f405343ed91987990881d7f5eeb438fc506c3a594eb4cb5d4e694e3d221bb60b71c3a482303c56c69e30be36916d82ea6432819ee49 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.powerpc64le-linux-gnu-cxx03.tar.gz/md5 b/deps/checksums/LLVM_full.v9.0.1-8.powerpc64le-linux-gnu-cxx03.tar.gz/md5 new file mode 100644 index 0000000..bc9c3eb --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.powerpc64le-linux-gnu-cxx03.tar.gz/md5 @@ -0,0 +1 @@ +532e6a15b1340ea72093162222c1deca diff --git a/deps/checksums/LLVM_full.v9.0.1-8.powerpc64le-linux-gnu-cxx03.tar.gz/sha512 b/deps/checksums/LLVM_full.v9.0.1-8.powerpc64le-linux-gnu-cxx03.tar.gz/sha512 new file mode 100644 index 0000000..95712ce --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.powerpc64le-linux-gnu-cxx03.tar.gz/sha512 @@ -0,0 +1 @@ +2953903586486f36c867f42bddecb30d4356511955ec3d90a63697e2ae3e198db839bcb221c4fb963177e1a235091d135f7e61e8ab2dc0c08581b4c194651942 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.powerpc64le-linux-gnu-cxx11.tar.gz/md5 b/deps/checksums/LLVM_full.v9.0.1-8.powerpc64le-linux-gnu-cxx11.tar.gz/md5 new file mode 100644 index 0000000..b3cb1f0 --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.powerpc64le-linux-gnu-cxx11.tar.gz/md5 @@ -0,0 +1 @@ +2d2255c86b2e38d1eb480d9681358f93 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.powerpc64le-linux-gnu-cxx11.tar.gz/sha512 b/deps/checksums/LLVM_full.v9.0.1-8.powerpc64le-linux-gnu-cxx11.tar.gz/sha512 new file mode 100644 index 0000000..a946fcd --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.powerpc64le-linux-gnu-cxx11.tar.gz/sha512 @@ -0,0 +1 @@ +f24b5e85fcf674a8dce06d0d2642035bf5104dda1650ee6483cdaccb4a8b388176ee30195fc418bc874f843a1f474392b0928c22d12eeb9ce74dcf91136e6bcd diff --git a/deps/checksums/LLVM_full.v9.0.1-8.x86_64-apple-darwin14-cxx03.tar.gz/md5 b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-apple-darwin14-cxx03.tar.gz/md5 new file mode 100644 index 0000000..09c62b4 --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-apple-darwin14-cxx03.tar.gz/md5 @@ -0,0 +1 @@ +e77cb41890b4a7608fc3ef900f979a80 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.x86_64-apple-darwin14-cxx03.tar.gz/sha512 b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-apple-darwin14-cxx03.tar.gz/sha512 new file mode 100644 index 0000000..3c908cd --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-apple-darwin14-cxx03.tar.gz/sha512 @@ -0,0 +1 @@ +15d5cff7bd8933f88f161c4ae28f797ba0651ada853c3022acae20a36fc836664a474f2a22590608f041023eb72ea7ae32f81de788dd91924273dc4229128e1f diff --git a/deps/checksums/LLVM_full.v9.0.1-8.x86_64-apple-darwin14-cxx11.tar.gz/md5 b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-apple-darwin14-cxx11.tar.gz/md5 new file mode 100644 index 0000000..bea4fb2 --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-apple-darwin14-cxx11.tar.gz/md5 @@ -0,0 +1 @@ +32977c46c5f29845758e2829de64a2c7 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.x86_64-apple-darwin14-cxx11.tar.gz/sha512 b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-apple-darwin14-cxx11.tar.gz/sha512 new file mode 100644 index 0000000..c6ff99c --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-apple-darwin14-cxx11.tar.gz/sha512 @@ -0,0 +1 @@ +d9cbff3772727e02f6f6dc09bda0bf89b53098b5dedcab06cae9a8f9dac5e589a11da097de27ac505e4935e966552b2dbc021683b569957bbb68dd9401b50ab6 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.x86_64-linux-gnu-cxx03.tar.gz/md5 b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-linux-gnu-cxx03.tar.gz/md5 new file mode 100644 index 0000000..a8558ff --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-linux-gnu-cxx03.tar.gz/md5 @@ -0,0 +1 @@ +538ee8f39b2b18fdb7d0da0f9356d362 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.x86_64-linux-gnu-cxx03.tar.gz/sha512 b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-linux-gnu-cxx03.tar.gz/sha512 new file mode 100644 index 0000000..34bbb43 --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-linux-gnu-cxx03.tar.gz/sha512 @@ -0,0 +1 @@ +07b38bb921043d7d16f9e137c888a0d91b9176e9f45f14960c694a158297548309f081ead6683cf653fd2535ef59c7fab1aef962dee9a5e4c78131ffa8013113 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.x86_64-linux-gnu-cxx11.tar.gz/md5 b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-linux-gnu-cxx11.tar.gz/md5 new file mode 100644 index 0000000..b49b19e --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-linux-gnu-cxx11.tar.gz/md5 @@ -0,0 +1 @@ +1e3ee9112e0bf0e8509727536d089b1c diff --git a/deps/checksums/LLVM_full.v9.0.1-8.x86_64-linux-gnu-cxx11.tar.gz/sha512 b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-linux-gnu-cxx11.tar.gz/sha512 new file mode 100644 index 0000000..7e42eae --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-linux-gnu-cxx11.tar.gz/sha512 @@ -0,0 +1 @@ +a015719599b9978ce9440d9525d991499e37ac1033259cc63e51fddce722846ba93b860e31f0eede74f18cecd28a64f5ae9aea97d6d26f07094934f0ff780ec5 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.x86_64-linux-musl-cxx03.tar.gz/md5 b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-linux-musl-cxx03.tar.gz/md5 new file mode 100644 index 0000000..67c38db --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-linux-musl-cxx03.tar.gz/md5 @@ -0,0 +1 @@ +e6571440b2392c3b45c13be986bc305b diff --git a/deps/checksums/LLVM_full.v9.0.1-8.x86_64-linux-musl-cxx03.tar.gz/sha512 b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-linux-musl-cxx03.tar.gz/sha512 new file mode 100644 index 0000000..e9949b4 --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-linux-musl-cxx03.tar.gz/sha512 @@ -0,0 +1 @@ +df7d77f8f2791639152fb0775e6bc9e2fdbc4007084c1d490afefc05c264721d4dcbb147b35bf8a1332b4d026fc8838ea7ea2f60e09cfa574e299cbc41b6d8d9 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.x86_64-linux-musl-cxx11.tar.gz/md5 b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-linux-musl-cxx11.tar.gz/md5 new file mode 100644 index 0000000..8905c15 --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-linux-musl-cxx11.tar.gz/md5 @@ -0,0 +1 @@ +50a36c5b0a5629b9423ee1a69ede412e diff --git a/deps/checksums/LLVM_full.v9.0.1-8.x86_64-linux-musl-cxx11.tar.gz/sha512 b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-linux-musl-cxx11.tar.gz/sha512 new file mode 100644 index 0000000..6507e29 --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-linux-musl-cxx11.tar.gz/sha512 @@ -0,0 +1 @@ +a4f2394286bc5bce720eccb5f3142b5b40afae4fb0bc24bacafa635e7bf37624dc65cb5d611379af60b1fc8838fe4a14bc52009b8eb45dd6897a2f72b66daa95 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.x86_64-unknown-freebsd11.1-cxx03.tar.gz/md5 b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-unknown-freebsd11.1-cxx03.tar.gz/md5 new file mode 100644 index 0000000..84565cb --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-unknown-freebsd11.1-cxx03.tar.gz/md5 @@ -0,0 +1 @@ +c67f938994e61ebd3798d4570b8c1e93 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.x86_64-unknown-freebsd11.1-cxx03.tar.gz/sha512 b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-unknown-freebsd11.1-cxx03.tar.gz/sha512 new file mode 100644 index 0000000..6949a37 --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-unknown-freebsd11.1-cxx03.tar.gz/sha512 @@ -0,0 +1 @@ +c0b29c8162c3b87b04a7d1446e1fef14064bf4f93f14dbf80ffe0554972ea80a37089cca5a0306aaca87427f6638a3f131b1021df5aa1e91e7be729839d01738 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.x86_64-unknown-freebsd11.1-cxx11.tar.gz/md5 b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-unknown-freebsd11.1-cxx11.tar.gz/md5 new file mode 100644 index 0000000..e1594be --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-unknown-freebsd11.1-cxx11.tar.gz/md5 @@ -0,0 +1 @@ +214ca9e0c6c57e018bdd85a97911f15d diff --git a/deps/checksums/LLVM_full.v9.0.1-8.x86_64-unknown-freebsd11.1-cxx11.tar.gz/sha512 b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-unknown-freebsd11.1-cxx11.tar.gz/sha512 new file mode 100644 index 0000000..68eda3a --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-unknown-freebsd11.1-cxx11.tar.gz/sha512 @@ -0,0 +1 @@ +974a2df704522d8dac788ed63116170b6b88e559a9f05d780c33b5288d915566f83a10172b501ffb320c395d1a7bae0b98c6efb381aab9deb34944d697553cb2 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.x86_64-w64-mingw32-cxx03.tar.gz/md5 b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-w64-mingw32-cxx03.tar.gz/md5 new file mode 100644 index 0000000..74b4fad --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-w64-mingw32-cxx03.tar.gz/md5 @@ -0,0 +1 @@ +c8e42bc6c633e2647b3f994168fc54d0 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.x86_64-w64-mingw32-cxx03.tar.gz/sha512 b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-w64-mingw32-cxx03.tar.gz/sha512 new file mode 100644 index 0000000..a11b718 --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-w64-mingw32-cxx03.tar.gz/sha512 @@ -0,0 +1 @@ +c3b4562355075abff7f7e4a3a8ab1ba391fa9843097634478253992bac3e041228d1d1344498051e2feb9ca8474ffa9668e8bd975417d4632aebbbf7510bfafa diff --git a/deps/checksums/LLVM_full.v9.0.1-8.x86_64-w64-mingw32-cxx11.tar.gz/md5 b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-w64-mingw32-cxx11.tar.gz/md5 new file mode 100644 index 0000000..c91ea97 --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-w64-mingw32-cxx11.tar.gz/md5 @@ -0,0 +1 @@ +234b60aca5887e6fdca6529ca5464aa0 diff --git a/deps/checksums/LLVM_full.v9.0.1-8.x86_64-w64-mingw32-cxx11.tar.gz/sha512 b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-w64-mingw32-cxx11.tar.gz/sha512 new file mode 100644 index 0000000..3858ee1 --- /dev/null +++ b/deps/checksums/LLVM_full.v9.0.1-8.x86_64-w64-mingw32-cxx11.tar.gz/sha512 @@ -0,0 +1 @@ +762767343f63baae33ae838e78e6096ac045ef152d79f961a73c906a8ea7bfc1eab823a1109db09f2e4e90c90b7b402752fc165cd7b78d2d9516a0df4f18740e diff --git a/deps/checksums/LibCURL.v7.66.0-1.aarch64-linux-gnu.tar.gz/md5 b/deps/checksums/LibCURL.v7.66.0-1.aarch64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..32e2838 --- /dev/null +++ b/deps/checksums/LibCURL.v7.66.0-1.aarch64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +a9b50e20ccb840657b88ef5964f5b50d diff --git a/deps/checksums/LibCURL.v7.66.0-1.aarch64-linux-gnu.tar.gz/sha512 b/deps/checksums/LibCURL.v7.66.0-1.aarch64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..74d1ffe --- /dev/null +++ b/deps/checksums/LibCURL.v7.66.0-1.aarch64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +1bea6285dbbcb69f1be36ccd25898f6bc0635e07a1ae8c6731b6967ab5c897774e2455324d34decf84682937ac4affd439c0801acdebcfc5bf2bf3545da06072 diff --git a/deps/checksums/LibCURL.v7.66.0-1.aarch64-linux-musl.tar.gz/md5 b/deps/checksums/LibCURL.v7.66.0-1.aarch64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..f36c3e7 --- /dev/null +++ b/deps/checksums/LibCURL.v7.66.0-1.aarch64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +eb113968d9770ca43cdcca8a40f25be8 diff --git a/deps/checksums/LibCURL.v7.66.0-1.aarch64-linux-musl.tar.gz/sha512 b/deps/checksums/LibCURL.v7.66.0-1.aarch64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..c3a421a --- /dev/null +++ b/deps/checksums/LibCURL.v7.66.0-1.aarch64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +6f96c6d982741aa6d84493bbc43495662943cf1158c2158b78f12085e3395e31fe958e742ea42de85fe69b63afb40ff255836cbdfa1aa39a57372d6cda0dbb1f diff --git a/deps/checksums/LibCURL.v7.66.0-1.armv7l-linux-gnueabihf.tar.gz/md5 b/deps/checksums/LibCURL.v7.66.0-1.armv7l-linux-gnueabihf.tar.gz/md5 new file mode 100644 index 0000000..d218f74 --- /dev/null +++ b/deps/checksums/LibCURL.v7.66.0-1.armv7l-linux-gnueabihf.tar.gz/md5 @@ -0,0 +1 @@ +f5a22f9885d44483940775cbce28fefb diff --git a/deps/checksums/LibCURL.v7.66.0-1.armv7l-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/LibCURL.v7.66.0-1.armv7l-linux-gnueabihf.tar.gz/sha512 new file mode 100644 index 0000000..ae08479 --- /dev/null +++ b/deps/checksums/LibCURL.v7.66.0-1.armv7l-linux-gnueabihf.tar.gz/sha512 @@ -0,0 +1 @@ +207bfab6aa91ffc89be3d8a8cf2d473c3b07dd2561a94a82e77887ba108ca2b7192d2531de23b1b9e3ae3d63e2f1c97add625eafdab9dcffe62f742f88d4ab66 diff --git a/deps/checksums/LibCURL.v7.66.0-1.armv7l-linux-musleabihf.tar.gz/md5 b/deps/checksums/LibCURL.v7.66.0-1.armv7l-linux-musleabihf.tar.gz/md5 new file mode 100644 index 0000000..f7f8597 --- /dev/null +++ b/deps/checksums/LibCURL.v7.66.0-1.armv7l-linux-musleabihf.tar.gz/md5 @@ -0,0 +1 @@ +8376478c61653188cdcee7b7f3bd7f56 diff --git a/deps/checksums/LibCURL.v7.66.0-1.armv7l-linux-musleabihf.tar.gz/sha512 b/deps/checksums/LibCURL.v7.66.0-1.armv7l-linux-musleabihf.tar.gz/sha512 new file mode 100644 index 0000000..8479927 --- /dev/null +++ b/deps/checksums/LibCURL.v7.66.0-1.armv7l-linux-musleabihf.tar.gz/sha512 @@ -0,0 +1 @@ +e8c174714d825a87e9d062161b4752cf1daefd7672ba98268c416e38195df6ed70d97c8cf60ebec495a158873d9c9b1c20d878bc91cc677f03189a9de7d2f6d7 diff --git a/deps/checksums/LibCURL.v7.66.0-1.i686-linux-gnu.tar.gz/md5 b/deps/checksums/LibCURL.v7.66.0-1.i686-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..4a716fc --- /dev/null +++ b/deps/checksums/LibCURL.v7.66.0-1.i686-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +0561371e7f5af0329174a5664cc502bc diff --git a/deps/checksums/LibCURL.v7.66.0-1.i686-linux-gnu.tar.gz/sha512 b/deps/checksums/LibCURL.v7.66.0-1.i686-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..5207499 --- /dev/null +++ b/deps/checksums/LibCURL.v7.66.0-1.i686-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +065204141e83ceb31747932c8be28af738040f1fb814ec47506f78ca76ef83543319964ab9d2695cb7e7346eadff94a4882f7cb6d8f48054ac8e1481c2075308 diff --git a/deps/checksums/LibCURL.v7.66.0-1.i686-linux-musl.tar.gz/md5 b/deps/checksums/LibCURL.v7.66.0-1.i686-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..cfd7018 --- /dev/null +++ b/deps/checksums/LibCURL.v7.66.0-1.i686-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +bab1dabc470c8056b21568409eda36c6 diff --git a/deps/checksums/LibCURL.v7.66.0-1.i686-linux-musl.tar.gz/sha512 b/deps/checksums/LibCURL.v7.66.0-1.i686-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..bdf87a5 --- /dev/null +++ b/deps/checksums/LibCURL.v7.66.0-1.i686-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +674d73397864df94bc519f9b538bb1c8d15f719c3f15ae037c347ce5c97b16a374cfd86ece572f2e9d56b90ffd00119ea9923c46792b038cb892965615f3f54c diff --git a/deps/checksums/LibCURL.v7.66.0-1.i686-w64-mingw32.tar.gz/md5 b/deps/checksums/LibCURL.v7.66.0-1.i686-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..4a292c1 --- /dev/null +++ b/deps/checksums/LibCURL.v7.66.0-1.i686-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +e43d122f5646d7e25bf50efd6e08009a diff --git a/deps/checksums/LibCURL.v7.66.0-1.i686-w64-mingw32.tar.gz/sha512 b/deps/checksums/LibCURL.v7.66.0-1.i686-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..62234e4 --- /dev/null +++ b/deps/checksums/LibCURL.v7.66.0-1.i686-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +ba36dbeda881db12f0f852a4f610e1bd5ec78168b462278f1f199455261e2acdc56909352dfe6cff81d09e8e8f3fecb19bf008c98e19084ecfd972dce08bab0a diff --git a/deps/checksums/LibCURL.v7.66.0-1.powerpc64le-linux-gnu.tar.gz/md5 b/deps/checksums/LibCURL.v7.66.0-1.powerpc64le-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..eaca7b6 --- /dev/null +++ b/deps/checksums/LibCURL.v7.66.0-1.powerpc64le-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +43eb69d7b7511c5847e5ba20113852f9 diff --git a/deps/checksums/LibCURL.v7.66.0-1.powerpc64le-linux-gnu.tar.gz/sha512 b/deps/checksums/LibCURL.v7.66.0-1.powerpc64le-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..ca02bc8 --- /dev/null +++ b/deps/checksums/LibCURL.v7.66.0-1.powerpc64le-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +63817f3d8f7aca038945c9e18cc272456ddfde8682823c8160674d4f26899d8a34c1d6c14e956bf59eb48d13db3ccd7e00d5d4aa27a7960be203d4915e388782 diff --git a/deps/checksums/LibCURL.v7.66.0-1.x86_64-apple-darwin14.tar.gz/md5 b/deps/checksums/LibCURL.v7.66.0-1.x86_64-apple-darwin14.tar.gz/md5 new file mode 100644 index 0000000..ca72b9e --- /dev/null +++ b/deps/checksums/LibCURL.v7.66.0-1.x86_64-apple-darwin14.tar.gz/md5 @@ -0,0 +1 @@ +5fcfbb7debf246db712b2370d641aec8 diff --git a/deps/checksums/LibCURL.v7.66.0-1.x86_64-apple-darwin14.tar.gz/sha512 b/deps/checksums/LibCURL.v7.66.0-1.x86_64-apple-darwin14.tar.gz/sha512 new file mode 100644 index 0000000..7e6b673 --- /dev/null +++ b/deps/checksums/LibCURL.v7.66.0-1.x86_64-apple-darwin14.tar.gz/sha512 @@ -0,0 +1 @@ +c501171baa8570a69117ca32aa8bbae18e7a2a1e7f5bde15ffec2c5080d02f9e1cfbbe40e5104ef5a5d044e64f51e4917f80534a1aedad2ac3a3173d6edbfa7c diff --git a/deps/checksums/LibCURL.v7.66.0-1.x86_64-linux-gnu.tar.gz/md5 b/deps/checksums/LibCURL.v7.66.0-1.x86_64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..17f0752 --- /dev/null +++ b/deps/checksums/LibCURL.v7.66.0-1.x86_64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +f9917fb981e190dca659288b42a44b72 diff --git a/deps/checksums/LibCURL.v7.66.0-1.x86_64-linux-gnu.tar.gz/sha512 b/deps/checksums/LibCURL.v7.66.0-1.x86_64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..b057bcf --- /dev/null +++ b/deps/checksums/LibCURL.v7.66.0-1.x86_64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +1b34ea6bdd756e1dcf01944eb625990a3076a407d3b7f5ad4db9e5b7c11375cacd16afff9913b3467d95c302ef7acf39600e25b0527b06578f237388285e5b48 diff --git a/deps/checksums/LibCURL.v7.66.0-1.x86_64-linux-musl.tar.gz/md5 b/deps/checksums/LibCURL.v7.66.0-1.x86_64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..39c7233 --- /dev/null +++ b/deps/checksums/LibCURL.v7.66.0-1.x86_64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +b8980cb7c553d615e99b4b605767689c diff --git a/deps/checksums/LibCURL.v7.66.0-1.x86_64-linux-musl.tar.gz/sha512 b/deps/checksums/LibCURL.v7.66.0-1.x86_64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..897f5e1 --- /dev/null +++ b/deps/checksums/LibCURL.v7.66.0-1.x86_64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +27feec073996c7c3ae43c3d9009041b1eb835814e07a7182450bd8b9615f8162b929e795c5d03d39a56aa472316db6d2c0574391de273e538ead0811817970bf diff --git a/deps/checksums/LibCURL.v7.66.0-1.x86_64-unknown-freebsd11.1.tar.gz/md5 b/deps/checksums/LibCURL.v7.66.0-1.x86_64-unknown-freebsd11.1.tar.gz/md5 new file mode 100644 index 0000000..2d7eb54 --- /dev/null +++ b/deps/checksums/LibCURL.v7.66.0-1.x86_64-unknown-freebsd11.1.tar.gz/md5 @@ -0,0 +1 @@ +4d329868c7dc53302870d08b79bdc39f diff --git a/deps/checksums/LibCURL.v7.66.0-1.x86_64-unknown-freebsd11.1.tar.gz/sha512 b/deps/checksums/LibCURL.v7.66.0-1.x86_64-unknown-freebsd11.1.tar.gz/sha512 new file mode 100644 index 0000000..08145c6 --- /dev/null +++ b/deps/checksums/LibCURL.v7.66.0-1.x86_64-unknown-freebsd11.1.tar.gz/sha512 @@ -0,0 +1 @@ +c8ea193706857d45f55f2360d6fe653fe2b13b1aa4b2012f85bdc001bcfe2881577dd2d2c6ed3ef52cae9fde0a6d4ed1dea392724ceada91182684a28ab2ddec diff --git a/deps/checksums/LibCURL.v7.66.0-1.x86_64-w64-mingw32.tar.gz/md5 b/deps/checksums/LibCURL.v7.66.0-1.x86_64-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..647fcdb --- /dev/null +++ b/deps/checksums/LibCURL.v7.66.0-1.x86_64-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +45d61e34397103dc0368841fd5a8566d diff --git a/deps/checksums/LibCURL.v7.66.0-1.x86_64-w64-mingw32.tar.gz/sha512 b/deps/checksums/LibCURL.v7.66.0-1.x86_64-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..bb7ec28 --- /dev/null +++ b/deps/checksums/LibCURL.v7.66.0-1.x86_64-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +88931ff63fca4864721072fc7094f3a25e0b487159c94f73761a51acbb5439f2cfca733d47c5aa59f02c7ff84b2b7f8c959d19f16b0662044158a12b1a78730e diff --git a/deps/checksums/LibGit2.v0.28.2-1.aarch64-linux-gnu.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-1.aarch64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..56213f1 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-1.aarch64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +d1bf78dc08e2aa5a8b96bf6daa294387 diff --git a/deps/checksums/LibGit2.v0.28.2-1.aarch64-linux-gnu.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-1.aarch64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..2b8a030 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-1.aarch64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +b432a73d5078093cc3a3d622746a160cdad5b11f0434df7e61479adeb0e1bf6cb56d6d5aa0fd534f4b236ef779012677379bf2e7d913811344223f1b29dc3b82 diff --git a/deps/checksums/LibGit2.v0.28.2-1.aarch64-linux-musl.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-1.aarch64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..4944234 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-1.aarch64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +43e4409595b449208eced5c58be28eae diff --git a/deps/checksums/LibGit2.v0.28.2-1.aarch64-linux-musl.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-1.aarch64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..437ce2a --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-1.aarch64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +6f5adba125e8be8e66317e18a2c36a9fa8f3d1f6647e0a9bb03957879a2f023717c7432651b3d33319b72c70e7b78eee0960c140308c6dad66ae6669a2d99922 diff --git a/deps/checksums/LibGit2.v0.28.2-1.armv7l-linux-gnueabihf.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-1.armv7l-linux-gnueabihf.tar.gz/md5 new file mode 100644 index 0000000..826d7f2 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-1.armv7l-linux-gnueabihf.tar.gz/md5 @@ -0,0 +1 @@ +8abf5ba687e262efa27c065cb20ecf20 diff --git a/deps/checksums/LibGit2.v0.28.2-1.armv7l-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-1.armv7l-linux-gnueabihf.tar.gz/sha512 new file mode 100644 index 0000000..e8c73e1 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-1.armv7l-linux-gnueabihf.tar.gz/sha512 @@ -0,0 +1 @@ +36e7f475622721b426180d63c13421bdd6f3147a9179831c82b8fccb8935c69b3b0f6dbdc42c937f83ccedae269cf743b09a689d8c46eb2b155749f8e6a94f80 diff --git a/deps/checksums/LibGit2.v0.28.2-1.armv7l-linux-musleabihf.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-1.armv7l-linux-musleabihf.tar.gz/md5 new file mode 100644 index 0000000..0ee52b5 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-1.armv7l-linux-musleabihf.tar.gz/md5 @@ -0,0 +1 @@ +c552d40dd57d25e288ecbe9cc6824df1 diff --git a/deps/checksums/LibGit2.v0.28.2-1.armv7l-linux-musleabihf.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-1.armv7l-linux-musleabihf.tar.gz/sha512 new file mode 100644 index 0000000..c207e7d --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-1.armv7l-linux-musleabihf.tar.gz/sha512 @@ -0,0 +1 @@ +96fab06657d2f90e1290db1229da94637fbc2b2d96e5882fb0cc237f0f6036f67c3295b6bb7a3671938e8aaa8fb9e75dc1ed7ad00c14e9cc50873396dc6748ef diff --git a/deps/checksums/LibGit2.v0.28.2-1.i686-linux-gnu.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-1.i686-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..de30adb --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-1.i686-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +a7b0088acfadf471b226a3e13aaa1e52 diff --git a/deps/checksums/LibGit2.v0.28.2-1.i686-linux-gnu.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-1.i686-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..a5c2462 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-1.i686-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +ce2842de3f5d5e96d7213f4f84216beed3264f4170fe1f3e550dc2429abd8ef5e12b5336dbf618362ca76a0458fef62c4096a2c4634dde05dcb9d0a8a7bfdad3 diff --git a/deps/checksums/LibGit2.v0.28.2-1.i686-linux-musl.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-1.i686-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..a3e33ef --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-1.i686-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +b58d9b986c0949a456c3351289122d03 diff --git a/deps/checksums/LibGit2.v0.28.2-1.i686-linux-musl.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-1.i686-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..c072ea8 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-1.i686-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +83b79203656fe6b227dc6be7cb284245e027bc85f4cfb5011ca97f7273e269effd0e5990792c6cfbc203ae23230d973eddb8a1b10371ae10e4d988d4f3ebf43a diff --git a/deps/checksums/LibGit2.v0.28.2-1.i686-w64-mingw32.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-1.i686-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..73e98f2 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-1.i686-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +8b3512062571498e79d18ece607c2f3a diff --git a/deps/checksums/LibGit2.v0.28.2-1.i686-w64-mingw32.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-1.i686-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..84e244e --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-1.i686-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +2609cb4c968dc87bb63be88888cdc02d5e7b2333604ecdbef5358883051ba391d2ef66e361d3dd5b79d4af8dce311a80ed11ce3f4be7a746f86c3e834ce97e6e diff --git a/deps/checksums/LibGit2.v0.28.2-1.powerpc64le-linux-gnu.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-1.powerpc64le-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..a808ab2 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-1.powerpc64le-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +091eeeac55b90fb3be253bc0b7294851 diff --git a/deps/checksums/LibGit2.v0.28.2-1.powerpc64le-linux-gnu.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-1.powerpc64le-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..42bab10 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-1.powerpc64le-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +1f9aa0457c665aa8afb4a808c923816583ee62fd3a1d6aecc0be4814530e599a31e4174b84548856cd0e7d2b1cb3aea177519a25a68c19014938e30a46a57fff diff --git a/deps/checksums/LibGit2.v0.28.2-1.x86_64-apple-darwin14.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-1.x86_64-apple-darwin14.tar.gz/md5 new file mode 100644 index 0000000..adc9171 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-1.x86_64-apple-darwin14.tar.gz/md5 @@ -0,0 +1 @@ +ade2d1d1f0215a63607cfaa3b491834c diff --git a/deps/checksums/LibGit2.v0.28.2-1.x86_64-apple-darwin14.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-1.x86_64-apple-darwin14.tar.gz/sha512 new file mode 100644 index 0000000..b82847d --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-1.x86_64-apple-darwin14.tar.gz/sha512 @@ -0,0 +1 @@ +6e56fc5fbae06c091cd07ef353d9fa023cbeaced6d85cf80c9b13c80524587d4ac9026bf92f10d2c1f698783804e208385b8f1821e491f44c7d522dbcddf657c diff --git a/deps/checksums/LibGit2.v0.28.2-1.x86_64-linux-gnu.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-1.x86_64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..bed26eb --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-1.x86_64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +71d9f246db80a4d2f60e6c12bd9a79e8 diff --git a/deps/checksums/LibGit2.v0.28.2-1.x86_64-linux-gnu.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-1.x86_64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..338f1dd --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-1.x86_64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +4cf4dd2ccaa218c2cc0223eaf19a4b3e834d7c36cf992e8e96a011c3e429347665774e44fdcc913b71c24a74232608fe3a8ce66e75184c58640ce5bb495a07f5 diff --git a/deps/checksums/LibGit2.v0.28.2-1.x86_64-linux-musl.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-1.x86_64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..5c29061 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-1.x86_64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +405902170ac8d62e8dcb985ca952c4e5 diff --git a/deps/checksums/LibGit2.v0.28.2-1.x86_64-linux-musl.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-1.x86_64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..52e793c --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-1.x86_64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +95f9f17d541d80ed06e915486d7ed73e605197d88f08cb34fdebf2ad89efb9144149cd05dc096c3b1583d3b93e02bbcc27a66d341cc2421d08d3f46ffb7cd733 diff --git a/deps/checksums/LibGit2.v0.28.2-1.x86_64-unknown-freebsd11.1.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-1.x86_64-unknown-freebsd11.1.tar.gz/md5 new file mode 100644 index 0000000..9da4583 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-1.x86_64-unknown-freebsd11.1.tar.gz/md5 @@ -0,0 +1 @@ +7e4c7190e4e7994cdb2d9b8cdc1b3bad diff --git a/deps/checksums/LibGit2.v0.28.2-1.x86_64-unknown-freebsd11.1.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-1.x86_64-unknown-freebsd11.1.tar.gz/sha512 new file mode 100644 index 0000000..406b870 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-1.x86_64-unknown-freebsd11.1.tar.gz/sha512 @@ -0,0 +1 @@ +64efd8bb016aa1e9ef8c36004d0b5feb9d433a303687d70eb88fd3eb7dfb3e11081cd6b8eaeb34a2ddd226f3133ed080f615ed2f56c0af5688ad57eafe7937c7 diff --git a/deps/checksums/LibGit2.v0.28.2-1.x86_64-w64-mingw32.tar.gz/md5 b/deps/checksums/LibGit2.v0.28.2-1.x86_64-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..4e7043c --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-1.x86_64-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +cb35d78529b0e4896c2a6101ad01800c diff --git a/deps/checksums/LibGit2.v0.28.2-1.x86_64-w64-mingw32.tar.gz/sha512 b/deps/checksums/LibGit2.v0.28.2-1.x86_64-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..2b96522 --- /dev/null +++ b/deps/checksums/LibGit2.v0.28.2-1.x86_64-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +dd02d1ad28befbc47a6ddfaaf748895e6ce5a9ead8ec76d81ba19e0004099746187fb5f8e6cd647c0fd1ec611393b65890e4e64de4d042c8288b162c1554f9b6 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/LibOSXUnwind.v0.0.6-0.x86_64-apple-darwin14.tar.gz/md5 b/deps/checksums/LibOSXUnwind.v0.0.6-0.x86_64-apple-darwin14.tar.gz/md5 new file mode 100644 index 0000000..3fcf373 --- /dev/null +++ b/deps/checksums/LibOSXUnwind.v0.0.6-0.x86_64-apple-darwin14.tar.gz/md5 @@ -0,0 +1 @@ +57ee184943cd407bcb0d19d0f8616565 diff --git a/deps/checksums/LibOSXUnwind.v0.0.6-0.x86_64-apple-darwin14.tar.gz/sha512 b/deps/checksums/LibOSXUnwind.v0.0.6-0.x86_64-apple-darwin14.tar.gz/sha512 new file mode 100644 index 0000000..435a62b --- /dev/null +++ b/deps/checksums/LibOSXUnwind.v0.0.6-0.x86_64-apple-darwin14.tar.gz/sha512 @@ -0,0 +1 @@ +b46997d83216da569a2337e4ff7da60e3332c9323b60ce2d9352ea0f84aff1b5d7cc849e20c5de387c422d9cb07b4d064b73a278f11d9dadfa6d2f9b28c5fde2 diff --git a/deps/checksums/LibSSH2.v1.9.0-1.aarch64-linux-gnu.tar.gz/md5 b/deps/checksums/LibSSH2.v1.9.0-1.aarch64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..926e3e8 --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-1.aarch64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +36ef163789d9123a0af39bd96e8ff79f diff --git a/deps/checksums/LibSSH2.v1.9.0-1.aarch64-linux-gnu.tar.gz/sha512 b/deps/checksums/LibSSH2.v1.9.0-1.aarch64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..b59259c --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-1.aarch64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +e4a64003193d9e25a4e1a23e67b8f1a52c478984cab43ed6c75a5e8fcebb4050b5b574cc553ed18c39a26b06327ebd4d73bec0dd680cfbd8f9ac6e431503a05f diff --git a/deps/checksums/LibSSH2.v1.9.0-1.aarch64-linux-musl.tar.gz/md5 b/deps/checksums/LibSSH2.v1.9.0-1.aarch64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..f2d931d --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-1.aarch64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +620dd756343f22afe53b6b00a25462ef diff --git a/deps/checksums/LibSSH2.v1.9.0-1.aarch64-linux-musl.tar.gz/sha512 b/deps/checksums/LibSSH2.v1.9.0-1.aarch64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..6cc9027 --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-1.aarch64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +7ac0e9a28647b4d411dd4e0ab76b2592f488ba76ba5c2651f42acc7e647f62c9804bd271225b2a652c697e6c4767a74ec70d7ba6e2682babd59a92dc9f0b089c diff --git a/deps/checksums/LibSSH2.v1.9.0-1.armv7l-linux-gnueabihf.tar.gz/md5 b/deps/checksums/LibSSH2.v1.9.0-1.armv7l-linux-gnueabihf.tar.gz/md5 new file mode 100644 index 0000000..ea289d0 --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-1.armv7l-linux-gnueabihf.tar.gz/md5 @@ -0,0 +1 @@ +4a271ec41134206741057b1d073aa0e2 diff --git a/deps/checksums/LibSSH2.v1.9.0-1.armv7l-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/LibSSH2.v1.9.0-1.armv7l-linux-gnueabihf.tar.gz/sha512 new file mode 100644 index 0000000..ca3a3cc --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-1.armv7l-linux-gnueabihf.tar.gz/sha512 @@ -0,0 +1 @@ +a33d5e96070310b4f7c1a0dc6d17e5ff665deede4264526817b298bb32cd3554c34ca2b42fefeb1ca6f4077c9b42c62330cd60fdc9b101ca0063712f957a7723 diff --git a/deps/checksums/LibSSH2.v1.9.0-1.armv7l-linux-musleabihf.tar.gz/md5 b/deps/checksums/LibSSH2.v1.9.0-1.armv7l-linux-musleabihf.tar.gz/md5 new file mode 100644 index 0000000..87a0203 --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-1.armv7l-linux-musleabihf.tar.gz/md5 @@ -0,0 +1 @@ +052b885ac693514f3065bfa7768b70a5 diff --git a/deps/checksums/LibSSH2.v1.9.0-1.armv7l-linux-musleabihf.tar.gz/sha512 b/deps/checksums/LibSSH2.v1.9.0-1.armv7l-linux-musleabihf.tar.gz/sha512 new file mode 100644 index 0000000..a6171dd --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-1.armv7l-linux-musleabihf.tar.gz/sha512 @@ -0,0 +1 @@ +1af323ce0ed7d8d3cad594dd2e23bdc88ad2e1f0bd30d4ae6fadb19de4966398ec021203619873bce668d7b8afab0e16c1584b0c95faa4b8eb3535985fe300e6 diff --git a/deps/checksums/LibSSH2.v1.9.0-1.i686-linux-gnu.tar.gz/md5 b/deps/checksums/LibSSH2.v1.9.0-1.i686-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..796cdc2 --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-1.i686-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +0e7ebeec5b7dc73f9ce2d2f3f81ee87a diff --git a/deps/checksums/LibSSH2.v1.9.0-1.i686-linux-gnu.tar.gz/sha512 b/deps/checksums/LibSSH2.v1.9.0-1.i686-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..554c623 --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-1.i686-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +c8b34527d47d5a2c91a3eabe36513d7e1c14998d14ca5e8561e0dcd219cf8f0c0e998229eb2755bda7d891f78d0d3326aa38efd58334fb4508dd10df12e5398b diff --git a/deps/checksums/LibSSH2.v1.9.0-1.i686-linux-musl.tar.gz/md5 b/deps/checksums/LibSSH2.v1.9.0-1.i686-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..5f53b3c --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-1.i686-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +be9ecaa94737cdb551f2c1c8b880f2a6 diff --git a/deps/checksums/LibSSH2.v1.9.0-1.i686-linux-musl.tar.gz/sha512 b/deps/checksums/LibSSH2.v1.9.0-1.i686-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..1f5e8f9 --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-1.i686-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +ff23a8437ae12a7f0f1f844d14f1853a54260cdcd0d3be4f0bc6032e3cb561290e25aac20f7532aa5561d3af66081fb7cd3cf5ed480b1799ffe86950383870a9 diff --git a/deps/checksums/LibSSH2.v1.9.0-1.i686-w64-mingw32.tar.gz/md5 b/deps/checksums/LibSSH2.v1.9.0-1.i686-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..887c003 --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-1.i686-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +b323bb6d450bd5d6e2943dfbf66de570 diff --git a/deps/checksums/LibSSH2.v1.9.0-1.i686-w64-mingw32.tar.gz/sha512 b/deps/checksums/LibSSH2.v1.9.0-1.i686-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..56f7954 --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-1.i686-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +044c673057552f18009510c012b90b98e50f9a2ac086b18e3faeef4cc7ca28aa1d894f4083c96f014ba4d0d38cfcb2e5c704c21de2101b834efb60c8940a05b0 diff --git a/deps/checksums/LibSSH2.v1.9.0-1.powerpc64le-linux-gnu.tar.gz/md5 b/deps/checksums/LibSSH2.v1.9.0-1.powerpc64le-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..cc11596 --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-1.powerpc64le-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +64fa75fec57f25a758e42cab73b4dde5 diff --git a/deps/checksums/LibSSH2.v1.9.0-1.powerpc64le-linux-gnu.tar.gz/sha512 b/deps/checksums/LibSSH2.v1.9.0-1.powerpc64le-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..7bd284f --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-1.powerpc64le-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +1d911876ee40d02c7ce81298652e6d62e7133ce6a408dc7ac9b93e8bf73cc89e1836ebccf9c946c3cf908fe5cc56648eed992351a1856ed7085d5af42683338a diff --git a/deps/checksums/LibSSH2.v1.9.0-1.x86_64-apple-darwin14.tar.gz/md5 b/deps/checksums/LibSSH2.v1.9.0-1.x86_64-apple-darwin14.tar.gz/md5 new file mode 100644 index 0000000..7e28cee --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-1.x86_64-apple-darwin14.tar.gz/md5 @@ -0,0 +1 @@ +5e88e48a97ab8a5cfefb7538d4f1e070 diff --git a/deps/checksums/LibSSH2.v1.9.0-1.x86_64-apple-darwin14.tar.gz/sha512 b/deps/checksums/LibSSH2.v1.9.0-1.x86_64-apple-darwin14.tar.gz/sha512 new file mode 100644 index 0000000..6140cf4 --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-1.x86_64-apple-darwin14.tar.gz/sha512 @@ -0,0 +1 @@ +20cf8fe1c2e5d7735bb11f3f464ef9fac1f6f5178dc40a42abdb640a39b6d08724c6ab76f5c2ddf9ed29c9ff6898acf204702fc576a8cf2985e8c511075f8adf diff --git a/deps/checksums/LibSSH2.v1.9.0-1.x86_64-linux-gnu.tar.gz/md5 b/deps/checksums/LibSSH2.v1.9.0-1.x86_64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..88264b0 --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-1.x86_64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +e1413e4b8017b830f722ec41e12b1d20 diff --git a/deps/checksums/LibSSH2.v1.9.0-1.x86_64-linux-gnu.tar.gz/sha512 b/deps/checksums/LibSSH2.v1.9.0-1.x86_64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..670f0b1 --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-1.x86_64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +ad2a951e581c3ef46bce3a79ba6e19c35e2ea403b2cd6417b539d7f8520488f57dc63e19ad516a1dcfd3b0f1cf68686d7ac9e050c07f5c16ca92b4322eda22d3 diff --git a/deps/checksums/LibSSH2.v1.9.0-1.x86_64-linux-musl.tar.gz/md5 b/deps/checksums/LibSSH2.v1.9.0-1.x86_64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..1008e4c --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-1.x86_64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +4d89b52c247a366985b37ba94998898e diff --git a/deps/checksums/LibSSH2.v1.9.0-1.x86_64-linux-musl.tar.gz/sha512 b/deps/checksums/LibSSH2.v1.9.0-1.x86_64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..74c1f86 --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-1.x86_64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +81e8e9731c5ff5b2349bf2c389a7da9eb57ac252ee5328e315b7562ef39a14b3cb274abebf5bb30215faff126569dd963802c1156fecfe19314a7bddff670939 diff --git a/deps/checksums/LibSSH2.v1.9.0-1.x86_64-unknown-freebsd11.1.tar.gz/md5 b/deps/checksums/LibSSH2.v1.9.0-1.x86_64-unknown-freebsd11.1.tar.gz/md5 new file mode 100644 index 0000000..2619c08 --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-1.x86_64-unknown-freebsd11.1.tar.gz/md5 @@ -0,0 +1 @@ +ce0354563496eabe705db1716cbb4aa2 diff --git a/deps/checksums/LibSSH2.v1.9.0-1.x86_64-unknown-freebsd11.1.tar.gz/sha512 b/deps/checksums/LibSSH2.v1.9.0-1.x86_64-unknown-freebsd11.1.tar.gz/sha512 new file mode 100644 index 0000000..70c1e09 --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-1.x86_64-unknown-freebsd11.1.tar.gz/sha512 @@ -0,0 +1 @@ +9b29b82aa53d44e580959465132ce39acbbf785c24fe0d141429f925509ce81dc3cb134a2793425c143064db9ba556418d941b5d7f59a1d5e641df9e7ce9a5a8 diff --git a/deps/checksums/LibSSH2.v1.9.0-1.x86_64-w64-mingw32.tar.gz/md5 b/deps/checksums/LibSSH2.v1.9.0-1.x86_64-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..8dcf66f --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-1.x86_64-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +49460ec6b30eeb99a786f75d42facfda diff --git a/deps/checksums/LibSSH2.v1.9.0-1.x86_64-w64-mingw32.tar.gz/sha512 b/deps/checksums/LibSSH2.v1.9.0-1.x86_64-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..da3018c --- /dev/null +++ b/deps/checksums/LibSSH2.v1.9.0-1.x86_64-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +467d0ebff3af6d3d8f47db82543fd2bbf38fdc4b26a2694c24bb468f765e3b5f23abefdfe41c5d609f55aac6a41b24a7aa7d0ba0d72971f9fe192f257ef515f6 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.armv7l-linux-gnueabihf.tar.gz/md5 b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.armv7l-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.armv7l-linux-gnueabihf.tar.gz/md5 @@ -0,0 +1 @@ +d574f0328b92f89dbad112fb7469b0e3 diff --git a/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.armv7l-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.armv7l-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.armv7l-linux-gnueabihf.tar.gz/sha512 @@ -0,0 +1 @@ +8e81de23baef1060307f8642ba8bae0c05566d4291f9c876da4a1409db67206527f8afce0b2cb94758f1efcefa3bfd1ca5b7ccb87d5b0bf291c4b154e1ad7c15 diff --git a/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.armv7l-linux-musleabihf.tar.gz/md5 b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.armv7l-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.armv7l-linux-musleabihf.tar.gz/md5 @@ -0,0 +1 @@ +f02bd3f826599fd654f75be6ef3c413f diff --git a/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.armv7l-linux-musleabihf.tar.gz/sha512 b/deps/checksums/LibUV.v2.0.0+1.24.0-julia-0.armv7l-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.armv7l-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.armv7l-linux-gnueabihf.tar.gz/md5 b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.armv7l-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.armv7l-linux-gnueabihf.tar.gz/md5 @@ -0,0 +1 @@ +ced3ff67ea4ed9b55cac37ae3e1a8ff9 diff --git a/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.armv7l-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.armv7l-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.armv7l-linux-gnueabihf.tar.gz/sha512 @@ -0,0 +1 @@ +9293cfa079236620d588e1bebddf06864b4f5ca1d19d0a79852325d3c74e8e4b4b1066996c22cf5c8d16346973dcc20fc3fed8b5e53d50075999b5cbe22615e0 diff --git a/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.armv7l-linux-musleabihf.tar.gz/md5 b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.armv7l-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.armv7l-linux-musleabihf.tar.gz/md5 @@ -0,0 +1 @@ +5a4d5bd0e2fb7c317d0e7fc079e657f6 diff --git a/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.armv7l-linux-musleabihf.tar.gz/sha512 b/deps/checksums/LibUV.v2.0.0+1.29.1-julia-0.armv7l-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.armv7l-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.armv7l-linux-gnueabihf.tar.gz/md5 b/deps/checksums/LibUnwind.v1.3.1-4.armv7l-linux-gnueabihf.tar.gz/md5 new file mode 100644 index 0000000..555b12b --- /dev/null +++ b/deps/checksums/LibUnwind.v1.3.1-4.armv7l-linux-gnueabihf.tar.gz/md5 @@ -0,0 +1 @@ +cfba31485165d109f899060ff5c8d86f diff --git a/deps/checksums/LibUnwind.v1.3.1-4.armv7l-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/LibUnwind.v1.3.1-4.armv7l-linux-gnueabihf.tar.gz/sha512 new file mode 100644 index 0000000..e0872a1 --- /dev/null +++ b/deps/checksums/LibUnwind.v1.3.1-4.armv7l-linux-gnueabihf.tar.gz/sha512 @@ -0,0 +1 @@ +1eee02d647b51ab4703c6fe75ca69250c0d70920230d1705307124281f883a18e3485d439dce8376dab46d8d56347bc61e3f11355e0a988a742f8ea527062267 diff --git a/deps/checksums/LibUnwind.v1.3.1-4.armv7l-linux-musleabihf.tar.gz/md5 b/deps/checksums/LibUnwind.v1.3.1-4.armv7l-linux-musleabihf.tar.gz/md5 new file mode 100644 index 0000000..bf4e990 --- /dev/null +++ b/deps/checksums/LibUnwind.v1.3.1-4.armv7l-linux-musleabihf.tar.gz/md5 @@ -0,0 +1 @@ +43e681f53a3886c97fd8496f50310a5c diff --git a/deps/checksums/LibUnwind.v1.3.1-4.armv7l-linux-musleabihf.tar.gz/sha512 b/deps/checksums/LibUnwind.v1.3.1-4.armv7l-linux-musleabihf.tar.gz/sha512 new file mode 100644 index 0000000..fff0858 --- /dev/null +++ b/deps/checksums/LibUnwind.v1.3.1-4.armv7l-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.1.0-1.aarch64-linux-gnu.tar.gz/md5 b/deps/checksums/MPFR.v4.1.0-1.aarch64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..045bf4b --- /dev/null +++ b/deps/checksums/MPFR.v4.1.0-1.aarch64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +480455278ac8186c988be4fd13ab08ed diff --git a/deps/checksums/MPFR.v4.1.0-1.aarch64-linux-gnu.tar.gz/sha512 b/deps/checksums/MPFR.v4.1.0-1.aarch64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..898fb2f --- /dev/null +++ b/deps/checksums/MPFR.v4.1.0-1.aarch64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +6dd5bd83b8ae7d839201760e245787c5c3caf19c209b0098bcd786e67186529c8fb2901c507aaae3fe26d01863b1ded8b30aa547312a238c4de68f64f90cd550 diff --git a/deps/checksums/MPFR.v4.1.0-1.aarch64-linux-musl.tar.gz/md5 b/deps/checksums/MPFR.v4.1.0-1.aarch64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..b08e224 --- /dev/null +++ b/deps/checksums/MPFR.v4.1.0-1.aarch64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +5bd187d72846c25104009678d761e6b5 diff --git a/deps/checksums/MPFR.v4.1.0-1.aarch64-linux-musl.tar.gz/sha512 b/deps/checksums/MPFR.v4.1.0-1.aarch64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..8d1b2a1 --- /dev/null +++ b/deps/checksums/MPFR.v4.1.0-1.aarch64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +86482e272578c72c3007d31103fd50ec449e2f92929f5ed991b947dd847e45f4362519e04c5407bae1845839c6aa11c6a5a817c5ef1418fa4ba67a03e395bf96 diff --git a/deps/checksums/MPFR.v4.1.0-1.armv7l-linux-gnueabihf.tar.gz/md5 b/deps/checksums/MPFR.v4.1.0-1.armv7l-linux-gnueabihf.tar.gz/md5 new file mode 100644 index 0000000..cedc60f --- /dev/null +++ b/deps/checksums/MPFR.v4.1.0-1.armv7l-linux-gnueabihf.tar.gz/md5 @@ -0,0 +1 @@ +9ee1860a6ab8e30d24e788d50e43d847 diff --git a/deps/checksums/MPFR.v4.1.0-1.armv7l-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/MPFR.v4.1.0-1.armv7l-linux-gnueabihf.tar.gz/sha512 new file mode 100644 index 0000000..d8583f9 --- /dev/null +++ b/deps/checksums/MPFR.v4.1.0-1.armv7l-linux-gnueabihf.tar.gz/sha512 @@ -0,0 +1 @@ +37fc21794e9afe69c0a1e7efcc550f511e7bc41e446df66ff9e9497a7ee81b9e4a72eac31b02305f0f22722ae15224897fad85abb7f03fa5cbb2b78d83fa1523 diff --git a/deps/checksums/MPFR.v4.1.0-1.armv7l-linux-musleabihf.tar.gz/md5 b/deps/checksums/MPFR.v4.1.0-1.armv7l-linux-musleabihf.tar.gz/md5 new file mode 100644 index 0000000..d2599f6 --- /dev/null +++ b/deps/checksums/MPFR.v4.1.0-1.armv7l-linux-musleabihf.tar.gz/md5 @@ -0,0 +1 @@ +8478d8f44b7765f0efebfe816613fc77 diff --git a/deps/checksums/MPFR.v4.1.0-1.armv7l-linux-musleabihf.tar.gz/sha512 b/deps/checksums/MPFR.v4.1.0-1.armv7l-linux-musleabihf.tar.gz/sha512 new file mode 100644 index 0000000..cd63900 --- /dev/null +++ b/deps/checksums/MPFR.v4.1.0-1.armv7l-linux-musleabihf.tar.gz/sha512 @@ -0,0 +1 @@ +9681a299a4b71c723b5b1c48bfad7b0586f636e83c662233d142db426c146c4e228191480c6a626b030423fbe34e8a32338b3f0bb8c50dbe9f74ad0a0a57d6fd diff --git a/deps/checksums/MPFR.v4.1.0-1.i686-linux-gnu.tar.gz/md5 b/deps/checksums/MPFR.v4.1.0-1.i686-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..aca72fb --- /dev/null +++ b/deps/checksums/MPFR.v4.1.0-1.i686-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +66d7d381aa1b706777bfca2f127384d6 diff --git a/deps/checksums/MPFR.v4.1.0-1.i686-linux-gnu.tar.gz/sha512 b/deps/checksums/MPFR.v4.1.0-1.i686-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..04308e6 --- /dev/null +++ b/deps/checksums/MPFR.v4.1.0-1.i686-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +c0d2d4c9be047739ccbf5417dd85b3407eb86bf56a9a7ca33ab62b4f3976037f9bfd079bb758054623c40ce405d98dc3db44a1619353da9c71e91dc0bc281c6e diff --git a/deps/checksums/MPFR.v4.1.0-1.i686-linux-musl.tar.gz/md5 b/deps/checksums/MPFR.v4.1.0-1.i686-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..4017b15 --- /dev/null +++ b/deps/checksums/MPFR.v4.1.0-1.i686-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +5588d1db9f15911d9c329d35d982edc1 diff --git a/deps/checksums/MPFR.v4.1.0-1.i686-linux-musl.tar.gz/sha512 b/deps/checksums/MPFR.v4.1.0-1.i686-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..50ba7a5 --- /dev/null +++ b/deps/checksums/MPFR.v4.1.0-1.i686-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +27f3cdadf1ffc91dd4f6d95e970d342f48194251b8d0e7729af413e6cd5e57169cc755460e1a05ded6a2fde44b55fe41d55125d6dfe8aaf2a2df944e396b26fe diff --git a/deps/checksums/MPFR.v4.1.0-1.i686-w64-mingw32.tar.gz/md5 b/deps/checksums/MPFR.v4.1.0-1.i686-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..1372049 --- /dev/null +++ b/deps/checksums/MPFR.v4.1.0-1.i686-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +5dd7e3fe4ad870901175acada852d155 diff --git a/deps/checksums/MPFR.v4.1.0-1.i686-w64-mingw32.tar.gz/sha512 b/deps/checksums/MPFR.v4.1.0-1.i686-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..fa9b3d9 --- /dev/null +++ b/deps/checksums/MPFR.v4.1.0-1.i686-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +0d11f6f85ccefbfacf2df83fe7de10db31ca0420c2c2a8be9879b903b46c5339a988ead03b7ec76010a4f7d0f1c00f97ebeb40afc5e5ed663fec80bf8849d9a5 diff --git a/deps/checksums/MPFR.v4.1.0-1.powerpc64le-linux-gnu.tar.gz/md5 b/deps/checksums/MPFR.v4.1.0-1.powerpc64le-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..27028a3 --- /dev/null +++ b/deps/checksums/MPFR.v4.1.0-1.powerpc64le-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +be9158b8c2b8959a48f2894ee46c0a05 diff --git a/deps/checksums/MPFR.v4.1.0-1.powerpc64le-linux-gnu.tar.gz/sha512 b/deps/checksums/MPFR.v4.1.0-1.powerpc64le-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..816a77c --- /dev/null +++ b/deps/checksums/MPFR.v4.1.0-1.powerpc64le-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +aeb2d9475ef5e87609b4f9a8b5b60735268af7757162ad405a02955c6eb501f69024f4748d76355c11b9585191a8d3441b1c26e6c4d3bf0967453237292226ff diff --git a/deps/checksums/MPFR.v4.1.0-1.x86_64-apple-darwin14.tar.gz/md5 b/deps/checksums/MPFR.v4.1.0-1.x86_64-apple-darwin14.tar.gz/md5 new file mode 100644 index 0000000..0ec714f --- /dev/null +++ b/deps/checksums/MPFR.v4.1.0-1.x86_64-apple-darwin14.tar.gz/md5 @@ -0,0 +1 @@ +d5bca8dd3d1d5b928fb280a2e69ec459 diff --git a/deps/checksums/MPFR.v4.1.0-1.x86_64-apple-darwin14.tar.gz/sha512 b/deps/checksums/MPFR.v4.1.0-1.x86_64-apple-darwin14.tar.gz/sha512 new file mode 100644 index 0000000..7dd82c9 --- /dev/null +++ b/deps/checksums/MPFR.v4.1.0-1.x86_64-apple-darwin14.tar.gz/sha512 @@ -0,0 +1 @@ +2a4035ccac6c30319d0b767748f332ae2748189a26e0d7cb61081a177819f08a5627e6de81dce6cb22fe5a1c34250d91b300befbf013a23dfeeb4579e628e383 diff --git a/deps/checksums/MPFR.v4.1.0-1.x86_64-linux-gnu.tar.gz/md5 b/deps/checksums/MPFR.v4.1.0-1.x86_64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..30a571f --- /dev/null +++ b/deps/checksums/MPFR.v4.1.0-1.x86_64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +6b00c09e1a54db66c215bf591c36ea67 diff --git a/deps/checksums/MPFR.v4.1.0-1.x86_64-linux-gnu.tar.gz/sha512 b/deps/checksums/MPFR.v4.1.0-1.x86_64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..4da6f42 --- /dev/null +++ b/deps/checksums/MPFR.v4.1.0-1.x86_64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +e9f00f761cb146ef314b0ed60df6d95a203e7cfbe4678fe3b54c3bf592e9ddaba79c406dd9e1f369ddb2d3f8d42ee37dd6e891b73b973300b470a3bb97425a22 diff --git a/deps/checksums/MPFR.v4.1.0-1.x86_64-linux-musl.tar.gz/md5 b/deps/checksums/MPFR.v4.1.0-1.x86_64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..b1d8472 --- /dev/null +++ b/deps/checksums/MPFR.v4.1.0-1.x86_64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +a359b259f20accd58f6056dae52ddf38 diff --git a/deps/checksums/MPFR.v4.1.0-1.x86_64-linux-musl.tar.gz/sha512 b/deps/checksums/MPFR.v4.1.0-1.x86_64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..bef979e --- /dev/null +++ b/deps/checksums/MPFR.v4.1.0-1.x86_64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +7e0e14fd0bd66c600e862c4e053eb12ba7b69f526cdd9a6e0a1a91de6e54d51b86ed9b6c38badec81e43485f97dbc8be3c9ae3d5431b5c8a671d295ff8fea3b6 diff --git a/deps/checksums/MPFR.v4.1.0-1.x86_64-unknown-freebsd11.1.tar.gz/md5 b/deps/checksums/MPFR.v4.1.0-1.x86_64-unknown-freebsd11.1.tar.gz/md5 new file mode 100644 index 0000000..a3bdcbf --- /dev/null +++ b/deps/checksums/MPFR.v4.1.0-1.x86_64-unknown-freebsd11.1.tar.gz/md5 @@ -0,0 +1 @@ +3fb5ea2f380e675cc951853016cb5fe3 diff --git a/deps/checksums/MPFR.v4.1.0-1.x86_64-unknown-freebsd11.1.tar.gz/sha512 b/deps/checksums/MPFR.v4.1.0-1.x86_64-unknown-freebsd11.1.tar.gz/sha512 new file mode 100644 index 0000000..590a91a --- /dev/null +++ b/deps/checksums/MPFR.v4.1.0-1.x86_64-unknown-freebsd11.1.tar.gz/sha512 @@ -0,0 +1 @@ +4a9666c2d5e6357ad4ca8a4913097f76090925bf0319705065d37f8115fdc5cd8feffdb36a6f9a0400af6c551531b57708a834b73ed508ca1dde1897154382ca diff --git a/deps/checksums/MPFR.v4.1.0-1.x86_64-w64-mingw32.tar.gz/md5 b/deps/checksums/MPFR.v4.1.0-1.x86_64-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..e3de1b2 --- /dev/null +++ b/deps/checksums/MPFR.v4.1.0-1.x86_64-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +653bbd918e4a340ace302d20ce096e35 diff --git a/deps/checksums/MPFR.v4.1.0-1.x86_64-w64-mingw32.tar.gz/sha512 b/deps/checksums/MPFR.v4.1.0-1.x86_64-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..ed4f945 --- /dev/null +++ b/deps/checksums/MPFR.v4.1.0-1.x86_64-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +77a91ba9c41049fb9511248344bac4ab9fbb1feb965dd0e78b09ce07c2d4d7df60c74528e4d1966e1a7000587b7766c904070fa3da9746e9341033f54df8b9c6 diff --git a/deps/checksums/MbedTLS.v2.16.0-1.aarch64-linux-gnu.tar.gz/md5 b/deps/checksums/MbedTLS.v2.16.0-1.aarch64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..ef64e95 --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-1.aarch64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +84175a5d9fc8347e2b9aa4ed7e8a69b8 diff --git a/deps/checksums/MbedTLS.v2.16.0-1.aarch64-linux-gnu.tar.gz/sha512 b/deps/checksums/MbedTLS.v2.16.0-1.aarch64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..c50beff --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-1.aarch64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +701ce72a948d5006a5d925acd6a5f8f3fe8409857d11fb94099f0e2ec39200aabd48f0312102d50bdf0099334661ae97c63606631255c286c93703b5791c6e14 diff --git a/deps/checksums/MbedTLS.v2.16.0-1.aarch64-linux-musl.tar.gz/md5 b/deps/checksums/MbedTLS.v2.16.0-1.aarch64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..c29750a --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-1.aarch64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +f34a99ee2f7d506aeaad73e7bff6f59e diff --git a/deps/checksums/MbedTLS.v2.16.0-1.aarch64-linux-musl.tar.gz/sha512 b/deps/checksums/MbedTLS.v2.16.0-1.aarch64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..cf05017 --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-1.aarch64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +61c04d0a48e3e5e1e6820996e351891bb9ae57398e9d0148604ddde077b63f80cf52f0d61a2e5bf3eb4717bfc1f92d1d8e088c5f443e3d6739547e27b9413b8d diff --git a/deps/checksums/MbedTLS.v2.16.0-1.armv7l-linux-gnueabihf.tar.gz/md5 b/deps/checksums/MbedTLS.v2.16.0-1.armv7l-linux-gnueabihf.tar.gz/md5 new file mode 100644 index 0000000..1d47362 --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-1.armv7l-linux-gnueabihf.tar.gz/md5 @@ -0,0 +1 @@ +146c9b4402a2d78cc8fafc9c0b332f86 diff --git a/deps/checksums/MbedTLS.v2.16.0-1.armv7l-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/MbedTLS.v2.16.0-1.armv7l-linux-gnueabihf.tar.gz/sha512 new file mode 100644 index 0000000..205defc --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-1.armv7l-linux-gnueabihf.tar.gz/sha512 @@ -0,0 +1 @@ +4bce90f403c1f245decfbc5371b80fe46f142e865fe32a6c9055aa7a6909e2334234500ef9bfc653e7450a9f415101f11036cbf23f9c14ee491dce9bfa84d7ec diff --git a/deps/checksums/MbedTLS.v2.16.0-1.armv7l-linux-musleabihf.tar.gz/md5 b/deps/checksums/MbedTLS.v2.16.0-1.armv7l-linux-musleabihf.tar.gz/md5 new file mode 100644 index 0000000..d5ef478 --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-1.armv7l-linux-musleabihf.tar.gz/md5 @@ -0,0 +1 @@ +427676696934a1f434ce051ad98d7722 diff --git a/deps/checksums/MbedTLS.v2.16.0-1.armv7l-linux-musleabihf.tar.gz/sha512 b/deps/checksums/MbedTLS.v2.16.0-1.armv7l-linux-musleabihf.tar.gz/sha512 new file mode 100644 index 0000000..9568de5 --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-1.armv7l-linux-musleabihf.tar.gz/sha512 @@ -0,0 +1 @@ +7f13355d6b2261efa79dcf38177da911bb2a049dbbf3c83b37c3875e3247daff7b52081a2688585ee93c1ccd31aaa21f45861369401ebb6a2cb5c248634977a8 diff --git a/deps/checksums/MbedTLS.v2.16.0-1.i686-linux-gnu.tar.gz/md5 b/deps/checksums/MbedTLS.v2.16.0-1.i686-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..5bb4e9b --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-1.i686-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +35c1711cf9943da73f2dba9005d1e765 diff --git a/deps/checksums/MbedTLS.v2.16.0-1.i686-linux-gnu.tar.gz/sha512 b/deps/checksums/MbedTLS.v2.16.0-1.i686-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..996d743 --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-1.i686-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +316926f0b6ba84942e527a154a6e2751edf698e3c289d8ca92d1cede18c7793b9ea39a7245bd818c3bf2091e2f58aabd9ac93311ea472f629d88d6c00a035ff4 diff --git a/deps/checksums/MbedTLS.v2.16.0-1.i686-linux-musl.tar.gz/md5 b/deps/checksums/MbedTLS.v2.16.0-1.i686-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..2181347 --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-1.i686-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +8a834419657970331b6e2b44b8fe4a5c diff --git a/deps/checksums/MbedTLS.v2.16.0-1.i686-linux-musl.tar.gz/sha512 b/deps/checksums/MbedTLS.v2.16.0-1.i686-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..f238fc6 --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-1.i686-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +c5d7329be655511273b3511712bc68874250984665a083d552970af1d08db292bc191dfe5296cf9ebc45c51090b5bb299d17525801447fd992e5926ffd21b1f4 diff --git a/deps/checksums/MbedTLS.v2.16.0-1.i686-w64-mingw32.tar.gz/md5 b/deps/checksums/MbedTLS.v2.16.0-1.i686-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..92dc7bf --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-1.i686-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +93a90d234ada9f8e0979d8aa53a31d5b diff --git a/deps/checksums/MbedTLS.v2.16.0-1.i686-w64-mingw32.tar.gz/sha512 b/deps/checksums/MbedTLS.v2.16.0-1.i686-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..67509d8 --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-1.i686-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +3506e157dec0f0e3351fe2b4d0580bc38747f199a2c6f0cbbee781aa81b8a093f7a31fbe864aa938f32f6686aaac92ccdff534e43065d669ecd1172f2bf1a40d diff --git a/deps/checksums/MbedTLS.v2.16.0-1.powerpc64le-linux-gnu.tar.gz/md5 b/deps/checksums/MbedTLS.v2.16.0-1.powerpc64le-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..ad346d5 --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-1.powerpc64le-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +f260bdc7f05fb81303bf22ffc5357548 diff --git a/deps/checksums/MbedTLS.v2.16.0-1.powerpc64le-linux-gnu.tar.gz/sha512 b/deps/checksums/MbedTLS.v2.16.0-1.powerpc64le-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..c643c20 --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-1.powerpc64le-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +0a7ff35e8b6f0358459ea7bae059fc11b57f8e626bb5bd5fc8586f39254b9c2db044eef470095c77aa35139cd2867414ad0a3155fc67cc848224b71178971a46 diff --git a/deps/checksums/MbedTLS.v2.16.0-1.x86_64-apple-darwin14.tar.gz/md5 b/deps/checksums/MbedTLS.v2.16.0-1.x86_64-apple-darwin14.tar.gz/md5 new file mode 100644 index 0000000..73f6139 --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-1.x86_64-apple-darwin14.tar.gz/md5 @@ -0,0 +1 @@ +35ea69e501f16196bb0a0c2d8baee77f diff --git a/deps/checksums/MbedTLS.v2.16.0-1.x86_64-apple-darwin14.tar.gz/sha512 b/deps/checksums/MbedTLS.v2.16.0-1.x86_64-apple-darwin14.tar.gz/sha512 new file mode 100644 index 0000000..bc092af --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-1.x86_64-apple-darwin14.tar.gz/sha512 @@ -0,0 +1 @@ +6809ced55c67233ae0c16807e21bb1a327642a7cd2a041aab7683b15770a5cd41b40c98a8b6a8c67757ec98e3041509ee79b5af756c97737af00ac23d1b0f545 diff --git a/deps/checksums/MbedTLS.v2.16.0-1.x86_64-linux-gnu.tar.gz/md5 b/deps/checksums/MbedTLS.v2.16.0-1.x86_64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..c37efb7 --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-1.x86_64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +44b6ef27dff86636b481782a81b670ff diff --git a/deps/checksums/MbedTLS.v2.16.0-1.x86_64-linux-gnu.tar.gz/sha512 b/deps/checksums/MbedTLS.v2.16.0-1.x86_64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..65e60d5 --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-1.x86_64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +d5bdbbfbe997dcf6a066f97642d3c15641fdb44e6005e7b5c89a56609ffffda70db1d1df3dbb8c07f922e690ca22eeba1605ee6c0db4eb94d7683cfffd5a1a03 diff --git a/deps/checksums/MbedTLS.v2.16.0-1.x86_64-linux-musl.tar.gz/md5 b/deps/checksums/MbedTLS.v2.16.0-1.x86_64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..6929024 --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-1.x86_64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +72259058d1151efe5bb7a6b5b8228525 diff --git a/deps/checksums/MbedTLS.v2.16.0-1.x86_64-linux-musl.tar.gz/sha512 b/deps/checksums/MbedTLS.v2.16.0-1.x86_64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..3ef6e56 --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-1.x86_64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +2c06b8fcf71f7aa4a62f12522508e027cc5bd719e9eea23d55fa317803e831e7ac310f086af9b6ca27fc9dd68af0fc1a92e9e3eb9e3cd48d4888653e4bd758ae diff --git a/deps/checksums/MbedTLS.v2.16.0-1.x86_64-unknown-freebsd11.1.tar.gz/md5 b/deps/checksums/MbedTLS.v2.16.0-1.x86_64-unknown-freebsd11.1.tar.gz/md5 new file mode 100644 index 0000000..b7c1d71 --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-1.x86_64-unknown-freebsd11.1.tar.gz/md5 @@ -0,0 +1 @@ +3539981369c2b7a54126e617e510db97 diff --git a/deps/checksums/MbedTLS.v2.16.0-1.x86_64-unknown-freebsd11.1.tar.gz/sha512 b/deps/checksums/MbedTLS.v2.16.0-1.x86_64-unknown-freebsd11.1.tar.gz/sha512 new file mode 100644 index 0000000..7f35aca --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-1.x86_64-unknown-freebsd11.1.tar.gz/sha512 @@ -0,0 +1 @@ +ab7078bba347b8e4b67561969a016bdc5e7b032e7db5c513cdd578df531a52c8274609862f4fd2732b7eb68abe2795f920fbe9f021a0a6090f4d6c9fcc2213f3 diff --git a/deps/checksums/MbedTLS.v2.16.0-1.x86_64-w64-mingw32.tar.gz/md5 b/deps/checksums/MbedTLS.v2.16.0-1.x86_64-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..2d43af4 --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-1.x86_64-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +e625b6e3e8bf2200a633dcdabf5ddba3 diff --git a/deps/checksums/MbedTLS.v2.16.0-1.x86_64-w64-mingw32.tar.gz/sha512 b/deps/checksums/MbedTLS.v2.16.0-1.x86_64-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..39ed0f5 --- /dev/null +++ b/deps/checksums/MbedTLS.v2.16.0-1.x86_64-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +09e24f2bf8af07f86897015e4c1139058af4c0c7266a6d5ea31bb88cb3c24b4a995fac285a2c3b600725a3ad6670bb874c2ee70aa7f99ddc370bf78768c3ac24 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.armv7l-linux-gnueabihf.tar.gz/md5 b/deps/checksums/Objconv.v2.49.0-0.armv7l-linux-gnueabihf.tar.gz/md5 new file mode 100644 index 0000000..c98ef35 --- /dev/null +++ b/deps/checksums/Objconv.v2.49.0-0.armv7l-linux-gnueabihf.tar.gz/md5 @@ -0,0 +1 @@ +5f87140ee2b7d2b5c4d7d5e666edb793 diff --git a/deps/checksums/Objconv.v2.49.0-0.armv7l-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/Objconv.v2.49.0-0.armv7l-linux-gnueabihf.tar.gz/sha512 new file mode 100644 index 0000000..bc4bdd9 --- /dev/null +++ b/deps/checksums/Objconv.v2.49.0-0.armv7l-linux-gnueabihf.tar.gz/sha512 @@ -0,0 +1 @@ +cb2b3665d3edcd3b3386101d0ff22002383b25c3e9b58b03eb5387888b5cae2782f984e6a39542d395f2c100c8fd81c89a0aed79e5d7581647bf024eefa5f6c3 diff --git a/deps/checksums/Objconv.v2.49.0-0.armv7l-linux-musleabihf.tar.gz/md5 b/deps/checksums/Objconv.v2.49.0-0.armv7l-linux-musleabihf.tar.gz/md5 new file mode 100644 index 0000000..5286ffd --- /dev/null +++ b/deps/checksums/Objconv.v2.49.0-0.armv7l-linux-musleabihf.tar.gz/md5 @@ -0,0 +1 @@ +8dc7f75d66ece64f490d803751ef2315 diff --git a/deps/checksums/Objconv.v2.49.0-0.armv7l-linux-musleabihf.tar.gz/sha512 b/deps/checksums/Objconv.v2.49.0-0.armv7l-linux-musleabihf.tar.gz/sha512 new file mode 100644 index 0000000..46aa7a6 --- /dev/null +++ b/deps/checksums/Objconv.v2.49.0-0.armv7l-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.9-4.aarch64-linux-gnu-libgfortran3.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-gnu-libgfortran3.tar.gz/md5 new file mode 100644 index 0000000..010efce --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-gnu-libgfortran3.tar.gz/md5 @@ -0,0 +1 @@ +da0ee6ad24ec90bc9be0391b0790902a diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-gnu-libgfortran3.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-gnu-libgfortran3.tar.gz/sha512 new file mode 100644 index 0000000..f4d2638 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-gnu-libgfortran3.tar.gz/sha512 @@ -0,0 +1 @@ +22d001d6aa2ad764dca5012cef7ab0f01feedfee7def4ac48e9f6af5767bc0b74578899ca103e0b0b5211ac6f785ed077cdf4e4a005d06133e652e08b513cd12 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-gnu-libgfortran4.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-gnu-libgfortran4.tar.gz/md5 new file mode 100644 index 0000000..a706983 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-gnu-libgfortran4.tar.gz/md5 @@ -0,0 +1 @@ +24d11082435adde0f7857e80a9f08751 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-gnu-libgfortran4.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-gnu-libgfortran4.tar.gz/sha512 new file mode 100644 index 0000000..47592c9 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-gnu-libgfortran4.tar.gz/sha512 @@ -0,0 +1 @@ +03bdc630d8ce8081620dd2effa8c4c694dbb6f5c6a673096d6ac75052b46f244e34da10f94098197163fe4fa49b51d5b3ed82b8ff6e51d70cca035d2d18bbf26 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-gnu-libgfortran5.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-gnu-libgfortran5.tar.gz/md5 new file mode 100644 index 0000000..ce6e2fe --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-gnu-libgfortran5.tar.gz/md5 @@ -0,0 +1 @@ +0e1454d22dbd6348d56292ce529595fe diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-gnu-libgfortran5.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-gnu-libgfortran5.tar.gz/sha512 new file mode 100644 index 0000000..a8a04e7 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-gnu-libgfortran5.tar.gz/sha512 @@ -0,0 +1 @@ +d1a2ebaabf8c50c39ab3d85b03ee9b981706327328630b43d5c55116af948db00965e49155b2f39ae176b301003faf03c9f11d13b6c77dee1c6b6aaa07ad8d78 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-musl-libgfortran3.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-musl-libgfortran3.tar.gz/md5 new file mode 100644 index 0000000..7c310d1 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-musl-libgfortran3.tar.gz/md5 @@ -0,0 +1 @@ +374928a5095ded5b3fe72a4ab97ae3c2 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-musl-libgfortran3.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-musl-libgfortran3.tar.gz/sha512 new file mode 100644 index 0000000..8967b26 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-musl-libgfortran3.tar.gz/sha512 @@ -0,0 +1 @@ +0accd3b0fd92e150813d08554389da009b88cbb3a4f0fd531c9516da8330edcf1be0a57f072ef2512289e206bab09beefaf422e9dd36f641503a3f10c13472c5 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-musl-libgfortran4.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-musl-libgfortran4.tar.gz/md5 new file mode 100644 index 0000000..b5a2f8f --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-musl-libgfortran4.tar.gz/md5 @@ -0,0 +1 @@ +7cde1584f0b178b314cc0d70fa64ecf4 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-musl-libgfortran4.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-musl-libgfortran4.tar.gz/sha512 new file mode 100644 index 0000000..9f3911e --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-musl-libgfortran4.tar.gz/sha512 @@ -0,0 +1 @@ +a2339c154b363287ca5b61e128ca3802677d1f9b9c32c915c9f05624a147edca9910eb556f540843fe962fcc53071e3a577589c1aff1a200d30d92d19db37be4 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-musl-libgfortran5.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-musl-libgfortran5.tar.gz/md5 new file mode 100644 index 0000000..5ad8ee8 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-musl-libgfortran5.tar.gz/md5 @@ -0,0 +1 @@ +593b52301ea24cd9f7f7fb3b972fa8e0 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-musl-libgfortran5.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-musl-libgfortran5.tar.gz/sha512 new file mode 100644 index 0000000..1d65cc7 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.aarch64-linux-musl-libgfortran5.tar.gz/sha512 @@ -0,0 +1 @@ +2712e0ae88632e23fcc273be4ae25acdff27ad94aa1e7efe9186a027a81507247c7466f97a90b0e53915f5e8fa1c78ebd2db25046d351daba7bf3032228b9add diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-gnueabihf-libgfortran3.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-gnueabihf-libgfortran3.tar.gz/md5 new file mode 100644 index 0000000..f6e390a --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-gnueabihf-libgfortran3.tar.gz/md5 @@ -0,0 +1 @@ +6f83283858762b9eff5aa8dfc99328a4 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-gnueabihf-libgfortran3.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-gnueabihf-libgfortran3.tar.gz/sha512 new file mode 100644 index 0000000..6baec07 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-gnueabihf-libgfortran3.tar.gz/sha512 @@ -0,0 +1 @@ +a09fbf43e555727baacdd01ff61872c4e709a3e7a5a6be7dd84f0c94e95f00663c6ca66538ce745e64bebabc0fb26f8785db84a0ff7f60c0c8c8c4d9ca97fdc7 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-gnueabihf-libgfortran4.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-gnueabihf-libgfortran4.tar.gz/md5 new file mode 100644 index 0000000..1f40f37 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-gnueabihf-libgfortran4.tar.gz/md5 @@ -0,0 +1 @@ +09ff5783773771f4e8bc498d42a2fecf diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-gnueabihf-libgfortran4.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-gnueabihf-libgfortran4.tar.gz/sha512 new file mode 100644 index 0000000..b5ae171 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-gnueabihf-libgfortran4.tar.gz/sha512 @@ -0,0 +1 @@ +5e77e0d850304c126f0a53b27221fe5132d5ef7a8a576c727206e6c94891a4e46d014ac895eb40a51d9e119f8bba71b05f2f3223ae038ae71947b64bd588864e diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-gnueabihf-libgfortran5.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-gnueabihf-libgfortran5.tar.gz/md5 new file mode 100644 index 0000000..b9024ad --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-gnueabihf-libgfortran5.tar.gz/md5 @@ -0,0 +1 @@ +a2c1037a54bbf0f1454a430f1d9b7d90 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-gnueabihf-libgfortran5.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-gnueabihf-libgfortran5.tar.gz/sha512 new file mode 100644 index 0000000..d95c3c4 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-gnueabihf-libgfortran5.tar.gz/sha512 @@ -0,0 +1 @@ +f8263dc617516d382b2b05a1b12e9cacdc96e3eee024643bd1b959a1c8590b8c466a009fd6e5fb5e9d99265c70a2bb967f65b6adf9aa178053b81075142483ea diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-musleabihf-libgfortran3.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-musleabihf-libgfortran3.tar.gz/md5 new file mode 100644 index 0000000..8e69dd8 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-musleabihf-libgfortran3.tar.gz/md5 @@ -0,0 +1 @@ +b29da7f232b6912389690b87cc506a74 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-musleabihf-libgfortran3.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-musleabihf-libgfortran3.tar.gz/sha512 new file mode 100644 index 0000000..4c18184 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-musleabihf-libgfortran3.tar.gz/sha512 @@ -0,0 +1 @@ +718a2f109b74082818e144a5ed89ff85b8a8ca25c554d390dd345f0c7201dfb6c0ff9100bba6c053b00e4f7a565086ee0baa9d3cd90a95787e010eb3a966a52a diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-musleabihf-libgfortran4.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-musleabihf-libgfortran4.tar.gz/md5 new file mode 100644 index 0000000..547485a --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-musleabihf-libgfortran4.tar.gz/md5 @@ -0,0 +1 @@ +db3f9526e20fbb912d21e92410e4db80 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-musleabihf-libgfortran4.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-musleabihf-libgfortran4.tar.gz/sha512 new file mode 100644 index 0000000..ee6d0fb --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-musleabihf-libgfortran4.tar.gz/sha512 @@ -0,0 +1 @@ +75fa5c62b5deff6346a3328f50dcb971313e9e1246545c6cd33e65e761d8c8eff617e454ec61eb4c642404ddb477929d00016f59fc7045ecae99b1c97bc33a4c diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-musleabihf-libgfortran5.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-musleabihf-libgfortran5.tar.gz/md5 new file mode 100644 index 0000000..9f28b6e --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-musleabihf-libgfortran5.tar.gz/md5 @@ -0,0 +1 @@ +c922a0f06afbc5a1d8c0a780b39e0b92 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-musleabihf-libgfortran5.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-musleabihf-libgfortran5.tar.gz/sha512 new file mode 100644 index 0000000..12e34d7 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.armv7l-linux-musleabihf-libgfortran5.tar.gz/sha512 @@ -0,0 +1 @@ +8dc83be721407af8252aa7b54f7edfea6fa7c9f98596078a1137caf7d9215f352e64dc5f6914d74c3414ae9b3265d2ec2639fa79258e58442ac221fe9843a05b diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-gnu-libgfortran3.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-gnu-libgfortran3.tar.gz/md5 new file mode 100644 index 0000000..e795eda --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-gnu-libgfortran3.tar.gz/md5 @@ -0,0 +1 @@ +731e7b9846c18ef2902191ad394cced5 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-gnu-libgfortran3.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-gnu-libgfortran3.tar.gz/sha512 new file mode 100644 index 0000000..e45a93a --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-gnu-libgfortran3.tar.gz/sha512 @@ -0,0 +1 @@ +f98a83b111a7b1ac3538313ac7c7ba844d7db8c78a48bb845f46bf3baadb818d10e8bf60fb7d389cafa20c9cfd270ec0b862c04171826cba6e17b5e01af58487 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-gnu-libgfortran4.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-gnu-libgfortran4.tar.gz/md5 new file mode 100644 index 0000000..43274d2 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-gnu-libgfortran4.tar.gz/md5 @@ -0,0 +1 @@ +7be0092c7d0def2accde275aec70b8b8 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-gnu-libgfortran4.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-gnu-libgfortran4.tar.gz/sha512 new file mode 100644 index 0000000..1b15c5f --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-gnu-libgfortran4.tar.gz/sha512 @@ -0,0 +1 @@ +1c1804af7a0e2b1425fb91a72d5c7086cdf33d8e70c16522e8a035de506c0c9660f1cf4c22a05452e2f2c9e5a50596f364b454fa0656cac5bccc5270b6c26a67 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-gnu-libgfortran5.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-gnu-libgfortran5.tar.gz/md5 new file mode 100644 index 0000000..9c3064d --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-gnu-libgfortran5.tar.gz/md5 @@ -0,0 +1 @@ +f6dc9cb85abfa29d187ad810b14b1a7e diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-gnu-libgfortran5.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-gnu-libgfortran5.tar.gz/sha512 new file mode 100644 index 0000000..6767fc9 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-gnu-libgfortran5.tar.gz/sha512 @@ -0,0 +1 @@ +3240102358ac9b097548825b7f9a4d4a05e2c08511286d429feb28c16fec0815ba58f48953fac6a6198bd5aaae25461cb37d35168a574b51794a676cbf03ebe6 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-musl-libgfortran3.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-musl-libgfortran3.tar.gz/md5 new file mode 100644 index 0000000..8944b76 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-musl-libgfortran3.tar.gz/md5 @@ -0,0 +1 @@ +2a495c43e23fab632f5b1b6f9ac0017e diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-musl-libgfortran3.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-musl-libgfortran3.tar.gz/sha512 new file mode 100644 index 0000000..4d4878c --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-musl-libgfortran3.tar.gz/sha512 @@ -0,0 +1 @@ +da63eadbf141558562ead29c2e91ab254bc68e4695d5ca0e0e7f9585a79988612405e717f826dee9b1f659969c94798b75fa69996503e05753113a2dfbb722f2 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-musl-libgfortran4.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-musl-libgfortran4.tar.gz/md5 new file mode 100644 index 0000000..e5a4fd9 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-musl-libgfortran4.tar.gz/md5 @@ -0,0 +1 @@ +07aab3c3ef6ca1da458c0d231dbf3478 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-musl-libgfortran4.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-musl-libgfortran4.tar.gz/sha512 new file mode 100644 index 0000000..756d54c --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-musl-libgfortran4.tar.gz/sha512 @@ -0,0 +1 @@ +3f5e6bdc089b5483e6944bdf0ed5818c856dbe3195811f8a18f1ab1a6a0f471b934a9d6efd505f6a95dd9a82304e9a1a6a63f3fc8475d547f1f1aa008da53f9e diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-musl-libgfortran5.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-musl-libgfortran5.tar.gz/md5 new file mode 100644 index 0000000..c18f086 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-musl-libgfortran5.tar.gz/md5 @@ -0,0 +1 @@ +f46af8c61ba5a73839c10884afabd05c diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-musl-libgfortran5.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-musl-libgfortran5.tar.gz/sha512 new file mode 100644 index 0000000..0b76470 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.i686-linux-musl-libgfortran5.tar.gz/sha512 @@ -0,0 +1 @@ +23d776a99acde9eec85b7ef734f8f0a697f105987b22f0d690045c4b8859a51f67821093744420e88d319ce4114cb07457c1dc23358001723195cc2cfb5446fc diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.i686-w64-mingw32-libgfortran3.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.i686-w64-mingw32-libgfortran3.tar.gz/md5 new file mode 100644 index 0000000..dabb53c --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.i686-w64-mingw32-libgfortran3.tar.gz/md5 @@ -0,0 +1 @@ +e8a49c1779dd8ec90a413b266f253914 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.i686-w64-mingw32-libgfortran3.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.i686-w64-mingw32-libgfortran3.tar.gz/sha512 new file mode 100644 index 0000000..54a7179 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.i686-w64-mingw32-libgfortran3.tar.gz/sha512 @@ -0,0 +1 @@ +e6801f2830d6e322c673d7f2784dc4f1c8bb87290b129ba4bd304c4d3430984fcb359e56fe5e931c1a610dd8fc29efad6bb9384ede79a94803624aa6ad65a510 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.i686-w64-mingw32-libgfortran4.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.i686-w64-mingw32-libgfortran4.tar.gz/md5 new file mode 100644 index 0000000..0fdeecd --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.i686-w64-mingw32-libgfortran4.tar.gz/md5 @@ -0,0 +1 @@ +597c4505edcf3f04586ab7c9bbaeb3b7 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.i686-w64-mingw32-libgfortran4.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.i686-w64-mingw32-libgfortran4.tar.gz/sha512 new file mode 100644 index 0000000..8ca1900 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.i686-w64-mingw32-libgfortran4.tar.gz/sha512 @@ -0,0 +1 @@ +e08d0d2244bb1731813398c7c3a87e86d70f64e3312896923a108fb5a4f7e02bd6da229482b38f46193f3d1eac63e3eb8181343a4c4325e4579488c8d1f22011 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.i686-w64-mingw32-libgfortran5.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.i686-w64-mingw32-libgfortran5.tar.gz/md5 new file mode 100644 index 0000000..7db1a67 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.i686-w64-mingw32-libgfortran5.tar.gz/md5 @@ -0,0 +1 @@ +f2cd765f25982e57a96576c57e95fd5d diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.i686-w64-mingw32-libgfortran5.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.i686-w64-mingw32-libgfortran5.tar.gz/sha512 new file mode 100644 index 0000000..795373f --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.i686-w64-mingw32-libgfortran5.tar.gz/sha512 @@ -0,0 +1 @@ +7e40412d7c9a55a0f8f8105af2ccf058d154de0a38dcf6650e76142c5d59d48c99ca13b1f00ecb818d578d959b244d92321d655fa7a2086db8964cd9d0297629 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.powerpc64le-linux-gnu-libgfortran3.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.powerpc64le-linux-gnu-libgfortran3.tar.gz/md5 new file mode 100644 index 0000000..5af94aa --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.powerpc64le-linux-gnu-libgfortran3.tar.gz/md5 @@ -0,0 +1 @@ +3bd6e775ca320bc9a6914638197bfab3 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.powerpc64le-linux-gnu-libgfortran3.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.powerpc64le-linux-gnu-libgfortran3.tar.gz/sha512 new file mode 100644 index 0000000..94a46d8 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.powerpc64le-linux-gnu-libgfortran3.tar.gz/sha512 @@ -0,0 +1 @@ +84298edd2d40cac2a6a563d2c46fe3ed65b7d19718a468391cd46272d541687c48a0f456a0b007077ea300f738bceb9d2278e1dbf166e0e25c3a5cb7d96bc2a6 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.powerpc64le-linux-gnu-libgfortran4.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.powerpc64le-linux-gnu-libgfortran4.tar.gz/md5 new file mode 100644 index 0000000..14468f4 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.powerpc64le-linux-gnu-libgfortran4.tar.gz/md5 @@ -0,0 +1 @@ +09b44ede0c0e6a2e6006e458279c42a8 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.powerpc64le-linux-gnu-libgfortran4.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.powerpc64le-linux-gnu-libgfortran4.tar.gz/sha512 new file mode 100644 index 0000000..37a4cc2 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.powerpc64le-linux-gnu-libgfortran4.tar.gz/sha512 @@ -0,0 +1 @@ +63ba10bd9e885b12b1092b1b52bcd38ea3cd7af23cfc45fc0180d925fe117fa48790c037562bf5413a04f530eea42d199f527e331b9c758a50bc6239d437e0d1 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.powerpc64le-linux-gnu-libgfortran5.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.powerpc64le-linux-gnu-libgfortran5.tar.gz/md5 new file mode 100644 index 0000000..9a7c448 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.powerpc64le-linux-gnu-libgfortran5.tar.gz/md5 @@ -0,0 +1 @@ +88d7bcd47244969c47169bbcb78f44ef diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.powerpc64le-linux-gnu-libgfortran5.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.powerpc64le-linux-gnu-libgfortran5.tar.gz/sha512 new file mode 100644 index 0000000..261d93b --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.powerpc64le-linux-gnu-libgfortran5.tar.gz/sha512 @@ -0,0 +1 @@ +aa5da0173fb0509114c0901f437786bfe70aea7d91a36b8459f70c92906e178abf919413f5ac9f2fd6adc7376095758f26083f6efde73bd654af51c709b396dd diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-apple-darwin14-libgfortran3.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-apple-darwin14-libgfortran3.tar.gz/md5 new file mode 100644 index 0000000..616d001 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-apple-darwin14-libgfortran3.tar.gz/md5 @@ -0,0 +1 @@ +2e5dcc9d83cbdd0fff28abb1a5d89cc3 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-apple-darwin14-libgfortran3.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-apple-darwin14-libgfortran3.tar.gz/sha512 new file mode 100644 index 0000000..4e67b2b --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-apple-darwin14-libgfortran3.tar.gz/sha512 @@ -0,0 +1 @@ +9905a8e5c44d29402266c1b009114cda856039c6c106f9601ee833769b5629b36d7a5127102261ddd503f6ec73bbb423d09576eaf98f07da20d4c1390058b97f diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-apple-darwin14-libgfortran4.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-apple-darwin14-libgfortran4.tar.gz/md5 new file mode 100644 index 0000000..52b06fb --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-apple-darwin14-libgfortran4.tar.gz/md5 @@ -0,0 +1 @@ +6c4aa0387513bcaa68d8335974f98992 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-apple-darwin14-libgfortran4.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-apple-darwin14-libgfortran4.tar.gz/sha512 new file mode 100644 index 0000000..23f9a20 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-apple-darwin14-libgfortran4.tar.gz/sha512 @@ -0,0 +1 @@ +b303f82adf9309e27c7822833591f4ba07a1f1e5dbf40ee0a27097e2049e6589205b7101918bba27e5af1921e61024ea68fe9bbf57318346f7b85f2468ad0a00 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-apple-darwin14-libgfortran5.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-apple-darwin14-libgfortran5.tar.gz/md5 new file mode 100644 index 0000000..db098d6 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-apple-darwin14-libgfortran5.tar.gz/md5 @@ -0,0 +1 @@ +e9cab08a045556b06907870143031d52 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-apple-darwin14-libgfortran5.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-apple-darwin14-libgfortran5.tar.gz/sha512 new file mode 100644 index 0000000..13793e8 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-apple-darwin14-libgfortran5.tar.gz/sha512 @@ -0,0 +1 @@ +ec963b45b7a91a8fe2709b0ec328f3957484566d59bc0eff4c909e7be929acfcd5cee8877732511cb2b9241ad464e16749afdcda205783f874e4fff33f954382 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-gnu-libgfortran3.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-gnu-libgfortran3.tar.gz/md5 new file mode 100644 index 0000000..c962d33 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-gnu-libgfortran3.tar.gz/md5 @@ -0,0 +1 @@ +3bdcca0d788e39c2dd23abea89624a98 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-gnu-libgfortran3.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-gnu-libgfortran3.tar.gz/sha512 new file mode 100644 index 0000000..fad8adb --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-gnu-libgfortran3.tar.gz/sha512 @@ -0,0 +1 @@ +5367756a38e72fd09370cbf3081eb96a85c40cd4d603e96263aa3d4101cad71cee16c9fe48615ce962191184447a26e31b4e4ddf4231b6eaa166ca2df6a23c42 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-gnu-libgfortran4.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-gnu-libgfortran4.tar.gz/md5 new file mode 100644 index 0000000..1c0bbf4 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-gnu-libgfortran4.tar.gz/md5 @@ -0,0 +1 @@ +3e241d4de2faa91d69d81d88cf9f47f1 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-gnu-libgfortran4.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-gnu-libgfortran4.tar.gz/sha512 new file mode 100644 index 0000000..3b7ca7b --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-gnu-libgfortran4.tar.gz/sha512 @@ -0,0 +1 @@ +6ad4ae3cf3376ab971a0a3a0516c26a9e0ea1e76ccb5f6f932e54adbaf1e0331efc295b8bd12202e1fc0366dad44029d86590793638add0ee734da325e8667c8 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-gnu-libgfortran5.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-gnu-libgfortran5.tar.gz/md5 new file mode 100644 index 0000000..d63327a --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-gnu-libgfortran5.tar.gz/md5 @@ -0,0 +1 @@ +c749b863138a583b673a926333d06ce9 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-gnu-libgfortran5.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-gnu-libgfortran5.tar.gz/sha512 new file mode 100644 index 0000000..3bf8480 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-gnu-libgfortran5.tar.gz/sha512 @@ -0,0 +1 @@ +80b9fe85627c9d14de9abbdf1280a931b3c3da7b01505ca6784394f43bbef80e8010ac2c158ffbcdcf876893526eff0695329b964a75913d3150c2bdf07d6554 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-musl-libgfortran3.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-musl-libgfortran3.tar.gz/md5 new file mode 100644 index 0000000..9368c6d --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-musl-libgfortran3.tar.gz/md5 @@ -0,0 +1 @@ +1eb3acb7f307deb3e798ceb29db18159 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-musl-libgfortran3.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-musl-libgfortran3.tar.gz/sha512 new file mode 100644 index 0000000..917cdb2 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-musl-libgfortran3.tar.gz/sha512 @@ -0,0 +1 @@ +6f2059f0035bc747cdc5ebc290c8afb2de40c17b3de1a47c93e3b04adedbf404e3374c4f1d8b0daee1a498dc95f9051dd1c5e5621510d1b9b44ecb3cac70454b diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-musl-libgfortran4.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-musl-libgfortran4.tar.gz/md5 new file mode 100644 index 0000000..8bd347f --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-musl-libgfortran4.tar.gz/md5 @@ -0,0 +1 @@ +e12089bcefc245df4ae1386ab77024bf diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-musl-libgfortran4.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-musl-libgfortran4.tar.gz/sha512 new file mode 100644 index 0000000..8f11951 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-musl-libgfortran4.tar.gz/sha512 @@ -0,0 +1 @@ +c7a1773fdd65e64213273ffa6106beb0dc661b327dfc2e7b74bca54bdff816a2d2629f69f66e8c5b6106ef86b5dc0285035c36c86c58f5c82c91833b77eeba44 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-musl-libgfortran5.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-musl-libgfortran5.tar.gz/md5 new file mode 100644 index 0000000..1c18d5c --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-musl-libgfortran5.tar.gz/md5 @@ -0,0 +1 @@ +7324cd761f75dfc66d1b70a70030f7d5 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-musl-libgfortran5.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-musl-libgfortran5.tar.gz/sha512 new file mode 100644 index 0000000..4d9283f --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-linux-musl-libgfortran5.tar.gz/sha512 @@ -0,0 +1 @@ +0f00289eb016ac05391ef6f3d8d9af3b3fc57579201b2d06bb2143ce3d9fc0817f7555a2a57452afea33dd0f0e67f3812724008a5205a4207bca15f54cb516ca diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-unknown-freebsd11.1-libgfortran3.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-unknown-freebsd11.1-libgfortran3.tar.gz/md5 new file mode 100644 index 0000000..4c2fc20 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-unknown-freebsd11.1-libgfortran3.tar.gz/md5 @@ -0,0 +1 @@ +62189e5b79b943e8280f43d20f913d50 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-unknown-freebsd11.1-libgfortran3.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-unknown-freebsd11.1-libgfortran3.tar.gz/sha512 new file mode 100644 index 0000000..ed9dae8 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-unknown-freebsd11.1-libgfortran3.tar.gz/sha512 @@ -0,0 +1 @@ +f5c1634aa17a6137fa78a01afd1186d7a38ee0e92399cc55e61aea29e07068db75f21c5aa9c7f2c99a7d00c8318678c0b02280c7f81fa3d4a3edaca48cd946fa diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-unknown-freebsd11.1-libgfortran4.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-unknown-freebsd11.1-libgfortran4.tar.gz/md5 new file mode 100644 index 0000000..b611d48 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-unknown-freebsd11.1-libgfortran4.tar.gz/md5 @@ -0,0 +1 @@ +4ad539dce77f1e7b813c44a6f246164f diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-unknown-freebsd11.1-libgfortran4.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-unknown-freebsd11.1-libgfortran4.tar.gz/sha512 new file mode 100644 index 0000000..1411d18 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-unknown-freebsd11.1-libgfortran4.tar.gz/sha512 @@ -0,0 +1 @@ +336d01dbf88e85a18028e3198310e9e14902478de8776bcec2f3bcfd9798994a7a380ed2e37bb250cfe28a233dc1658f4e0732349b9ba1af61a213f9ccf70ed5 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-unknown-freebsd11.1-libgfortran5.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-unknown-freebsd11.1-libgfortran5.tar.gz/md5 new file mode 100644 index 0000000..176fe03 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-unknown-freebsd11.1-libgfortran5.tar.gz/md5 @@ -0,0 +1 @@ +4ec5669f1583c5be1448dfa243dbc0b0 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-unknown-freebsd11.1-libgfortran5.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-unknown-freebsd11.1-libgfortran5.tar.gz/sha512 new file mode 100644 index 0000000..003213b --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-unknown-freebsd11.1-libgfortran5.tar.gz/sha512 @@ -0,0 +1 @@ +26b88f8467a87b813b84e0585c268e958bb6c4003e5f99196304186b4cb236fd7e00ce526e138a5336ad4ddb39caa4bbf84fec9c42a24bc3743832d7d2323c17 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-w64-mingw32-libgfortran3.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-w64-mingw32-libgfortran3.tar.gz/md5 new file mode 100644 index 0000000..3b2ae9a --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-w64-mingw32-libgfortran3.tar.gz/md5 @@ -0,0 +1 @@ +19b43c20edd438edfb5dd91b14433582 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-w64-mingw32-libgfortran3.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-w64-mingw32-libgfortran3.tar.gz/sha512 new file mode 100644 index 0000000..0cd1356 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-w64-mingw32-libgfortran3.tar.gz/sha512 @@ -0,0 +1 @@ +9d83ccd99b40847e0761ff98ca27078910fe20f4fd5819519c3b81e92b93e60f4d0ffae5fafb754538850bbccdf42b52db29cecf589753b559dd828ba55d5c8a diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-w64-mingw32-libgfortran4.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-w64-mingw32-libgfortran4.tar.gz/md5 new file mode 100644 index 0000000..fcca142 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-w64-mingw32-libgfortran4.tar.gz/md5 @@ -0,0 +1 @@ +00b44e2506ec768de996f02e4e004679 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-w64-mingw32-libgfortran4.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-w64-mingw32-libgfortran4.tar.gz/sha512 new file mode 100644 index 0000000..6f05f1b --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-w64-mingw32-libgfortran4.tar.gz/sha512 @@ -0,0 +1 @@ +65f14d81b00d8fe67f33c273376746951ba6e54e6db750c995f8b407bd6141bdd164245ee78fabf0f03c3ef0e3ae0e68aa542f29e7b738051a926f0c07226abd diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-w64-mingw32-libgfortran5.tar.gz/md5 b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-w64-mingw32-libgfortran5.tar.gz/md5 new file mode 100644 index 0000000..817b043 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-w64-mingw32-libgfortran5.tar.gz/md5 @@ -0,0 +1 @@ +0a5e4f35363410da9d22e8253894e2f9 diff --git a/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-w64-mingw32-libgfortran5.tar.gz/sha512 b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-w64-mingw32-libgfortran5.tar.gz/sha512 new file mode 100644 index 0000000..9cf1d25 --- /dev/null +++ b/deps/checksums/OpenBLAS.v0.3.9-4.x86_64-w64-mingw32-libgfortran5.tar.gz/sha512 @@ -0,0 +1 @@ +af6b355bb0c2f23a6678f3c7a58f0a4013fdb395b1972dc15838ad5202571ed130b151a43061a99e4994f1e8edfe8a989e96bc5fe76d25f7f92e2756c8fb83d1 diff --git a/deps/checksums/OpenLibm.v0.7.0-0.aarch64-linux-gnu.tar.gz/md5 b/deps/checksums/OpenLibm.v0.7.0-0.aarch64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..10c0186 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.7.0-0.aarch64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +3f445fab4bbc703837fce894c31483f3 diff --git a/deps/checksums/OpenLibm.v0.7.0-0.aarch64-linux-gnu.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.7.0-0.aarch64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..c9d5e8a --- /dev/null +++ b/deps/checksums/OpenLibm.v0.7.0-0.aarch64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +7d3dcd96ab672e2c4323d7ba4e7b44f1cf0e8283fe3df13d0add40f0a5ed31176bd5cf0f222720e08f1fb9368027527bd495d72d54195330fb0671e004b30096 diff --git a/deps/checksums/OpenLibm.v0.7.0-0.aarch64-linux-musl.tar.gz/md5 b/deps/checksums/OpenLibm.v0.7.0-0.aarch64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..b482e64 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.7.0-0.aarch64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +b73fc13eb967d8f7502ffd5b314e80b2 diff --git a/deps/checksums/OpenLibm.v0.7.0-0.aarch64-linux-musl.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.7.0-0.aarch64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..3ae7e12 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.7.0-0.aarch64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +bd46a21fc6308a1bbc626b5002c4ff029f50d738fac4d58391afc3301ec13feb4e781bfe6d664c962d02a84e970221aa2e7e17a9328d4cf5e3db7b4b6fcbbe7b diff --git a/deps/checksums/OpenLibm.v0.7.0-0.armv7l-linux-gnueabihf.tar.gz/md5 b/deps/checksums/OpenLibm.v0.7.0-0.armv7l-linux-gnueabihf.tar.gz/md5 new file mode 100644 index 0000000..8ac7ccc --- /dev/null +++ b/deps/checksums/OpenLibm.v0.7.0-0.armv7l-linux-gnueabihf.tar.gz/md5 @@ -0,0 +1 @@ +78dc354ee31636c3bdc6bd59bd0254cc diff --git a/deps/checksums/OpenLibm.v0.7.0-0.armv7l-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.7.0-0.armv7l-linux-gnueabihf.tar.gz/sha512 new file mode 100644 index 0000000..2bdc2f5 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.7.0-0.armv7l-linux-gnueabihf.tar.gz/sha512 @@ -0,0 +1 @@ +161cfad57e4adb911895c548e38a722d7b8245741b0cc147d10a4656c0bbe1c5ed2b7b1450d88dbef666dc9882bc9f32897b584e306f96e1ced71cc8d8365699 diff --git a/deps/checksums/OpenLibm.v0.7.0-0.armv7l-linux-musleabihf.tar.gz/md5 b/deps/checksums/OpenLibm.v0.7.0-0.armv7l-linux-musleabihf.tar.gz/md5 new file mode 100644 index 0000000..6a28da6 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.7.0-0.armv7l-linux-musleabihf.tar.gz/md5 @@ -0,0 +1 @@ +926a9897fc997ef5b316e49d7ff64f2a diff --git a/deps/checksums/OpenLibm.v0.7.0-0.armv7l-linux-musleabihf.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.7.0-0.armv7l-linux-musleabihf.tar.gz/sha512 new file mode 100644 index 0000000..b69e57a --- /dev/null +++ b/deps/checksums/OpenLibm.v0.7.0-0.armv7l-linux-musleabihf.tar.gz/sha512 @@ -0,0 +1 @@ +e47af6f94def69faf48fd356057643a197b48c19b9f76ee46894abb53a48c93a4bc32d3ce85d68e271e387b9ad1478d31a2f0eb9bfa13d4e9f02885f3ae610b4 diff --git a/deps/checksums/OpenLibm.v0.7.0-0.i686-linux-gnu.tar.gz/md5 b/deps/checksums/OpenLibm.v0.7.0-0.i686-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..5b30391 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.7.0-0.i686-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +bd5f5add68aff856744ffcfe2f4546fe diff --git a/deps/checksums/OpenLibm.v0.7.0-0.i686-linux-gnu.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.7.0-0.i686-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..bb84b8c --- /dev/null +++ b/deps/checksums/OpenLibm.v0.7.0-0.i686-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +8b1364665b0a2d18fe9c9fef3865790bc96679b665aecf7852decab7ca3ef1ebdbffb464b7c32806bedd74f770f1116aebf4c135dd35483bfdf4cef0fab32f40 diff --git a/deps/checksums/OpenLibm.v0.7.0-0.i686-linux-musl.tar.gz/md5 b/deps/checksums/OpenLibm.v0.7.0-0.i686-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..8a2f87c --- /dev/null +++ b/deps/checksums/OpenLibm.v0.7.0-0.i686-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +13d9c4562fc22b840c2e6f5756dc91ab diff --git a/deps/checksums/OpenLibm.v0.7.0-0.i686-linux-musl.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.7.0-0.i686-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..a47a57a --- /dev/null +++ b/deps/checksums/OpenLibm.v0.7.0-0.i686-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +20ab2007fac054a9231b06634fe2a368ffee6a5a5c6db80c8e44b033abefbcd5058008b694d3a0c5bdb360bd4ff1ee7be564da3c783084599c602077da966e36 diff --git a/deps/checksums/OpenLibm.v0.7.0-0.i686-w64-mingw32.tar.gz/md5 b/deps/checksums/OpenLibm.v0.7.0-0.i686-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..171610b --- /dev/null +++ b/deps/checksums/OpenLibm.v0.7.0-0.i686-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +76b2cf1608c3fd4f41383ccc9e921925 diff --git a/deps/checksums/OpenLibm.v0.7.0-0.i686-w64-mingw32.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.7.0-0.i686-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..05a0b96 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.7.0-0.i686-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +cfc878c19d27f75d6b2d5c659d2b9acd527f6eca1aa1a8dc8528eac49da87785730807fe30955ee313d53fd074ff261e630935d882c93922f80612eb20dd07a2 diff --git a/deps/checksums/OpenLibm.v0.7.0-0.powerpc64le-linux-gnu.tar.gz/md5 b/deps/checksums/OpenLibm.v0.7.0-0.powerpc64le-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..3c99964 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.7.0-0.powerpc64le-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +7c9aaaf1b6cd7e5254fc04db07b7335c diff --git a/deps/checksums/OpenLibm.v0.7.0-0.powerpc64le-linux-gnu.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.7.0-0.powerpc64le-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..32d485d --- /dev/null +++ b/deps/checksums/OpenLibm.v0.7.0-0.powerpc64le-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +e29d8c43768d8673c7491ad566aef4571b0540bac948ec414ca54269453e5c702afb74ac942f755be0a9adf33120a439b591926ba0ebab46ba2b325fa77c25cf diff --git a/deps/checksums/OpenLibm.v0.7.0-0.x86_64-apple-darwin14.tar.gz/md5 b/deps/checksums/OpenLibm.v0.7.0-0.x86_64-apple-darwin14.tar.gz/md5 new file mode 100644 index 0000000..e814d7a --- /dev/null +++ b/deps/checksums/OpenLibm.v0.7.0-0.x86_64-apple-darwin14.tar.gz/md5 @@ -0,0 +1 @@ +02a808e8940dfe0233a32ea10629a2df diff --git a/deps/checksums/OpenLibm.v0.7.0-0.x86_64-apple-darwin14.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.7.0-0.x86_64-apple-darwin14.tar.gz/sha512 new file mode 100644 index 0000000..238f6cf --- /dev/null +++ b/deps/checksums/OpenLibm.v0.7.0-0.x86_64-apple-darwin14.tar.gz/sha512 @@ -0,0 +1 @@ +c6ecbc8667995859cc16e89551cb826627af70cb019729afd7277003150eb61e673eda812ebbb4fd592a3f2c9da960d833b69ff5faa148674779446cf277c31c diff --git a/deps/checksums/OpenLibm.v0.7.0-0.x86_64-linux-gnu.tar.gz/md5 b/deps/checksums/OpenLibm.v0.7.0-0.x86_64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..7b52ca7 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.7.0-0.x86_64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +2526f73a38ef407ec118ae3ea4e5f8d8 diff --git a/deps/checksums/OpenLibm.v0.7.0-0.x86_64-linux-gnu.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.7.0-0.x86_64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..f0fe38b --- /dev/null +++ b/deps/checksums/OpenLibm.v0.7.0-0.x86_64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +79f495b9a5332eabae4742cd6f5233403f69ffb30e6fff30d8dee5cc10e4a278e9e00c82fc32936a4b42e3446ecad208a96fbfe2b2e9d91db15b52195f5e1fe8 diff --git a/deps/checksums/OpenLibm.v0.7.0-0.x86_64-linux-musl.tar.gz/md5 b/deps/checksums/OpenLibm.v0.7.0-0.x86_64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..9ad3a57 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.7.0-0.x86_64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +08b36c9c40568e64f76a87484ff4736c diff --git a/deps/checksums/OpenLibm.v0.7.0-0.x86_64-linux-musl.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.7.0-0.x86_64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..4ae2933 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.7.0-0.x86_64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +8ed05913066a39338e487833574b895e3d88ae79eaa9038972e33f94ec2afab93a84a8833ecc90bacc0940f5bb6f22af8b9117bcf7e943fe1f9114ba2576f698 diff --git a/deps/checksums/OpenLibm.v0.7.0-0.x86_64-unknown-freebsd11.1.tar.gz/md5 b/deps/checksums/OpenLibm.v0.7.0-0.x86_64-unknown-freebsd11.1.tar.gz/md5 new file mode 100644 index 0000000..30ae042 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.7.0-0.x86_64-unknown-freebsd11.1.tar.gz/md5 @@ -0,0 +1 @@ +f037929da10ae741953fcc8ffcf3802c diff --git a/deps/checksums/OpenLibm.v0.7.0-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.7.0-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 new file mode 100644 index 0000000..55c1b90 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.7.0-0.x86_64-unknown-freebsd11.1.tar.gz/sha512 @@ -0,0 +1 @@ +bf802cc44e8638fcd8d263070e56c3bdea37899405ffe8a67d8bc16a70dba46985d6e32e5651b5596e47608db272d1e0b53a23b23633b84d6dd3a51a5b52eb88 diff --git a/deps/checksums/OpenLibm.v0.7.0-0.x86_64-w64-mingw32.tar.gz/md5 b/deps/checksums/OpenLibm.v0.7.0-0.x86_64-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..a89d308 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.7.0-0.x86_64-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +6e8c1ee1b5733301aacd19165d8376c1 diff --git a/deps/checksums/OpenLibm.v0.7.0-0.x86_64-w64-mingw32.tar.gz/sha512 b/deps/checksums/OpenLibm.v0.7.0-0.x86_64-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..9e971b9 --- /dev/null +++ b/deps/checksums/OpenLibm.v0.7.0-0.x86_64-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +d24463200b065bd0137c5c87c78cc36bf8c157fca7c6b07e44f0763eb580a2fecb5aed80433a942f2ed528d0807ad045c05d73516f8f6e84e9d5cb8255deefb9 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.armv7l-linux-gnueabihf.tar.gz/md5 b/deps/checksums/PCRE2.v10.31.0-0.armv7l-linux-gnueabihf.tar.gz/md5 new file mode 100644 index 0000000..c940a87 --- /dev/null +++ b/deps/checksums/PCRE2.v10.31.0-0.armv7l-linux-gnueabihf.tar.gz/md5 @@ -0,0 +1 @@ +8a138c21e69e2ef27e97df2393a24f62 diff --git a/deps/checksums/PCRE2.v10.31.0-0.armv7l-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/PCRE2.v10.31.0-0.armv7l-linux-gnueabihf.tar.gz/sha512 new file mode 100644 index 0000000..4691d6d --- /dev/null +++ b/deps/checksums/PCRE2.v10.31.0-0.armv7l-linux-gnueabihf.tar.gz/sha512 @@ -0,0 +1 @@ +aae6e19f3d8464cf195612e6c5bb1d79ea14d2c2f03207ee1796aa8e5374197ff1693e8f170620f5f5769aa2ed6f0d4b84220475376d85d111f5f9d7c33e135a diff --git a/deps/checksums/PCRE2.v10.31.0-0.armv7l-linux-musleabihf.tar.gz/md5 b/deps/checksums/PCRE2.v10.31.0-0.armv7l-linux-musleabihf.tar.gz/md5 new file mode 100644 index 0000000..1a590e3 --- /dev/null +++ b/deps/checksums/PCRE2.v10.31.0-0.armv7l-linux-musleabihf.tar.gz/md5 @@ -0,0 +1 @@ +a2d5325168a333f60dac0dd083022224 diff --git a/deps/checksums/PCRE2.v10.31.0-0.armv7l-linux-musleabihf.tar.gz/sha512 b/deps/checksums/PCRE2.v10.31.0-0.armv7l-linux-musleabihf.tar.gz/sha512 new file mode 100644 index 0000000..f9b9209 --- /dev/null +++ b/deps/checksums/PCRE2.v10.31.0-0.armv7l-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-1542f285243b8ac31e666dee3c6690bdaf26a0be.tar.gz/md5 b/deps/checksums/Pkg-1542f285243b8ac31e666dee3c6690bdaf26a0be.tar.gz/md5 new file mode 100644 index 0000000..3bb05ee --- /dev/null +++ b/deps/checksums/Pkg-1542f285243b8ac31e666dee3c6690bdaf26a0be.tar.gz/md5 @@ -0,0 +1 @@ +14d2db2253d6a561d7bf1cf66d0d3421 diff --git a/deps/checksums/Pkg-1542f285243b8ac31e666dee3c6690bdaf26a0be.tar.gz/sha512 b/deps/checksums/Pkg-1542f285243b8ac31e666dee3c6690bdaf26a0be.tar.gz/sha512 new file mode 100644 index 0000000..fa860ae --- /dev/null +++ b/deps/checksums/Pkg-1542f285243b8ac31e666dee3c6690bdaf26a0be.tar.gz/sha512 @@ -0,0 +1 @@ +da49f34f57e61f86e2124440676d44678a43a98fc96970debcd9ded931be3329acbadea858cc4100204ff1ecd7027b033f759a0c2595dcad6d6f114aadb3507d diff --git a/deps/checksums/Statistics-cde87c8062032883165cd242f4a5c6b7943cb0b1.tar.gz/md5 b/deps/checksums/Statistics-cde87c8062032883165cd242f4a5c6b7943cb0b1.tar.gz/md5 new file mode 100644 index 0000000..a7f8d6e --- /dev/null +++ b/deps/checksums/Statistics-cde87c8062032883165cd242f4a5c6b7943cb0b1.tar.gz/md5 @@ -0,0 +1 @@ +f038b99cd73f2ed8d132b88676b4ec64 diff --git a/deps/checksums/Statistics-cde87c8062032883165cd242f4a5c6b7943cb0b1.tar.gz/sha512 b/deps/checksums/Statistics-cde87c8062032883165cd242f4a5c6b7943cb0b1.tar.gz/sha512 new file mode 100644 index 0000000..3c6eb1a --- /dev/null +++ b/deps/checksums/Statistics-cde87c8062032883165cd242f4a5c6b7943cb0b1.tar.gz/sha512 @@ -0,0 +1 @@ +7c3b7086e74e53465405c7130182c8016d894151a89f16af8037018aebde495cf599edfd631c7fc14faca5a5052c168b1f0373bc124bec3c0ac7851667689bcf 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..d111708 --- /dev/null +++ b/deps/checksums/SuiteSparse-5.4.0.tar.gz/md5 @@ -0,0 +1 @@ +d73dc3527c66804ecf41b09d057142ff 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..7e097a1 --- /dev/null +++ b/deps/checksums/SuiteSparse-5.4.0.tar.gz/sha512 @@ -0,0 +1 @@ +65065fff86ff31e2ecc706525c3a2240cec7fff67f1bdf5371df7c55b661bd797e2fdbde8952f9ca7b7d8aa39bfcd6b6e0355c3acb59f66069cb0e14cb76701d diff --git a/deps/checksums/SuiteSparse.v5.4.0-6.aarch64-linux-gnu.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-6.aarch64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..f1e62bb --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-6.aarch64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +a32df8ce15d204a993d00b5fdc8681a9 diff --git a/deps/checksums/SuiteSparse.v5.4.0-6.aarch64-linux-gnu.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-6.aarch64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..1c2d2dd --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-6.aarch64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +b10c14909f2996da524098f8a2804335510decd119026a65027443280e8ee1661a13568a4eeabc2e4157a018e68b030953fe713aef9e4562f1cea784223591fb diff --git a/deps/checksums/SuiteSparse.v5.4.0-6.aarch64-linux-musl.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-6.aarch64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..23ced59 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-6.aarch64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +15884c89e8491a6b147c3d36ea4f0f5c diff --git a/deps/checksums/SuiteSparse.v5.4.0-6.aarch64-linux-musl.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-6.aarch64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..7090c0d --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-6.aarch64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +7b2e823452fe181a6bd295c4b70dd6e0d0fcbd282151ed40da4288a480674786ee6307ca2502986f6cf9b482e2198e9fdee9c6c6818686666f9157a5096905cb diff --git a/deps/checksums/SuiteSparse.v5.4.0-6.armv7l-linux-gnueabihf.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-6.armv7l-linux-gnueabihf.tar.gz/md5 new file mode 100644 index 0000000..9785a02 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-6.armv7l-linux-gnueabihf.tar.gz/md5 @@ -0,0 +1 @@ +2da0587fd842c14504d2cd60e809f2ea diff --git a/deps/checksums/SuiteSparse.v5.4.0-6.armv7l-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-6.armv7l-linux-gnueabihf.tar.gz/sha512 new file mode 100644 index 0000000..58a7f9f --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-6.armv7l-linux-gnueabihf.tar.gz/sha512 @@ -0,0 +1 @@ +85cb01448c0f33e21f478af57f25ae12b9ae83b66daf94952f89871852ed125b9de8e595613d8dd438d9eaea6a352bbfd8526fd98851d2a197e52d142c187294 diff --git a/deps/checksums/SuiteSparse.v5.4.0-6.armv7l-linux-musleabihf.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-6.armv7l-linux-musleabihf.tar.gz/md5 new file mode 100644 index 0000000..af23bd7 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-6.armv7l-linux-musleabihf.tar.gz/md5 @@ -0,0 +1 @@ +bbdf9391f98ef320ec33cc7faaaa5bc2 diff --git a/deps/checksums/SuiteSparse.v5.4.0-6.armv7l-linux-musleabihf.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-6.armv7l-linux-musleabihf.tar.gz/sha512 new file mode 100644 index 0000000..949e11d --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-6.armv7l-linux-musleabihf.tar.gz/sha512 @@ -0,0 +1 @@ +03fe617178ecbb14f64f3f06c059db3732090e4bff08f33fa5096847e3d0aecb28917644ecf90d31f61edd670c58b46155a1d8ca1d409c4b378647864898ff59 diff --git a/deps/checksums/SuiteSparse.v5.4.0-6.i686-linux-gnu.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-6.i686-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..3df549d --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-6.i686-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +1fce85a472ac3ed816cc09ac7d023161 diff --git a/deps/checksums/SuiteSparse.v5.4.0-6.i686-linux-gnu.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-6.i686-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..0dec762 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-6.i686-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +3bac1f8d6e6cef1323f19988f5e8b23ddc7eae2e7a1e814ac22f318bb79e2f0cc28517ae97e409148de101b8ea27e0c738731e63b6567fda9d947f0bc057fa8c diff --git a/deps/checksums/SuiteSparse.v5.4.0-6.i686-linux-musl.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-6.i686-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..d9c582d --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-6.i686-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +935a8082e5b2616e8e6d20c153c27373 diff --git a/deps/checksums/SuiteSparse.v5.4.0-6.i686-linux-musl.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-6.i686-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..dea3dea --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-6.i686-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +30cc703b2bfd8397adc113ef8d88d5444182b3ce3be8583efa26105d428d659a7560a99748099c757a11b5de75c3f18e4ae33dbd5ea3de3601f51bdc6d1bb444 diff --git a/deps/checksums/SuiteSparse.v5.4.0-6.i686-w64-mingw32.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-6.i686-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..7bab202 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-6.i686-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +8826cb6cdd778a279a63e58f18c008bb diff --git a/deps/checksums/SuiteSparse.v5.4.0-6.i686-w64-mingw32.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-6.i686-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..c04bd14 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-6.i686-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +19fe4b5949462e703e11d32596411ee41403bbfa303dc33420d6ae02123e6008efa741daeae4e2242d643c90df1c836fc7235c5398e585f9b316a68788ec0b44 diff --git a/deps/checksums/SuiteSparse.v5.4.0-6.powerpc64le-linux-gnu.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-6.powerpc64le-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..043c601 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-6.powerpc64le-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +7cd85be4fbb1190b56e5806e5a048ac0 diff --git a/deps/checksums/SuiteSparse.v5.4.0-6.powerpc64le-linux-gnu.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-6.powerpc64le-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..85d9325 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-6.powerpc64le-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +a0dbde6e328266c3210687d2bc4094a8bd878f6db3fc0a65331823b1c5bf79afff34bcd60e589820cd55866a0ad9c774cfbd48923a58f22ddb8e198bf0690dcb diff --git a/deps/checksums/SuiteSparse.v5.4.0-6.x86_64-apple-darwin14.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-6.x86_64-apple-darwin14.tar.gz/md5 new file mode 100644 index 0000000..4c78dac --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-6.x86_64-apple-darwin14.tar.gz/md5 @@ -0,0 +1 @@ +bf73957713ddc7ddec4b300cc4e2294b diff --git a/deps/checksums/SuiteSparse.v5.4.0-6.x86_64-apple-darwin14.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-6.x86_64-apple-darwin14.tar.gz/sha512 new file mode 100644 index 0000000..0ae62a2 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-6.x86_64-apple-darwin14.tar.gz/sha512 @@ -0,0 +1 @@ +584eacdd1a5a1624fb478165216de10d97d125f516aace1dc993d2a1e13defc9e1270cd9b6e023b266d3776350d8dd72cb9cd509ebcf0f33392c997be744c2d4 diff --git a/deps/checksums/SuiteSparse.v5.4.0-6.x86_64-linux-gnu.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-6.x86_64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..6269d2a --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-6.x86_64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +2a353f6c7917ba5a9749147ca43786e8 diff --git a/deps/checksums/SuiteSparse.v5.4.0-6.x86_64-linux-gnu.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-6.x86_64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..db6c854 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-6.x86_64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +75d9ea6e46325f8e249dce356e79512801afc9b67e7e4a2ebe56c2a92fd8d14de2b1b135a4c02cf36c2080ee00d8cdcb6e572b4990971dd806f8b66ab5656fbc diff --git a/deps/checksums/SuiteSparse.v5.4.0-6.x86_64-linux-musl.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-6.x86_64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..53770b2 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-6.x86_64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +fa443a09ca62fb25bdedc1887e04559f diff --git a/deps/checksums/SuiteSparse.v5.4.0-6.x86_64-linux-musl.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-6.x86_64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..d79eb7e --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-6.x86_64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +6cce5d984e1d9f51189b3442bc6fdf0d7f173c6224cbca3be4a1a144abea76a0a454e6ce8564df7c1e677df7aee653e81dfcae1a352288d3704f6ce087c2e985 diff --git a/deps/checksums/SuiteSparse.v5.4.0-6.x86_64-unknown-freebsd11.1.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-6.x86_64-unknown-freebsd11.1.tar.gz/md5 new file mode 100644 index 0000000..1befded --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-6.x86_64-unknown-freebsd11.1.tar.gz/md5 @@ -0,0 +1 @@ +df8e6168f2ed3f1792e44d1f0bfd8ecc diff --git a/deps/checksums/SuiteSparse.v5.4.0-6.x86_64-unknown-freebsd11.1.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-6.x86_64-unknown-freebsd11.1.tar.gz/sha512 new file mode 100644 index 0000000..f2388c3 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-6.x86_64-unknown-freebsd11.1.tar.gz/sha512 @@ -0,0 +1 @@ +838081c9b172158b8d59c8491770a9665f3441d49cc2ce7bae0b656c81791210af6d30585f0e4f4b2a08b11be8913ac709dc488384f43085869a192754a2f211 diff --git a/deps/checksums/SuiteSparse.v5.4.0-6.x86_64-w64-mingw32.tar.gz/md5 b/deps/checksums/SuiteSparse.v5.4.0-6.x86_64-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..f135db1 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-6.x86_64-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +d0354a7a698e39fc76702faab6ee283e diff --git a/deps/checksums/SuiteSparse.v5.4.0-6.x86_64-w64-mingw32.tar.gz/sha512 b/deps/checksums/SuiteSparse.v5.4.0-6.x86_64-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..54129a0 --- /dev/null +++ b/deps/checksums/SuiteSparse.v5.4.0-6.x86_64-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +880eaed5b817d09413a6f5c18c502fdd1c61b47b0c9d436717b4308c7649ef1d01b2a2e63e452e7b3ddcb92990b6ebc85c8fb5b697f143e8e1aa84e3d3b98a7f diff --git a/deps/checksums/UnicodeData-13.0.0.txt/md5 b/deps/checksums/UnicodeData-13.0.0.txt/md5 new file mode 100644 index 0000000..2b3ffc1 --- /dev/null +++ b/deps/checksums/UnicodeData-13.0.0.txt/md5 @@ -0,0 +1 @@ +85879f1976cc8eb739ee5585a93938e2 diff --git a/deps/checksums/UnicodeData-13.0.0.txt/sha512 b/deps/checksums/UnicodeData-13.0.0.txt/sha512 new file mode 100644 index 0000000..a93ba01 --- /dev/null +++ b/deps/checksums/UnicodeData-13.0.0.txt/sha512 @@ -0,0 +1 @@ +1a4a662e2ab33469976bf5f91aa6933ed9b73f6d4179a2daffb349e1869d7d6cfa885b164e82d15dcdad7458cd451c81add58d875eb0c70de854589dc97b2055 diff --git a/deps/checksums/Zlib.v1.2.11-10.aarch64-linux-gnu.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-10.aarch64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..5cfb66f --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.aarch64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +51358c64e42d3b8d923451278fc7aa18 diff --git a/deps/checksums/Zlib.v1.2.11-10.aarch64-linux-gnu.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-10.aarch64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..54ce95d --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.aarch64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +4812a79c1abf7b6301f7d1b4d65116e1add5edcaee574c6797d5c6c75b97fc9557d4e321d69afcf2dc1cd7c86b5f23f26a5cffd4541235e43c3d929498793554 diff --git a/deps/checksums/Zlib.v1.2.11-10.aarch64-linux-musl.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-10.aarch64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..7190114 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.aarch64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +d967ee51bc241699462b50466208b7a1 diff --git a/deps/checksums/Zlib.v1.2.11-10.aarch64-linux-musl.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-10.aarch64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..cd3df5f --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.aarch64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +b534781b3aa10ed2d5b426c2e4773ebf02aa09dd3b99326b3d74361260459ca4aff8e0638f5bafc2bc7b1c5e9eb27d37815194667ba09bf75360489caac2dc07 diff --git a/deps/checksums/Zlib.v1.2.11-10.armv7l-linux-gnueabihf.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-10.armv7l-linux-gnueabihf.tar.gz/md5 new file mode 100644 index 0000000..c0c8fee --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.armv7l-linux-gnueabihf.tar.gz/md5 @@ -0,0 +1 @@ +4324accedf22fe4733323f91ec408eca diff --git a/deps/checksums/Zlib.v1.2.11-10.armv7l-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-10.armv7l-linux-gnueabihf.tar.gz/sha512 new file mode 100644 index 0000000..08588f6 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.armv7l-linux-gnueabihf.tar.gz/sha512 @@ -0,0 +1 @@ +e7bc261ef2ff5e1dad9135dd6278365db0865b99b75d4086a27793e8c45276a609556c8145188fdfa9e617f583ac1c8992d02854862453d40849e49c39ae4a2e diff --git a/deps/checksums/Zlib.v1.2.11-10.armv7l-linux-musleabihf.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-10.armv7l-linux-musleabihf.tar.gz/md5 new file mode 100644 index 0000000..f0c5f60 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.armv7l-linux-musleabihf.tar.gz/md5 @@ -0,0 +1 @@ +6544bd96203abd28b5a9ab4de809cfb3 diff --git a/deps/checksums/Zlib.v1.2.11-10.armv7l-linux-musleabihf.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-10.armv7l-linux-musleabihf.tar.gz/sha512 new file mode 100644 index 0000000..f2430d4 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.armv7l-linux-musleabihf.tar.gz/sha512 @@ -0,0 +1 @@ +cec06a2b7a1bd44f69c5281aa4db0f032506598b5d9b2f81716587f07321528aefc8f52c3ad686c16b923715fa13b0d7a0f182d7dacafafe55059346f1ea24cb diff --git a/deps/checksums/Zlib.v1.2.11-10.i686-linux-gnu.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-10.i686-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..3c4eb40 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.i686-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +760ea40b76d53f6de170f09a17fc3aec diff --git a/deps/checksums/Zlib.v1.2.11-10.i686-linux-gnu.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-10.i686-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..eb38825 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.i686-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +550e950ddf02598eefa013c47f9c13ded8468fdf871602de85156f2c4654f515a56ba3b8881441144a78453ebed99b9ea4b580ad940b9d587208309b20da385c diff --git a/deps/checksums/Zlib.v1.2.11-10.i686-linux-musl.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-10.i686-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..b42db35 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.i686-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +390f2326a32f1c5ee44585a49a60ecf9 diff --git a/deps/checksums/Zlib.v1.2.11-10.i686-linux-musl.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-10.i686-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..65c0a0b --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.i686-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +357beb491a420dbbd6640df3ff8bb3e32be9fe0308d4b44c956b1c50f40f08dd6d77118c7ae46a4149e48772e6b545987542e7709d545cc486374710a6bc8cb3 diff --git a/deps/checksums/Zlib.v1.2.11-10.i686-w64-mingw32.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-10.i686-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..42c3fcf --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.i686-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +0b4e00c3d2ef8a4aaf7154c18ada2348 diff --git a/deps/checksums/Zlib.v1.2.11-10.i686-w64-mingw32.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-10.i686-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..8949fa6 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.i686-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +9bb1f68607b07c8f20f18d839d7b13ca87e45ac57a6c3bffcf27db26c11d99433e08af391939b61c13f49e5df5feddce2ff5d9d962ca0d7e89d850288f563172 diff --git a/deps/checksums/Zlib.v1.2.11-10.powerpc64le-linux-gnu.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-10.powerpc64le-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..746718f --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.powerpc64le-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +becb958e9f99b08a152824c8a44454d5 diff --git a/deps/checksums/Zlib.v1.2.11-10.powerpc64le-linux-gnu.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-10.powerpc64le-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..d0fb756 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.powerpc64le-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +857225765e3af15463128b7d4aca87c358c34c802fbf7863cdeed06f958b2c3976750c8583bfcf0ef12c106519563248f80a676337d340fddfc005f7cab09b7c diff --git a/deps/checksums/Zlib.v1.2.11-10.x86_64-apple-darwin14.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-10.x86_64-apple-darwin14.tar.gz/md5 new file mode 100644 index 0000000..36752a8 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.x86_64-apple-darwin14.tar.gz/md5 @@ -0,0 +1 @@ +b073327b80d77fcb321aa90ac054f3bc diff --git a/deps/checksums/Zlib.v1.2.11-10.x86_64-apple-darwin14.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-10.x86_64-apple-darwin14.tar.gz/sha512 new file mode 100644 index 0000000..4625788 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.x86_64-apple-darwin14.tar.gz/sha512 @@ -0,0 +1 @@ +3e568123255eb2e3fd6b59395ebc72407743f596ead1cc2efaf1f2cbdb88f554ea53cf7caa3f062a8a6695eeb591a6f6ff1304bd0915b148fce4849c36e915e2 diff --git a/deps/checksums/Zlib.v1.2.11-10.x86_64-linux-gnu.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-10.x86_64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000..0223a5f --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.x86_64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +9b387576651cf42d2cba762509603aa0 diff --git a/deps/checksums/Zlib.v1.2.11-10.x86_64-linux-gnu.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-10.x86_64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000..62af548 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.x86_64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +31020590c9f122954eb545515adeb3b93894caa7098e6a12b70cbbd71a9e1b71f4d3185d0960f4f2e192fbe74a0d2c944d1cd897c715aa3e7182ea3cfc82a63f diff --git a/deps/checksums/Zlib.v1.2.11-10.x86_64-linux-musl.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-10.x86_64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000..7a9589b --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.x86_64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +8eb752c5fb3f1f013a91ba84db33f268 diff --git a/deps/checksums/Zlib.v1.2.11-10.x86_64-linux-musl.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-10.x86_64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000..1b6ab53 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.x86_64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +9c4b198cceffc5e3c87e91fa4f9223b973b090f5a343b1ea84425cbb445a7d9a34926ca55e4febae8783fbba3390b3a1aa2bd59b3177335a544c862bd55b8ec5 diff --git a/deps/checksums/Zlib.v1.2.11-10.x86_64-unknown-freebsd11.1.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-10.x86_64-unknown-freebsd11.1.tar.gz/md5 new file mode 100644 index 0000000..7a22827 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.x86_64-unknown-freebsd11.1.tar.gz/md5 @@ -0,0 +1 @@ +bffe25dd8fb78bbf881785eef64202d5 diff --git a/deps/checksums/Zlib.v1.2.11-10.x86_64-unknown-freebsd11.1.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-10.x86_64-unknown-freebsd11.1.tar.gz/sha512 new file mode 100644 index 0000000..07ff8b9 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.x86_64-unknown-freebsd11.1.tar.gz/sha512 @@ -0,0 +1 @@ +2215ee8ebd3415f35a4f488537b2bb525bc8fef74419b2182bb8bc5b2e236fa7fb637c6aaa234e840c94c3940a71441e5a2dc9e7fb5d674372175a30dbb5fa19 diff --git a/deps/checksums/Zlib.v1.2.11-10.x86_64-w64-mingw32.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-10.x86_64-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000..957c62c --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.x86_64-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +9efba92de9aa49b43822309fcdce046a diff --git a/deps/checksums/Zlib.v1.2.11-10.x86_64-w64-mingw32.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-10.x86_64-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000..6e8d8f6 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-10.x86_64-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +c76553c7b79c8f2ed20dcbfbb55ebe06636e238c3ba7028beef84547cf1ee82c2a2a293712c9241e7b60cb5cf6c907eec7046b272a0ef26aed805eae4947a9c6 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-2020-07-22.pem/md5 b/deps/checksums/cacert-2020-07-22.pem/md5 new file mode 100644 index 0000000..ec3b25e --- /dev/null +++ b/deps/checksums/cacert-2020-07-22.pem/md5 @@ -0,0 +1 @@ +18c68c9898be980227f33c213a2464aa diff --git a/deps/checksums/cacert-2020-07-22.pem/sha512 b/deps/checksums/cacert-2020-07-22.pem/sha512 new file mode 100644 index 0000000..a376952 --- /dev/null +++ b/deps/checksums/cacert-2020-07-22.pem/sha512 @@ -0,0 +1 @@ +0d49bd1435a25b113a34ac38b337a9c904b6ac720824fd55d410ff6d8f6d0f637b54fd92cdff31d1c632b6a77f35fe55de9c756f35365387cea94f0fd93631b1 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/curl-7.66.0.tar.bz2/md5 b/deps/checksums/curl-7.66.0.tar.bz2/md5 new file mode 100644 index 0000000..45a0297 --- /dev/null +++ b/deps/checksums/curl-7.66.0.tar.bz2/md5 @@ -0,0 +1 @@ +c238aa394e3aa47ca4fcb0491774149f diff --git a/deps/checksums/curl-7.66.0.tar.bz2/sha512 b/deps/checksums/curl-7.66.0.tar.bz2/sha512 new file mode 100644 index 0000000..2bc1f4d --- /dev/null +++ b/deps/checksums/curl-7.66.0.tar.bz2/sha512 @@ -0,0 +1 @@ +dd4bebd7f1ede8843dc7416b7d172c361f7e5170a963a40cc8187e7cfdfc4c99849cc2885252dd0a8ac35cf1af4133eb21cce05044c8b116a8ed1597ead67323 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.armv7l-linux-gnueabihf.tar.gz/md5 b/deps/checksums/dSFMT.v2.2.3-0.armv7l-linux-gnueabihf.tar.gz/md5 new file mode 100644 index 0000000..0933516 --- /dev/null +++ b/deps/checksums/dSFMT.v2.2.3-0.armv7l-linux-gnueabihf.tar.gz/md5 @@ -0,0 +1 @@ +10b22af1aafd1d06efcfb9bad249d99d diff --git a/deps/checksums/dSFMT.v2.2.3-0.armv7l-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/dSFMT.v2.2.3-0.armv7l-linux-gnueabihf.tar.gz/sha512 new file mode 100644 index 0000000..287054b --- /dev/null +++ b/deps/checksums/dSFMT.v2.2.3-0.armv7l-linux-gnueabihf.tar.gz/sha512 @@ -0,0 +1 @@ +545a8801d8e176448306ed77b482393f62f97a0e7a3e169ee08a868d5a405b5e87f4698e2915740d56587173117f0894686b59d0f81d29f3c1668a40d0526ad8 diff --git a/deps/checksums/dSFMT.v2.2.3-0.armv7l-linux-musleabihf.tar.gz/md5 b/deps/checksums/dSFMT.v2.2.3-0.armv7l-linux-musleabihf.tar.gz/md5 new file mode 100644 index 0000000..0a401a0 --- /dev/null +++ b/deps/checksums/dSFMT.v2.2.3-0.armv7l-linux-musleabihf.tar.gz/md5 @@ -0,0 +1 @@ +eb01530f3e4be7d2a0b7b3480e71dadd diff --git a/deps/checksums/dSFMT.v2.2.3-0.armv7l-linux-musleabihf.tar.gz/sha512 b/deps/checksums/dSFMT.v2.2.3-0.armv7l-linux-musleabihf.tar.gz/sha512 new file mode 100644 index 0000000..7a5874e --- /dev/null +++ b/deps/checksums/dSFMT.v2.2.3-0.armv7l-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.9.0.tgz/md5 b/deps/checksums/lapack-3.9.0.tgz/md5 new file mode 100644 index 0000000..62a352c --- /dev/null +++ b/deps/checksums/lapack-3.9.0.tgz/md5 @@ -0,0 +1 @@ +0b251e2a8d5f949f99b50dd5e2200ee2 diff --git a/deps/checksums/lapack-3.9.0.tgz/sha512 b/deps/checksums/lapack-3.9.0.tgz/sha512 new file mode 100644 index 0000000..8133dd1 --- /dev/null +++ b/deps/checksums/lapack-3.9.0.tgz/sha512 @@ -0,0 +1 @@ +424956ad941a60a4b71e0d451ad48db12a692f8a71a90f3ca7f71d6ecc1922f392746ea84df1c47a46577ed2db32e9e47ec44ad248207c5ac7da179becb712ef 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.6.tar.gz/md5 b/deps/checksums/libosxunwind-0.0.6.tar.gz/md5 new file mode 100644 index 0000000..07aca33 --- /dev/null +++ b/deps/checksums/libosxunwind-0.0.6.tar.gz/md5 @@ -0,0 +1 @@ +62ce12eb88867fe3974904dbf06fb8e9 diff --git a/deps/checksums/libosxunwind-0.0.6.tar.gz/sha512 b/deps/checksums/libosxunwind-0.0.6.tar.gz/sha512 new file mode 100644 index 0000000..62b3d80 --- /dev/null +++ b/deps/checksums/libosxunwind-0.0.6.tar.gz/sha512 @@ -0,0 +1 @@ +901d1d0e9826b9e691991932897dac3185cd72e668658a319ba71a7f4ab6ac7ae328aa7e67d4c5cbce1a1b7a306d98a754544e8a7530a82d00406e9e42761425 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-10.0.0.src.tar.xz/md5 b/deps/checksums/llvm-10.0.0.src.tar.xz/md5 new file mode 100644 index 0000000..603ca6a --- /dev/null +++ b/deps/checksums/llvm-10.0.0.src.tar.xz/md5 @@ -0,0 +1 @@ +693cefdc49d618f828144486a18b473f diff --git a/deps/checksums/llvm-10.0.0.src.tar.xz/sha512 b/deps/checksums/llvm-10.0.0.src.tar.xz/sha512 new file mode 100644 index 0000000..29cefdd --- /dev/null +++ b/deps/checksums/llvm-10.0.0.src.tar.xz/sha512 @@ -0,0 +1 @@ +7dc961aacee3a01ecc002ff2b688a2ef50661856d2abd5ecc90566ffcad7566e4976736cd339ea96592e452cd5a17aaceba9712b2effec805661cca8ff020ee7 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/llvm-8.0.1.src.tar.xz/md5 b/deps/checksums/llvm-8.0.1.src.tar.xz/md5 new file mode 100644 index 0000000..b76ea81 --- /dev/null +++ b/deps/checksums/llvm-8.0.1.src.tar.xz/md5 @@ -0,0 +1 @@ +9a3b63df01c52556f7afb5617934e79e diff --git a/deps/checksums/llvm-8.0.1.src.tar.xz/sha512 b/deps/checksums/llvm-8.0.1.src.tar.xz/sha512 new file mode 100644 index 0000000..bdc9a5e --- /dev/null +++ b/deps/checksums/llvm-8.0.1.src.tar.xz/sha512 @@ -0,0 +1 @@ +82e120be5cabdfd5111aebbea68a663fe229c8861d73802d6ab09a3bf48f60de333e07e61f8fb61beaa14ac2bea24fcd74fa6f761acaf62469f536b79fcb1e16 diff --git a/deps/checksums/llvm-9.0.0.src.tar.xz/md5 b/deps/checksums/llvm-9.0.0.src.tar.xz/md5 new file mode 100644 index 0000000..92e662e --- /dev/null +++ b/deps/checksums/llvm-9.0.0.src.tar.xz/md5 @@ -0,0 +1 @@ +0fd4283ff485dffb71a4f1cc8fd3fc72 diff --git a/deps/checksums/llvm-9.0.0.src.tar.xz/sha512 b/deps/checksums/llvm-9.0.0.src.tar.xz/sha512 new file mode 100644 index 0000000..4fc5026 --- /dev/null +++ b/deps/checksums/llvm-9.0.0.src.tar.xz/sha512 @@ -0,0 +1 @@ +1bb3341e1d231559b948f1505b33c2e2e03989f9b8bbfef0e0cdaff5ac43f85574c9ec5ac53399b914f497d6899425d861411024e8d7e1d1a338c1c6951ac658 diff --git a/deps/checksums/llvm-9.0.1.src.tar.xz/md5 b/deps/checksums/llvm-9.0.1.src.tar.xz/md5 new file mode 100644 index 0000000..8d8f5fa --- /dev/null +++ b/deps/checksums/llvm-9.0.1.src.tar.xz/md5 @@ -0,0 +1 @@ +31eb9ce73dd2a0f8dcab8319fb03f8fc diff --git a/deps/checksums/llvm-9.0.1.src.tar.xz/sha512 b/deps/checksums/llvm-9.0.1.src.tar.xz/sha512 new file mode 100644 index 0000000..8e64aa8 --- /dev/null +++ b/deps/checksums/llvm-9.0.1.src.tar.xz/sha512 @@ -0,0 +1 @@ +bfb6960a4dd1e18f4005f324f478a781c69e8ec7c20569d9b243fcb9372dc7733b254f26c683373537990cc9c109c78eaf0f65449629ee17caca1bce9b9ccccd 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.1.0.tar.bz2/md5 b/deps/checksums/mpfr-4.1.0.tar.bz2/md5 new file mode 100644 index 0000000..c0d910b --- /dev/null +++ b/deps/checksums/mpfr-4.1.0.tar.bz2/md5 @@ -0,0 +1 @@ +44b892bc5a45bafb4294d134e13aad1d diff --git a/deps/checksums/mpfr-4.1.0.tar.bz2/sha512 b/deps/checksums/mpfr-4.1.0.tar.bz2/sha512 new file mode 100644 index 0000000..3ccc303 --- /dev/null +++ b/deps/checksums/mpfr-4.1.0.tar.bz2/sha512 @@ -0,0 +1 @@ +410208ee0d48474c1c10d3d4a59decd2dfa187064183b09358ec4c4666e34d74383128436b404123b831e585d81a9176b24c7ced9d913967c5fce35d4040a0b4 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-5f36f18148603facb6c3540e673610d6b24cbfbb.tar.gz/md5 b/deps/checksums/openblas-5f36f18148603facb6c3540e673610d6b24cbfbb.tar.gz/md5 new file mode 100644 index 0000000..d6ff5f6 --- /dev/null +++ b/deps/checksums/openblas-5f36f18148603facb6c3540e673610d6b24cbfbb.tar.gz/md5 @@ -0,0 +1 @@ +89796d4b7661725d4362212ad7059431 diff --git a/deps/checksums/openblas-5f36f18148603facb6c3540e673610d6b24cbfbb.tar.gz/sha512 b/deps/checksums/openblas-5f36f18148603facb6c3540e673610d6b24cbfbb.tar.gz/sha512 new file mode 100644 index 0000000..e0acff2 --- /dev/null +++ b/deps/checksums/openblas-5f36f18148603facb6c3540e673610d6b24cbfbb.tar.gz/sha512 @@ -0,0 +1 @@ +424d1c9c6fd97f6af4fdb7d7dce320cf5eab867a027e576531e39df934358736c4efab88782f781ec8f4afa8d992544040e3bbff3a4736c20cf4256d3a8e517d diff --git a/deps/checksums/openlibm-5efed306d509905714e3c43fc3a43fb26f3df743.tar.gz/md5 b/deps/checksums/openlibm-5efed306d509905714e3c43fc3a43fb26f3df743.tar.gz/md5 new file mode 100644 index 0000000..8adcba9 --- /dev/null +++ b/deps/checksums/openlibm-5efed306d509905714e3c43fc3a43fb26f3df743.tar.gz/md5 @@ -0,0 +1 @@ +7c2e3d84b66d95b43b8a34ccde2b18a2 diff --git a/deps/checksums/openlibm-5efed306d509905714e3c43fc3a43fb26f3df743.tar.gz/sha512 b/deps/checksums/openlibm-5efed306d509905714e3c43fc3a43fb26f3df743.tar.gz/sha512 new file mode 100644 index 0000000..e65ffb1 --- /dev/null +++ b/deps/checksums/openlibm-5efed306d509905714e3c43fc3a43fb26f3df743.tar.gz/sha512 @@ -0,0 +1 @@ +59464f33fd8b507c6d1a76a29beff71c6e242e1c1609feb6ca3011e0e19fded7ef53d617f2c54c70b9d0ab02d963f2c50f21cfba3988bf8846c3c9ddf41a6cbf 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.armv7l-linux-gnueabihf.tar.gz/md5 b/deps/checksums/p7zip.v16.2.0-1.armv7l-linux-gnueabihf.tar.gz/md5 new file mode 100644 index 0000000..1327706 --- /dev/null +++ b/deps/checksums/p7zip.v16.2.0-1.armv7l-linux-gnueabihf.tar.gz/md5 @@ -0,0 +1 @@ +3e170698971fbe88c72933f1b5040588 diff --git a/deps/checksums/p7zip.v16.2.0-1.armv7l-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/p7zip.v16.2.0-1.armv7l-linux-gnueabihf.tar.gz/sha512 new file mode 100644 index 0000000..3e5eda9 --- /dev/null +++ b/deps/checksums/p7zip.v16.2.0-1.armv7l-linux-gnueabihf.tar.gz/sha512 @@ -0,0 +1 @@ +5a1026a5540a40ec6b5b7cbf1660cca7b6b8b50693b7737ced333e137e7a881c7eb0f1d0f4b3f82d92c07b7fec754375caabd1399196bde731bc9505cd1c5ada diff --git a/deps/checksums/p7zip.v16.2.0-1.armv7l-linux-musleabihf.tar.gz/md5 b/deps/checksums/p7zip.v16.2.0-1.armv7l-linux-musleabihf.tar.gz/md5 new file mode 100644 index 0000000..3067103 --- /dev/null +++ b/deps/checksums/p7zip.v16.2.0-1.armv7l-linux-musleabihf.tar.gz/md5 @@ -0,0 +1 @@ +da71ac0e0a16b11fca6953b8dbaf921c diff --git a/deps/checksums/p7zip.v16.2.0-1.armv7l-linux-musleabihf.tar.gz/sha512 b/deps/checksums/p7zip.v16.2.0-1.armv7l-linux-musleabihf.tar.gz/sha512 new file mode 100644 index 0000000..10e3faa --- /dev/null +++ b/deps/checksums/p7zip.v16.2.0-1.armv7l-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-0890a538bf8238cded9be0c81171f57e43f2c755.tar.gz/md5 b/deps/checksums/utf8proc-0890a538bf8238cded9be0c81171f57e43f2c755.tar.gz/md5 new file mode 100644 index 0000000..d72cedb --- /dev/null +++ b/deps/checksums/utf8proc-0890a538bf8238cded9be0c81171f57e43f2c755.tar.gz/md5 @@ -0,0 +1 @@ +2e1fd70d1580d6a950bd30118c3cd1df diff --git a/deps/checksums/utf8proc-0890a538bf8238cded9be0c81171f57e43f2c755.tar.gz/sha512 b/deps/checksums/utf8proc-0890a538bf8238cded9be0c81171f57e43f2c755.tar.gz/sha512 new file mode 100644 index 0000000..07849bb --- /dev/null +++ b/deps/checksums/utf8proc-0890a538bf8238cded9be0c81171f57e43f2c755.tar.gz/sha512 @@ -0,0 +1 @@ +95d6dba2bbdfc5003f6d21fd02c926c7db9661895010041aacee55fa11dc2524bcffaa90965e2a9a8f2a9e49e93ec7572d6a4de6d5d9f9e075b787537864ea20 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..c7d8d61 --- /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/JuliaBinaryWrappers/LibCURL_jll.jl/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..e53fade --- /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/JuliaBinaryWrappers/LibGit2_jll.jl/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..9913df4 --- /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/JuliaBinaryWrappers/LibSSH2_jll.jl/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..6e07cea --- /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..20e3114 --- /dev/null +++ b/deps/llvm-options.mk @@ -0,0 +1,34 @@ +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 + + +ifeq ($(LLVM_VER),svn) +LLVM_MONOSRC_DIR:=$(SRCCACHE)/llvm-project-$(LLVM_VER) +LLVM_SRC_DIR:=$(LLVM_MONOSRC_DIR)/llvm +LIBCXX_ROOT_DIR:=$(LLVM_MONOSRC_DIR) +else +LLVM_MONOSRC_DIR:= +LLVM_SRC_DIR:=$(SRCCACHE)/llvm-$(LLVM_VER) +LIBCXX_ROOT_DIR:=$(LLVM_SRC_DIR)/projects +endif +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..18dc41e --- /dev/null +++ b/deps/llvm.mk @@ -0,0 +1,518 @@ +## LLVM ## +include $(SRCDIR)/llvm-ver.make + +ifneq ($(USE_BINARYBUILDER_LLVM), 1) +LLVM_GIT_URL ?= https://github.com/llvm/llvm-project.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 + +# for Monorepo +LLVM_ENABLE_PROJECTS := +ifeq ($(BUILD_LLVM_CLANG), 1) +LLVM_ENABLE_PROJECTS := $(LLVM_ENABLE_PROJECTS);clang;compiler-rt +endif +ifeq ($(USE_POLLY), 1) +LLVM_ENABLE_PROJECTS := $(LLVM_ENABLE_PROJECTS);polly +endif +ifeq ($(BUILD_LLDB), 1) +LLVM_ENABLE_PROJECTS := $(LLVM_ENABLE_PROJECTS);lldb +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;WebAssembly + +LLVM_CFLAGS := +LLVM_CXXFLAGS := +LLVM_CPPFLAGS := +LLVM_LDFLAGS := +LLVM_CMAKE := + +# MONOREPO +ifeq ($(LLVM_VER),svn) +LLVM_CMAKE += -DLLVM_ENABLE_PROJECTS="$(LLVM_ENABLE_PROJECTS)" +endif + +# 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)" -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=$(call rel_path,$(build_prefix),$(build_depsbindir)) +LLVM_CMAKE += -DLLVM_UTILS_INSTALL_DIR=$(call rel_path,$(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)) +ifeq (${USECLANG},0) +LLVM_CXXFLAGS += -mminimal-toc +endif +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 + +ifneq ($(LLVM_VER),svn) +ifeq (,$(findstring rc,$(LLVM_VER))) +LLVM_SRC_URL := https://github.com/llvm/llvm-project/releases/download/llvmorg-$(LLVM_VER) +else +LLVM_VER_SPLIT := $(subst rc, ,$(LLVM_VER)) +LLVM_SRC_URL := https://prereleases.llvm.org/$(word 1,$(LLVM_VER_SPLIT))/rc$(word 2,$(LLVM_VER_SPLIT)) +endif + +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 +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 + +$(LIBCXX_ROOT_DIR)/libcxx: $(LLVM_LIBCXX_TAR) | $(LLVM_SRC_DIR)/source-extracted +$(LIBCXX_ROOT_DIR)/libcxxabi: $(LLVM_LIBCXXABI_TAR) | $(LLVM_SRC_DIR)/source-extracted +$(LLVM_BUILD_DIR)/libcxx-build/Makefile: | $(LIBCXX_ROOT_DIR)/libcxx $(LIBCXX_ROOT_DIR)/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="$(LIBCXX_ROOT_DIR)/libcxxabi/include" $(LIBCXX_ROOT_DIR)/libcxx -DCMAKE_SHARED_LINKER_FLAGS="$(LDFLAGS) -L$(build_libdir) $(LIBCXX_EXTRA_FLAGS)" +$(LLVM_BUILD_DIR)/libcxxabi-build/Makefile: | $(LIBCXX_ROOT_DIR)/libcxxabi $(LIBCXX_ROOT_DIR)/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)" $(LIBCXX_ROOT_DIR)/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 $(LIBCXX_ROOT_DIR)/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 $(LIBCXX_ROOT_DIR)/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: $(LIBCXX_ROOT_DIR)/libcxx +get-libcxxabi: $(LIBCXX_ROOT_DIR)/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, can't use `LIBCXX_ROOT_DIR` since that might come from +# the monorepo. +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)" + +# change the SONAME of Julia's private LLVM +# i.e. libLLVM-6.0jl.so +# see #32462 +LLVM_CMAKE += -DLLVM_VERSION_SUFFIX:STRING="jl" + +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_MONOSRC_DIR) ] && \ + git clone $(LLVM_GIT_URL) $(LLVM_MONOSRC_DIR) ) || \ + (cd $(LLVM_MONOSRC_DIR) && \ + git pull --ff-only) +ifneq ($(LLVM_GIT_VER),) + (cd $(LLVM_MONOSRC_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 +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),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-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,llvm-exegesis-mingw)) # mingw build +$(eval $(call LLVM_PATCH,llvm-test-plugin-mingw)) # mingw build +$(eval $(call LLVM_PATCH,llvm-8.0-D66401-mingw-reloc)) # remove for 9.0 +$(eval $(call LLVM_PATCH,llvm7-revert-D44485)) +$(eval $(call LLVM_PATCH,llvm-8.0-D63688-wasm-isLocal)) # remove for 9.0 +$(eval $(call LLVM_PATCH,llvm-8.0-D55758-tablegen-cond)) # remove for 9.0 +$(eval $(call LLVM_PATCH,llvm-8.0-D59389-refactor-wmma)) # remove for 9.0 +$(eval $(call LLVM_PATCH,llvm-8.0-D59393-mma-ptx63-fix)) # remove for 9.0 +$(eval $(call LLVM_PATCH,llvm-8.0-D66657-codegen-degenerate)) # remove for 10.0 +$(eval $(call LLVM_PATCH,llvm-8.0-D71495-vectorize-freduce)) # remove for 10.0 +$(eval $(call LLVM_PATCH,llvm-8.0-D75072-SCEV-add-type)) +$(eval $(call LLVM_PATCH,llvm-8.0-D65174-limit-merge-stores)) # remove for 10.0 +endif # LLVM_VER 8.0 + +ifeq ($(LLVM_VER_SHORT),9.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-7.0-D44650)) # mingw32 build fix +$(eval $(call LLVM_PATCH,llvm9-D50010-VNCoercion-ni)) +$(eval $(call LLVM_PATCH,llvm-exegesis-mingw)) # mingw build +$(eval $(call LLVM_PATCH,llvm-test-plugin-mingw)) # mingw build +$(eval $(call LLVM_PATCH,llvm7-revert-D44485)) +$(eval $(call LLVM_PATCH,llvm-8.0-D66657-codegen-degenerate)) # remove for 10.0 +$(eval $(call LLVM_PATCH,llvm-8.0-D71495-vectorize-freduce)) # remove for 10.0 +$(eval $(call LLVM_PATCH,llvm-D75072-SCEV-add-type)) +$(eval $(call LLVM_PATCH,llvm-9.0-D65174-limit-merge-stores)) # remove for 10.0 +$(eval $(call LLVM_PATCH,llvm9-D71443-PPC-MC-redef-symbol)) # remove for 10.0 +$(eval $(call LLVM_PATCH,llvm-9.0-D78196)) # remove for 11.0 +$(eval $(call LLVM_PATCH,llvm-9.0-D85499)) +endif # LLVM_VER 9.0 + +ifeq ($(LLVM_VER_SHORT),10.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-7.0-D44650)) # mingw32 build fix +$(eval $(call LLVM_PATCH,llvm9-D50010-VNCoercion-ni)) +$(eval $(call LLVM_PATCH,llvm-exegesis-mingw)) # mingw build +$(eval $(call LLVM_PATCH,llvm-test-plugin-mingw)) # mingw build +$(eval $(call LLVM_PATCH,llvm7-revert-D44485)) +$(eval $(call LLVM_PATCH,llvm-D75072-SCEV-add-type)) +$(eval $(call LLVM_PATCH,llvm-10.0-PPC_SELECT_CC)) # delete for LLVM 11 +$(eval $(call LLVM_PATCH,llvm-10.0-PPC-LI-Elimination)) # delete for LLVM 11 +endif # LLVM_VER 10.0 + +# Add a JL prefix to the version map. DO NOT REMOVE +ifneq ($(LLVM_VER), svn) +$(eval $(call LLVM_PATCH,llvm7-symver-jlprefix)) +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_MONOSRC_DIR) && \ + git pull --ff-only +endif +else # USE_BINARYBUILDER_LLVM +ifneq ($(BINARYBUILDER_LLVM_ASSERTS), 1) +LLVM_BB_REPO_NAME := LLVM_full +else +LLVM_BB_REPO_NAME := LLVM_full_assert +LLVM_BB_NAME := LLVM.asserts.v$(LLVM_VER) +endif +LLVM_BB_NAME := $(LLVM_BB_REPO_NAME).v$(LLVM_VER) +LLVM_BB_URL_BASE := https://github.com/JuliaBinaryWrappers/$(LLVM_BB_REPO_NAME)_jll.jl/releases/download/$(LLVM_BB_REPO_NAME)-v$(LLVM_VER)+$(LLVM_BB_REL) + +$(eval $(call bb-install,llvm,LLVM,false,true)) + +endif # USE_BINARYBUILDER_LLVM diff --git a/deps/mbedtls.mk b/deps/mbedtls.mk new file mode 100644 index 0000000..785a2d9 --- /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/JuliaBinaryWrappers/MbedTLS_jll.jl/releases/download/MbedTLS-v$(MBEDTLS_VER)+$(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..f05dc60 --- /dev/null +++ b/deps/mpfr.mk @@ -0,0 +1,81 @@ +## MPFR ## + +ifeq ($(USE_SYSTEM_GMP), 0) +$(BUILDDIR)/mpfr-$(MPFR_VER)/build-configured: | $(build_prefix)/manifest/gmp +endif + +ifneq ($(USE_BINARYBUILDER_MPFR),1) + +MPFR_OPTS := --enable-thread-safe --enable-shared-cache --disable-float128 --disable-decimal-float +ifeq ($(USE_SYSTEM_GMP), 0) +MPFR_OPTS += --with-gmp-include=$(abspath $(build_includedir)) --with-gmp-lib=$(abspath $(build_shlibdir)) +endif +ifeq ($(BUILD_OS),WINNT) +ifeq ($(OS),WINNT) +MPFR_OPTS += 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..b258bf3 --- /dev/null +++ b/deps/openblas.version @@ -0,0 +1,2 @@ +OPENBLAS_BRANCH=v0.3.9 +OPENBLAS_SHA1=33f76a6c378681ac2af76b20e55ff682191a1937 diff --git a/deps/openlibm.mk b/deps/openlibm.mk new file mode 100644 index 0000000..7af66c6 --- /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_OPENLIBM + +OPENLIBM_BB_URL_BASE := https://github.com/JuliaBinaryWrappers/OpenLibm_jll.jl/releases/download/OpenLibm-v$(OPENLIBM_VER)+$(OPENLIBM_BB_REL) +OPENLIBM_BB_NAME := OpenLibm.v$(OPENLIBM_VER) + +$(eval $(call bb-install,openlibm,OPENLIBM,false)) +endif diff --git a/deps/openlibm.version b/deps/openlibm.version new file mode 100644 index 0000000..06eed6d --- /dev/null +++ b/deps/openlibm.version @@ -0,0 +1,2 @@ +OPENLIBM_BRANCH=v0.7.0 +OPENLIBM_SHA1=5efed306d509905714e3c43fc3a43fb26f3df743 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-10.0-PPC-LI-Elimination.patch b/deps/patches/llvm-10.0-PPC-LI-Elimination.patch new file mode 100644 index 0000000..f47a2c9 --- /dev/null +++ b/deps/patches/llvm-10.0-PPC-LI-Elimination.patch @@ -0,0 +1,161 @@ +From 5423496713e84dea2650e1703821de620f934573 Mon Sep 17 00:00:00 2001 +From: Nemanja Ivanovic +Date: Thu, 9 Apr 2020 21:34:30 -0500 +Subject: [PATCH] [PowerPC] Bail out of redundant LI elimination on an implicit + kill + +The transformation currently does not differentiate between explicit +and implicit kills. However, it is not valid to later simply clear +an implicit kill flag since the kill could be due to a call or return. + +Fixes: https://bugs.llvm.org/show_bug.cgi?id=45374 +--- + .../lib/Target/PowerPC/PPCPreEmitPeephole.cpp | 10 ++ + .../remove-redundant-li-skip-imp-kill.mir | 114 ++++++++++++++++++ + 2 files changed, 124 insertions(+) + create mode 100644 llvm/test/CodeGen/PowerPC/remove-redundant-li-skip-imp-kill.mir + +diff --git llvm/lib/Target/PowerPC/PPCPreEmitPeephole.cpp llvm/lib/Target/PowerPC/PPCPreEmitPeephole.cpp +index a4b4bf2973d..4ea714ff15f 100644 +--- llvm/lib/Target/PowerPC/PPCPreEmitPeephole.cpp ++++ llvm/lib/Target/PowerPC/PPCPreEmitPeephole.cpp +@@ -109,6 +109,16 @@ namespace { + // Track the operand that kill Reg. We would unset the kill flag of + // the operand if there is a following redundant load immediate. + int KillIdx = AfterBBI->findRegisterUseOperandIdx(Reg, true, TRI); ++ ++ // We can't just clear implicit kills, so if we encounter one, stop ++ // looking further. ++ if (KillIdx != -1 && AfterBBI->getOperand(KillIdx).isImplicit()) { ++ LLVM_DEBUG(dbgs() ++ << "Encountered an implicit kill, cannot proceed: "); ++ LLVM_DEBUG(AfterBBI->dump()); ++ break; ++ } ++ + if (KillIdx != -1) { + assert(!DeadOrKillToUnset && "Shouldn't kill same register twice"); + DeadOrKillToUnset = &AfterBBI->getOperand(KillIdx); +diff --git llvm/test/CodeGen/PowerPC/remove-redundant-li-skip-imp-kill.mir llvm/test/CodeGen/PowerPC/remove-redundant-li-skip-imp-kill.mir +new file mode 100644 +index 00000000000..78091d027ce +--- /dev/null ++++ llvm/test/CodeGen/PowerPC/remove-redundant-li-skip-imp-kill.mir +@@ -0,0 +1,114 @@ ++# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py ++# RUN: llc -mcpu=pwr9 -mtriple=powerpc64le-unknown-unknown -run-pass \ ++# RUN: ppc-pre-emit-peephole %s -o - | FileCheck %s ++--- | ++ ; ModuleID = 'a.ll' ++ source_filename = "a.ll" ++ target datalayout = "e-m:e-i64:64-n32:64" ++ ++ ; Function Attrs: nounwind ++ define dso_local signext i32 @b(i32 signext %a, i32* nocapture %b) local_unnamed_addr #0 { ++ entry: ++ %call = tail call signext i32 @g(i32 signext %a) ++ store i32 %call, i32* %b, align 4 ++ %call1 = tail call signext i32 @g(i32 signext %a) ++ ret i32 %call1 ++ } ++ ++ ; Function Attrs: nounwind ++ declare signext i32 @g(i32 signext) local_unnamed_addr #0 ++ ++ ; Function Attrs: nounwind ++ declare void @llvm.stackprotector(i8*, i8**) #0 ++ ++ attributes #0 = { nounwind } ++ ++... ++--- ++name: b ++alignment: 16 ++exposesReturnsTwice: false ++legalized: false ++regBankSelected: false ++selected: false ++failedISel: false ++tracksRegLiveness: true ++hasWinCFI: false ++registers: [] ++liveins: ++ - { reg: '$x3', virtual-reg: '' } ++ - { reg: '$x4', virtual-reg: '' } ++frameInfo: ++ isFrameAddressTaken: false ++ isReturnAddressTaken: false ++ hasStackMap: false ++ hasPatchPoint: false ++ stackSize: 64 ++ offsetAdjustment: 0 ++ maxAlignment: 1 ++ adjustsStack: true ++ hasCalls: true ++ stackProtector: '' ++ maxCallFrameSize: 32 ++ cvBytesOfCalleeSavedRegisters: 0 ++ hasOpaqueSPAdjustment: false ++ hasVAStart: false ++ hasMustTailInVarArgFunc: false ++ localFrameSize: 0 ++ savePoint: '' ++ restorePoint: '' ++fixedStack: ++ - { id: 0, type: spill-slot, offset: -80, size: 8, alignment: 16, stack-id: default, ++ callee-saved-register: '$x30', callee-saved-restored: true, debug-info-variable: '', ++ debug-info-expression: '', debug-info-location: '' } ++ - { id: 1, type: spill-slot, offset: -88, size: 8, alignment: 8, stack-id: default, ++ callee-saved-register: '$x29', callee-saved-restored: true, debug-info-variable: '', ++ debug-info-expression: '', debug-info-location: '' } ++stack: [] ++callSites: [] ++constants: [] ++machineFunctionInfo: {} ++body: | ++ bb.0.entry: ++ liveins: $x3, $x4, $x29, $x30 ++ ++ ; CHECK-LABEL: name: b ++ ; CHECK: liveins: $x3, $x4, $x29, $x30 ++ ; CHECK: $x0 = MFLR8 implicit $lr8 ++ ; CHECK: STD killed $x29, -24, $x1 :: (store 8 into %fixed-stack.0) ++ ; CHECK: STD killed $x30, -16, $x1 :: (store 8 into %fixed-stack.1, align 16) ++ ; CHECK: STD killed $x0, 16, $x1 ++ ; CHECK: $x1 = STDU $x1, -64, $x1 ++ ; CHECK: $x30 = OR8 killed $x4, $x4 ++ ; CHECK: dead $r4 = LI 10, implicit-def $x4 ++ ; CHECK: $x29 = OR8 $x3, $x3 ++ ; CHECK: BL8_NOP @g, csr_ppc64_r2_altivec, implicit-def dead $lr8, implicit $rm, implicit killed $x3, implicit killed $x4, implicit $x2, implicit-def $r1, implicit-def $x3 ++ ; CHECK: STW8 killed renamable $x3, 0, killed renamable $x30 :: (store 4 into %ir.b) ++ ; CHECK: $x3 = OR8 killed $x29, $x29 ++ ; CHECK: BL8_NOP @g, csr_ppc64_r2_altivec, implicit-def dead $lr8, implicit $rm, implicit killed $x3, implicit $x2, implicit-def $r1, implicit-def $x3 ++ ; CHECK: $x1 = ADDI8 $x1, 64 ++ ; CHECK: $x0 = LD 16, $x1 ++ ; CHECK: $x30 = LD -16, $x1 :: (load 8 from %fixed-stack.1, align 16) ++ ; CHECK: $x29 = LD -24, $x1 :: (load 8 from %fixed-stack.0) ++ ; CHECK: MTLR8 killed $x0, implicit-def $lr8 ++ ; CHECK: BLR8 implicit $lr8, implicit $rm, implicit killed $x3 ++ $x0 = MFLR8 implicit $lr8 ++ STD killed $x29, -24, $x1 :: (store 8 into %fixed-stack.1) ++ STD killed $x30, -16, $x1 :: (store 8 into %fixed-stack.0, align 16) ++ STD killed $x0, 16, $x1 ++ $x1 = STDU $x1, -64, $x1 ++ $x30 = OR8 killed $x4, $x4 ++ dead $r4 = LI 10, implicit-def $x4 ++ $x29 = OR8 $x3, $x3 ++ BL8_NOP @g, csr_ppc64_r2_altivec, implicit-def dead $lr8, implicit $rm, implicit killed $x3, implicit killed $x4, implicit $x2, implicit-def $r1, implicit-def $x3 ++ STW8 killed renamable $x3, 0, killed renamable $x30 :: (store 4 into %ir.b) ++ $x3 = OR8 killed $x29, $x29 ++ BL8_NOP @g, csr_ppc64_r2_altivec, implicit-def dead $lr8, implicit $rm, implicit killed $x3, implicit $x2, implicit-def $r1, implicit-def $x3 ++ $x1 = ADDI8 $x1, 64 ++ $x0 = LD 16, $x1 ++ $x30 = LD -16, $x1 :: (load 8 from %fixed-stack.0, align 16) ++ $x29 = LD -24, $x1 :: (load 8 from %fixed-stack.1) ++ MTLR8 killed $x0, implicit-def $lr8 ++ BLR8 implicit $lr8, implicit $rm, implicit killed $x3 ++ ++... +-- +2.26.0 + diff --git a/deps/patches/llvm-10.0-PPC_SELECT_CC.patch b/deps/patches/llvm-10.0-PPC_SELECT_CC.patch new file mode 100644 index 0000000..a56b20e --- /dev/null +++ b/deps/patches/llvm-10.0-PPC_SELECT_CC.patch @@ -0,0 +1,135 @@ +From 4765db99fa35257a4209e2976903d81087a3f0d7 Mon Sep 17 00:00:00 2001 +From: Nemanja Ivanovic +Date: Thu, 9 Apr 2020 13:53:02 -0500 +Subject: [PATCH] [PowerPC] Don't assert on SELECT_CC with i1 type + +When we try to select a SELECT_CC on Power9, we check if it can be matched to a +SETB instruction. In that function, we assert that the output type is i32/i64. +This is unnecessary as it is perfectly reasonable to have an i1 SELECT_CC. +Change that from an assert to an early exit condition. +Fixes: https://bugs.llvm.org/show_bug.cgi?id=45448 +--- + llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp | 7 +- + llvm/test/CodeGen/PowerPC/pr45448.ll | 90 +++++++++++++++++++++ + 2 files changed, 92 insertions(+), 5 deletions(-) + create mode 100644 llvm/test/CodeGen/PowerPC/pr45448.ll + +diff --git llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp +index 776ec52e260..9230ce4118b 100644 +--- llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp ++++ llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp +@@ -4241,13 +4241,10 @@ static bool mayUseP9Setb(SDNode *N, const ISD::CondCode &CC, SelectionDAG *DAG, + SDValue TrueRes = N->getOperand(2); + SDValue FalseRes = N->getOperand(3); + ConstantSDNode *TrueConst = dyn_cast(TrueRes); +- if (!TrueConst) ++ if (!TrueConst || (N->getSimpleValueType(0) != MVT::i64 && ++ N->getSimpleValueType(0) != MVT::i32)) + return false; + +- assert((N->getSimpleValueType(0) == MVT::i64 || +- N->getSimpleValueType(0) == MVT::i32) && +- "Expecting either i64 or i32 here."); +- + // We are looking for any of: + // (select_cc lhs, rhs, 1, (sext (setcc [lr]hs, [lr]hs, cc2)), cc1) + // (select_cc lhs, rhs, -1, (zext (setcc [lr]hs, [lr]hs, cc2)), cc1) +diff --git llvm/test/CodeGen/PowerPC/pr45448.ll llvm/test/CodeGen/PowerPC/pr45448.ll +new file mode 100644 +index 00000000000..eb0a61cb075 +--- /dev/null ++++ llvm/test/CodeGen/PowerPC/pr45448.ll +@@ -0,0 +1,90 @@ ++; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py ++; RUN: llc -verify-machineinstrs -mtriple=powerpc64le-unknown-linux-gnu \ ++; RUN: -mcpu=pwr9 -ppc-asm-full-reg-names -ppc-vsr-nums-as-vr < %s | \ ++; RUN: FileCheck %s ++define hidden void @julia_tryparse_internal_45896() #0 { ++; CHECK-LABEL: julia_tryparse_internal_45896: ++; CHECK: # %bb.0: # %top ++; CHECK-NEXT: ld r3, 0(r3) ++; CHECK-NEXT: cmpldi r3, 0 ++; CHECK-NEXT: beq cr0, .LBB0_3 ++; CHECK-NEXT: # %bb.1: # %top ++; CHECK-NEXT: cmpldi r3, 10 ++; CHECK-NEXT: beq cr0, .LBB0_4 ++; CHECK-NEXT: # %bb.2: # %top ++; CHECK-NEXT: .LBB0_3: # %fail194 ++; CHECK-NEXT: .LBB0_4: # %L294 ++; CHECK-NEXT: bc 12, 4*cr5+lt, .LBB0_6 ++; CHECK-NEXT: # %bb.5: # %L294 ++; CHECK-NEXT: bc 4, 4*cr5+lt, .LBB0_7 ++; CHECK-NEXT: .LBB0_6: # %L1057.preheader ++; CHECK-NEXT: .LBB0_7: # %L670 ++; CHECK-NEXT: lis r5, 4095 ++; CHECK-NEXT: ori r5, r5, 65533 ++; CHECK-NEXT: sldi r5, r5, 4 ++; CHECK-NEXT: cmpdi r3, 0 ++; CHECK-NEXT: sradi r4, r3, 63 ++; CHECK-NEXT: mulhdu r3, r3, r5 ++; CHECK-NEXT: maddld r6, r4, r5, r3 ++; CHECK-NEXT: crnor 4*cr5+gt, eq, eq ++; CHECK-NEXT: cmpld r6, r3 ++; CHECK-NEXT: mulld r3, r4, r5 ++; CHECK-NEXT: cmpldi cr1, r3, 0 ++; CHECK-NEXT: crandc 4*cr5+lt, lt, 4*cr1+eq ++; CHECK-NEXT: mulhdu. r3, r4, r5 ++; CHECK-NEXT: bc 4, 4*cr5+gt, .LBB0_10 ++; CHECK-NEXT: # %bb.8: # %L670 ++; CHECK-NEXT: crorc 4*cr5+lt, 4*cr5+lt, eq ++; CHECK-NEXT: bc 4, 4*cr5+lt, .LBB0_10 ++; CHECK-NEXT: # %bb.9: # %L917 ++; CHECK-NEXT: .LBB0_10: # %L994 ++top: ++ %0 = load i64, i64* undef, align 8 ++ %1 = icmp ne i64 %0, 0 ++ %2 = sext i64 %0 to i128 ++ switch i64 %0, label %pass195 [ ++ i64 10, label %L294 ++ i64 16, label %L294.fold.split ++ i64 0, label %fail194 ++ ] ++ ++L294.fold.split: ; preds = %top ++ unreachable ++ ++L294: ; preds = %top ++ %3 = add nsw i32 0, -48 ++ %4 = zext i32 %3 to i128 ++ %5 = add i128 %4, 0 ++ switch i32 undef, label %L670 [ ++ i32 -1031471104, label %L1057.preheader ++ i32 536870912, label %L1057.preheader ++ ] ++ ++L670: ; preds = %L294 ++ br label %L898 ++ ++L1057.preheader: ; preds = %L294, %L294 ++ unreachable ++ ++L898: ; preds = %L670 ++ %umul = call { i128, i1 } @llvm.umul.with.overflow.i128(i128 %2, i128 %5) ++ %umul.ov = extractvalue { i128, i1 } %umul, 1 ++ %value_phi102 = and i1 %1, %umul.ov ++ %6 = or i1 %value_phi102, false ++ br i1 %6, label %L917, label %L994 ++ ++L917: ; preds = %L898 ++ unreachable ++ ++L994: ; preds = %L898 ++ unreachable ++ ++fail194: ; preds = %top ++ unreachable ++ ++pass195: ; preds = %top ++ unreachable ++} ++ ++; Function Attrs: nounwind readnone speculatable willreturn ++declare { i128, i1 } @llvm.umul.with.overflow.i128(i128, i128) #1 +-- +2.26.0 + 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-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-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-8.0-D55758-tablegen-cond.patch b/deps/patches/llvm-8.0-D55758-tablegen-cond.patch new file mode 100644 index 0000000..ae9e610 --- /dev/null +++ b/deps/patches/llvm-8.0-D55758-tablegen-cond.patch @@ -0,0 +1,794 @@ +From 95135c5a18ee14ca091d3513cc7801521d4eb204 Mon Sep 17 00:00:00 2001 +From: Javed Absar +Date: Fri, 25 Jan 2019 10:25:25 +0000 +Subject: [PATCH] [TblGen] Extend !if semantics through new feature !cond + +This patch extends TableGen language with !cond operator. +Instead of embedding !if inside !if which can get cumbersome, +one can now use !cond. +Below is an example to convert an integer 'x' into a string: + + !cond(!lt(x,0) : "Negative", + !eq(x,0) : "Zero", + !eq(x,1) : "One, + 1 : "MoreThanOne") + +Reviewed By: hfinkel, simon_tatham, greened +Differential Revision: https://reviews.llvm.org/D55758 + +llvm-svn: 352185 +--- + docs/TableGen/LangIntro.rst | 14 +++ + docs/TableGen/LangRef.rst | 10 +- + include/llvm/TableGen/Record.h | 78 ++++++++++++++++ + lib/TableGen/Record.cpp | 131 +++++++++++++++++++++++++++ + lib/TableGen/TGLexer.cpp | 1 + + lib/TableGen/TGLexer.h | 2 +- + lib/TableGen/TGParser.cpp | 90 ++++++++++++++++++ + lib/TableGen/TGParser.h | 1 + + test/TableGen/cond-bitlist.td | 27 ++++++ + test/TableGen/cond-default.td | 11 +++ + test/TableGen/cond-empty-list-arg.td | 8 ++ + test/TableGen/cond-inheritance.td | 22 +++++ + test/TableGen/cond-let.td | 36 ++++++++ + test/TableGen/cond-list.td | 38 ++++++++ + test/TableGen/cond-subclass.td | 27 ++++++ + test/TableGen/cond-type.td | 11 +++ + test/TableGen/cond-usage.td | 29 ++++++ + test/TableGen/condsbit.td | 15 +++ + 18 files changed, 549 insertions(+), 2 deletions(-) + create mode 100644 llvm/test/TableGen/cond-bitlist.td + create mode 100644 llvm/test/TableGen/cond-default.td + create mode 100644 llvm/test/TableGen/cond-empty-list-arg.td + create mode 100644 llvm/test/TableGen/cond-inheritance.td + create mode 100644 llvm/test/TableGen/cond-let.td + create mode 100644 llvm/test/TableGen/cond-list.td + create mode 100644 llvm/test/TableGen/cond-subclass.td + create mode 100644 llvm/test/TableGen/cond-type.td + create mode 100644 llvm/test/TableGen/cond-usage.td + create mode 100644 llvm/test/TableGen/condsbit.td + +diff --git a/docs/TableGen/LangIntro.rst b/docs/TableGen/LangIntro.rst +index ea46550ffc0..390f941f0ca 100644 +--- a/docs/TableGen/LangIntro.rst ++++ b/docs/TableGen/LangIntro.rst +@@ -258,6 +258,20 @@ supported include: + ``!if(a,b,c)`` + 'b' if the result of 'int' or 'bit' operator 'a' is nonzero, 'c' otherwise. + ++``!cond(condition_1 : val1, condition_2 : val2, ..., condition_n : valn)`` ++ Instead of embedding !if inside !if which can get cumbersome, ++ one can use !cond. !cond returns 'val1' if the result of 'int' or 'bit' ++ operator 'condition1' is nonzero. Otherwise, it checks 'condition2'. ++ If 'condition2' is nonzero, returns 'val2', and so on. ++ If all conditions are zero, it reports an error. ++ ++ Below is an example to convert an integer 'x' into a string: ++ ++ !cond(!lt(x,0) : "Negative", ++ !eq(x,0) : "Zero", ++ !eq(x,1) : "One, ++ 1 : "MoreThanOne") ++ + ``!eq(a,b)`` + 'bit 1' if string a is equal to string b, 0 otherwise. This only operates + on string, int and bit objects. Use !cast to compare other types of +diff --git a/docs/TableGen/LangRef.rst b/docs/TableGen/LangRef.rst +index 2efee12ec9d..a3dbf363151 100644 +--- a/docs/TableGen/LangRef.rst ++++ b/docs/TableGen/LangRef.rst +@@ -102,6 +102,12 @@ wide variety of meanings: + :!isa !dag !le !lt !ge + :!gt !ne + ++TableGen also has !cond operator that needs a slightly different ++syntax compared to other "bang operators": ++ ++.. productionlist:: ++ CondOperator: !cond ++ + + Syntax + ====== +@@ -140,7 +146,7 @@ considered to define the class if any of the following is true: + #. The :token:`Body` in the :token:`ObjectBody` is present and is not empty. + #. The :token:`BaseClassList` in the :token:`ObjectBody` is present. + +-You can declare an empty class by giving and empty :token:`TemplateArgList` ++You can declare an empty class by giving an empty :token:`TemplateArgList` + and an empty :token:`ObjectBody`. This can serve as a restricted form of + forward declaration: note that records deriving from the forward-declared + class will inherit no fields from it since the record expansion is done +@@ -315,6 +321,8 @@ The initial :token:`DagArg` is called the "operator" of the dag. + + .. productionlist:: + SimpleValue: `BangOperator` ["<" `Type` ">"] "(" `ValueListNE` ")" ++ :| `CondOperator` "(" `CondVal` ("," `CondVal`)* ")" ++ CondVal: `Value` ":" `Value` + + Bodies + ------ +diff --git a/include/llvm/TableGen/Record.h b/include/llvm/TableGen/Record.h +index e022bc82b4e..3ca67ec72bd 100644 +--- a/include/llvm/TableGen/Record.h ++++ b/include/llvm/TableGen/Record.h +@@ -316,6 +316,7 @@ protected: + IK_TernOpInit, + IK_UnOpInit, + IK_LastOpInit, ++ IK_CondOpInit, + IK_FoldOpInit, + IK_IsAOpInit, + IK_StringInit, +@@ -912,6 +913,83 @@ public: + std::string getAsString() const override; + }; + ++/// !cond(condition_1: value1, ... , condition_n: value) ++/// Selects the first value for which condition is true. ++/// Otherwise reports an error. ++class CondOpInit final : public TypedInit, public FoldingSetNode, ++ public TrailingObjects { ++ unsigned NumConds; ++ RecTy *ValType; ++ ++ CondOpInit(unsigned NC, RecTy *Type) ++ : TypedInit(IK_CondOpInit, Type), ++ NumConds(NC), ValType(Type) {} ++ ++ size_t numTrailingObjects(OverloadToken) const { ++ return 2*NumConds; ++ } ++ ++public: ++ CondOpInit(const CondOpInit &) = delete; ++ CondOpInit &operator=(const CondOpInit &) = delete; ++ ++ static bool classof(const Init *I) { ++ return I->getKind() == IK_CondOpInit; ++ } ++ ++ static CondOpInit *get(ArrayRef C, ArrayRef V, ++ RecTy *Type); ++ ++ void Profile(FoldingSetNodeID &ID) const; ++ ++ RecTy *getValType() const { return ValType; } ++ ++ unsigned getNumConds() const { return NumConds; } ++ ++ Init *getCond(unsigned Num) const { ++ assert(Num < NumConds && "Condition number out of range!"); ++ return getTrailingObjects()[Num]; ++ } ++ ++ Init *getVal(unsigned Num) const { ++ assert(Num < NumConds && "Val number out of range!"); ++ return getTrailingObjects()[Num+NumConds]; ++ } ++ ++ ArrayRef getConds() const { ++ return makeArrayRef(getTrailingObjects(), NumConds); ++ } ++ ++ ArrayRef getVals() const { ++ return makeArrayRef(getTrailingObjects()+NumConds, NumConds); ++ } ++ ++ Init *Fold(Record *CurRec) const; ++ ++ Init *resolveReferences(Resolver &R) const override; ++ ++ bool isConcrete() const override; ++ bool isComplete() const override; ++ std::string getAsString() const override; ++ ++ using const_case_iterator = SmallVectorImpl::const_iterator; ++ using const_val_iterator = SmallVectorImpl::const_iterator; ++ ++ inline const_case_iterator arg_begin() const { return getConds().begin(); } ++ inline const_case_iterator arg_end () const { return getConds().end(); } ++ ++ inline size_t case_size () const { return NumConds; } ++ inline bool case_empty() const { return NumConds == 0; } ++ ++ inline const_val_iterator name_begin() const { return getVals().begin();} ++ inline const_val_iterator name_end () const { return getVals().end(); } ++ ++ inline size_t val_size () const { return NumConds; } ++ inline bool val_empty() const { return NumConds == 0; } ++ ++ Init *getBit(unsigned Bit) const override; ++}; ++ + /// !foldl (a, b, expr, start, lst) - Fold over a list. + class FoldOpInit : public TypedInit, public FoldingSetNode { + private: +diff --git a/lib/TableGen/Record.cpp b/lib/TableGen/Record.cpp +index cf1685a2e8c..26ffe761b66 100644 +--- a/lib/TableGen/Record.cpp ++++ b/lib/TableGen/Record.cpp +@@ -1694,6 +1694,137 @@ Init *FieldInit::Fold(Record *CurRec) const { + return const_cast(this); + } + ++static void ProfileCondOpInit(FoldingSetNodeID &ID, ++ ArrayRef CondRange, ++ ArrayRef ValRange, ++ const RecTy *ValType) { ++ assert(CondRange.size() == ValRange.size() && ++ "Number of conditions and values must match!"); ++ ID.AddPointer(ValType); ++ ArrayRef::iterator Case = CondRange.begin(); ++ ArrayRef::iterator Val = ValRange.begin(); ++ ++ while (Case != CondRange.end()) { ++ ID.AddPointer(*Case++); ++ ID.AddPointer(*Val++); ++ } ++} ++ ++void CondOpInit::Profile(FoldingSetNodeID &ID) const { ++ ProfileCondOpInit(ID, ++ makeArrayRef(getTrailingObjects(), NumConds), ++ makeArrayRef(getTrailingObjects() + NumConds, NumConds), ++ ValType); ++} ++ ++CondOpInit * ++CondOpInit::get(ArrayRef CondRange, ++ ArrayRef ValRange, RecTy *Ty) { ++ assert(CondRange.size() == ValRange.size() && ++ "Number of conditions and values must match!"); ++ ++ static FoldingSet ThePool; ++ FoldingSetNodeID ID; ++ ProfileCondOpInit(ID, CondRange, ValRange, Ty); ++ ++ void *IP = nullptr; ++ if (CondOpInit *I = ThePool.FindNodeOrInsertPos(ID, IP)) ++ return I; ++ ++ void *Mem = Allocator.Allocate(totalSizeToAlloc(2*CondRange.size()), ++ alignof(BitsInit)); ++ CondOpInit *I = new(Mem) CondOpInit(CondRange.size(), Ty); ++ ++ std::uninitialized_copy(CondRange.begin(), CondRange.end(), ++ I->getTrailingObjects()); ++ std::uninitialized_copy(ValRange.begin(), ValRange.end(), ++ I->getTrailingObjects()+CondRange.size()); ++ ThePool.InsertNode(I, IP); ++ return I; ++} ++ ++Init *CondOpInit::resolveReferences(Resolver &R) const { ++ SmallVector NewConds; ++ bool Changed = false; ++ for (const Init *Case : getConds()) { ++ Init *NewCase = Case->resolveReferences(R); ++ NewConds.push_back(NewCase); ++ Changed |= NewCase != Case; ++ } ++ ++ SmallVector NewVals; ++ for (const Init *Val : getVals()) { ++ Init *NewVal = Val->resolveReferences(R); ++ NewVals.push_back(NewVal); ++ Changed |= NewVal != Val; ++ } ++ ++ if (Changed) ++ return (CondOpInit::get(NewConds, NewVals, ++ getValType()))->Fold(R.getCurrentRecord()); ++ ++ return const_cast(this); ++} ++ ++Init *CondOpInit::Fold(Record *CurRec) const { ++ for ( unsigned i = 0; i < NumConds; ++i) { ++ Init *Cond = getCond(i); ++ Init *Val = getVal(i); ++ ++ if (IntInit *CondI = dyn_cast_or_null( ++ Cond->convertInitializerTo(IntRecTy::get()))) { ++ if (CondI->getValue()) ++ return Val->convertInitializerTo(getValType()); ++ } else ++ return const_cast(this); ++ } ++ ++ PrintFatalError(CurRec->getLoc(), ++ CurRec->getName() + ++ " does not have any true condition in:" + ++ this->getAsString()); ++ return nullptr; ++} ++ ++bool CondOpInit::isConcrete() const { ++ for (const Init *Case : getConds()) ++ if (!Case->isConcrete()) ++ return false; ++ ++ for (const Init *Val : getVals()) ++ if (!Val->isConcrete()) ++ return false; ++ ++ return true; ++} ++ ++bool CondOpInit::isComplete() const { ++ for (const Init *Case : getConds()) ++ if (!Case->isComplete()) ++ return false; ++ ++ for (const Init *Val : getVals()) ++ if (!Val->isConcrete()) ++ return false; ++ ++ return true; ++} ++ ++std::string CondOpInit::getAsString() const { ++ std::string Result = "!cond("; ++ for (unsigned i = 0; i < getNumConds(); i++) { ++ Result += getCond(i)->getAsString() + ": "; ++ Result += getVal(i)->getAsString(); ++ if (i != getNumConds()-1) ++ Result += ", "; ++ } ++ return Result + ")"; ++} ++ ++Init *CondOpInit::getBit(unsigned Bit) const { ++ return VarBitInit::get(const_cast(this), Bit); ++} ++ + static void ProfileDagInit(FoldingSetNodeID &ID, Init *V, StringInit *VN, + ArrayRef ArgRange, + ArrayRef NameRange) { +diff --git a/lib/TableGen/TGLexer.cpp b/lib/TableGen/TGLexer.cpp +index 16aeee56107..f733cc3c134 100644 +--- a/lib/TableGen/TGLexer.cpp ++++ b/lib/TableGen/TGLexer.cpp +@@ -545,6 +545,7 @@ tgtok::TokKind TGLexer::LexExclaim() { + .Case("ge", tgtok::XGe) + .Case("gt", tgtok::XGt) + .Case("if", tgtok::XIf) ++ .Case("cond", tgtok::XCond) + .Case("isa", tgtok::XIsA) + .Case("head", tgtok::XHead) + .Case("tail", tgtok::XTail) +diff --git a/lib/TableGen/TGLexer.h b/lib/TableGen/TGLexer.h +index e9980b36b97..9bdb01cf3dd 100644 +--- a/lib/TableGen/TGLexer.h ++++ b/lib/TableGen/TGLexer.h +@@ -51,7 +51,7 @@ namespace tgtok { + + // !keywords. + XConcat, XADD, XAND, XOR, XSRA, XSRL, XSHL, XListConcat, XStrConcat, XCast, +- XSubst, XForEach, XFoldl, XHead, XTail, XSize, XEmpty, XIf, XEq, XIsA, XDag, ++ XSubst, XForEach, XFoldl, XHead, XTail, XSize, XEmpty, XIf, XCond, XEq, XIsA, XDag, + XNe, XLe, XLt, XGe, XGt, + + // Integer value. +diff --git a/lib/TableGen/TGParser.cpp b/lib/TableGen/TGParser.cpp +index 1d1f3603c83..200190acd59 100644 +--- a/lib/TableGen/TGParser.cpp ++++ b/lib/TableGen/TGParser.cpp +@@ -1445,6 +1445,9 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) { + return (TernOpInit::get(Code, LHS, MHS, RHS, Type))->Fold(CurRec); + } + ++ case tgtok::XCond: ++ return ParseOperationCond(CurRec, ItemType); ++ + case tgtok::XFoldl: { + // Value ::= !foldl '(' Id ',' Id ',' Value ',' Value ',' Value ')' + Lex.Lex(); // eat the operation +@@ -1603,6 +1606,91 @@ RecTy *TGParser::ParseOperatorType() { + return Type; + } + ++Init *TGParser::ParseOperationCond(Record *CurRec, RecTy *ItemType) { ++ Lex.Lex(); // eat the operation 'cond' ++ ++ if (Lex.getCode() != tgtok::l_paren) { ++ TokError("expected '(' after !cond operator"); ++ return nullptr; ++ } ++ Lex.Lex(); // eat the '(' ++ ++ // Parse through '[Case: Val,]+' ++ SmallVector Case; ++ SmallVector Val; ++ while (true) { ++ if (Lex.getCode() == tgtok::r_paren) { ++ Lex.Lex(); // eat the ')' ++ break; ++ } ++ ++ Init *V = ParseValue(CurRec); ++ if (!V) ++ return nullptr; ++ Case.push_back(V); ++ ++ if (Lex.getCode() != tgtok::colon) { ++ TokError("expected ':' following a condition in !cond operator"); ++ return nullptr; ++ } ++ Lex.Lex(); // eat the ':' ++ ++ V = ParseValue(CurRec, ItemType); ++ if (!V) ++ return nullptr; ++ Val.push_back(V); ++ ++ if (Lex.getCode() == tgtok::r_paren) { ++ Lex.Lex(); // eat the ')' ++ break; ++ } ++ ++ if (Lex.getCode() != tgtok::comma) { ++ TokError("expected ',' or ')' following a value in !cond operator"); ++ return nullptr; ++ } ++ Lex.Lex(); // eat the ',' ++ } ++ ++ if (Case.size() < 1) { ++ TokError("there should be at least 1 'condition : value' in the !cond operator"); ++ return nullptr; ++ } ++ ++ // resolve type ++ RecTy *Type = nullptr; ++ for (Init *V : Val) { ++ RecTy *VTy = nullptr; ++ if (TypedInit *Vt = dyn_cast(V)) ++ VTy = Vt->getType(); ++ if (BitsInit *Vbits = dyn_cast(V)) ++ VTy = BitsRecTy::get(Vbits->getNumBits()); ++ if (isa(V)) ++ VTy = BitRecTy::get(); ++ ++ if (Type == nullptr) { ++ if (!isa(V)) ++ Type = VTy; ++ } else { ++ if (!isa(V)) { ++ RecTy *RType = resolveTypes(Type, VTy); ++ if (!RType) { ++ TokError(Twine("inconsistent types '") + Type->getAsString() + ++ "' and '" + VTy->getAsString() + "' for !cond"); ++ return nullptr; ++ } ++ Type = RType; ++ } ++ } ++ } ++ ++ if (!Type) { ++ TokError("could not determine type for !cond from its arguments"); ++ return nullptr; ++ } ++ return CondOpInit::get(Case, Val, Type)->Fold(CurRec); ++} ++ + /// ParseSimpleValue - Parse a tblgen value. This returns null on error. + /// + /// SimpleValue ::= IDValue +@@ -1621,6 +1709,7 @@ RecTy *TGParser::ParseOperatorType() { + /// SimpleValue ::= SRLTOK '(' Value ',' Value ')' + /// SimpleValue ::= LISTCONCATTOK '(' Value ',' Value ')' + /// SimpleValue ::= STRCONCATTOK '(' Value ',' Value ')' ++/// SimpleValue ::= COND '(' [Value ':' Value,]+ ')' + /// + Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType, + IDParseMode Mode) { +@@ -1933,6 +2022,7 @@ Init *TGParser::ParseSimpleValue(Record *CurRec, RecTy *ItemType, + case tgtok::XListConcat: + case tgtok::XStrConcat: // Value ::= !binop '(' Value ',' Value ')' + case tgtok::XIf: ++ case tgtok::XCond: + case tgtok::XFoldl: + case tgtok::XForEach: + case tgtok::XSubst: { // Value ::= !ternop '(' Value ',' Value ',' Value ')' +diff --git a/lib/TableGen/TGParser.h b/lib/TableGen/TGParser.h +index e3849043513..215b9dad770 100644 +--- a/lib/TableGen/TGParser.h ++++ b/lib/TableGen/TGParser.h +@@ -194,6 +194,7 @@ private: // Parser methods. + bool ParseRangePiece(SmallVectorImpl &Ranges); + RecTy *ParseType(); + Init *ParseOperation(Record *CurRec, RecTy *ItemType); ++ Init *ParseOperationCond(Record *CurRec, RecTy *ItemType); + RecTy *ParseOperatorType(); + Init *ParseObjectName(MultiClass *CurMultiClass); + Record *ParseClassID(); +diff --git a/test/TableGen/cond-bitlist.td b/test/TableGen/cond-bitlist.td +new file mode 100644 +index 00000000000..bce615838df +--- /dev/null ++++ b/test/TableGen/cond-bitlist.td +@@ -0,0 +1,27 @@ ++// RUN: llvm-tblgen %s | FileCheck %s ++// XFAIL: vg_leak ++ ++class S { ++ bits<2> val = !cond(!eq(s, 8): {0, 0}, ++ !eq(s, 16): 0b01, ++ !eq(s, 32): 2, ++ !eq(s, 64): {1, 1}, ++ 1 : ?); ++} ++ ++def D8 : S<8>; ++def D16 : S<16>; ++def D32 : S<32>; ++def D64 : S<64>; ++def D128: S<128>; ++// CHECK: def D128 ++// CHECK-NEXT: bits<2> val = { ?, ? }; ++// CHECK: def D16 ++// CHECK-NEXT: bits<2> val = { 0, 1 }; ++// CHECK: def D32 ++// CHECK-NEXT: bits<2> val = { 1, 0 }; ++// CHECK: def D64 ++// CHECK-NEXT: bits<2> val = { 1, 1 }; ++// CHECK: def D8 ++// CHECK-NEXT: bits<2> val = { 0, 0 }; ++ +diff --git a/test/TableGen/cond-default.td b/test/TableGen/cond-default.td +new file mode 100644 +index 00000000000..816bf10676f +--- /dev/null ++++ b/test/TableGen/cond-default.td +@@ -0,0 +1,11 @@ ++// Check that not specifying a valid condition results in error ++ ++// RUN: not llvm-tblgen %s 2>&1 | FileCheck %s ++// XFAIL: vg_leak ++ ++class C { ++ string s = !cond(!lt(x,0) : "negative", !gt(x,0) : "positive"); ++} ++ ++def Zero : C<0>; ++//CHECK: error: Zero does not have any true condition in:!cond(0: "negative", 0: "positive") +diff --git a/test/TableGen/cond-empty-list-arg.td b/test/TableGen/cond-empty-list-arg.td +new file mode 100644 +index 00000000000..5f4ccade169 +--- /dev/null ++++ b/test/TableGen/cond-empty-list-arg.td +@@ -0,0 +1,8 @@ ++// RUN: llvm-tblgen %s ++// XFAIL: vg_leak ++ ++class C { ++ bit true = 1; ++ list X = !cond(cond: [1, 2, 3], true : []); ++ list Y = !cond(cond: [], true : [4, 5, 6]); ++} +diff --git a/test/TableGen/cond-inheritance.td b/test/TableGen/cond-inheritance.td +new file mode 100644 +index 00000000000..4b4abdf72f3 +--- /dev/null ++++ b/test/TableGen/cond-inheritance.td +@@ -0,0 +1,22 @@ ++// Make sure !cond gets propagated across multiple layers of inheritance. ++// RUN: llvm-tblgen %s | FileCheck %s ++// XFAIL: vg_leak ++ ++class getInt { ++ int ret = !cond(c: 0, 1 : 1); ++} ++ ++class I1 { ++ int i = getInt.ret; ++} ++ ++class I2 : I1; ++ ++def DI1: I1<1>; ++// CHECK: def DI1 { // I1 ++// CHECK-NEXT: int i = 0; ++ ++// CHECK: def DI2 { // I1 I2 ++// CHECK-NEXT: int i = 0; ++def DI2: I2<1>; ++ +diff --git a/test/TableGen/cond-let.td b/test/TableGen/cond-let.td +new file mode 100644 +index 00000000000..044878f2ab8 +--- /dev/null ++++ b/test/TableGen/cond-let.td +@@ -0,0 +1,36 @@ ++// Check support for `!cond' operator as part of a `let' statement. ++// RUN: llvm-tblgen %s | FileCheck %s ++// XFAIL: vg_leak ++ ++ ++class C x, bits<4> y, bit z> { ++ bits<16> n; ++ ++ let n{11} = !cond(y{3}: 1, ++ y{2}: x{0}, ++ y{1}: x{1}, ++ y{0}: x{2}, ++ {1} :?); ++ let n{10-9}= !cond(x{2}: y{3-2}, ++ x{1}: y{2-1}, ++ x{1}: y{1-0}, ++ {1} : ?); ++ let n{8-6} = !cond(x{2}: 0b010, 1 : 0b110); ++ let n{5-4} = !cond(x{1}: y{3-2}, 1 : {0, 1}); ++ let n{3-0} = !cond(x{0}: y{3-0}, 1 : {z, y{2}, y{1}, y{0}}); ++} ++ ++ ++def C1 : C<{1, 0, 1}, {0, 1, 0, 1}, 0>; ++def C2 : C<{0, 1, 0}, {1, 0, 1, 0}, 1>; ++def C3 : C<{0, 0, 0}, {1, 0, 1, 0}, 0>; ++def C4 : C<{0, 0, 0}, {0, 0, 0, 0}, 0>; ++ ++// CHECK: def C1 ++// CHECK-NEXT: bits<16> n = { ?, ?, ?, ?, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1 }; ++// CHECK: def C2 ++// CHECK-NEXT: bits<16> n = { ?, ?, ?, ?, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0 }; ++// CHECK: def C3 ++// CHECK-NEXT: bits<16> n = { ?, ?, ?, ?, 1, ?, ?, 1, 1, 0, 0, 1, 0, 0, 1, 0 }; ++// CHECK: def C4 ++// CHECK-NEXT: bits<16> n = { ?, ?, ?, ?, ?, ?, ?, 1, 1, 0, 0, 1, 0, 0, 0, 0 }; +diff --git a/test/TableGen/cond-list.td b/test/TableGen/cond-list.td +new file mode 100644 +index 00000000000..aa013cea4e1 +--- /dev/null ++++ b/test/TableGen/cond-list.td +@@ -0,0 +1,38 @@ ++// RUN: llvm-tblgen %s | FileCheck %s ++// XFAIL: vg_leak ++ ++ ++class A> vals> { ++ list first = vals[0]; ++ list rest = !cond(!empty(!tail(vals)): vals[0], ++ 1 : vals[1]); ++} ++ ++def A_OneEl : A<[[1,2,3]]>; ++// CHECK: def A_OneEl { // A ++// CHECK-NEXT: list first = [1, 2, 3]; ++// CHECK-NEXT: list rest = [1, 2, 3]; ++// CHECK-NEXT: } ++ ++def A_TwoEl : A<[[1,2,3], [4,5,6]]>; ++// CHECK: def A_TwoEl { // A ++// CHECK-NEXT: list first = [1, 2, 3]; ++// CHECK-NEXT: list rest = [4, 5, 6]; ++// CHECK-NEXT: } ++ ++ ++class B v> { ++ list vals = v; ++} ++class BB> vals> : B; ++class BBB> vals> : BB; ++ ++def B_OneEl : BBB<[[1,2,3]]>; ++// CHECK: def B_OneEl { // B BB BBB ++// CHECK-NEXT: list vals = [1, 2, 3]; ++// CHECK-NEXT: } ++ ++def B_TwoEl : BBB<[[1,2,3],[4,5,6]]>; ++// CHECK: def B_TwoEl { // B BB BBB ++// CHECK-NEXT: list vals = [4, 5, 6]; ++// CHECK-NEXT: } +diff --git a/test/TableGen/cond-subclass.td b/test/TableGen/cond-subclass.td +new file mode 100644 +index 00000000000..9f6f6e2cb8c +--- /dev/null ++++ b/test/TableGen/cond-subclass.td +@@ -0,0 +1,27 @@ ++// Check that !cond with operands of different subtypes can ++// initialize a supertype variable. ++// RUN: llvm-tblgen %s | FileCheck %s ++// XFAIL: vg_leak ++ ++class E {} ++class E1 : E {} ++class E2 : E {} ++ ++class EX { ++ E x = !cond(cc: b, 1 : c); ++} ++ ++def E1d : E1<0>; ++def E2d : E2<0>; ++ ++def EXd1 : EX<1, E1d, E2d>; ++def EXd2 : EX<0, E1d, E2d>; ++ ++// CHECK: def EXd1 { ++// CHECK: E x = E1d; ++// CHECK: } ++// ++// CHECK: def EXd2 { ++// CHECK: E x = E2d; ++// CHECK: } ++ +diff --git a/test/TableGen/cond-type.td b/test/TableGen/cond-type.td +new file mode 100644 +index 00000000000..fd2a3cc52b7 +--- /dev/null ++++ b/test/TableGen/cond-type.td +@@ -0,0 +1,11 @@ ++// RUN: not llvm-tblgen %s 2>&1 | FileCheck %s ++// XFAIL: vg_leak ++ ++class A {} ++class B : A {} ++class C : A {} ++ ++// CHECK: Value 'x' of type 'C' is incompatible with initializer '{{.*}}' of type 'A' ++class X { ++ C x = !cond(cc: b, 1 : c); ++} +diff --git a/test/TableGen/cond-usage.td b/test/TableGen/cond-usage.td +new file mode 100644 +index 00000000000..055fd6d7c69 +--- /dev/null ++++ b/test/TableGen/cond-usage.td +@@ -0,0 +1,29 @@ ++// RUN: llvm-tblgen %s | FileCheck %s ++// XFAIL: vg_leak ++ ++// Check that !cond picks the first true value ++// CHECK: class A ++// CHECK-NEXT: string S = !cond(!eq(A:x, 10): "ten", !eq(A:x, 11): "eleven", !eq(A:x, 10): "TEN", !gt(A:x, 9): "MoreThanNine", 1: "unknown"); ++// CHECK: B1 ++// CHECK-NEXT: string S = "unknown" ++// CHECK: B10 ++// CHECK-NEXT: string S = "ten"; ++// CHECK: def B11 ++// CHECK-NEXT: string S = "eleven"; ++// CHECK: def B12 ++// CHECK-NEXT: string S = "MoreThanNine"; ++// CHECK: def B9 ++// CHECK-NEXT: string S = "unknown" ++ ++class A { ++ string S = !cond(!eq(x,10) : "ten", ++ !eq(x,11) : "eleven", ++ !eq(x,10) : "TEN", ++ !gt(x,9) : "MoreThanNine", ++ !eq(1,1) : "unknown"); ++} ++def B1 : A<1>; ++def B9 : A<9>; ++def B10 : A<10>; ++def B11 : A<11>; ++def B12 : A<12>; +diff --git a/test/TableGen/condsbit.td b/test/TableGen/condsbit.td +new file mode 100644 +index 00000000000..e08ac97f68b +--- /dev/null ++++ b/test/TableGen/condsbit.td +@@ -0,0 +1,15 @@ ++// check that !cond works well with bit conditional values ++// RUN: llvm-tblgen %s | FileCheck %s ++// XFAIL: vg_leak ++// CHECK: a = 6 ++// CHECK: a = 5 ++ ++class A { ++ bit true = 1; ++ int a = !cond(b: 5, true : 6); ++ bit c = !cond(b: 0, true : 1); ++ bits<1> d = !cond(b: 0, true : 1); ++} ++ ++def X : A<0>; ++def Y : A; +-- +2.17.1 + diff --git a/deps/patches/llvm-8.0-D59389-refactor-wmma.patch b/deps/patches/llvm-8.0-D59389-refactor-wmma.patch new file mode 100644 index 0000000..31af524 --- /dev/null +++ b/deps/patches/llvm-8.0-D59389-refactor-wmma.patch @@ -0,0 +1,899 @@ +From e9737bf498597707d084398b9485676dc7421644 Mon Sep 17 00:00:00 2001 +From: Artem Belevich +Date: Thu, 25 Apr 2019 22:27:35 +0000 +Subject: [PATCH] [NVPTX] Refactor generation of MMA intrinsics and + instructions. NFC. + +Generalized constructions of 'fragments' of MMA operations to provide +common primitives for construction of the ops. This will make it easier +to add new variants of the instructions that operate on integer types. + +Use nested foreach loops which makes it possible to better control +naming of the intrinsics. + +This patch does not affect LLVM's output, so there are no test changes. + +Differential Revision: https://reviews.llvm.org/D59389 + +llvm-svn: 359245 +--- + include/llvm/IR/IntrinsicsNVVM.td | 258 ++++++-------- + lib/Target/NVPTX/NVPTXIntrinsics.td | 512 ++++++++++------------------ + 2 files changed, 295 insertions(+), 475 deletions(-) + +diff --git a/include/llvm/IR/IntrinsicsNVVM.td b/include/llvm/IR/IntrinsicsNVVM.td +index 7f694f68969..e30a27613a6 100644 +--- a/include/llvm/IR/IntrinsicsNVVM.td ++++ b/include/llvm/IR/IntrinsicsNVVM.td +@@ -38,6 +38,69 @@ def llvm_anyi64ptr_ty : LLVMAnyPointerType; // (space)i64* + // MISC + // + ++// Helper class for construction of n-element list [t,t,...,t] ++class RepLLVMType { ++ list ret = !if(N, !listconcat(RepLLVMType.ret, [T]), []); ++} ++ ++// Helper class that represents a 'fragment' of an NVPTX *MMA instruction. ++// Geom: mnk. E.g. m8n32k16 ++// Frag: [abcd] ++// PtxEltType: PTX type for the element. ++class WMMA_REGS { ++ string geom = Geom; ++ string frag = Frag; ++ string ptx_elt_type = PtxEltType; ++ string ft = frag#":"#ptx_elt_type; ++ list regs = !cond( ++ // fp16 -> fp16/fp32 @ m16n16k16/m8n32k16/m32n8k16 ++ // All currently supported geometries use the same fragment format, ++ // so we only need to consider {fragment, type}. ++ !eq(ft,"a:f16") : RepLLVMType<8, llvm_v2f16_ty>.ret, ++ !eq(ft,"b:f16") : RepLLVMType<8, llvm_v2f16_ty>.ret, ++ !eq(ft,"c:f16") : RepLLVMType<4, llvm_v2f16_ty>.ret, ++ !eq(ft,"d:f16") : RepLLVMType<4, llvm_v2f16_ty>.ret, ++ !eq(ft,"c:f32") : RepLLVMType<8, llvm_float_ty>.ret, ++ !eq(ft,"d:f32") : RepLLVMType<8, llvm_float_ty>.ret); ++} ++ ++class WMMA_NAME_LDST { ++ string intr = "llvm.nvvm.wmma." ++ # Frag.geom ++ # "." # Op ++ # "." # Frag.frag ++ # "." # Layout ++ # !if(WithStride, ".stride", "") ++ # "." # Frag.ptx_elt_type ++ ; ++ // TODO(tra): record name should ideally use the same field order as the intrinsic. ++ // E.g. string record = !subst("llvm", "int", ++ // !subst(".", "_", llvm)); ++ string record = "int_nvvm_wmma_" ++ # Frag.geom ++ # "_" # Op ++ # "_" # Frag.frag ++ # "_" # Frag.ptx_elt_type ++ # "_" # Layout ++ # !if(WithStride, "_stride", ""); ++} ++ ++class WMMA_NAME_MMA { ++ string llvm = "llvm.nvvm.wmma." ++ # C.geom ++ # ".mma" ++ # "." # ALayout ++ # "." # BLayout ++ # "." # D.ptx_elt_type // Intrinsic encodes 'd' first. ++ # "." # C.ptx_elt_type ++ # !if(Satfinite, ".satfinite", ""); ++ ++ string record = !subst(".", "_", ++ !subst("llvm.", "int_", llvm)); ++} ++ + let TargetPrefix = "nvvm" in { + def int_nvvm_prmt : GCCBuiltin<"__nvvm_prmt">, + Intrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i32_ty, llvm_i32_ty], +@@ -3882,166 +3945,69 @@ def int_nvvm_match_all_sync_i64p : + // + // WMMA instructions + // +- + // WMMA.LOAD +-class NVVM_WMMA_LD_GALSTS +- : Intrinsic ++ : Intrinsic, NoCapture<0>], +- "llvm.nvvm.wmma." +- # Geometry +- # ".load" +- # "." # Abc +- # "." # Layout +- # !if(WithStride, ".stride", "") +- # "." # Type>; +- +-multiclass NVVM_WMMA_LD_GALT { +- def _stride: NVVM_WMMA_LD_GALSTS; +- def NAME : NVVM_WMMA_LD_GALSTS; +-} +- +-multiclass NVVM_WMMA_LD_GAT { +- defm _row: NVVM_WMMA_LD_GALT; +- defm _col: NVVM_WMMA_LD_GALT; +-} +- +-multiclass NVVM_WMMA_LD_G { +- defm _a_f16: NVVM_WMMA_LD_GAT; +- defm _b_f16: NVVM_WMMA_LD_GAT; +- defm _c_f16: NVVM_WMMA_LD_GAT; +- defm _c_f32: NVVM_WMMA_LD_GAT; +-} +- +-multiclass NVVM_WMMA_LD { +- defm _m32n8k16_load: NVVM_WMMA_LD_G<"m32n8k16">; +- defm _m16n16k16_load: NVVM_WMMA_LD_G<"m16n16k16">; +- defm _m8n32k16_load: NVVM_WMMA_LD_G<"m8n32k16">; +-} +- +-defm int_nvvm_wmma: NVVM_WMMA_LD; ++ WMMA_NAME_LDST<"load", Frag, Layout, WithStride>.intr>; + + // WMMA.STORE.D +-class NVVM_WMMA_STD_GLSTSEmpty=[]> ++class NVVM_WMMA_ST + : Intrinsic<[], + !listconcat( + [llvm_anyptr_ty], +- !if(!eq(Type,"f16"), +- [regty, regty, regty, regty], +- [regty, regty, regty, regty, +- regty, regty, regty, regty]), +- !if(WithStride, [llvm_i32_ty], Empty)), ++ Frag.regs, ++ !if(WithStride, [llvm_i32_ty], [])), + [IntrWriteMem, IntrArgMemOnly, WriteOnly<0>, NoCapture<0>], +- "llvm.nvvm.wmma." +- # Geometry +- # ".store.d" +- # "." # Layout +- # !if(WithStride, ".stride", "") +- # "." # Type>; +- +-multiclass NVVM_WMMA_STD_GLT { +- def _stride: NVVM_WMMA_STD_GLSTS; +- def NAME: NVVM_WMMA_STD_GLSTS; +-} +- +-multiclass NVVM_WMMA_STD_GT { +- defm _row: NVVM_WMMA_STD_GLT; +- defm _col: NVVM_WMMA_STD_GLT; +-} +-multiclass NVVM_WMMA_STD_G { +- defm _d_f16: NVVM_WMMA_STD_GT; +- defm _d_f32: NVVM_WMMA_STD_GT; +-} +- +-multiclass NVVM_WMMA_STD { +- defm _m32n8k16_store: NVVM_WMMA_STD_G<"m32n8k16">; +- defm _m16n16k16_store: NVVM_WMMA_STD_G<"m16n16k16">; +- defm _m8n32k16_store: NVVM_WMMA_STD_G<"m8n32k16">; ++ WMMA_NAME_LDST<"store", Frag, Layout, WithStride>.intr>; ++ ++// Create all load/store variants ++foreach geom = ["m16n16k16", "m32n8k16", "m8n32k16" ] in { ++ foreach layout = ["row", "col"] in { ++ foreach stride = [0, 1] in { ++ foreach frag = [WMMA_REGS, ++ WMMA_REGS, ++ WMMA_REGS, ++ WMMA_REGS] in { ++ def WMMA_NAME_LDST<"load", frag, layout, stride>.record ++ : NVVM_WMMA_LD; ++ } ++ foreach frag = [WMMA_REGS, ++ WMMA_REGS] in { ++ def WMMA_NAME_LDST<"store", frag, layout, stride>.record ++ : NVVM_WMMA_ST; ++ } ++ } ++ } + } + +-defm int_nvvm_wmma: NVVM_WMMA_STD; +- + // WMMA.MMA +-class NVVM_WMMA_MMA_GABDCS +- : Intrinsic ++ : Intrinsic.regs, ++ WMMA_REGS.regs, ++ C.regs), + [IntrNoMem], +- "llvm.nvvm.wmma." +- # Geometry +- # ".mma" +- # "." # ALayout +- # "." # BLayout +- # "." # DType +- # "." # CType +- # Satfinite> { +-} +- +-multiclass NVVM_WMMA_MMA_GABDC { +- def NAME : NVVM_WMMA_MMA_GABDCS; +- def _satfinite: NVVM_WMMA_MMA_GABDCS; +-} +- +-multiclass NVVM_WMMA_MMA_GABD { +- defm _f16: NVVM_WMMA_MMA_GABDC; +- defm _f32: NVVM_WMMA_MMA_GABDC; +-} +- +-multiclass NVVM_WMMA_MMA_GAB { +- defm _f16: NVVM_WMMA_MMA_GABD; +- defm _f32: NVVM_WMMA_MMA_GABD; +-} +- +-multiclass NVVM_WMMA_MMA_GA { +- defm _col: NVVM_WMMA_MMA_GAB; +- defm _row: NVVM_WMMA_MMA_GAB; +-} +- +-multiclass NVVM_WMMA_MMA_G { +- defm _col: NVVM_WMMA_MMA_GA; +- defm _row: NVVM_WMMA_MMA_GA; +-} +- +-multiclass NVVM_WMMA_MMA { +- defm _m32n8k16_mma : NVVM_WMMA_MMA_G<"m32n8k16">; +- defm _m16n16k16_mma : NVVM_WMMA_MMA_G<"m16n16k16">; +- defm _m8n32k16_mma : NVVM_WMMA_MMA_G<"m8n32k16">; ++ WMMA_NAME_MMA.llvm>; ++ ++foreach geom = ["m16n16k16", "m32n8k16", "m8n32k16" ] in { ++ foreach layout_a = ["row", "col"] in { ++ foreach layout_b = ["row", "col"] in { ++ foreach frag_c = [WMMA_REGS, ++ WMMA_REGS] in { ++ foreach frag_d = [WMMA_REGS, ++ WMMA_REGS] in { ++ foreach satf = [0, 1] in { ++ def WMMA_NAME_MMA.record ++ : NVVM_WMMA_MMA; ++ } ++ } ++ } ++ } ++ } + } + +-defm int_nvvm_wmma : NVVM_WMMA_MMA; +- + } // let TargetPrefix = "nvvm" +diff --git a/lib/Target/NVPTX/NVPTXIntrinsics.td b/lib/Target/NVPTX/NVPTXIntrinsics.td +index 47dcdcf6e0b..b9a67ba5ed3 100644 +--- a/lib/Target/NVPTX/NVPTXIntrinsics.td ++++ b/lib/Target/NVPTX/NVPTXIntrinsics.td +@@ -27,7 +27,17 @@ def immDouble1 : PatLeaf<(fpimm), [{ + return (d==1.0); + }]>; + +- ++def AS_match { ++ code generic = [{ ++ return ChkMemSDNodeAddressSpace(N, llvm::ADDRESS_SPACE_GENERIC); ++ }]; ++ code shared = [{ ++ return ChkMemSDNodeAddressSpace(N, llvm::ADDRESS_SPACE_SHARED); ++ }]; ++ code global = [{ ++ return ChkMemSDNodeAddressSpace(N, llvm::ADDRESS_SPACE_GLOBAL); ++ }]; ++} + + //----------------------------------- + // Synchronization and shuffle functions +@@ -1007,17 +1017,11 @@ def INT_FNS_iii : INT_FNS_MBO<(ins i32imm:$mask, i32imm:$base, i32imm:$ + //----------------------------------- + + class ATOMIC_GLOBAL_CHK +- : PatFrag; ++ : PatFrag; + class ATOMIC_SHARED_CHK +- : PatFrag; ++ : PatFrag; + class ATOMIC_GENERIC_CHK +- : PatFrag; ++ : PatFrag; + + multiclass F_ATOMIC_2_imp; + +-// +-// wmma.load.[a|b|c].sync.[row|col].m16n16k16[|.global|.shared].[f16|f32] +-// +- + class EmptyNVPTXInst : NVPTXInst<(outs), (ins), "?", []>; ++// Generates list of n sequential register names. ++class RegSeq { ++ list ret = !if(n, !listconcat(RegSeq.ret, ++ [prefix # !add(n, -1)]), ++ []); ++} + +-class WMMA_LOAD_GALSTOS +- : EmptyNVPTXInst, +- Requires<[!if(!eq(Geometry, "m16n16k16"), +- hasPTX60, +- hasPTX61), +- hasSM70]> { +- // Pattern (created by WMMA_LOAD_INTR_HELPER below) that matches the intrinsic +- // for this function. +- PatFrag IntrMatcher = !cast("INT_WMMA_" +- # Geometry # "_load_" +- # !subst("c", "c_" # Type, Abc) +- # "_" # Layout +- # !subst(".", "_", Space) +- # !if(WithStride,"_stride", "") +- # "_Intr"); +- dag OutsR03 = (outs regclass:$r0, regclass:$r1, regclass:$r2, regclass:$r3); +- dag OutsR47 = (outs regclass:$r4, regclass:$r5, regclass:$r6, regclass:$r7); +- dag Outs = !if(!eq(Abc#Type,"cf16"), OutsR03, !con(OutsR03, OutsR47)); +- +- dag StrideArg = !if(WithStride, (ins Int32Regs:$ldm), (ins)); +- dag Ins = !con((ins SrcOp:$src), StrideArg); ++// Helper class that represents a 'fragment' of an NVPTX *MMA instruction. ++// In addition to target-independent fields provided by WMMA_REGS, it adds ++// the fields commonly used to implement specific PTX instruction -- register ++// types and names, constraints, parts of assembly, etc. ++class WMMA_REGINFO ++ : WMMA_REGS { ++ // NVPTX register types used to carry fragment data. ++ NVPTXRegClass regclass = !cond( ++ !eq(PtxEltType, "f16") : Float16x2Regs, ++ !eq(PtxEltType, "f32") : Float32Regs); ++ ++ // Instruction input/output arguments for the fragment. ++ list ptx_regs = !foreach(tmp, regs, regclass); ++ ++ // List of register names for the fragment -- ["ra0", "ra1",...] ++ list reg_names = RegSeq.ret; ++ // Generates "{{$r0, $r1,.... $rN-1}}" for use in asm string construction. ++ string regstring = "{{$" # !head(reg_names) ++ # !foldl("", !tail(reg_names), a, b, ++ !strconcat(a, ", $", b)) ++ # "}}"; ++ ++ // Predicates for particular fragment variant. Technically those are ++ // per-instruction predicates, but currently all fragments that can be used in ++ // a given instruction are subject to the same constraints, so an instruction ++ // can use predicates from any of its fragments. If/when this is no ++ // longer the case, we can concat all per-fragment predicates to enforce that ++ // all fragments of the instruction are viable. ++ list Predicates = !cond( ++ // fp16 -> fp16/fp32 @ m16n16k16 ++ !and(!eq(Geom, "m16n16k16"), ++ !or(!eq(PtxEltType, "f16"), ++ !eq(PtxEltType, "f32"))) : [hasSM70, hasPTX60], ++ ++ // fp16 -> fp16/fp32 @ m8n32k16/m32n8k16 ++ !and(!or(!eq(Geom, "m8n32k16"), ++ !eq(Geom, "m32n8k16")), ++ !or(!eq(PtxEltType, "f16"), ++ !eq(PtxEltType, "f32"))) : [hasSM70, hasPTX61]); ++ ++ // template DAGs for instruction inputs/output. ++ dag Outs = !dag(outs, ptx_regs, reg_names); ++ dag Ins = !dag(ins, ptx_regs, reg_names); ++} + ++class BuildPattern { + // Build a dag pattern that matches the intrinsic call. + // We want a dag that looks like this: + // (set , (intrinsic )) where input and +@@ -7431,277 +7459,127 @@ class WMMA_LOAD_GALSTOS ++// ++// wmma.load.[a|b|c].sync.[row|col].m16n16k16[|.global|.shared].[f16|f32] ++// ++ ++class WMMA_LOAD_INTR_HELPER + : PatFrag <(ops),(ops)> { + // Intrinsic that matches this instruction. +- Intrinsic Intr = !cast("int_nvvm_wmma" +- # "_" # Geometry # "_load_" +- # Abc # "_" # Type # "_" # Layout +- # !if(WithStride,"_stride", "")); +- code match_generic = [{ +- return ChkMemSDNodeAddressSpace(N, llvm::ADDRESS_SPACE_GENERIC); +- }]; +- code match_shared = [{ +- return ChkMemSDNodeAddressSpace(N, llvm::ADDRESS_SPACE_SHARED); +- }]; +- code match_global = [{ +- return ChkMemSDNodeAddressSpace(N, llvm::ADDRESS_SPACE_GLOBAL); +- }]; +- ++ Intrinsic Intr = !cast(WMMA_NAME_LDST<"load", Frag, Layout, ++ WithStride>.record); + let Operands = !if(WithStride, (ops node:$src, node:$ldm), (ops node:$src)); + let Fragments = [!foreach(tmp, Operands, !subst(ops, Intr, tmp))]; +- let PredicateCode = !if(!eq(Space, ".shared"), match_shared, +- !if(!eq(Space, ".global"), match_global, match_generic)); +-} +- +-multiclass WMMA_LOAD_GALSTS { +- def _avar: WMMA_LOAD_GALSTOS; +- def _areg: WMMA_LOAD_GALSTOS; +- def _areg64: WMMA_LOAD_GALSTOS; +- def _ari: WMMA_LOAD_GALSTOS; +- def _ari64: WMMA_LOAD_GALSTOS; ++ let PredicateCode = !cond(!eq(Space, ".shared"): AS_match.shared, ++ !eq(Space, ".global"): AS_match.global, ++ 1: AS_match.generic); + } + +-multiclass WMMA_LOAD_GALSTSh { +- // Define a PatFrag that matches appropriate intrinsic that loads from the +- // given address space. +- def _Intr: WMMA_LOAD_INTR_HELPER; +- defm NAME: WMMA_LOAD_GALSTS; +-} +- +-multiclass WMMA_LOAD_GALST { +- defm _stride: WMMA_LOAD_GALSTSh; +- defm NAME: WMMA_LOAD_GALSTSh; +-} +- +-multiclass WMMA_LOAD_GALT { +- defm _global: WMMA_LOAD_GALST; +- defm _shared: WMMA_LOAD_GALST; +- defm NAME: WMMA_LOAD_GALST; +-} +- +-multiclass WMMA_LOAD_GAT { +- defm _row: WMMA_LOAD_GALT; +- defm _col: WMMA_LOAD_GALT; +-} ++class WMMA_LOAD ++ : EmptyNVPTXInst, ++ Requires { ++ // Pattern that matches the intrinsic for this instruction variant. ++ PatFrag IntrMatcher = WMMA_LOAD_INTR_HELPER; ++ dag Ins = !con((ins SrcOp:$src), !if(WithStride, (ins Int32Regs:$ldm), (ins))); + +-multiclass WMMA_LOAD_G { +- defm _load_a: WMMA_LOAD_GAT; +- defm _load_b: WMMA_LOAD_GAT; +- defm _load_c_f16: WMMA_LOAD_GAT; +- defm _load_c_f32: WMMA_LOAD_GAT; ++ let Pattern = [BuildPattern.ret]; ++ let OutOperandList = Frag.Outs; ++ let InOperandList = Ins; ++ let AsmString = "wmma.load." ++ # Frag.frag ++ # ".sync" ++ # "." # Layout ++ # "." # Frag.geom ++ # Space ++ # "." # Frag.ptx_elt_type # " \t" ++ # Frag.regstring ++ # ", [$src]" ++ # !if(WithStride, ", $ldm", "") ++ # ";"; + } + +-defm INT_WMMA_m32n8k16: WMMA_LOAD_G<"m32n8k16">; +-defm INT_WMMA_m16n16k16: WMMA_LOAD_G<"m16n16k16">; +-defm INT_WMMA_m8n32k16: WMMA_LOAD_G<"m8n32k16">; +- + // + // wmma.store.d.sync.[row|col].m16n16k16[|.global|.shared].[f16|f32] + // +-class WMMA_STORE_D_GLSTSO ++class WMMA_STORE_INTR_HELPER ++ : PatFrag <(ops),(ops)> { ++ // Intrinsic that matches this instruction. ++ Intrinsic Intr = !cast(WMMA_NAME_LDST<"store", Frag, Layout, ++ WithStride>.record); ++ let Operands = !con((ops node:$dst), ++ !dag(ops, !foreach(tmp, Frag.regs, node), Frag.reg_names), ++ !if(WithStride, (ops node:$ldm), (ops))); ++ let Fragments = [!foreach(tmp, Operands, !subst(ops, Intr, tmp))]; ++ let PredicateCode = !cond(!eq(Space, ".shared"): AS_match.shared, ++ !eq(Space, ".global"): AS_match.global, ++ 1: AS_match.generic); ++} ++ ++class WMMA_STORE + : EmptyNVPTXInst, +- Requires<[!if(!eq(Geometry, "m16n16k16"), +- hasPTX60, +- hasPTX61), +- hasSM70]> { +- PatFrag IntrMatcher = !cast("INT_WMMA" +- # "_" # Geometry # "_store_d" +- # "_" # Type +- # "_" # Layout +- # !subst(".", "_", Space) +- # !if(WithStride,"_stride", "") +- # "_Intr"); +- dag InsR03 = (ins DstOp:$src, regclass:$r0, regclass:$r1, +- regclass:$r2, regclass:$r3); +- dag InsR47 = (ins regclass:$r4, regclass:$r5, +- regclass:$r6, regclass:$r7); +- dag InsR = !if(!eq(Type,"f16"), InsR03, !con(InsR03, InsR47)); +- dag StrideArg = !if(WithStride, (ins Int32Regs:$ldm), (ins)); +- dag Ins = !con(InsR, StrideArg); +- +- // Construct the pattern to match corresponding intrinsic call. See the +- // details in the comments in WMMA_LOAD_ALSTOS. +- dag PatArgs = !foreach(tmp, Ins, +- !subst(imem, ADDRvar, +- !subst(MEMri64, ADDRri64, +- !subst(MEMri, ADDRri, +- !subst(ins, IntrMatcher, tmp))))); +- let Pattern = [PatArgs]; ++ Requires { ++ PatFrag IntrMatcher = WMMA_STORE_INTR_HELPER; ++ dag Ins = !con((ins DstOp:$src), ++ Frag.Ins, ++ !if(WithStride, (ins Int32Regs:$ldm), (ins))); ++ let Pattern = [BuildPattern<(set), IntrMatcher, Ins>.ret]; + let OutOperandList = (outs); + let InOperandList = Ins; + let AsmString = "wmma.store.d.sync." + # Layout +- # "." # Geometry ++ # "." # Frag.geom + # Space +- # "." # Type ++ # "." # Frag.ptx_elt_type + # " \t[$src]," +- # !if(!eq(Type,"f16"), +- "{{$r0, $r1, $r2, $r3}}", +- "{{$r0, $r1, $r2, $r3, $r4, $r5, $r6, $r7}}") ++ # Frag.regstring + # !if(WithStride, ", $ldm", "") + # ";"; +- + } + +-class WMMA_STORE_INTR_HELPER +- : PatFrag <(ops),(ops)> { +- // Intrinsic that matches this instruction. +- Intrinsic Intr = !cast("int_nvvm_wmma_" +- # Geometry +- # "_store_d" +- # "_" # Type +- # "_" # Layout +- # !if(WithStride, "_stride", "")); +- code match_generic = [{ +- return ChkMemSDNodeAddressSpace(N, llvm::ADDRESS_SPACE_GENERIC); +- }]; +- code match_shared = [{ +- return ChkMemSDNodeAddressSpace(N, llvm::ADDRESS_SPACE_SHARED); +- }]; +- code match_global = [{ +- return ChkMemSDNodeAddressSpace(N, llvm::ADDRESS_SPACE_GLOBAL); +- }]; +- +- dag Args = !if(!eq(Type,"f16"), +- (ops node:$dst, node:$r0, node:$r1, node:$r2, node:$r3), +- (ops node:$dst, node:$r0, node:$r1, node:$r2, node:$r3, +- node:$r4, node:$r5, node:$r6, node:$r7)); +- dag StrideArg = !if(WithStride, (ops node:$ldm), (ops)); +- let Operands = !con(Args, StrideArg); +- let Fragments = [!foreach(tmp, Operands, !subst(ops, Intr, tmp))]; +- let PredicateCode = !if(!eq(Space, ".shared"), match_shared, +- !if(!eq(Space, ".global"), match_global, match_generic)); +-} +- +-multiclass WMMA_STORE_D_GLSTS { +- def _avar: WMMA_STORE_D_GLSTSO; +- def _areg: WMMA_STORE_D_GLSTSO; +- def _areg64: WMMA_STORE_D_GLSTSO; +- def _ari: WMMA_STORE_D_GLSTSO; +- def _ari64: WMMA_STORE_D_GLSTSO; +-} +- +-multiclass WMMA_STORE_D_GLSTSh { +- // Define a PatFrag that matches appropriate intrinsic that loads from the +- // given address space. +- def _Intr: WMMA_STORE_INTR_HELPER; +- defm NAME: WMMA_STORE_D_GLSTS; +-} +- +-multiclass WMMA_STORE_D_GLST { +- defm _stride: WMMA_STORE_D_GLSTSh; +- defm NAME: WMMA_STORE_D_GLSTSh; +-} +- +-multiclass WMMA_STORE_D_GLT { +- defm _global: WMMA_STORE_D_GLST; +- defm _shared: WMMA_STORE_D_GLST; +- defm NAME: WMMA_STORE_D_GLST; +-} +- +-multiclass WMMA_STORE_D_GT { +- defm _row: WMMA_STORE_D_GLT; +- defm _col: WMMA_STORE_D_GLT; +-} +- +-multiclass WMMA_STORE_D_G { +- defm _store_d_f16: WMMA_STORE_D_GT; +- defm _store_d_f32: WMMA_STORE_D_GT; +-} +- +-defm INT_WMMA_m32n8k16: WMMA_STORE_D_G<"m32n8k16">; +-defm INT_WMMA_m16n16k16: WMMA_STORE_D_G<"m16n16k16">; +-defm INT_WMMA_m8n32k16: WMMA_STORE_D_G<"m8n32k16">; ++// Create all load/store variants ++foreach geom = ["m16n16k16", "m32n8k16", "m8n32k16" ] in { ++ foreach layout = ["row", "col"] in { ++ foreach stride = [0, 1] in { ++ foreach space = [".global", ".shared", ""] in { ++ foreach addr = [imem, Int32Regs, Int64Regs, MEMri, MEMri64] in { ++ foreach frag = [WMMA_REGINFO, ++ WMMA_REGINFO, ++ WMMA_REGINFO, ++ WMMA_REGINFO] in { ++ def : WMMA_LOAD; ++ } ++ foreach frag = [WMMA_REGINFO, ++ WMMA_REGINFO] in { ++ def : WMMA_STORE; ++ } ++ } // addr ++ } // space ++ } // stride ++ } // layout ++} // geom + + // WMMA.MMA +-class WMMA_MMA_GABDCS ++class WMMA_MMA + : EmptyNVPTXInst, +- Requires<[!if(!eq(Geometry, "m16n16k16"), +- hasPTX60, +- hasPTX61), +- hasSM70]> { +- Intrinsic Intr = !cast("int_nvvm_wmma_" +- # Geometry +- # "_mma" +- # "_" # ALayout +- # "_" # BLayout +- # "_" # DType +- # "_" # CType +- # !subst(".", "_", Satfinite)); +- dag Outs = !if(!eq(DType,"f16"), +- (outs d_reg:$d0, d_reg:$d1, d_reg:$d2, d_reg:$d3), +- (outs d_reg:$d0, d_reg:$d1, d_reg:$d2, d_reg:$d3, +- d_reg:$d4, d_reg:$d5, d_reg:$d6, d_reg:$d7)); +- dag InsExtraCArgs = !if(!eq(CType,"f16"), +- (ins), +- (ins c_reg:$c4, c_reg:$c5, c_reg:$c6, c_reg:$c7)); +- dag Ins = !con((ins ab_reg:$a0, ab_reg:$a1, ab_reg:$a2, ab_reg:$a3, +- ab_reg:$a4, ab_reg:$a5, ab_reg:$a6, ab_reg:$a7, +- ab_reg:$b0, ab_reg:$b1, ab_reg:$b2, ab_reg:$b3, +- ab_reg:$b4, ab_reg:$b5, ab_reg:$b6, ab_reg:$b7, +- c_reg:$c0, c_reg:$c1, c_reg:$c2, c_reg:$c3), +- InsExtraCArgs); +- +- // Construct the pattern to match corresponding intrinsic call. See the +- // details in the comments in WMMA_LOAD_ALSTOS. ++ Requires { ++ //Intrinsic Intr = int_nvvm_suld_1d_v4i32_zero; ++ Intrinsic Intr = !cast(WMMA_NAME_MMA.record); ++ dag Outs = FragD.Outs; ++ dag Ins = !con(FragA.Ins, ++ FragB.Ins, ++ FragC.Ins); ++ ++ // Construct the pattern to match corresponding intrinsic call. ++ // mma does not load/store anything, so we don't need complex operand matching here. + dag PatOuts = !foreach(tmp, Outs, !subst(outs, set, tmp)); + dag PatArgs = !foreach(tmp, Ins, !subst(ins, Intr, tmp)); + let Pattern = [!con(PatOuts, (set PatArgs))]; +@@ -7710,54 +7588,30 @@ class WMMA_MMA_GABDCS { +- def _satfinite: WMMA_MMA_GABDCS; +- def NAME: WMMA_MMA_GABDCS; +-} +- +-multiclass WMMA_MMA_GABD { +- defm _f16: WMMA_MMA_GABDC; +- defm _f32: WMMA_MMA_GABDC; +-} +- +-multiclass WMMA_MMA_GAB { +- defm _f16: WMMA_MMA_GABD; +- defm _f32: WMMA_MMA_GABD; +-} +- +-multiclass WMMA_MMA_GA { +- defm _col: WMMA_MMA_GAB; +- defm _row: WMMA_MMA_GAB; +-} +- +-multiclass WMMA_MMA_G { +- defm _col: WMMA_MMA_GA; +- defm _row: WMMA_MMA_GA; ++ # "." # FragA.geom ++ # "." # FragD.ptx_elt_type ++ # "." # FragC.ptx_elt_type ++ # !if(Satfinite, ".satfinite", "") # "\n\t\t" ++ # FragD.regstring # ",\n\t\t" ++ # FragA.regstring # ",\n\t\t" ++ # FragB.regstring # ",\n\t\t" ++ # FragC.regstring # ";"; + } + +-defm INT_WMMA_MMA_m32n8k16 : WMMA_MMA_G<"m32n8k16">; +-defm INT_WMMA_MMA_m16n16k16 : WMMA_MMA_G<"m16n16k16">; +-defm INT_WMMA_MMA_m8n32k16 : WMMA_MMA_G<"m8n32k16">; ++foreach geom = ["m16n16k16", "m32n8k16", "m8n32k16" ] in { ++ foreach layout_a = ["row", "col"] in { ++ foreach layout_b = ["row", "col"] in { ++ foreach frag_c = [WMMA_REGINFO, ++ WMMA_REGINFO] in { ++ foreach frag_d = [WMMA_REGINFO, ++ WMMA_REGINFO] in { ++ foreach satf = [0, 1] in { ++ def : WMMA_MMA, ++ WMMA_REGINFO, ++ frag_c, frag_d, layout_a, layout_b, satf>; ++ } // satf ++ } // frag_d ++ } // frag_c ++ } // layout_b ++ } // layout_a ++} // geom +-- +2.17.1 + diff --git a/deps/patches/llvm-8.0-D59393-mma-ptx63-fix.patch b/deps/patches/llvm-8.0-D59393-mma-ptx63-fix.patch new file mode 100644 index 0000000..10e6cd7 --- /dev/null +++ b/deps/patches/llvm-8.0-D59393-mma-ptx63-fix.patch @@ -0,0 +1,510 @@ +From be924be7f9e699775fe7690d4b421bebfed73aa9 Mon Sep 17 00:00:00 2001 +From: Artem Belevich +Date: Thu, 25 Apr 2019 22:27:46 +0000 +Subject: [PATCH] [NVPTX] generate correct MMA instruction mnemonics with + PTX63+. + +PTX 6.3 requires using ".aligned" in the MMA instruction names. +In order to generate correct name, now we pass current +PTX version to each instruction as an extra constant operand +and InstPrinter adjusts its output accordingly. + +Differential Revision: https://reviews.llvm.org/D59393 + +llvm-svn: 359246 +--- + .../NVPTX/InstPrinter/NVPTXInstPrinter.cpp | 14 + + .../NVPTX/InstPrinter/NVPTXInstPrinter.h | 2 + + lib/Target/NVPTX/NVPTXInstrInfo.td | 4 + + lib/Target/NVPTX/NVPTXIntrinsics.td | 279 ++++++++++-------- + test/CodeGen/NVPTX/wmma.py | 17 +- + 5 files changed, 184 insertions(+), 132 deletions(-) + +diff --git a/lib/Target/NVPTX/InstPrinter/NVPTXInstPrinter.cpp b/lib/Target/NVPTX/InstPrinter/NVPTXInstPrinter.cpp +index b774fe169d7..6fb577d5499 100644 +--- a/lib/Target/NVPTX/InstPrinter/NVPTXInstPrinter.cpp ++++ b/lib/Target/NVPTX/InstPrinter/NVPTXInstPrinter.cpp +@@ -270,6 +270,20 @@ void NVPTXInstPrinter::printLdStCode(const MCInst *MI, int OpNum, + llvm_unreachable("Empty Modifier"); + } + ++void NVPTXInstPrinter::printMmaCode(const MCInst *MI, int OpNum, raw_ostream &O, ++ const char *Modifier) { ++ const MCOperand &MO = MI->getOperand(OpNum); ++ int Imm = (int)MO.getImm(); ++ if (Modifier == nullptr || strcmp(Modifier, "version") == 0) { ++ O << Imm; // Just print out PTX version ++ } else if (strcmp(Modifier, "aligned") == 0) { ++ // PTX63 requires '.aligned' in the name of the instruction. ++ if (Imm >= 63) ++ O << ".aligned"; ++ } else ++ llvm_unreachable("Unknown Modifier"); ++} ++ + void NVPTXInstPrinter::printMemOperand(const MCInst *MI, int OpNum, + raw_ostream &O, const char *Modifier) { + printOperand(MI, OpNum, O); +diff --git a/lib/Target/NVPTX/InstPrinter/NVPTXInstPrinter.h b/lib/Target/NVPTX/InstPrinter/NVPTXInstPrinter.h +index f0f223aa057..588439137f9 100644 +--- a/lib/Target/NVPTX/InstPrinter/NVPTXInstPrinter.h ++++ b/lib/Target/NVPTX/InstPrinter/NVPTXInstPrinter.h +@@ -41,6 +41,8 @@ public: + const char *Modifier = nullptr); + void printLdStCode(const MCInst *MI, int OpNum, + raw_ostream &O, const char *Modifier = nullptr); ++ void printMmaCode(const MCInst *MI, int OpNum, raw_ostream &O, ++ const char *Modifier = nullptr); + void printMemOperand(const MCInst *MI, int OpNum, + raw_ostream &O, const char *Modifier = nullptr); + void printProtoIdent(const MCInst *MI, int OpNum, +diff --git a/lib/Target/NVPTX/NVPTXInstrInfo.td b/lib/Target/NVPTX/NVPTXInstrInfo.td +index 02a40b9f526..603d3212395 100644 +--- a/lib/Target/NVPTX/NVPTXInstrInfo.td ++++ b/lib/Target/NVPTX/NVPTXInstrInfo.td +@@ -1549,6 +1549,10 @@ def LdStCode : Operand { + let PrintMethod = "printLdStCode"; + } + ++def MmaCode : Operand { ++ let PrintMethod = "printMmaCode"; ++} ++ + def SDTWrapper : SDTypeProfile<1, 1, [SDTCisSameAs<0, 1>, SDTCisPtrTy<0>]>; + def Wrapper : SDNode<"NVPTXISD::Wrapper", SDTWrapper>; + +diff --git a/lib/Target/NVPTX/NVPTXIntrinsics.td b/lib/Target/NVPTX/NVPTXIntrinsics.td +index b9a67ba5ed3..5cd534914f7 100644 +--- a/lib/Target/NVPTX/NVPTXIntrinsics.td ++++ b/lib/Target/NVPTX/NVPTXIntrinsics.td +@@ -39,6 +39,24 @@ def AS_match { + }]; + } + ++// A node that will be replaced with the current PTX version. ++class PTX { ++ SDNodeXForm PTXVerXform = SDNodeXFormgetPTXVersion(), SDLoc(N)); ++ }]>; ++ // (i32 0) will be XForm'ed to the currently used PTX version. ++ dag version = (PTXVerXform (i32 0)); ++} ++def ptx : PTX; ++ ++// Generates list of n sequential register names. ++// E.g. RegNames<3,"r">.ret -> ["r0", "r1", "r2" ] ++class RegSeq { ++ list ret = !if(n, !listconcat(RegSeq.ret, ++ [prefix # !add(n, -1)]), ++ []); ++} ++ + //----------------------------------- + // Synchronization and shuffle functions + //----------------------------------- +@@ -7385,14 +7403,6 @@ def INT_PTX_SREG_WARPSIZE : + NVPTXInst<(outs Int32Regs:$dst), (ins), "mov.u32 \t$dst, WARP_SZ;", + [(set Int32Regs:$dst, (int_nvvm_read_ptx_sreg_warpsize))]>; + +-class EmptyNVPTXInst : NVPTXInst<(outs), (ins), "?", []>; +-// Generates list of n sequential register names. +-class RegSeq { +- list ret = !if(n, !listconcat(RegSeq.ret, +- [prefix # !add(n, -1)]), +- []); +-} +- + // Helper class that represents a 'fragment' of an NVPTX *MMA instruction. + // In addition to target-independent fields provided by WMMA_REGS, it adds + // the fields commonly used to implement specific PTX instruction -- register +@@ -7409,6 +7419,7 @@ class WMMA_REGINFO + + // List of register names for the fragment -- ["ra0", "ra1",...] + list reg_names = RegSeq.ret; ++ + // Generates "{{$r0, $r1,.... $rN-1}}" for use in asm string construction. + string regstring = "{{$" # !head(reg_names) + # !foldl("", !tail(reg_names), a, b, +@@ -7438,61 +7449,65 @@ class WMMA_REGINFO + dag Ins = !dag(ins, ptx_regs, reg_names); + } + +-class BuildPattern { ++// Convert dag of arguments into a dag to match given intrinsic. ++class BuildPatternI { ++ // Build a dag pattern that matches the intrinsic call. ++ dag ret = !foreach(tmp, Ins, ++ !subst(imem, ADDRvar, ++ !subst(MEMri64, ADDRri64, ++ !subst(MEMri, ADDRri, ++ !subst(ins, Intr, tmp))))); ++} ++ ++// Same as above, but uses PatFrag instead of an Intrinsic. ++class BuildPatternPF { + // Build a dag pattern that matches the intrinsic call. +- // We want a dag that looks like this: +- // (set , (intrinsic )) where input and +- // output arguments are named patterns that would match corresponding +- // input/output arguments of the instruction. +- // +- // First we construct (set ) from instruction's outs dag by +- // replacing dag operator 'outs' with 'set'. +- dag PatOuts = !foreach(tmp, Outs, !subst(outs, set, tmp)); +- // Similarly, construct (intrinsic ) sub-dag from +- // instruction's input arguments, only now we also need to replace operands +- // with patterns that would match them and the operator 'ins' with the +- // intrinsic. +- dag PatArgs = !foreach(tmp, Ins, +- !subst(imem, ADDRvar, +- !subst(MEMri64, ADDRri64, +- !subst(MEMri, ADDRri, +- !subst(ins, IntrMatcher, tmp))))); +- // Finally, consatenate both parts together. !con() requires both dags to have +- // the same operator, so we wrap PatArgs in a (set ...) dag. +- dag ret = !con(PatOuts, (set PatArgs)); ++ dag ret = !foreach(tmp, Ins, ++ !subst(imem, ADDRvar, ++ !subst(MEMri64, ADDRri64, ++ !subst(MEMri, ADDRri, ++ !subst(ins, Intr, tmp))))); ++} ++ ++// Common WMMA-related fields used for building patterns for all MMA instructions. ++class WMMA_INSTR _Args> ++ : NVPTXInst<(outs), (ins), "?", []> { ++ Intrinsic Intr = !cast(_Intr); ++ // Concatenate all arguments into a single dag. ++ dag Args = !foldl((ins), _Args, a, b, !con(a,b)); ++ // Pre-build the pattern to match (intrinsic arg0, arg1, ...). ++ dag IntrinsicPattern = BuildPatternI(Intr), Args>.ret; + } + + // + // wmma.load.[a|b|c].sync.[row|col].m16n16k16[|.global|.shared].[f16|f32] + // + +-class WMMA_LOAD_INTR_HELPER +- : PatFrag <(ops),(ops)> { +- // Intrinsic that matches this instruction. +- Intrinsic Intr = !cast(WMMA_NAME_LDST<"load", Frag, Layout, +- WithStride>.record); +- let Operands = !if(WithStride, (ops node:$src, node:$ldm), (ops node:$src)); +- let Fragments = [!foreach(tmp, Operands, !subst(ops, Intr, tmp))]; +- let PredicateCode = !cond(!eq(Space, ".shared"): AS_match.shared, +- !eq(Space, ".global"): AS_match.global, +- 1: AS_match.generic); +-} +- + class WMMA_LOAD +- : EmptyNVPTXInst, ++ : WMMA_INSTR.record, ++ [!con((ins SrcOp:$src), ++ !if(WithStride, (ins Int32Regs:$ldm), (ins)))]>, + Requires { +- // Pattern that matches the intrinsic for this instruction variant. +- PatFrag IntrMatcher = WMMA_LOAD_INTR_HELPER; +- dag Ins = !con((ins SrcOp:$src), !if(WithStride, (ins Int32Regs:$ldm), (ins))); ++ // Load/store intrinsics are overloaded on pointer's address space. ++ // To match the right intrinsic, we need to build AS-constrained PatFrag. ++ // Operands is a dag equivalent in shape to Args, but using (ops node:$name, .....). ++ dag PFOperands = !if(WithStride, (ops node:$src, node:$ldm), (ops node:$src)); ++ // Build PatFrag that only matches particular address space. ++ PatFrag IntrFrag = PatFrag; ++ // Build AS-constrained pattern. ++ let IntrinsicPattern = BuildPatternPF.ret; + +- let Pattern = [BuildPattern.ret]; + let OutOperandList = Frag.Outs; +- let InOperandList = Ins; ++ let InOperandList = !con(Args, (ins MmaCode:$ptx)); + let AsmString = "wmma.load." + # Frag.frag + # ".sync" ++ # "${ptx:aligned}" + # "." # Layout + # "." # Frag.geom + # Space +@@ -7506,87 +7521,79 @@ class WMMA_LOAD +- : PatFrag <(ops),(ops)> { +- // Intrinsic that matches this instruction. +- Intrinsic Intr = !cast(WMMA_NAME_LDST<"store", Frag, Layout, +- WithStride>.record); +- let Operands = !con((ops node:$dst), +- !dag(ops, !foreach(tmp, Frag.regs, node), Frag.reg_names), +- !if(WithStride, (ops node:$ldm), (ops))); +- let Fragments = [!foreach(tmp, Operands, !subst(ops, Intr, tmp))]; +- let PredicateCode = !cond(!eq(Space, ".shared"): AS_match.shared, +- !eq(Space, ".global"): AS_match.global, +- 1: AS_match.generic); +-} +- +-class WMMA_STORE +- : EmptyNVPTXInst, ++class WMMA_STORE_D ++ : WMMA_INSTR.record, ++ [!con((ins DstOp:$dst), ++ Frag.Ins, ++ !if(WithStride, (ins Int32Regs:$ldm), (ins)))]>, + Requires { +- PatFrag IntrMatcher = WMMA_STORE_INTR_HELPER; +- dag Ins = !con((ins DstOp:$src), +- Frag.Ins, +- !if(WithStride, (ins Int32Regs:$ldm), (ins))); +- let Pattern = [BuildPattern<(set), IntrMatcher, Ins>.ret]; ++ ++ // Load/store intrinsics are overloaded on pointer's address space. ++ // To match the right intrinsic, we need to build AS-constrained PatFrag. ++ // Operands is a dag equivalent in shape to Args, but using (ops node:$name, .....). ++ dag PFOperands = !con((ops node:$dst), ++ !dag(ops, !foreach(tmp, Frag.regs, node), Frag.reg_names), ++ !if(WithStride, (ops node:$ldm), (ops))); ++ // Build PatFrag that only matches particular address space. ++ PatFrag IntrFrag = PatFrag; ++ // Build AS-constrained pattern. ++ let IntrinsicPattern = BuildPatternPF.ret; ++ ++ let InOperandList = !con(Args, (ins MmaCode:$ptx)); + let OutOperandList = (outs); +- let InOperandList = Ins; +- let AsmString = "wmma.store.d.sync." +- # Layout ++ let AsmString = "wmma.store.d.sync" ++ # "${ptx:aligned}" ++ # "." # Layout + # "." # Frag.geom + # Space + # "." # Frag.ptx_elt_type +- # " \t[$src]," ++ # " \t[$dst]," + # Frag.regstring + # !if(WithStride, ", $ldm", "") + # ";"; + } + + // Create all load/store variants +-foreach geom = ["m16n16k16", "m32n8k16", "m8n32k16" ] in { +- foreach layout = ["row", "col"] in { +- foreach stride = [0, 1] in { +- foreach space = [".global", ".shared", ""] in { +- foreach addr = [imem, Int32Regs, Int64Regs, MEMri, MEMri64] in { +- foreach frag = [WMMA_REGINFO, +- WMMA_REGINFO, +- WMMA_REGINFO, +- WMMA_REGINFO] in { +- def : WMMA_LOAD; +- } +- foreach frag = [WMMA_REGINFO, +- WMMA_REGINFO] in { +- def : WMMA_STORE; +- } +- } // addr +- } // space +- } // stride +- } // layout +-} // geom ++defset list MMA_LDSTs = { ++ foreach geom = ["m16n16k16", "m32n8k16", "m8n32k16" ] in { ++ foreach layout = ["row", "col"] in { ++ foreach stride = [0, 1] in { ++ foreach space = [".global", ".shared", ""] in { ++ foreach addr = [imem, Int32Regs, Int64Regs, MEMri, MEMri64] in { ++ foreach frag = [WMMA_REGINFO, ++ WMMA_REGINFO, ++ WMMA_REGINFO, ++ WMMA_REGINFO] in { ++ def : WMMA_LOAD; ++ } ++ foreach frag = [WMMA_REGINFO, ++ WMMA_REGINFO] in { ++ def : WMMA_STORE_D; ++ } ++ } // addr ++ } // space ++ } // stride ++ } // layout ++ } // geom ++} // defset + + // WMMA.MMA + class WMMA_MMA +- : EmptyNVPTXInst, ++ : WMMA_INSTR.record, ++ [FragA.Ins, FragB.Ins, FragC.Ins]>, + Requires { +- //Intrinsic Intr = int_nvvm_suld_1d_v4i32_zero; +- Intrinsic Intr = !cast(WMMA_NAME_MMA.record); +- dag Outs = FragD.Outs; +- dag Ins = !con(FragA.Ins, +- FragB.Ins, +- FragC.Ins); +- +- // Construct the pattern to match corresponding intrinsic call. +- // mma does not load/store anything, so we don't need complex operand matching here. +- dag PatOuts = !foreach(tmp, Outs, !subst(outs, set, tmp)); +- dag PatArgs = !foreach(tmp, Ins, !subst(ins, Intr, tmp)); +- let Pattern = [!con(PatOuts, (set PatArgs))]; +- let OutOperandList = Outs; +- let InOperandList = Ins; +- let AsmString = "wmma.mma.sync." +- # ALayout ++ let OutOperandList = FragD.Outs; ++ let InOperandList = !con(Args, (ins MmaCode:$ptx)); ++ let AsmString = "wmma.mma.sync" ++ # "${ptx:aligned}" ++ # "." # ALayout + # "." # BLayout + # "." # FragA.geom + # "." # FragD.ptx_elt_type +@@ -7598,20 +7605,34 @@ class WMMA_MMA, +- WMMA_REGINFO] in { +- foreach frag_d = [WMMA_REGINFO, +- WMMA_REGINFO] in { +- foreach satf = [0, 1] in { +- def : WMMA_MMA, +- WMMA_REGINFO, +- frag_c, frag_d, layout_a, layout_b, satf>; +- } // satf +- } // frag_d +- } // frag_c +- } // layout_b +- } // layout_a +-} // geom ++defset list MMAs = { ++ foreach geom = ["m16n16k16", "m32n8k16", "m8n32k16" ] in { ++ foreach layout_a = ["row", "col"] in { ++ foreach layout_b = ["row", "col"] in { ++ foreach frag_c = [WMMA_REGINFO, ++ WMMA_REGINFO] in { ++ foreach frag_d = [WMMA_REGINFO, ++ WMMA_REGINFO] in { ++ foreach satf = [0, 1] in { ++ def : WMMA_MMA, ++ WMMA_REGINFO, ++ frag_c, frag_d, layout_a, layout_b, satf>; ++ } // satf ++ } // frag_d ++ } // frag_c ++ } // layout_b ++ } // layout_a ++ } // geom ++} // defset ++ ++// Constructing non-flat DAGs is still a pain. I can't !subst a dag node with a ++// dag, so the ptx.version must be appended *after* foreach replaces 'ins' with ++// the instruction record. ++class WMMA_PAT ++ : Pat; ++ ++// Build intrinsic->instruction patterns for all MMA instructions. ++foreach mma = !listconcat(MMAs, MMA_LDSTs) in ++ def : WMMA_PAT; +diff --git a/test/CodeGen/NVPTX/wmma.py b/test/CodeGen/NVPTX/wmma.py +index 14bbfd7df09..72d189ca050 100644 +--- a/test/CodeGen/NVPTX/wmma.py ++++ b/test/CodeGen/NVPTX/wmma.py +@@ -3,9 +3,12 @@ + + # RUN: python %s > %t.ll + # RUN: llc < %t.ll -march=nvptx64 -mcpu=sm_70 -mattr=+ptx61 | FileCheck %t.ll ++# RUN: python %s --ptx=63 > %t-ptx63.ll ++# RUN: llc < %t-ptx63.ll -march=nvptx64 -mcpu=sm_70 -mattr=+ptx63 | FileCheck %t-ptx63.ll + + from __future__ import print_function + ++import argparse + from itertools import product + from string import Template + +@@ -64,7 +67,7 @@ define ${ret_ty} @test_${function}_o(i8 ${as}* %src ${extra_args}) { + } + """ + intrinsic_template = "llvm.nvvm.wmma.${geom}.load.${abc}.${layout}${stride}.${itype}.${pspace}" +- instruction_template = "wmma.load.${abc}.sync.${layout}.${geom}${space}.${itype}" ++ instruction_template = "wmma.load.${abc}.sync${aligned}.${layout}.${geom}${space}.${itype}" + + for geom, abc, layout, space, stride, itype in product( + known_geoms, +@@ -76,6 +79,7 @@ define ${ret_ty} @test_${function}_o(i8 ${as}* %src ${extra_args}) { + + params = { + "abc" : abc, ++ "aligned" : ".aligned" if ptx_version >= 63 else "", + "layout" : layout, + "space" : space, + "stride" : stride, +@@ -135,7 +139,7 @@ define void @test_${function}_o(i8 ${as}* %src, ${args}${extra_args}) { + } + """ + intrinsic_template = "llvm.nvvm.wmma.${geom}.store.${abc}.${layout}${stride}.${itype}.${pspace}" +- instruction_template = "wmma.store.${abc}.sync.${layout}.${geom}${space}.${itype}" ++ instruction_template = "wmma.store.${abc}.sync${aligned}.${layout}.${geom}${space}.${itype}" + + for geom, abc, layout, space, stride, itype in product( + known_geoms, +@@ -147,6 +151,7 @@ define void @test_${function}_o(i8 ${as}* %src, ${args}${extra_args}) { + + params = { + "abc" : abc, ++ "aligned" : ".aligned" if ptx_version >= 63 else "", + "layout" : layout, + "space" : space, + "stride" : stride, +@@ -191,7 +196,7 @@ define ${ret_ty} @test_${function}( + } + """ + intrinsic_template = "llvm.nvvm.wmma.${geom}.mma.${alayout}.${blayout}.${dtype}.${ctype}${satf}" +- instruction_template = "wmma.mma.sync.${alayout}.${blayout}.${geom}.${dtype}.${ctype}${satf}" ++ instruction_template = "wmma.mma.sync${aligned}.${alayout}.${blayout}.${geom}.${dtype}.${ctype}${satf}" + + for geom, alayout, blayout, ctype, dtype, satf in product( + known_geoms, +@@ -202,6 +207,7 @@ define ${ret_ty} @test_${function}( + [".satfinite", ""]): + + params = { ++ "aligned" : ".aligned" if ptx_version >= 63 else "", + "alayout" : alayout, + "blayout" : blayout, + "ctype" : ctype, +@@ -230,4 +236,9 @@ def main(): + gen_wmma_store_tests() + gen_wmma_mma_tests() + ++parser = argparse.ArgumentParser() ++parser.add_argument('--ptx', type=int, default=60) ++args = parser.parse_args() ++ptx_version = args.ptx ++ + main() +-- +2.17.1 + diff --git a/deps/patches/llvm-8.0-D63688-wasm-isLocal.patch b/deps/patches/llvm-8.0-D63688-wasm-isLocal.patch new file mode 100644 index 0000000..820363d --- /dev/null +++ b/deps/patches/llvm-8.0-D63688-wasm-isLocal.patch @@ -0,0 +1,39 @@ +From 83d5085a7fcbb4596d964dbe037c5ebf4de02b69 Mon Sep 17 00:00:00 2001 +From: Keno Fischer +Date: Sun, 23 Jun 2019 00:29:59 +0000 +Subject: [PATCH] [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 +Differential Revision: https://reviews.llvm.org/D63688 + +llvm-svn: 364143 +(cherry picked from commit 5f4ae7c45718618c4c571495e7d910d5722f70ad) +--- + llvm/lib/Support/Unix/Path.inc | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/lib/Support/Unix/Path.inc b/lib/Support/Unix/Path.inc +index d7cc0d627d0..eb38a71fffb 100644 +--- a/lib/Support/Unix/Path.inc ++++ b/lib/Support/Unix/Path.inc +@@ -398,6 +398,9 @@ static bool is_local_impl(struct STATVFS &Vfs) { + #elif defined(__Fuchsia__) + // Fuchsia doesn't yet support remote filesystem mounts. + return true; ++#elif defined(__EMSCRIPTEN__) ++ // Emscripten doesn't currently support remote filesystem mounts. ++ return true; + #elif defined(__HAIKU__) + // Haiku doesn't expose this information. + return false; +-- +2.24.0 + diff --git a/deps/patches/llvm-8.0-D65174-limit-merge-stores.patch b/deps/patches/llvm-8.0-D65174-limit-merge-stores.patch new file mode 100644 index 0000000..646c44f --- /dev/null +++ b/deps/patches/llvm-8.0-D65174-limit-merge-stores.patch @@ -0,0 +1,119 @@ +From 19992a8c7f2df2000ea7fd4a284ec7b407400fb0 Mon Sep 17 00:00:00 2001 +From: Wei Mi +Date: Sun, 29 Mar 2020 17:14:12 -0400 +Subject: [PATCH] [DAGCombine] Limit the number of times for the same store and + root nodes to bail out in store merging dependence check. + +We run into a case where dependence check in store merging bail out many times +for the same store and root nodes in a huge basicblock. That increases compile +time by almost 100x. The patch add a map to track how many times the bailing +out happen for the same store and root, and if it is over a limit, stop +considering the store with the same root as a merging candidate. + +Differential Revision: https://reviews.llvm.org/D65174 +--- + llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 45 +++++++++++++++++-- + 1 file changed, 42 insertions(+), 3 deletions(-) + +diff --git llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp +index 6af01423ca1..9c7e37d6945 100644 +--- llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp ++++ llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp +@@ -112,6 +112,11 @@ static cl::opt + MaySplitLoadIndex("combiner-split-load-index", cl::Hidden, cl::init(true), + cl::desc("DAG combiner may split indexing from loads")); + ++static cl::opt StoreMergeDependenceLimit( ++ "combiner-store-merge-dependence-limit", cl::Hidden, cl::init(10), ++ cl::desc("Limit the number of times for the same StoreNode and RootNode " ++ "to bail out in store merging dependence check")); ++ + namespace { + + class DAGCombiner { +@@ -145,6 +150,14 @@ namespace { + /// which have not yet been combined to the worklist. + SmallPtrSet CombinedNodes; + ++ /// Map from candidate StoreNode to the pair of RootNode and count. ++ /// The count is used to track how many times we have seen the StoreNode ++ /// with the same RootNode bail out in dependence check. If we have seen ++ /// the bail out for the same pair many times over a limit, we won't ++ /// consider the StoreNode with the same RootNode as store merging ++ /// candidate again. ++ DenseMap> StoreRootCountMap; ++ + // AA - Used for DAG load/store alias analysis. + AliasAnalysis *AA; + +@@ -190,6 +203,7 @@ namespace { + /// Remove all instances of N from the worklist. + void removeFromWorklist(SDNode *N) { + CombinedNodes.erase(N); ++ StoreRootCountMap.erase(N); + + auto It = WorklistMap.find(N); + if (It == WorklistMap.end()) +@@ -14423,6 +14437,18 @@ void DAGCombiner::getStoreMergeCandidates( + return (BasePtr.equalBaseIndex(Ptr, DAG, Offset)); + }; + ++ // Check if the pair of StoreNode and the RootNode already bail out many ++ // times which is over the limit in dependence check. ++ auto OverLimitInDependenceCheck = [&](SDNode *StoreNode, ++ SDNode *RootNode) -> bool { ++ auto RootCount = StoreRootCountMap.find(StoreNode); ++ if (RootCount != StoreRootCountMap.end() && ++ RootCount->second.first == RootNode && ++ RootCount->second.second > StoreMergeDependenceLimit) ++ return true; ++ return false; ++ }; ++ + // We looking for a root node which is an ancestor to all mergable + // stores. We search up through a load, to our root and then down + // through all children. For instance we will find Store{1,2,3} if +@@ -14450,7 +14476,8 @@ void DAGCombiner::getStoreMergeCandidates( + if (StoreSDNode *OtherST = dyn_cast(*I2)) { + BaseIndexOffset Ptr; + int64_t PtrDiff; +- if (CandidateMatch(OtherST, Ptr, PtrDiff)) ++ if (CandidateMatch(OtherST, Ptr, PtrDiff) && ++ !OverLimitInDependenceCheck(OtherST, RootNode)) + StoreNodes.push_back(MemOpLink(OtherST, PtrDiff)); + } + } else +@@ -14459,7 +14486,8 @@ void DAGCombiner::getStoreMergeCandidates( + if (StoreSDNode *OtherST = dyn_cast(*I)) { + BaseIndexOffset Ptr; + int64_t PtrDiff; +- if (CandidateMatch(OtherST, Ptr, PtrDiff)) ++ if (CandidateMatch(OtherST, Ptr, PtrDiff) && ++ !OverLimitInDependenceCheck(OtherST, RootNode)) + StoreNodes.push_back(MemOpLink(OtherST, PtrDiff)); + } + } +@@ -14517,8 +14545,19 @@ bool DAGCombiner::checkMergeStoreCandidatesForDependencies( + // Search through DAG. We can stop early if we find a store node. + for (unsigned i = 0; i < NumStores; ++i) + if (SDNode::hasPredecessorHelper(StoreNodes[i].MemNode, Visited, Worklist, +- Max)) ++ Max)) { ++ // If the searching bail out, record the StoreNode and RootNode in the ++ // StoreRootCountMap. If we have seen the pair many times over a limit, ++ // we won't add the StoreNode into StoreNodes set again. ++ if (Visited.size() >= Max) { ++ auto &RootCount = StoreRootCountMap[StoreNodes[i].MemNode]; ++ if (RootCount.first == RootNode) ++ RootCount.second++; ++ else ++ RootCount = {RootNode, 1}; ++ } + return false; ++ } + return true; + } + +-- +2.25.2 + diff --git a/deps/patches/llvm-8.0-D66401-mingw-reloc.patch b/deps/patches/llvm-8.0-D66401-mingw-reloc.patch new file mode 100644 index 0000000..384399f --- /dev/null +++ b/deps/patches/llvm-8.0-D66401-mingw-reloc.patch @@ -0,0 +1,69 @@ +diff --git a/test/CodeGen/X86/mingw-refptr.ll b/test/CodeGen/X86/mingw-refptr.ll +--- a/test/CodeGen/X86/mingw-refptr.ll ++++ b/test/CodeGen/X86/mingw-refptr.ll +@@ -1,5 +1,6 @@ + ; RUN: llc < %s -mtriple=x86_64-w64-mingw32 | FileCheck %s -check-prefix=CHECK-X64 + ; RUN: llc < %s -mtriple=i686-w64-mingw32 | FileCheck %s -check-prefix=CHECK-X86 ++; RUN: llc < %s -mtriple=i686-w64-mingw32-none-elf | FileCheck %s -check-prefix=CHECK-X86-ELF + + @var = external local_unnamed_addr global i32, align 4 + @dsolocalvar = external dso_local local_unnamed_addr global i32, align 4 +@@ -16,6 +17,9 @@ + ; CHECK-X86: movl .refptr._var, %eax + ; CHECK-X86: movl (%eax), %eax + ; CHECK-X86: retl ++; CHECK-X86-ELF-LABEL: getVar: ++; CHECK-X86-ELF: movl var, %eax ++; CHECK-X86-ELF: retl + entry: + %0 = load i32, i32* @var, align 4 + ret i32 %0 +@@ -66,6 +70,9 @@ + ; CHECK-X86: movl __imp__extvar, %eax + ; CHECK-X86: movl (%eax), %eax + ; CHECK-X86: retl ++; CHECK-X86-ELF-LABEL: getExtVar: ++; CHECK-X86-ELF: movl extvar, %eax ++; CHECK-X86-ELF: retl + entry: + %0 = load i32, i32* @extvar, align 4 + ret i32 %0 +diff --git a/lib/Target/X86/X86Subtarget.cpp b/lib/Target/X86/X86Subtarget.cpp +--- a/lib/Target/X86/X86Subtarget.cpp ++++ b/lib/Target/X86/X86Subtarget.cpp +@@ -146,6 +146,9 @@ + return X86II::MO_DLLIMPORT; + return X86II::MO_COFFSTUB; + } ++ // Some JIT users use *-win32-elf triples; these shouldn't use GOT tables. ++ if (isOSWindows()) ++ return X86II::MO_NO_FLAG; + + if (is64Bit()) { + // ELF supports a large, truly PIC code model with non-PC relative GOT +diff --git a/lib/Target/TargetMachine.cpp b/lib/Target/TargetMachine.cpp +--- a/lib/Target/TargetMachine.cpp ++++ b/lib/Target/TargetMachine.cpp +@@ -128,8 +128,8 @@ + // don't assume the variables to be DSO local unless we actually know + // that for sure. This only has to be done for variables; for functions + // the linker can insert thunks for calling functions from another DLL. +- if (TT.isWindowsGNUEnvironment() && GV && GV->isDeclarationForLinker() && +- isa(GV)) ++ if (TT.isWindowsGNUEnvironment() && TT.isOSBinFormatCOFF() && GV && ++ GV->isDeclarationForLinker() && isa(GV)) + return false; + + // On COFF, don't mark 'extern_weak' symbols as DSO local. If these symbols +@@ -142,7 +142,9 @@ + // Make an exception for windows OS in the triple: Some firmware builds use + // *-win32-macho triples. This (accidentally?) produced windows relocations + // without GOT tables in older clang versions; Keep this behaviour. +- if (TT.isOSBinFormatCOFF() || (TT.isOSWindows() && TT.isOSBinFormatMachO())) ++ // Some JIT users use *-win32-elf triples; these shouldn't use GOT tables ++ // either. ++ if (TT.isOSBinFormatCOFF() || TT.isOSWindows()) + return true; + + // Most PIC code sequences that assume that a symbol is local cannot + diff --git a/deps/patches/llvm-8.0-D66657-codegen-degenerate.patch b/deps/patches/llvm-8.0-D66657-codegen-degenerate.patch new file mode 100644 index 0000000..ddcf4dc --- /dev/null +++ b/deps/patches/llvm-8.0-D66657-codegen-degenerate.patch @@ -0,0 +1,65 @@ +From 4c7e1defbddafcfcfe1211b041d43a36114a8f48 Mon Sep 17 00:00:00 2001 +From: Valentin Churavy +Date: Sat, 14 Dec 2019 10:33:30 -0500 +Subject: [PATCH 2/2] [CodegenPrepare] Guard against degenerate branches + +Summary: +Guard against a potential crash observed in https://github.com/JuliaLang/julia/issues/32994#issuecomment-524249628 +If two branches are collapsed we can encounter a degenerate conditional branch `TBB==FBB`. +The subsequent code assumes that they differ, so we exit out early. + +Reviewers: ributzka, spatel + +Subscribers: loladiro, dexonsmith, hiraditya, llvm-commits + +Tags: #llvm + +Differential Revision: https://reviews.llvm.org/D66657 +--- + llvm/lib/CodeGen/CodeGenPrepare.cpp | 4 ++++ + .../CodeGen/X86/codegen-prepare-collapse.ll | 18 ++++++++++++++++++ + 2 files changed, 22 insertions(+) + create mode 100644 llvm/test/CodeGen/X86/codegen-prepare-collapse.ll + +diff --git a/lib/CodeGen/CodeGenPrepare.cpp b/lib/CodeGen/CodeGenPrepare.cpp +index c35f8666fa3..3647641c594 100644 +--- a/lib/CodeGen/CodeGenPrepare.cpp ++++ b/lib/CodeGen/CodeGenPrepare.cpp +@@ -6929,6 +6929,10 @@ bool CodeGenPrepare::splitBranchCondition(Function &F) { + if (Br1->getMetadata(LLVMContext::MD_unpredictable)) + continue; + ++ // The merging of mostly empty BB can cause a degenerate branch. ++ if (TBB == FBB) ++ continue; ++ + unsigned Opc; + Value *Cond1, *Cond2; + if (match(LogicOp, m_And(m_OneUse(m_Value(Cond1)), +diff --git a/test/CodeGen/X86/codegen-prepare-collapse.ll b/test/CodeGen/X86/codegen-prepare-collapse.ll +new file mode 100644 +index 00000000000..18e3ef7afbd +--- /dev/null ++++ b/test/CodeGen/X86/codegen-prepare-collapse.ll +@@ -0,0 +1,18 @@ ++; RUN: llc -fast-isel=true -O1 -mtriple=x86_64-unkown-linux-gnu -start-before=codegenprepare -stop-after=codegenprepare -o - < %s | FileCheck %s ++ ++; CHECK-LABEL: @foo ++define void @foo() { ++top: ++; CHECK: br label %L34 ++ br label %L34 ++ ++L34: ; preds = %L34, %L34, %top ++ %.sroa.075.0 = phi i64 [ undef, %top ], [ undef, %L34 ], [ undef, %L34 ] ++ %0 = icmp sgt i8 undef, -1 ++ %cond5896 = icmp eq i8 0, 2 ++ %cond58 = and i1 %cond5896, %0 ++; During codegenprepare such degenerate branches can occur and should not ++; lead to crashes. ++; CHECK: br label %L34 ++ br i1 %cond58, label %L34, label %L34 ++} +-- +2.24.1 + diff --git a/deps/patches/llvm-8.0-D71495-vectorize-freduce.patch b/deps/patches/llvm-8.0-D71495-vectorize-freduce.patch new file mode 100644 index 0000000..fb46192 --- /dev/null +++ b/deps/patches/llvm-8.0-D71495-vectorize-freduce.patch @@ -0,0 +1,94 @@ +From 7c30e23f115ae285b497ef11af0153703111dff2 Mon Sep 17 00:00:00 2001 +From: Valentin Churavy +Date: Sun, 22 Dec 2019 14:25:50 -0500 +Subject: [PATCH 1/2] [SelectionDAG] Copy FP flags when visiting a binary + instruction. + +Summary: +We noticed in Julia that the sequence below no longer turned into +a sequence of FMA instructions in LLVM 7+, but it did in LLVM 6. + +``` + %29 = fmul contract <4 x double> %wide.load, %wide.load16 + %30 = fmul contract <4 x double> %wide.load13, %wide.load17 + %31 = fmul contract <4 x double> %wide.load14, %wide.load18 + %32 = fmul contract <4 x double> %wide.load15, %wide.load19 + %33 = fadd fast <4 x double> %vec.phi, %29 + %34 = fadd fast <4 x double> %vec.phi10, %30 + %35 = fadd fast <4 x double> %vec.phi11, %31 + %36 = fadd fast <4 x double> %vec.phi12, %32 +``` + +Unlike Clang, Julia doesn't set the `unsafe-fp-math=true` function +attribute, but rather emits more local instruction flags. + +This partially undoes https://reviews.llvm.org/D46854 and if required I can try to minimize the test further. + +Reviewers: spatel, mcberg2017 + +Reviewed By: spatel + +Subscribers: chriselrod, merge_guards_bot, hiraditya, llvm-commits + +Tags: #llvm + +Differential Revision: https://reviews.llvm.org/D71495 +--- + .../SelectionDAG/SelectionDAGBuilder.cpp | 7 +++++ + llvm/test/CodeGen/X86/fmf-reduction.ll | 26 +++++++++++++++++++ + 2 files changed, 33 insertions(+) + create mode 100644 llvm/test/CodeGen/X86/fmf-reduction.ll + +diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +index bfeb3d1bc2b..e6362c19691 100644 +--- a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp ++++ b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +@@ -2833,6 +2833,13 @@ void SelectionDAGBuilder::visitBinary(const User &I, unsigned Opcode) { + if (isVectorReductionOp(&I)) { + Flags.setVectorReduction(true); + LLVM_DEBUG(dbgs() << "Detected a reduction operation:" << I << "\n"); ++ ++ // If no flags are set we will propagate the incoming flags, if any flags ++ // are set, we will intersect them with the incoming flag and so we need to ++ // copy the FMF flags here. ++ if (auto *FPOp = dyn_cast(&I)) { ++ Flags.copyFMF(*FPOp); ++ } + } + + SDValue Op1 = getValue(I.getOperand(0)); +diff --git a/test/CodeGen/X86/fmf-reduction.ll b/test/CodeGen/X86/fmf-reduction.ll +new file mode 100644 +index 00000000000..1d669d2a924 +--- /dev/null ++++ b/test/CodeGen/X86/fmf-reduction.ll +@@ -0,0 +1,26 @@ ++; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py ++; RUN: llc < %s -mtriple=x86_64-- -mattr=fma | FileCheck %s ++ ++; Propagation of IR FMF should not drop flags when adding the DAG reduction flag. ++; This should include an FMA instruction, not separate FMUL/FADD. ++ ++define double @julia_dotf(<4 x double> %x, <4 x double> %y, <4 x double> %z, i1 %t3) { ++; CHECK-LABEL: julia_dotf: ++; CHECK: # %bb.0: ++; CHECK-NEXT: vfmadd213pd {{.*#+}} ymm0 = (ymm1 * ymm0) + ymm2 ++; CHECK-NEXT: vextractf128 $1, %ymm0, %xmm1 ++; CHECK-NEXT: vaddpd %xmm1, %xmm0, %xmm0 ++; CHECK-NEXT: vpermilpd {{.*#+}} xmm1 = xmm0[1,0] ++; CHECK-NEXT: vaddsd %xmm1, %xmm0, %xmm0 ++; CHECK-NEXT: vzeroupper ++; CHECK-NEXT: retq ++ %t1 = fmul contract <4 x double> %x, %y ++ %t2 = fadd fast <4 x double> %z, %t1 ++ %rdx.shuf = shufflevector <4 x double> %t2, <4 x double> undef, <4 x i32> ++ %bin.rdx22 = fadd fast <4 x double> %t2, %rdx.shuf ++ %rdx.shuf23 = shufflevector <4 x double> %bin.rdx22, <4 x double> undef, <4 x i32> ++ %bin.rdx24 = fadd fast <4 x double> %bin.rdx22, %rdx.shuf23 ++ %t4 = extractelement <4 x double> %bin.rdx24, i32 0 ++ ret double %t4 ++} ++ +-- +2.24.1 + diff --git a/deps/patches/llvm-8.0-D75072-SCEV-add-type.patch b/deps/patches/llvm-8.0-D75072-SCEV-add-type.patch new file mode 100644 index 0000000..6418eca --- /dev/null +++ b/deps/patches/llvm-8.0-D75072-SCEV-add-type.patch @@ -0,0 +1,415 @@ +From f11f45a45ce8b90c798dd939d2782205e4291360 Mon Sep 17 00:00:00 2001 +From: Keno Fischer +Date: Fri, 6 Mar 2020 10:29:20 -0500 +Subject: [PATCH] [SCEV] Record NI types in add exprs + +Summary: +(Rebased to LLVM 8 from the original LLVM 9 patch) +This fixes a case where loop-reduce introduces ptrtoint/inttoptr for +non-integral address space pointers. Over the past several years, we +have gradually improved the SCEVExpander to actually do something +sensible for non-integral pointer types. However, that obviously +relies on the expander knowing what the type of the SCEV expression is. +That is usually the case, but there is one important case where it's +not: The type of an add expression is just the type of the last operand, +so if the non-integral pointer is not the last operand, later uses of +that SCEV may not realize that the given add expression contains +non-integral pointers and may try to expand it as integers. + +One interesting observation is that we do get away with this scheme in +shockingly many cases. The reason for this is that SCEV expressions +often have an `scUnknown` pointer base, which our sort order on the +operands of add expressions sort behind basically everything else, +so it usually ends up as the last operand. + +One situation where this fails is included as a test case. This test +case was bugpoint-reduced from the issue reported at +https://github.com/JuliaLang/julia/issues/31156. What happens here +is that the pointer base is an scAddRec from an outer loop, plus an +scUnknown integer offset. By our sort order, the scUnknown gets sorted +after the scAddRec pointer base, thus making an add expression of these +two operands have integer type. This then confuses the expander, into +attempting to expand the whole thing as integers, which will obviously +fail when reaching the non-integral pointer. + +I considered a few options to solve this, but here's what I ended up +settling on: The AddExpr class gains a new subclass that explicitly +stores the type of the expression. This subclass is used whenever one +of the operands is a non-integral pointer. To reduce the impact for the +regular case (where the SCEV expression contains no non-integral +pointers), a bit flag is kept in each flag expression to indicate +whether it is of non-integral pointer type (this should give the same +answer as asking if getType() is non-integral, but performing that +query may involve a pointer chase and requires the DataLayout). For +add expressions that flag is also used to indicate whether we're using +the subclass or not. This is slightly inefficient, because it uses +the subclass even in the (not uncommon) case where the last operand +does actually accurately reflect the non-integral pointer type. However, +it didn't seem worth the extra flag bit and complexity to do this +micro-optimization. + +I had hoped that we could additionally restrict mul exprs from +containing any non-integral pointers, and also require add exprs to +only have one operand containg such pointers (but not more), but this +turned out not to work. The reason for this is that SCEV wants to +form differences between pointers, which it represents as `A + B*-1`, +so we need to allow both multiplication by `-1` and addition with +multiple non-integral pointer arguments. I'm not super happy with +that situation, but I think it exposes a more general problem with +non-integral pointers in LLVM. We don't actually have a way to express +the difference between two non-integral pointers at the IR level. +In theory this is a problem for SCEV, because it means that we can't +materialize such SCEV expression. However, in practice, these +expressions generally have the same base pointer, so SCEV will +appropriately simplify them to just the integer components. +Nevertheless it is a bit unsatisfying. Perhaps we could have an +intrinsic that takes the byte difference between two pointers to the +same allocated object (in the same sense as is used in getelementptr), +which should be a sensible operation even for non-integral pointers. +However, given the practical considerations above, that's a project +for another time. For now, simply allowing the existing pointer-diff +pattern for non-integral pointers seems to work ok. + +Reviewers: sanjoy, reames, vtjnash, vchuravy + +Subscribers: hiraditya, javed.absar, llvm-commits + +Tags: #llvm, #julialang + +Differential Revision: https://reviews.llvm.org/D75072 +--- + llvm/include/llvm/Analysis/ScalarEvolution.h | 26 +++++-- + .../Analysis/ScalarEvolutionExpressions.h | 70 ++++++++++++++++--- + llvm/lib/Analysis/ScalarEvolution.cpp | 44 +++++++++--- + .../LoopStrengthReduce/nonintegral.ll | 35 +++++++++- + 4 files changed, 150 insertions(+), 25 deletions(-) + +diff --git llvm/include/llvm/Analysis/ScalarEvolution.h llvm/include/llvm/Analysis/ScalarEvolution.h +index 5286f6a220e..f27fceb70d2 100644 +--- llvm/include/llvm/Analysis/ScalarEvolution.h ++++ llvm/include/llvm/Analysis/ScalarEvolution.h +@@ -116,6 +116,19 @@ public: + NoWrapMask = (1 << 3) - 1 + }; + ++ /// HasNonIntegralPointerFlag are bitfield indices into SubclassData. ++ /// ++ /// When constructing SCEV expressions for LLVM expressions with non-integral ++ /// pointer types, some additional processing is required to ensure that we ++ /// don't introduce any illegal transformations. However, non-integral pointer ++ /// types are a very rarely used feature, so we want to make sure to only do ++ /// such processing if they are actually used. To ensure minimal performance ++ /// impact, we memoize that fact in using these flags. ++ enum HasNonIntegralPointerFlag { ++ FlagNoNIPointers = 0, ++ FlagHasNIPointers = (1 << 3) ++ }; ++ + explicit SCEV(const FoldingSetNodeIDRef ID, unsigned SCEVTy) + : FastID(ID), SCEVType(SCEVTy) {} + SCEV(const SCEV &) = delete; +@@ -138,6 +138,10 @@ public: + /// Return true if the specified scev is negated, but not a constant. + bool isNonConstantNegative() const; + ++ bool hasNonIntegralPointers() const { ++ return SubclassData & FlagHasNIPointers; ++ } ++ + /// Print out the internal representation of this scalar to the specified + /// stream. This should really only be used for debugging purposes. + void print(raw_ostream &OS) const; +diff --git llvm/include/llvm/Analysis/ScalarEvolutionExpressions.h llvm/include/llvm/Analysis/ScalarEvolutionExpressions.h +index 876d68438ef..b9ea23c0086 100644 +--- llvm/include/llvm/Analysis/ScalarEvolutionExpressions.h ++++ llvm/include/llvm/Analysis/ScalarEvolutionExpressions.h +@@ -181,6 +184,13 @@ class Type; + return getNoWrapFlags(FlagNW) != FlagAnyWrap; + } + ++ void setHasNIPtr(bool HasNIPtr) { ++ if (HasNIPtr) ++ SubclassData |= FlagHasNIPointers; ++ else ++ SubclassData &= ~FlagHasNIPointers; ++ } ++ + /// Methods for support type inquiry through isa, cast, and dyn_cast: + static bool classof(const SCEV *S) { + return S->getSCEVType() == scAddExpr || S->getSCEVType() == scMulExpr || +@@ -215,24 +220,54 @@ class Type; + class SCEVAddExpr : public SCEVCommutativeExpr { + friend class ScalarEvolution; + ++ protected: + SCEVAddExpr(const FoldingSetNodeIDRef ID, + const SCEV *const *O, size_t N) + : SCEVCommutativeExpr(ID, scAddExpr, O, N) {} + + public: +- Type *getType() const { +- // Use the type of the last operand, which is likely to be a pointer +- // type, if there is one. This doesn't usually matter, but it can help +- // reduce casts when the expressions are expanded. +- return getOperand(getNumOperands() - 1)->getType(); ++ /// Returns the type of the add expression, by looking either at the last ++ /// operand or deferring to the SCEVAddNIExpr subclass for non-integral ++ /// pointers. ++ Type *getType() const; ++ ++ /// Methods for support type inquiry through isa, cast, and dyn_cast: ++ static bool classof(const SCEV *S) { return S->getSCEVType() == scAddExpr; } ++ }; ++ ++ /// This node represents an addition of some number of SCEVs, one which ++ /// is a non-integral pointer type, requiring us to know the type exactly for ++ /// correctness. ++ class SCEVAddNIExpr : public SCEVAddExpr { ++ friend class ScalarEvolution; ++ PointerType *NIType; ++ ++ SCEVAddNIExpr(const FoldingSetNodeIDRef ID, const SCEV *const *O, size_t N, ++ PointerType *NIType) ++ : SCEVAddExpr(ID, O, N), NIType(NIType) { ++ SubclassData |= FlagHasNIPointers; + } + ++ public: ++ Type *getType() const { return NIType; } ++ + /// Methods for support type inquiry through isa, cast, and dyn_cast: + static bool classof(const SCEV *S) { +- return S->getSCEVType() == scAddExpr; ++ return S->getSCEVType() == scAddExpr && S->hasNonIntegralPointers(); + } + }; + ++ inline Type *SCEVAddExpr::getType() const { ++ // In general, use the type of the last operand, which is likely to be a ++ // pointer type, if there is one. This doesn't usually matter, but it can ++ // help reduce casts when the expressions are expanded. In the (unusual) ++ // case that we're working with non-integral pointers, we have a subclass ++ // that stores that type explicitly. ++ if (hasNonIntegralPointers()) ++ return cast(this)->getType(); ++ return getOperand(getNumOperands() - 1)->getType(); ++ } ++ + /// This node represents multiplication of some number of SCEVs. + class SCEVMulExpr : public SCEVCommutativeExpr { + friend class ScalarEvolution; +@@ -242,6 +273,18 @@ class Type; + : SCEVCommutativeExpr(ID, scMulExpr, O, N) {} + + public: ++ Type *getType() const { ++ // In general, we can't form SCEVMulExprs with non-integral pointer types, ++ // but for the moment we need to allow a special case: Multiplying by ++ // -1 to be able express the difference between two pointers. In order ++ // to maintain the invariant that SCEVs with the NI flag set should have ++ // a type corresponding to the contained NI ptr, we need to return the ++ // type of the pointer here. ++ if (hasNonIntegralPointers()) ++ return getOperand(getNumOperands() - 1)->getType(); ++ return SCEVCommutativeExpr::getType(); ++ } ++ + /// Methods for support type inquiry through isa, cast, and dyn_cast: + static bool classof(const SCEV *S) { + return S->getSCEVType() == scMulExpr; +@@ -467,9 +690,12 @@ class Type; + /// instances owned by a ScalarEvolution. + SCEVUnknown *Next; + +- SCEVUnknown(const FoldingSetNodeIDRef ID, Value *V, +- ScalarEvolution *se, SCEVUnknown *next) : +- SCEV(ID, scUnknown), CallbackVH(V), SE(se), Next(next) {} ++ SCEVUnknown(const FoldingSetNodeIDRef ID, Value *V, ScalarEvolution *se, ++ SCEVUnknown *next, bool ValueIsNIPtr) ++ : SCEV(ID, scUnknown), CallbackVH(V), SE(se), Next(next) { ++ if (ValueIsNIPtr) ++ SubclassData |= FlagHasNIPointers; ++ } + + // Implement CallbackVH. + void deleted() override; +diff --git llvm/lib/Analysis/ScalarEvolution.cpp llvm/lib/Analysis/ScalarEvolution.cpp +index cd74815a895..09e98345d0f 100644 +--- llvm/lib/Analysis/ScalarEvolution.cpp ++++ llvm/lib/Analysis/ScalarEvolution.cpp +@@ -354,12 +354,13 @@ Type *SCEV::getType() const { + case scSignExtend: + return cast(this)->getType(); + case scAddRecExpr: +- case scMulExpr: + case scUMaxExpr: + case scSMaxExpr: + case scUMinExpr: + case scSMinExpr: + return cast(this)->getType(); ++ case scMulExpr: ++ return cast(this)->getType(); + case scAddExpr: + return cast(this)->getType(); + case scUDivExpr: +@@ -2419,8 +2420,9 @@ const SCEV *ScalarEvolution::getAddExpr(SmallVectorImpl &Ops, + } + + // Limit recursion calls depth. +- if (Depth > MaxArithDepth) ++ if (Depth > MaxArithDepth) { + return getOrCreateAddExpr(Ops, Flags); ++ } + + // Okay, check to see if the same value occurs in the operand list more than + // once. If so, merge them together into an multiply expression. Since we +@@ -2761,16 +2763,27 @@ ScalarEvolution::getOrCreateAddExpr(ArrayRef Ops, + SCEV::NoWrapFlags Flags) { + FoldingSetNodeID ID; + ID.AddInteger(scAddExpr); +- for (const SCEV *Op : Ops) +- ID.AddPointer(Op); ++ bool HasNIPtr = false; ++ PointerType *NIPtrType = nullptr; ++ for (unsigned i = 0, e = Ops.size(); i != e; ++i) { ++ ID.AddPointer(Ops[i]); ++ if (Ops[i]->hasNonIntegralPointers()) { ++ HasNIPtr = true; ++ NIPtrType = cast(Ops[i]->getType()); ++ } ++ } + void *IP = nullptr; + SCEVAddExpr *S = + static_cast(UniqueSCEVs.FindNodeOrInsertPos(ID, IP)); + if (!S) { + const SCEV **O = SCEVAllocator.Allocate(Ops.size()); + std::uninitialized_copy(Ops.begin(), Ops.end(), O); +- S = new (SCEVAllocator) +- SCEVAddExpr(ID.Intern(SCEVAllocator), O, Ops.size()); ++ if (HasNIPtr) ++ S = new (SCEVAllocator) ++ SCEVAddNIExpr(ID.Intern(SCEVAllocator), O, Ops.size(), NIPtrType); ++ else ++ S = new (SCEVAllocator) ++ SCEVAddExpr(ID.Intern(SCEVAllocator), O, Ops.size()); + UniqueSCEVs.InsertNode(S, IP); + addToLoopUseLists(S); + } +@@ -2783,8 +2763,10 @@ ScalarEvolution::getOrCreateAddRecExpr(ArrayRef Ops, + const Loop *L, SCEV::NoWrapFlags Flags) { + FoldingSetNodeID ID; + ID.AddInteger(scAddRecExpr); +- for (unsigned i = 0, e = Ops.size(); i != e; ++i) ++ for (unsigned i = 0, e = Ops.size(); i != e; ++i) { ++ assert(i == 0 || !Ops[i]->hasNonIntegralPointers()); + ID.AddPointer(Ops[i]); ++ } + ID.AddPointer(L); + void *IP = nullptr; + SCEVAddRecExpr *S = +@@ -2798,6 +2813,7 @@ ScalarEvolution::getOrCreateAddRecExpr(ArrayRef Ops, + addToLoopUseLists(S); + } + S->setNoWrapFlags(Flags); ++ S->setHasNIPtr(Ops[0]->hasNonIntegralPointers()); + return S; + } + +@@ -2806,8 +2822,11 @@ ScalarEvolution::getOrCreateMulExpr(ArrayRef Ops, + SCEV::NoWrapFlags Flags) { + FoldingSetNodeID ID; + ID.AddInteger(scMulExpr); +- for (unsigned i = 0, e = Ops.size(); i != e; ++i) ++ bool HasNIPtr = false; ++ for (unsigned i = 0, e = Ops.size(); i != e; ++i) { ++ HasNIPtr |= Ops[i]->hasNonIntegralPointers(); + ID.AddPointer(Ops[i]); ++ } + void *IP = nullptr; + SCEVMulExpr *S = + static_cast(UniqueSCEVs.FindNodeOrInsertPos(ID, IP)); +@@ -2820,6 +2839,7 @@ ScalarEvolution::getOrCreateMulExpr(ArrayRef Ops, + addToLoopUseLists(S); + } + S->setNoWrapFlags(Flags); ++ S->setHasNIPtr(HasNIPtr); + return S; + } + +@@ -3631,8 +3591,11 @@ const SCEV *ScalarEvolution::getMinMaxExpr(unsigned Kind, + 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) SCEVMinMaxExpr( ++ SCEVMinMaxExpr *S = new (SCEVAllocator) SCEVMinMaxExpr( + ID.Intern(SCEVAllocator), static_cast(Kind), O, Ops.size()); ++ // For MinMaxExprs it's sufficient to see if the first Op has NI data, as the ++ // operands all need to be of the same type. ++ S->setHasNIPtr(Ops[0]->hasNonIntegralPointers()); + UniqueSCEVs.InsertNode(S, IP); + addToLoopUseLists(S); + return S; +@@ -3708,8 +3731,9 @@ const SCEV *ScalarEvolution::getUnknown(Value *V) { + "Stale SCEVUnknown in uniquing map!"); + return S; + } ++ bool ValueIsNIPtr = getDataLayout().isNonIntegralPointerType(V->getType()); + SCEV *S = new (SCEVAllocator) SCEVUnknown(ID.Intern(SCEVAllocator), V, this, +- FirstUnknown); ++ FirstUnknown, ValueIsNIPtr); + FirstUnknown = cast(S); + UniqueSCEVs.InsertNode(S, IP); + return S; +diff --git llvm/test/Transforms/LoopStrengthReduce/nonintegral.ll llvm/test/Transforms/LoopStrengthReduce/nonintegral.ll +index 5648e3aa74a..6936521f3a6 100644 +--- llvm/test/Transforms/LoopStrengthReduce/nonintegral.ll ++++ llvm/test/Transforms/LoopStrengthReduce/nonintegral.ll +@@ -2,7 +2,7 @@ + + ; Address Space 10 is non-integral. The optimizer is not allowed to use + ; ptrtoint/inttoptr instructions. Make sure that this doesn't happen +-target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128-ni:10:11:12" ++target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128-ni:10:11:12:13" + target triple = "x86_64-unknown-linux-gnu" + + define void @japi1__unsafe_getindex_65028(i64 addrspace(10)* %arg) { +@@ -43,3 +43,36 @@ if38: ; preds = %L119 + done: ; preds = %if38 + ret void + } ++ ++; This is a bugpoint-reduced regression test - It doesn't make too much sense by itself, ++; but creates the correct SCEV expressions to reproduce the issue. See ++; https://github.com/JuliaLang/julia/issues/31156 for the original bug report. ++define void @"japi1_permutedims!_4259"(i64 %a, i64 %b, i64 %c, i64 %d, i64 %e, i64 %f, i1 %g, i8 addrspace(13)* %base) #0 { ++; CHECK-NOT: inttoptr ++; CHECK-NOT: ptrtoint ++; CHECK: getelementptr i8, i8 addrspace(13)* {{.*}}, i64 {{.*}} ++top: ++ br label %L42.L46_crit_edge.us ++ ++L42.L46_crit_edge.us: ; preds = %L82.us.us.loopexit, %top ++ %value_phi11.us = phi i64 [ %a, %top ], [ %2, %L82.us.us.loopexit ] ++ %0 = sub i64 %value_phi11.us, %b ++ %1 = add i64 %0, %c ++ %spec.select = select i1 %g, i64 %d, i64 0 ++ br label %L62.us.us ++ ++L82.us.us.loopexit: ; preds = %L62.us.us ++ %2 = add i64 %e, %value_phi11.us ++ br label %L42.L46_crit_edge.us ++ ++L62.us.us: ; preds = %L62.us.us, %L42.L46_crit_edge.us ++ %value_phi21.us.us = phi i64 [ %6, %L62.us.us ], [ %spec.select, %L42.L46_crit_edge.us ] ++ %3 = add i64 %1, %value_phi21.us.us ++ %4 = getelementptr inbounds i8, i8 addrspace(13)* %base, i64 %3 ++ %5 = load i8, i8 addrspace(13)* %4, align 1 ++ %6 = add i64 %f, %value_phi21.us.us ++ br i1 %g, label %L82.us.us.loopexit, label %L62.us.us, !llvm.loop !1 ++} ++ ++!1 = distinct !{!1, !2} ++!2 = !{!"llvm.loop.isvectorized", i32 1} +-- +2.25.1 + diff --git a/deps/patches/llvm-9.0-D65174-limit-merge-stores.patch b/deps/patches/llvm-9.0-D65174-limit-merge-stores.patch new file mode 100644 index 0000000..6d6cfb4 --- /dev/null +++ b/deps/patches/llvm-9.0-D65174-limit-merge-stores.patch @@ -0,0 +1,116 @@ +commit f49c107f06c6a98d11a09d758f08554c78b9b933 +Author: Wei Mi +Date: Wed Jul 31 19:59:24 2019 +0000 + + [DAGCombine] Limit the number of times for the same store and root nodes + to bail out in store merging dependence check. + + We run into a case where dependence check in store merging bail out many times + for the same store and root nodes in a huge basicblock. That increases compile + time by almost 100x. The patch add a map to track how many times the bailing + out happen for the same store and root, and if it is over a limit, stop + considering the store with the same root as a merging candidate. + + Differential Revision: https://reviews.llvm.org/D65174 + + llvm-svn: 367472 + +diff --git a/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/lib/CodeGen/SelectionDAG/DAGCombiner.cpp +index bf62aa86509..2e5ba82af22 100644 +--- a/lib/CodeGen/SelectionDAG/DAGCombiner.cpp ++++ b/lib/CodeGen/SelectionDAG/DAGCombiner.cpp +@@ -120,6 +120,11 @@ static cl::opt TokenFactorInlineLimit( + "combiner-tokenfactor-inline-limit", cl::Hidden, cl::init(2048), + cl::desc("Limit the number of operands to inline for Token Factors")); + ++static cl::opt StoreMergeDependenceLimit( ++ "combiner-store-merge-dependence-limit", cl::Hidden, cl::init(10), ++ cl::desc("Limit the number of times for the same StoreNode and RootNode " ++ "to bail out in store merging dependence check")); ++ + namespace { + + class DAGCombiner { +@@ -157,6 +162,14 @@ namespace { + /// which have not yet been combined to the worklist. + SmallPtrSet CombinedNodes; + ++ /// Map from candidate StoreNode to the pair of RootNode and count. ++ /// The count is used to track how many times we have seen the StoreNode ++ /// with the same RootNode bail out in dependence check. If we have seen ++ /// the bail out for the same pair many times over a limit, we won't ++ /// consider the StoreNode with the same RootNode as store merging ++ /// candidate again. ++ DenseMap> StoreRootCountMap; ++ + // AA - Used for DAG load/store alias analysis. + AliasAnalysis *AA; + +@@ -241,6 +254,7 @@ namespace { + void removeFromWorklist(SDNode *N) { + CombinedNodes.erase(N); + PruningList.remove(N); ++ StoreRootCountMap.erase(N); + + auto It = WorklistMap.find(N); + if (It == WorklistMap.end()) +@@ -15423,6 +15437,18 @@ void DAGCombiner::getStoreMergeCandidates( + return (BasePtr.equalBaseIndex(Ptr, DAG, Offset)); + }; + ++ // Check if the pair of StoreNode and the RootNode already bail out many ++ // times which is over the limit in dependence check. ++ auto OverLimitInDependenceCheck = [&](SDNode *StoreNode, ++ SDNode *RootNode) -> bool { ++ auto RootCount = StoreRootCountMap.find(StoreNode); ++ if (RootCount != StoreRootCountMap.end() && ++ RootCount->second.first == RootNode && ++ RootCount->second.second > StoreMergeDependenceLimit) ++ return true; ++ return false; ++ }; ++ + // We looking for a root node which is an ancestor to all mergable + // stores. We search up through a load, to our root and then down + // through all children. For instance we will find Store{1,2,3} if +@@ -15452,7 +15478,8 @@ void DAGCombiner::getStoreMergeCandidates( + if (StoreSDNode *OtherST = dyn_cast(*I2)) { + BaseIndexOffset Ptr; + int64_t PtrDiff; +- if (CandidateMatch(OtherST, Ptr, PtrDiff)) ++ if (CandidateMatch(OtherST, Ptr, PtrDiff) && ++ !OverLimitInDependenceCheck(OtherST, RootNode)) + StoreNodes.push_back(MemOpLink(OtherST, PtrDiff)); + } + } else +@@ -15462,7 +15489,8 @@ void DAGCombiner::getStoreMergeCandidates( + if (StoreSDNode *OtherST = dyn_cast(*I)) { + BaseIndexOffset Ptr; + int64_t PtrDiff; +- if (CandidateMatch(OtherST, Ptr, PtrDiff)) ++ if (CandidateMatch(OtherST, Ptr, PtrDiff) && ++ !OverLimitInDependenceCheck(OtherST, RootNode)) + StoreNodes.push_back(MemOpLink(OtherST, PtrDiff)); + } + } +@@ -15520,8 +15548,19 @@ bool DAGCombiner::checkMergeStoreCandidatesForDependencies( + // Search through DAG. We can stop early if we find a store node. + for (unsigned i = 0; i < NumStores; ++i) + if (SDNode::hasPredecessorHelper(StoreNodes[i].MemNode, Visited, Worklist, +- Max)) ++ Max)) { ++ // If the searching bail out, record the StoreNode and RootNode in the ++ // StoreRootCountMap. If we have seen the pair many times over a limit, ++ // we won't add the StoreNode into StoreNodes set again. ++ if (Visited.size() >= Max) { ++ auto &RootCount = StoreRootCountMap[StoreNodes[i].MemNode]; ++ if (RootCount.first == RootNode) ++ RootCount.second++; ++ else ++ RootCount = {RootNode, 1}; ++ } + return false; ++ } + return true; + } + diff --git a/deps/patches/llvm-9.0-D78196.patch b/deps/patches/llvm-9.0-D78196.patch new file mode 100644 index 0000000..6ae23fd --- /dev/null +++ b/deps/patches/llvm-9.0-D78196.patch @@ -0,0 +1,15 @@ +diff --git a/lib/Target/PowerPC/MCTargetDesc/PPCMCTargetDesc.cpp b/lib/Target/PowerPC/MCTargetDesc/PPCMCTargetDesc.cpp +--- a/lib/Target/PowerPC/MCTargetDesc/PPCMCTargetDesc.cpp ++++ b/lib/Target/PowerPC/MCTargetDesc/PPCMCTargetDesc.cpp +@@ -210,6 +210,10 @@ + for (auto *Sym : UpdateOther) + if (Sym->isVariable()) + copyLocalEntry(Sym, Sym->getVariableValue()); ++ ++ // Clear the set of symbols that needs to be updated so the streamer can ++ // be reused without issues. ++ UpdateOther.clear(); + } + + private: + diff --git a/deps/patches/llvm-9.0-D85499.patch b/deps/patches/llvm-9.0-D85499.patch new file mode 100644 index 0000000..1be91fc --- /dev/null +++ b/deps/patches/llvm-9.0-D85499.patch @@ -0,0 +1,425 @@ +commit ac8729e23232d0fd3933b76093a40b7c65332aff +Author: Keno Fischer +Date: Fri Aug 7 00:31:43 2020 -0400 + + [X86] Canonicalize andnp for bitmask arithmetic + + We have a DAG combine that tries to fold (vselect cond, 0000..., X) -> (andnp cond, x). + However, it does so by attempting to create an i64 vector with the number + of elements obtained by truncating division by 64 from the bitwidth. This is + bad for mask vectors like v8i1, since that division is just zero. Besides, + we don't want i64 vectors anyway. The easy change is just to avoid changing + the VT, but this is slightly problematic because the canonical pattern for + `kandn` is `(and (vnot a) b)` rather than `(x86andnp a b)`, so this fails + to select. Rather than playing games here with having the mask vectors + use a different canonical representation, the bulk of this commit switches + the canonical ISD representation for `kandn` to `(x86andnp a b)` such + that all vector types may be handled equally here. To avoid regressing + other tests, we need to extend a few other folds to handle `x86andnp` in + addition to plain `and`. However, that should be generally a good + improvement, since x86andnp is already canonical for non-i1 vectors + prior to this commit, and said folds were just missing. + + When all is said and done, fixes the issue reported in + https://github.com/JuliaLang/julia/issues/36955. + + Differential Revision: https://reviews.llvm.org/D85499 + +diff --git a/lib/Target/X86/X86ISelDAGToDAG.cpp b/lib/Target/X86/X86ISelDAGToDAG.cpp +index 34ad589d205..eb21b0de89d 100644 +--- a/lib/Target/X86/X86ISelDAGToDAG.cpp ++++ b/lib/Target/X86/X86ISelDAGToDAG.cpp +@@ -503,7 +503,7 @@ namespace { + bool isMaskZeroExtended(SDNode *N) const; + bool tryShiftAmountMod(SDNode *N); + bool tryShrinkShlLogicImm(SDNode *N); +- bool tryVPTESTM(SDNode *Root, SDValue Setcc, SDValue Mask); ++ bool tryVPTESTM(SDNode *Root, SDValue Setcc, SDValue Mask, bool Invert); + + MachineSDNode *emitPCMPISTR(unsigned ROpc, unsigned MOpc, bool MayFoldLoad, + const SDLoc &dl, MVT VT, SDNode *Node); +@@ -2998,7 +2998,7 @@ bool X86DAGToDAGISel::foldLoadStoreIntoMemOperand(SDNode *Node) { + bool IsNegOne = isAllOnesConstant(StoredVal.getOperand(1)); + // ADD/SUB with 1/-1 and carry flag isn't used can use inc/dec. + if ((IsOne || IsNegOne) && hasNoCarryFlagUses(StoredVal.getValue(1))) { +- unsigned NewOpc = ++ unsigned NewOpc = + ((Opc == X86ISD::ADD) == IsOne) + ? SelectOpcode(X86::INC64m, X86::INC32m, X86::INC16m, X86::INC8m) + : SelectOpcode(X86::DEC64m, X86::DEC32m, X86::DEC16m, X86::DEC8m); +@@ -3999,8 +3999,8 @@ static unsigned getVPTESTMOpc(MVT TestVT, bool IsTestN, bool FoldedLoad, + + // Try to create VPTESTM instruction. If InMask is not null, it will be used + // to form a masked operation. +-bool X86DAGToDAGISel::tryVPTESTM(SDNode *Root, SDValue Setcc, +- SDValue InMask) { ++bool X86DAGToDAGISel::tryVPTESTM(SDNode *Root, SDValue Setcc, SDValue InMask, ++ bool Invert) { + assert(Subtarget->hasAVX512() && "Expected AVX512!"); + assert(Setcc.getSimpleValueType().getVectorElementType() == MVT::i1 && + "Unexpected VT!"); +@@ -4140,6 +4140,9 @@ bool X86DAGToDAGISel::tryVPTESTM(SDNode *Root, SDValue Setcc, + } + + bool IsTestN = CC == ISD::SETEQ; ++ if (Invert) ++ IsTestN = !IsTestN; ++ + unsigned Opc = getVPTESTMOpc(CmpVT, IsTestN, FoldedLoad, FoldedBCast, + IsMasked); + +@@ -4309,16 +4312,27 @@ void X86DAGToDAGISel::Select(SDNode *Node) { + return; + break; + ++ case X86ISD::ANDNP: ++ if (NVT.isVector() && NVT.getVectorElementType() == MVT::i1) { ++ SDValue N0 = Node->getOperand(0); ++ SDValue N1 = Node->getOperand(1); ++ // Try to form a masked VPTESTM ++ if (N0.getOpcode() == ISD::SETCC && N0.hasOneUse() && ++ tryVPTESTM(Node, N0, N1, true)) ++ return; ++ } ++ break; ++ + case ISD::AND: + if (NVT.isVector() && NVT.getVectorElementType() == MVT::i1) { + // Try to form a masked VPTESTM. Operands can be in either order. + SDValue N0 = Node->getOperand(0); + SDValue N1 = Node->getOperand(1); + if (N0.getOpcode() == ISD::SETCC && N0.hasOneUse() && +- tryVPTESTM(Node, N0, N1)) ++ tryVPTESTM(Node, N0, N1, false)) + return; + if (N1.getOpcode() == ISD::SETCC && N1.hasOneUse() && +- tryVPTESTM(Node, N1, N0)) ++ tryVPTESTM(Node, N1, N0, false)) + return; + } + +@@ -5000,7 +5014,7 @@ void X86DAGToDAGISel::Select(SDNode *Node) { + } + + case ISD::SETCC: { +- if (NVT.isVector() && tryVPTESTM(Node, SDValue(Node, 0), SDValue())) ++ if (NVT.isVector() && tryVPTESTM(Node, SDValue(Node, 0), SDValue(), false)) + return; + + break; +diff --git a/lib/Target/X86/X86ISelLowering.cpp b/lib/Target/X86/X86ISelLowering.cpp +index 920cdd7e625..6b9738074c7 100644 +--- a/lib/Target/X86/X86ISelLowering.cpp ++++ b/lib/Target/X86/X86ISelLowering.cpp +@@ -196,7 +196,7 @@ X86TargetLowering::X86TargetLowering(const X86TargetMachine &TM, + // Integer absolute. + if (Subtarget.hasCMov()) { + setOperationAction(ISD::ABS , MVT::i16 , Custom); +- setOperationAction(ISD::ABS , MVT::i32 , Custom); ++ setOperationAction(ISD::ABS , MVT::i32 , Custom); + } + setOperationAction(ISD::ABS , MVT::i64 , Custom); + +@@ -26053,7 +26053,7 @@ X86TargetLowering::lowerIdempotentRMWIntoFencedLoad(AtomicRMWInst *AI) const { + + // If this is a canonical idempotent atomicrmw w/no uses, we have a better + // lowering available in lowerAtomicArith. +- // TODO: push more cases through this path. ++ // TODO: push more cases through this path. + if (auto *C = dyn_cast(AI->getValOperand())) + if (AI->getOperation() == AtomicRMWInst::Or && C->isZero() && + AI->use_empty()) +@@ -26111,7 +26111,7 @@ X86TargetLowering::lowerIdempotentRMWIntoFencedLoad(AtomicRMWInst *AI) const { + /// Emit a locked operation on a stack location which does not change any + /// memory location, but does involve a lock prefix. Location is chosen to be + /// a) very likely accessed only by a single thread to minimize cache traffic, +-/// and b) definitely dereferenceable. Returns the new Chain result. ++/// and b) definitely dereferenceable. Returns the new Chain result. + static SDValue emitLockedStackOp(SelectionDAG &DAG, + const X86Subtarget &Subtarget, + SDValue Chain, SDLoc DL) { +@@ -26120,22 +26120,22 @@ static SDValue emitLockedStackOp(SelectionDAG &DAG, + // operations issued by the current processor. As such, the location + // referenced is not relevant for the ordering properties of the instruction. + // See: Intel® 64 and IA-32 ArchitecturesSoftware Developer’s Manual, +- // 8.2.3.9 Loads and Stores Are Not Reordered with Locked Instructions ++ // 8.2.3.9 Loads and Stores Are Not Reordered with Locked Instructions + // 2) Using an immediate operand appears to be the best encoding choice + // here since it doesn't require an extra register. + // 3) OR appears to be very slightly faster than ADD. (Though, the difference + // is small enough it might just be measurement noise.) + // 4) When choosing offsets, there are several contributing factors: + // a) If there's no redzone, we default to TOS. (We could allocate a cache +- // line aligned stack object to improve this case.) ++ // line aligned stack object to improve this case.) + // b) To minimize our chances of introducing a false dependence, we prefer +- // to offset the stack usage from TOS slightly. ++ // to offset the stack usage from TOS slightly. + // c) To minimize concerns about cross thread stack usage - in particular, + // the idiomatic MyThreadPool.run([&StackVars]() {...}) pattern which + // captures state in the TOS frame and accesses it from many threads - + // we want to use an offset such that the offset is in a distinct cache + // line from the TOS frame. +- // ++ // + // For a general discussion of the tradeoffs and benchmark results, see: + // https://shipilev.net/blog/2014/on-the-fence-with-dependencies/ + +@@ -26188,7 +26188,7 @@ static SDValue LowerATOMIC_FENCE(SDValue Op, const X86Subtarget &Subtarget, + if (Subtarget.hasMFence()) + return DAG.getNode(X86ISD::MFENCE, dl, MVT::Other, Op.getOperand(0)); + +- SDValue Chain = Op.getOperand(0); ++ SDValue Chain = Op.getOperand(0); + return emitLockedStackOp(DAG, Subtarget, Chain, dl); + } + +@@ -26677,12 +26677,12 @@ static SDValue lowerAtomicArith(SDValue N, SelectionDAG &DAG, + // seq_cst which isn't SingleThread, everything just needs to be preserved + // during codegen and then dropped. Note that we expect (but don't assume), + // that orderings other than seq_cst and acq_rel have been canonicalized to +- // a store or load. ++ // a store or load. + if (AN->getOrdering() == AtomicOrdering::SequentiallyConsistent && + AN->getSyncScopeID() == SyncScope::System) { + // Prefer a locked operation against a stack location to minimize cache + // traffic. This assumes that stack locations are very likely to be +- // accessed only by the owning thread. ++ // accessed only by the owning thread. + SDValue NewChain = emitLockedStackOp(DAG, Subtarget, Chain, DL); + assert(!N->hasAnyUseOfValue(0)); + // NOTE: The getUNDEF is needed to give something for the unused result 0. +@@ -35620,7 +35620,7 @@ static SDValue scalarizeExtEltFP(SDNode *ExtElt, SelectionDAG &DAG) { + } + + // TODO: This switch could include FNEG and the x86-specific FP logic ops +- // (FAND, FANDN, FOR, FXOR). But that may require enhancements to avoid ++ // (FAND, FANDN, FOR, FXOR). But that may require enhancements to avoid + // missed load folding and fma+fneg combining. + switch (Vec.getOpcode()) { + case ISD::FMA: // Begin 3 operands +@@ -35935,10 +35935,8 @@ combineVSelectWithAllOnesOrZeros(SDNode *N, SelectionDAG &DAG, + + // vselect Cond, 000..., X -> andn Cond, X + if (TValIsAllZeros) { +- MVT AndNVT = MVT::getVectorVT(MVT::i64, CondVT.getSizeInBits() / 64); +- SDValue CastCond = DAG.getBitcast(AndNVT, Cond); +- SDValue CastRHS = DAG.getBitcast(AndNVT, RHS); +- SDValue AndN = DAG.getNode(X86ISD::ANDNP, DL, AndNVT, CastCond, CastRHS); ++ SDValue CastRHS = DAG.getBitcast(CondVT, RHS); ++ SDValue AndN = DAG.getNode(X86ISD::ANDNP, DL, CondVT, Cond, CastRHS); + return DAG.getBitcast(VT, AndN); + } + +@@ -38147,12 +38145,17 @@ static SDValue IsNOT(SDValue V, SelectionDAG &DAG) { + return SDValue(); + } + +-/// Try to fold: (and (xor X, -1), Y) -> (andnp X, Y). +-static SDValue combineANDXORWithAllOnesIntoANDNP(SDNode *N, SelectionDAG &DAG) { ++ ++/// Try to fold: ++/// (and (not X), Y) -> (andnp X, Y) ++/// (and (xor X, -1), Y) -> (andnp X, Y). ++static SDValue combineANDXORWithAllOnesIntoANDNP(SDNode *N, SelectionDAG &DAG, ++ const X86Subtarget &Subtarget) { + assert(N->getOpcode() == ISD::AND); + + MVT VT = N->getSimpleValueType(0); +- if (!VT.is128BitVector() && !VT.is256BitVector() && !VT.is512BitVector()) ++ if (!VT.is128BitVector() && !VT.is256BitVector() && !VT.is512BitVector() && ++ !(VT.isVector() && VT.getVectorElementType() == MVT::i1 && Subtarget.hasAVX512())) + return SDValue(); + + SDValue X, Y; +@@ -38558,7 +38561,7 @@ static SDValue combineAnd(SDNode *N, SelectionDAG &DAG, + if (SDValue FPLogic = convertIntLogicToFPLogic(N, DAG, Subtarget)) + return FPLogic; + +- if (SDValue R = combineANDXORWithAllOnesIntoANDNP(N, DAG)) ++ if (SDValue R = combineANDXORWithAllOnesIntoANDNP(N, DAG, Subtarget)) + return R; + + if (SDValue ShiftRight = combineAndMaskToShift(N, DAG, Subtarget)) +diff --git a/lib/Target/X86/X86InstrAVX512.td b/lib/Target/X86/X86InstrAVX512.td +index 54eddeacaa1..91027fa903f 100644 +--- a/lib/Target/X86/X86InstrAVX512.td ++++ b/lib/Target/X86/X86InstrAVX512.td +@@ -2978,7 +2978,6 @@ multiclass avx512_mask_binop_all opc, string OpcodeStr, + def andn : PatFrag<(ops node:$i0, node:$i1), (and (not node:$i0), node:$i1)>; + def xnor : PatFrag<(ops node:$i0, node:$i1), (not (xor node:$i0, node:$i1))>; + // These nodes use 'vnot' instead of 'not' to support vectors. +-def vandn : PatFrag<(ops node:$i0, node:$i1), (and (vnot node:$i0), node:$i1)>; + def vxnor : PatFrag<(ops node:$i0, node:$i1), (vnot (xor node:$i0, node:$i1))>; + + // TODO - do we need a X86SchedWriteWidths::KMASK type? +@@ -2986,7 +2985,7 @@ defm KAND : avx512_mask_binop_all<0x41, "kand", and, SchedWriteVecLogic.XM + defm KOR : avx512_mask_binop_all<0x45, "kor", or, SchedWriteVecLogic.XMM, 1>; + defm KXNOR : avx512_mask_binop_all<0x46, "kxnor", vxnor, SchedWriteVecLogic.XMM, 1>; + defm KXOR : avx512_mask_binop_all<0x47, "kxor", xor, SchedWriteVecLogic.XMM, 1>; +-defm KANDN : avx512_mask_binop_all<0x42, "kandn", vandn, SchedWriteVecLogic.XMM, 0>; ++defm KANDN : avx512_mask_binop_all<0x42, "kandn", X86andnp, SchedWriteVecLogic.XMM, 0>; + defm KADD : avx512_mask_binop_all<0x4A, "kadd", X86kadd, SchedWriteVecLogic.XMM, 1, HasDQI>; + + multiclass avx512_binop_pat; +-defm : avx512_binop_pat; ++defm : avx512_binop_pat; + defm : avx512_binop_pat; + defm : avx512_binop_pat; + defm : avx512_binop_pat; +@@ -11570,7 +11569,7 @@ multiclass avx512_fixupimm_scalar opc, string OpcodeStr, + } + + multiclass avx512_fixupimm_packed_all { + let Predicates = [HasAVX512] in + defm Z : avx512_fixupimm_packed_sae<0x54, "vfixupimm", sched.ZMM, +@@ -11687,7 +11686,7 @@ multiclass AVX512_scalar_math_fp_patterns("V"#OpcPrefix#Zrr_Intkz) ++ (!cast("V"#OpcPrefix#Zrr_Intkz) + VK1WM:$mask, _.VT:$src1, + (_.VT (COPY_TO_REGCLASS _.FRC:$src2, VR128X)))>; + def : Pat<(MoveNode (_.VT VR128X:$src1), +diff --git a/test/CodeGen/X86/avx512-select.ll b/test/CodeGen/X86/avx512-select.ll +index 1ed7b408baf..64320d63eac 100644 +--- a/test/CodeGen/X86/avx512-select.ll ++++ b/test/CodeGen/X86/avx512-select.ll +@@ -595,3 +595,74 @@ define <16 x i64> @narrowExtractedVectorSelect_crash(<16 x i64> %arg, <16 x i16> + %tmp3 = zext <16 x i16> %tmp2 to <16 x i64> + ret <16 x i64> %tmp3 + } ++ ++; Regression test from https://github.com/JuliaLang/julia/issues/36955 ++define i8 @julia_issue36955(<8 x i1> %mask, <8 x double> %a) { ++; X86-AVX512F-LABEL: julia_issue36955: ++; X86-AVX512F: # %bb.0: ++; X86-AVX512F-NEXT: vpmovsxwq %xmm0, %zmm0 ++; X86-AVX512F-NEXT: vpsllq $63, %zmm0, %zmm0 ++; X86-AVX512F-NEXT: vptestmq %zmm0, %zmm0, %k0 ++; X86-AVX512F-NEXT: vpxor %xmm0, %xmm0, %xmm0 ++; X86-AVX512F-NEXT: vcmpnlepd %zmm0, %zmm1, %k1 ++; X86-AVX512F-NEXT: kandnw %k0, %k1, %k0 ++; X86-AVX512F-NEXT: kandw %k1, %k0, %k0 ++; X86-AVX512F-NEXT: knotw %k1, %k1 ++; X86-AVX512F-NEXT: korw %k1, %k0, %k0 ++; X86-AVX512F-NEXT: kmovw %k0, %eax ++; X86-AVX512F-NEXT: # kill: def $al killed $al killed $eax ++; X86-AVX512F-NEXT: vzeroupper ++; X86-AVX512F-NEXT: retl ++; ++; X64-AVX512F-LABEL: julia_issue36955: ++; X64-AVX512F: # %bb.0: ++; X64-AVX512F-NEXT: vpmovsxwq %xmm0, %zmm0 ++; X64-AVX512F-NEXT: vpsllq $63, %zmm0, %zmm0 ++; X64-AVX512F-NEXT: vptestmq %zmm0, %zmm0, %k0 ++; X64-AVX512F-NEXT: vpxor %xmm0, %xmm0, %xmm0 ++; X64-AVX512F-NEXT: vcmpnlepd %zmm0, %zmm1, %k1 ++; X64-AVX512F-NEXT: kandnw %k0, %k1, %k0 ++; X64-AVX512F-NEXT: kandw %k1, %k0, %k0 ++; X64-AVX512F-NEXT: knotw %k1, %k1 ++; X64-AVX512F-NEXT: korw %k1, %k0, %k0 ++; X64-AVX512F-NEXT: kmovw %k0, %eax ++; X64-AVX512F-NEXT: # kill: def $al killed $al killed $eax ++; X64-AVX512F-NEXT: vzeroupper ++; X64-AVX512F-NEXT: retq ++; ++; X86-AVX512BW-LABEL: julia_issue36955: ++; X86-AVX512BW: # %bb.0: ++; X86-AVX512BW-NEXT: vpsllw $15, %xmm0, %xmm0 ++; X86-AVX512BW-NEXT: vpmovw2m %zmm0, %k0 ++; X86-AVX512BW-NEXT: vpxor %xmm0, %xmm0, %xmm0 ++; X86-AVX512BW-NEXT: vcmpnlepd %zmm0, %zmm1, %k1 ++; X86-AVX512BW-NEXT: kandnw %k0, %k1, %k0 ++; X86-AVX512BW-NEXT: kandw %k1, %k0, %k0 ++; X86-AVX512BW-NEXT: knotw %k1, %k1 ++; X86-AVX512BW-NEXT: korw %k1, %k0, %k0 ++; X86-AVX512BW-NEXT: kmovd %k0, %eax ++; X86-AVX512BW-NEXT: # kill: def $al killed $al killed $eax ++; X86-AVX512BW-NEXT: vzeroupper ++; X86-AVX512BW-NEXT: retl ++; ++; X64-AVX512BW-LABEL: julia_issue36955: ++; X64-AVX512BW: # %bb.0: ++; X64-AVX512BW-NEXT: vpsllw $15, %xmm0, %xmm0 ++; X64-AVX512BW-NEXT: vpmovw2m %zmm0, %k0 ++; X64-AVX512BW-NEXT: vpxor %xmm0, %xmm0, %xmm0 ++; X64-AVX512BW-NEXT: vcmpnlepd %zmm0, %zmm1, %k1 ++; X64-AVX512BW-NEXT: kandnw %k0, %k1, %k0 ++; X64-AVX512BW-NEXT: kandw %k1, %k0, %k0 ++; X64-AVX512BW-NEXT: knotw %k1, %k1 ++; X64-AVX512BW-NEXT: korw %k1, %k0, %k0 ++; X64-AVX512BW-NEXT: kmovd %k0, %eax ++; X64-AVX512BW-NEXT: # kill: def $al killed $al killed $eax ++; X64-AVX512BW-NEXT: vzeroupper ++; X64-AVX512BW-NEXT: retq ++ %fcmp = fcmp ugt <8 x double> %a, zeroinitializer ++ %xor = xor <8 x i1> %fcmp, ++ %select1 = select <8 x i1> %fcmp, <8 x i1> zeroinitializer, <8 x i1> %mask ++ %select2 = select <8 x i1> %xor, <8 x i1> , <8 x i1> %select1 ++ %ret = bitcast <8 x i1> %select2 to i8 ++ ret i8 %ret ++} +diff --git a/test/CodeGen/X86/combine-bitselect.ll b/test/CodeGen/X86/combine-bitselect.ll +index 8cb6a4dca09..3c08a871c86 100644 +--- a/test/CodeGen/X86/combine-bitselect.ll ++++ b/test/CodeGen/X86/combine-bitselect.ll +@@ -616,13 +616,13 @@ define <4 x i1> @bitselect_v4i1_loop(<4 x i32> %a0, <4 x i32> %a1) { + ; AVX512F: # %bb.0: # %bb + ; AVX512F-NEXT: # kill: def $xmm1 killed $xmm1 def $zmm1 + ; AVX512F-NEXT: # kill: def $xmm0 killed $xmm0 def $zmm0 +-; AVX512F-NEXT: vpbroadcastd {{.*#+}} xmm2 = [12,12,12,12] +-; AVX512F-NEXT: vpcmpeqd %zmm2, %zmm1, %k1 ++; AVX512F-NEXT: vptestmd %zmm0, %zmm0, %k1 ++; AVX512F-NEXT: vpbroadcastd {{.*#+}} xmm0 = [12,12,12,12] + ; AVX512F-NEXT: vpbroadcastd {{.*#+}} xmm2 = [15,15,15,15] +-; AVX512F-NEXT: vpcmpeqd %zmm2, %zmm1, %k2 +-; AVX512F-NEXT: vptestnmd %zmm0, %zmm0, %k0 {%k2} +-; AVX512F-NEXT: vptestmd %zmm0, %zmm0, %k1 {%k1} +-; AVX512F-NEXT: korw %k0, %k1, %k1 ++; AVX512F-NEXT: vpcmpeqd %zmm2, %zmm1, %k0 ++; AVX512F-NEXT: vpcmpeqd %zmm0, %zmm1, %k2 {%k1} ++; AVX512F-NEXT: kandnw %k0, %k1, %k0 ++; AVX512F-NEXT: korw %k0, %k2, %k1 + ; AVX512F-NEXT: vpternlogd $255, %zmm0, %zmm0, %zmm0 {%k1} {z} + ; AVX512F-NEXT: # kill: def $xmm0 killed $xmm0 killed $zmm0 + ; AVX512F-NEXT: vzeroupper +diff --git a/test/CodeGen/X86/vec_ssubo.ll b/test/CodeGen/X86/vec_ssubo.ll +index 515dc5c5aa2..dfb1e7c4dee 100644 +--- a/test/CodeGen/X86/vec_ssubo.ll ++++ b/test/CodeGen/X86/vec_ssubo.ll +@@ -1640,7 +1640,7 @@ define <4 x i32> @ssubo_v4i1(<4 x i1> %a0, <4 x i1> %a1, <4 x i1>* %p2) nounwind + ; AVX512-NEXT: vptestmd %xmm1, %xmm1, %k0 + ; AVX512-NEXT: vpslld $31, %xmm0, %xmm0 + ; AVX512-NEXT: vptestmd %xmm0, %xmm0, %k1 +-; AVX512-NEXT: vptestnmd %xmm1, %xmm1, %k2 {%k1} ++; AVX512-NEXT: kandnw %k1, %k0, %k2 + ; AVX512-NEXT: kxorw %k0, %k1, %k0 + ; AVX512-NEXT: kxorw %k2, %k0, %k1 + ; AVX512-NEXT: vpcmpeqd %xmm0, %xmm0, %xmm0 +diff --git a/test/CodeGen/X86/vec_usubo.ll b/test/CodeGen/X86/vec_usubo.ll +index c5a7b19cf14..367c491d25a 100644 +--- a/test/CodeGen/X86/vec_usubo.ll ++++ b/test/CodeGen/X86/vec_usubo.ll +@@ -1244,10 +1244,10 @@ define <4 x i32> @usubo_v4i1(<4 x i1> %a0, <4 x i1> %a1, <4 x i1>* %p2) nounwind + ; AVX512: # %bb.0: + ; AVX512-NEXT: vpslld $31, %xmm0, %xmm0 + ; AVX512-NEXT: vptestmd %xmm0, %xmm0, %k0 +-; AVX512-NEXT: vpslld $31, %xmm1, %xmm1 +-; AVX512-NEXT: vptestmd %xmm1, %xmm1, %k1 ++; AVX512-NEXT: vpslld $31, %xmm1, %xmm0 ++; AVX512-NEXT: vptestmd %xmm0, %xmm0, %k1 + ; AVX512-NEXT: kxorw %k1, %k0, %k1 +-; AVX512-NEXT: vptestnmd %xmm0, %xmm0, %k2 {%k1} ++; AVX512-NEXT: kandnw %k1, %k0, %k2 + ; AVX512-NEXT: vpcmpeqd %xmm0, %xmm0, %xmm0 + ; AVX512-NEXT: vmovdqa32 %xmm0, %xmm0 {%k2} {z} + ; AVX512-NEXT: kmovd %k1, %eax 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-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-D75072-SCEV-add-type.patch b/deps/patches/llvm-D75072-SCEV-add-type.patch new file mode 100644 index 0000000..9a9e801 --- /dev/null +++ b/deps/patches/llvm-D75072-SCEV-add-type.patch @@ -0,0 +1,415 @@ +commit a55a3ab4dc5c66c153b2988fc4fa46b39bfc92fc +Author: Keno Fischer +Date: Mon Feb 24 14:18:22 2020 -0500 + + [SCEV] Record NI types in add exprs + + Summary: + This fixes a case where loop-reduce introduces ptrtoint/inttoptr for + non-integral address space pointers. Over the past several years, we + have gradually improved the SCEVExpander to actually do something + sensible for non-integral pointer types. However, that obviously + relies on the expander knowing what the type of the SCEV expression is. + That is usually the case, but there is one important case where it's + not: The type of an add expression is just the type of the last operand, + so if the non-integral pointer is not the last operand, later uses of + that SCEV may not realize that the given add expression contains + non-integral pointers and may try to expand it as integers. + + One interesting observation is that we do get away with this scheme in + shockingly many cases. The reason for this is that SCEV expressions + often have an `scUnknown` pointer base, which our sort order on the + operands of add expressions sort behind basically everything else, + so it usually ends up as the last operand. + + One situation where this fails is included as a test case. This test + case was bugpoint-reduced from the issue reported at + https://github.com/JuliaLang/julia/issues/31156. What happens here + is that the pointer base is an scAddRec from an outer loop, plus an + scUnknown integer offset. By our sort order, the scUnknown gets sorted + after the scAddRec pointer base, thus making an add expression of these + two operands have integer type. This then confuses the expander, into + attempting to expand the whole thing as integers, which will obviously + fail when reaching the non-integral pointer. + + I considered a few options to solve this, but here's what I ended up + settling on: The AddExpr class gains a new subclass that explicitly + stores the type of the expression. This subclass is used whenever one + of the operands is a non-integral pointer. To reduce the impact for the + regular case (where the SCEV expression contains no non-integral + pointers), a bit flag is kept in each flag expression to indicate + whether it is of non-integral pointer type (this should give the same + answer as asking if getType() is non-integral, but performing that + query may involve a pointer chase and requires the DataLayout). For + add expressions that flag is also used to indicate whether we're using + the subclass or not. This is slightly inefficient, because it uses + the subclass even in the (not uncommon) case where the last operand + does actually accurately reflect the non-integral pointer type. However, + it didn't seem worth the extra flag bit and complexity to do this + micro-optimization. + + I had hoped that we could additionally restrict mul exprs from + containing any non-integral pointers, and also require add exprs to + only have one operand containg such pointers (but not more), but this + turned out not to work. The reason for this is that SCEV wants to + form differences between pointers, which it represents as `A + B*-1`, + so we need to allow both multiplication by `-1` and addition with + multiple non-integral pointer arguments. I'm not super happy with + that situation, but I think it exposes a more general problem with + non-integral pointers in LLVM. We don't actually have a way to express + the difference between two non-integral pointers at the IR level. + In theory this is a problem for SCEV, because it means that we can't + materialize such SCEV expression. However, in practice, these + expressions generally have the same base pointer, so SCEV will + appropriately simplify them to just the integer components. + Nevertheless it is a bit unsatisfying. Perhaps we could have an + intrinsic that takes the byte difference between two pointers to the + same allocated object (in the same sense as is used in getelementptr), + which should be a sensible operation even for non-integral pointers. + However, given the practical considerations above, that's a project + for another time. For now, simply allowing the existing pointer-diff + pattern for non-integral pointers seems to work ok. + + Reviewers: sanjoy, reames, vtjnash, vchuravy + + Subscribers: hiraditya, javed.absar, llvm-commits + + Tags: #llvm + + Differential Revision: https://reviews.llvm.org/D75072 + +diff --git llvm/include/llvm/Analysis/ScalarEvolution.h llvm/include/llvm/Analysis/ScalarEvolution.h +index 0bd98ef37e7..317bdeac3f0 100644 +--- llvm/include/llvm/Analysis/ScalarEvolution.h ++++ llvm/include/llvm/Analysis/ScalarEvolution.h +@@ -118,6 +118,19 @@ public: + NoWrapMask = (1 << 3) - 1 + }; + ++ /// HasNonIntegralPointerFlag are bitfield indices into SubclassData. ++ /// ++ /// When constructing SCEV expressions for LLVM expressions with non-integral ++ /// pointer types, some additional processing is required to ensure that we ++ /// don't introduce any illegal transformations. However, non-integral pointer ++ /// types are a very rarely used feature, so we want to make sure to only do ++ /// such processing if they are actually used. To ensure minimal performance ++ /// impact, we memoize that fact in using these flags. ++ enum HasNonIntegralPointerFlag { ++ FlagNoNIPointers = 0, ++ FlagHasNIPointers = (1 << 3) ++ }; ++ + explicit SCEV(const FoldingSetNodeIDRef ID, unsigned SCEVTy, + unsigned short ExpressionSize) + : FastID(ID), SCEVType(SCEVTy), ExpressionSize(ExpressionSize) {} +@@ -154,6 +167,10 @@ public: + return ExpressionSize; + } + ++ bool hasNonIntegralPointers() const { ++ return SubclassData & FlagHasNIPointers; ++ } ++ + /// Print out the internal representation of this scalar to the specified + /// stream. This should really only be used for debugging purposes. + void print(raw_ostream &OS) const; +@@ -747,7 +764,7 @@ public: + BasicBlock *ExitingBlock); + + /// Return the number of times the backedge executes before the given exit +- /// would be taken; if not exactly computable, return SCEVCouldNotCompute. ++ /// would be taken; if not exactly computable, return SCEVCouldNotCompute. + /// For a single exit loop, this value is equivelent to the result of + /// getBackedgeTakenCount. The loop is guaranteed to exit (via *some* exit) + /// before the backedge is executed (ExitCount + 1) times. Note that there +diff --git llvm/include/llvm/Analysis/ScalarEvolutionExpressions.h llvm/include/llvm/Analysis/ScalarEvolutionExpressions.h +index d008af7b7e6..39ab35a8b8c 100644 +--- llvm/include/llvm/Analysis/ScalarEvolutionExpressions.h ++++ llvm/include/llvm/Analysis/ScalarEvolutionExpressions.h +@@ -188,6 +188,13 @@ class Type; + return getNoWrapFlags(FlagNW) != FlagAnyWrap; + } + ++ void setHasNIPtr(bool HasNIPtr) { ++ if (HasNIPtr) ++ SubclassData |= FlagHasNIPointers; ++ else ++ SubclassData &= ~FlagHasNIPointers; ++ } ++ + /// Methods for support type inquiry through isa, cast, and dyn_cast: + static bool classof(const SCEV *S) { + return S->getSCEVType() == scAddExpr || S->getSCEVType() == scMulExpr || +@@ -222,24 +229,54 @@ class Type; + class SCEVAddExpr : public SCEVCommutativeExpr { + friend class ScalarEvolution; + ++ protected: + SCEVAddExpr(const FoldingSetNodeIDRef ID, + const SCEV *const *O, size_t N) + : SCEVCommutativeExpr(ID, scAddExpr, O, N) {} + + public: +- Type *getType() const { +- // Use the type of the last operand, which is likely to be a pointer +- // type, if there is one. This doesn't usually matter, but it can help +- // reduce casts when the expressions are expanded. +- return getOperand(getNumOperands() - 1)->getType(); ++ /// Returns the type of the add expression, by looking either at the last ++ /// operand or deferring to the SCEVAddNIExpr subclass for non-integral ++ /// pointers. ++ Type *getType() const; ++ ++ /// Methods for support type inquiry through isa, cast, and dyn_cast: ++ static bool classof(const SCEV *S) { return S->getSCEVType() == scAddExpr; } ++ }; ++ ++ /// This node represents an addition of some number of SCEVs, one which ++ /// is a non-integral pointer type, requiring us to know the type exactly for ++ /// correctness. ++ class SCEVAddNIExpr : public SCEVAddExpr { ++ friend class ScalarEvolution; ++ PointerType *NIType; ++ ++ SCEVAddNIExpr(const FoldingSetNodeIDRef ID, const SCEV *const *O, size_t N, ++ PointerType *NIType) ++ : SCEVAddExpr(ID, O, N), NIType(NIType) { ++ SubclassData |= FlagHasNIPointers; + } + ++ public: ++ Type *getType() const { return NIType; } ++ + /// Methods for support type inquiry through isa, cast, and dyn_cast: + static bool classof(const SCEV *S) { +- return S->getSCEVType() == scAddExpr; ++ return S->getSCEVType() == scAddExpr && S->hasNonIntegralPointers(); + } + }; + ++ inline Type *SCEVAddExpr::getType() const { ++ // In general, use the type of the last operand, which is likely to be a ++ // pointer type, if there is one. This doesn't usually matter, but it can ++ // help reduce casts when the expressions are expanded. In the (unusual) ++ // case that we're working with non-integral pointers, we have a subclass ++ // that stores that type explicitly. ++ if (hasNonIntegralPointers()) ++ return cast(this)->getType(); ++ return getOperand(getNumOperands() - 1)->getType(); ++ } ++ + /// This node represents multiplication of some number of SCEVs. + class SCEVMulExpr : public SCEVCommutativeExpr { + friend class ScalarEvolution; +@@ -249,6 +286,18 @@ class Type; + : SCEVCommutativeExpr(ID, scMulExpr, O, N) {} + + public: ++ Type *getType() const { ++ // In general, we can't form SCEVMulExprs with non-integral pointer types, ++ // but for the moment we need to allow a special case: Multiplying by ++ // -1 to be able express the difference between two pointers. In order ++ // to maintain the invariant that SCEVs with the NI flag set should have ++ // a type corresponding to the contained NI ptr, we need to return the ++ // type of the pointer here. ++ if (hasNonIntegralPointers()) ++ return getOperand(getNumOperands() - 1)->getType(); ++ return SCEVCommutativeExpr::getType(); ++ } ++ + /// Methods for support type inquiry through isa, cast, and dyn_cast: + static bool classof(const SCEV *S) { + return S->getSCEVType() == scMulExpr; +@@ -475,9 +524,12 @@ class Type; + /// instances owned by a ScalarEvolution. + SCEVUnknown *Next; + +- SCEVUnknown(const FoldingSetNodeIDRef ID, Value *V, +- ScalarEvolution *se, SCEVUnknown *next) : +- SCEV(ID, scUnknown, 1), CallbackVH(V), SE(se), Next(next) {} ++ SCEVUnknown(const FoldingSetNodeIDRef ID, Value *V, ScalarEvolution *se, ++ SCEVUnknown *next, bool ValueIsNIPtr) ++ : SCEV(ID, scUnknown, 1), CallbackVH(V), SE(se), Next(next) { ++ if (ValueIsNIPtr) ++ SubclassData |= FlagHasNIPointers; ++ } + + // Implement CallbackVH. + void deleted() override; +diff --git llvm/lib/Analysis/ScalarEvolution.cpp llvm/lib/Analysis/ScalarEvolution.cpp +index bc2cfd6fcc4..2f8eb665c5d 100644 +--- llvm/lib/Analysis/ScalarEvolution.cpp ++++ llvm/lib/Analysis/ScalarEvolution.cpp +@@ -358,12 +358,13 @@ Type *SCEV::getType() const { + case scSignExtend: + return cast(this)->getType(); + case scAddRecExpr: +- case scMulExpr: + case scUMaxExpr: + case scSMaxExpr: + case scUMinExpr: + case scSMinExpr: + return cast(this)->getType(); ++ case scMulExpr: ++ return cast(this)->getType(); + case scAddExpr: + return cast(this)->getType(); + case scUDivExpr: +@@ -2441,8 +2442,9 @@ const SCEV *ScalarEvolution::getAddExpr(SmallVectorImpl &Ops, + } + + // Limit recursion calls depth. +- if (Depth > MaxArithDepth || hasHugeExpression(Ops)) ++ if (Depth > MaxArithDepth || hasHugeExpression(Ops)) { + return getOrCreateAddExpr(Ops, Flags); ++ } + + // Okay, check to see if the same value occurs in the operand list more than + // once. If so, merge them together into an multiply expression. Since we +@@ -2783,16 +2785,27 @@ ScalarEvolution::getOrCreateAddExpr(ArrayRef Ops, + SCEV::NoWrapFlags Flags) { + FoldingSetNodeID ID; + ID.AddInteger(scAddExpr); +- for (const SCEV *Op : Ops) +- ID.AddPointer(Op); ++ bool HasNIPtr = false; ++ PointerType *NIPtrType = nullptr; ++ for (unsigned i = 0, e = Ops.size(); i != e; ++i) { ++ ID.AddPointer(Ops[i]); ++ if (Ops[i]->hasNonIntegralPointers()) { ++ HasNIPtr = true; ++ NIPtrType = cast(Ops[i]->getType()); ++ } ++ } + void *IP = nullptr; + SCEVAddExpr *S = + static_cast(UniqueSCEVs.FindNodeOrInsertPos(ID, IP)); + if (!S) { + const SCEV **O = SCEVAllocator.Allocate(Ops.size()); + std::uninitialized_copy(Ops.begin(), Ops.end(), O); +- S = new (SCEVAllocator) +- SCEVAddExpr(ID.Intern(SCEVAllocator), O, Ops.size()); ++ if (HasNIPtr) ++ S = new (SCEVAllocator) ++ SCEVAddNIExpr(ID.Intern(SCEVAllocator), O, Ops.size(), NIPtrType); ++ else ++ S = new (SCEVAllocator) ++ SCEVAddExpr(ID.Intern(SCEVAllocator), O, Ops.size()); + UniqueSCEVs.InsertNode(S, IP); + addToLoopUseLists(S); + } +@@ -2805,8 +2818,10 @@ ScalarEvolution::getOrCreateAddRecExpr(ArrayRef Ops, + const Loop *L, SCEV::NoWrapFlags Flags) { + FoldingSetNodeID ID; + ID.AddInteger(scAddRecExpr); +- for (unsigned i = 0, e = Ops.size(); i != e; ++i) ++ for (unsigned i = 0, e = Ops.size(); i != e; ++i) { ++ assert(i == 0 || !Ops[i]->hasNonIntegralPointers()); + ID.AddPointer(Ops[i]); ++ } + ID.AddPointer(L); + void *IP = nullptr; + SCEVAddRecExpr *S = +@@ -2820,6 +2835,7 @@ ScalarEvolution::getOrCreateAddRecExpr(ArrayRef Ops, + addToLoopUseLists(S); + } + S->setNoWrapFlags(Flags); ++ S->setHasNIPtr(Ops[0]->hasNonIntegralPointers()); + return S; + } + +@@ -2828,8 +2844,11 @@ ScalarEvolution::getOrCreateMulExpr(ArrayRef Ops, + SCEV::NoWrapFlags Flags) { + FoldingSetNodeID ID; + ID.AddInteger(scMulExpr); +- for (unsigned i = 0, e = Ops.size(); i != e; ++i) ++ bool HasNIPtr = false; ++ for (unsigned i = 0, e = Ops.size(); i != e; ++i) { ++ HasNIPtr |= Ops[i]->hasNonIntegralPointers(); + ID.AddPointer(Ops[i]); ++ } + void *IP = nullptr; + SCEVMulExpr *S = + static_cast(UniqueSCEVs.FindNodeOrInsertPos(ID, IP)); +@@ -2842,6 +2861,7 @@ ScalarEvolution::getOrCreateMulExpr(ArrayRef Ops, + addToLoopUseLists(S); + } + S->setNoWrapFlags(Flags); ++ S->setHasNIPtr(HasNIPtr); + return S; + } + +@@ -3666,8 +3686,11 @@ const SCEV *ScalarEvolution::getMinMaxExpr(unsigned Kind, + return ExistingSCEV; + const SCEV **O = SCEVAllocator.Allocate(Ops.size()); + std::uninitialized_copy(Ops.begin(), Ops.end(), O); +- SCEV *S = new (SCEVAllocator) SCEVMinMaxExpr( ++ SCEVMinMaxExpr *S = new (SCEVAllocator) SCEVMinMaxExpr( + ID.Intern(SCEVAllocator), static_cast(Kind), O, Ops.size()); ++ // For MinMaxExprs it's sufficient to see if the first Op has NI data, as the ++ // operands all need to be of the same type. ++ S->setHasNIPtr(Ops[0]->hasNonIntegralPointers()); + + UniqueSCEVs.InsertNode(S, IP); + addToLoopUseLists(S); +@@ -3744,8 +3767,9 @@ const SCEV *ScalarEvolution::getUnknown(Value *V) { + "Stale SCEVUnknown in uniquing map!"); + return S; + } ++ bool ValueIsNIPtr = getDataLayout().isNonIntegralPointerType(V->getType()); + SCEV *S = new (SCEVAllocator) SCEVUnknown(ID.Intern(SCEVAllocator), V, this, +- FirstUnknown); ++ FirstUnknown, ValueIsNIPtr); + FirstUnknown = cast(S); + UniqueSCEVs.InsertNode(S, IP); + return S; +diff --git llvm/test/Transforms/LoopStrengthReduce/nonintegral.ll llvm/test/Transforms/LoopStrengthReduce/nonintegral.ll +index 5648e3aa74a..6936521f3a6 100644 +--- llvm/test/Transforms/LoopStrengthReduce/nonintegral.ll ++++ llvm/test/Transforms/LoopStrengthReduce/nonintegral.ll +@@ -2,7 +2,7 @@ + + ; Address Space 10 is non-integral. The optimizer is not allowed to use + ; ptrtoint/inttoptr instructions. Make sure that this doesn't happen +-target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128-ni:10:11:12" ++target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128-ni:10:11:12:13" + target triple = "x86_64-unknown-linux-gnu" + + define void @japi1__unsafe_getindex_65028(i64 addrspace(10)* %arg) { +@@ -43,3 +43,36 @@ if38: ; preds = %L119 + done: ; preds = %if38 + ret void + } ++ ++; This is a bugpoint-reduced regression test - It doesn't make too much sense by itself, ++; but creates the correct SCEV expressions to reproduce the issue. See ++; https://github.com/JuliaLang/julia/issues/31156 for the original bug report. ++define void @"japi1_permutedims!_4259"(i64 %a, i64 %b, i64 %c, i64 %d, i64 %e, i64 %f, i1 %g, i8 addrspace(13)* %base) #0 { ++; CHECK-NOT: inttoptr ++; CHECK-NOT: ptrtoint ++; CHECK: getelementptr i8, i8 addrspace(13)* {{.*}}, i64 {{.*}} ++top: ++ br label %L42.L46_crit_edge.us ++ ++L42.L46_crit_edge.us: ; preds = %L82.us.us.loopexit, %top ++ %value_phi11.us = phi i64 [ %a, %top ], [ %2, %L82.us.us.loopexit ] ++ %0 = sub i64 %value_phi11.us, %b ++ %1 = add i64 %0, %c ++ %spec.select = select i1 %g, i64 %d, i64 0 ++ br label %L62.us.us ++ ++L82.us.us.loopexit: ; preds = %L62.us.us ++ %2 = add i64 %e, %value_phi11.us ++ br label %L42.L46_crit_edge.us ++ ++L62.us.us: ; preds = %L62.us.us, %L42.L46_crit_edge.us ++ %value_phi21.us.us = phi i64 [ %6, %L62.us.us ], [ %spec.select, %L42.L46_crit_edge.us ] ++ %3 = add i64 %1, %value_phi21.us.us ++ %4 = getelementptr inbounds i8, i8 addrspace(13)* %base, i64 %3 ++ %5 = load i8, i8 addrspace(13)* %4, align 1 ++ %6 = add i64 %f, %value_phi21.us.us ++ br i1 %g, label %L82.us.us.loopexit, label %L62.us.us, !llvm.loop !1 ++} ++ ++!1 = distinct !{!1, !2} ++!2 = !{!"llvm.loop.isvectorized", i32 1} diff --git a/deps/patches/llvm-exegesis-mingw.patch b/deps/patches/llvm-exegesis-mingw.patch new file mode 100644 index 0000000..ff11f4d --- /dev/null +++ b/deps/patches/llvm-exegesis-mingw.patch @@ -0,0 +1,24 @@ +From 9ba86352649a39b03adce98670714c4c8eb5341d Mon Sep 17 00:00:00 2001 +From: Valentin Churavy +Date: Wed, 24 Jul 2019 21:19:20 -0400 +Subject: [PATCH] Fix build of llvm-exegis on mingw32 + +--- + llvm/tools/llvm-exegesis/CMakeLists.txt | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tools/llvm-exegesis/CMakeLists.txt b/tools/llvm-exegesis/CMakeLists.txt +index a59e1b74024..7a30e0ea98f 100644 +--- a/tools/llvm-exegesis/CMakeLists.txt ++++ b/tools/llvm-exegesis/CMakeLists.txt +@@ -4,7 +4,7 @@ set(LLVM_LINK_COMPONENTS + native + ) + +-add_llvm_tool(llvm-exegesis ++add_llvm_tool(llvm-exegesis DISABLE_LLVM_LINK_LLVM_DYLIB + llvm-exegesis.cpp + ) + +-- +2.22.0 diff --git a/deps/patches/llvm-symver-jlprefix.patch b/deps/patches/llvm-symver-jlprefix.patch new file mode 100644 index 0000000..5987238 --- /dev/null +++ b/deps/patches/llvm-symver-jlprefix.patch @@ -0,0 +1,18 @@ +From f23277bb91a4925ba8763337137a3123a7600557 Mon Sep 17 00:00:00 2001 +From: Valentin Churavy +Date: Tue, 16 Jan 2018 17:29:05 -0500 +Subject: [PATCH] add JL prefix to all LLVM version suffixes + +--- + tools/llvm-shlib/simple_version_script.map.in | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tools/llvm-shlib/simple_version_script.map.in b/tools/llvm-shlib/simple_version_script.map.in +index e9515fe7862..af082581627 100644 +--- a/tools/llvm-shlib/simple_version_script.map.in ++++ b/tools/llvm-shlib/simple_version_script.map.in +@@ -1 +1 @@ +-LLVM_@LLVM_VERSION_MAJOR@.@LLVM_VERSION_MINOR@ { global: *; }; ++JL_LLVM_@LLVM_VERSION_MAJOR@.@LLVM_VERSION_MINOR@ { global: *; }; +-- +2.15.1 diff --git a/deps/patches/llvm-test-plugin-mingw.patch b/deps/patches/llvm-test-plugin-mingw.patch new file mode 100644 index 0000000..14cf071 --- /dev/null +++ b/deps/patches/llvm-test-plugin-mingw.patch @@ -0,0 +1,24 @@ +From 9bd3774db73533c8df475639805ff1516aea274c Mon Sep 17 00:00:00 2001 +From: Valentin Churavy +Date: Wed, 24 Jul 2019 21:45:33 -0400 +Subject: [PATCH] add missing components to TestPlugin + +--- + llvm/unittests/Passes/CMakeLists.txt | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/unittests/Passes/CMakeLists.txt b/unittests/Passes/CMakeLists.txt +index 3e83b527958..4b09f47c234 100644 +--- a/unittests/Passes/CMakeLists.txt ++++ b/unittests/Passes/CMakeLists.txt +@@ -14,7 +14,7 @@ add_llvm_unittest(PluginsTests + export_executable_symbols(PluginsTests) + target_link_libraries(PluginsTests PRIVATE LLVMTestingSupport) + +-set(LLVM_LINK_COMPONENTS) ++set(LLVM_LINK_COMPONENTS Support Passes Core) + add_llvm_library(TestPlugin MODULE BUILDTREE_ONLY + TestPlugin.cpp + ) +-- +2.22.0 diff --git a/deps/patches/llvm7-D50010-VNCoercion-ni.patch b/deps/patches/llvm7-D50010-VNCoercion-ni.patch new file mode 100644 index 0000000..729c418 --- /dev/null +++ b/deps/patches/llvm7-D50010-VNCoercion-ni.patch @@ -0,0 +1,67 @@ +diff --git a/lib/Transforms/Utils/VNCoercion.cpp b/lib/Transforms/Utils/VNCoercion.cpp +index 948d9bd5baa..fbd5b9bb3be 100644 +--- a/lib/Transforms/Utils/VNCoercion.cpp ++++ b/lib/Transforms/Utils/VNCoercion.cpp +@@ -20,7 +20,8 @@ bool canCoerceMustAliasedValueToLoad(Value *StoredVal, Type *LoadTy, + StoredVal->getType()->isStructTy() || StoredVal->getType()->isArrayTy()) + return false; + +- uint64_t StoreSize = DL.getTypeSizeInBits(StoredVal->getType()); ++ Type *StoredValTy = StoredVal->getType(); ++ uint64_t StoreSize = DL.getTypeSizeInBits(StoredValTy); + + // The store size must be byte-aligned to support future type casts. + if (llvm::alignTo(StoreSize, 8) != StoreSize) +@@ -30,10 +31,15 @@ bool canCoerceMustAliasedValueToLoad(Value *StoredVal, Type *LoadTy, + if (StoreSize < 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/llvm7-revert-D44485.patch b/deps/patches/llvm7-revert-D44485.patch new file mode 100644 index 0000000..121e197 --- /dev/null +++ b/deps/patches/llvm7-revert-D44485.patch @@ -0,0 +1,94 @@ +From 4370214628487ac8495f963ae05960b5ecc31103 Mon Sep 17 00:00:00 2001 +From: Jameson Nash +Date: Thu, 12 Sep 2019 11:45:07 -0400 +Subject: [PATCH] Revert "[MC] Always emit relocations for same-section + function references" + +This reverts commit 9232972575cafac29c3e4817c8714c9aca0e8585. +--- + lib/MC/WinCOFFObjectWriter.cpp | 12 +++++------- + test/MC/COFF/diff.s | 25 ++++++++----------------- + 2 files changed, 13 insertions(+), 24 deletions(-) + +diff --git a/lib/MC/WinCOFFObjectWriter.cpp b/lib/MC/WinCOFFObjectWriter.cpp +index 9ffecd99df6..0214161e03c 100644 +--- a/lib/MC/WinCOFFObjectWriter.cpp ++++ b/lib/MC/WinCOFFObjectWriter.cpp +@@ -690,14 +690,12 @@ void WinCOFFObjectWriter::executePostLayoutBinding(MCAssembler &Asm, + bool WinCOFFObjectWriter::isSymbolRefDifferenceFullyResolvedImpl( + const MCAssembler &Asm, const MCSymbol &SymA, const MCFragment &FB, + bool InSet, bool IsPCRel) const { +- // Don't drop relocations between functions, even if they are in the same text +- // section. Multiple Visual C++ linker features depend on having the +- // relocations present. The /INCREMENTAL flag will cause these relocations to +- // point to thunks, and the /GUARD:CF flag assumes that it can use relocations +- // to approximate the set of all address taken functions. LLD's implementation +- // of /GUARD:CF also relies on the existance of these relocations. ++ // MS LINK expects to be able to replace all references to a function with a ++ // thunk to implement their /INCREMENTAL feature. Make sure we don't optimize ++ // away any relocations to functions. + uint16_t Type = cast(SymA).getType(); +- if ((Type >> COFF::SCT_COMPLEX_TYPE_SHIFT) == COFF::IMAGE_SYM_DTYPE_FUNCTION) ++ if (Asm.isIncrementalLinkerCompatible() && ++ (Type >> COFF::SCT_COMPLEX_TYPE_SHIFT) == COFF::IMAGE_SYM_DTYPE_FUNCTION) + return false; + return MCObjectWriter::isSymbolRefDifferenceFullyResolvedImpl(Asm, SymA, FB, + InSet, IsPCRel); +diff --git a/test/MC/COFF/diff.s b/test/MC/COFF/diff.s +index f89e4ed8901..d68e628577b 100644 +--- a/test/MC/COFF/diff.s ++++ b/test/MC/COFF/diff.s +@@ -1,14 +1,19 @@ + // RUN: llvm-mc -filetype=obj -triple i686-pc-mingw32 %s | llvm-readobj -s -sr -sd | FileCheck %s + +-// COFF resolves differences between labels in the same section, unless that +-// label is declared with function type. +- + .section baz, "xr" ++ .def X ++ .scl 2; ++ .type 32; ++ .endef + .globl X + X: + mov Y-X+42, %eax + retl + ++ .def Y ++ .scl 2; ++ .type 32; ++ .endef + .globl Y + Y: + retl +@@ -25,11 +30,6 @@ _foobar: # @foobar + # %bb.0: + ret + +- .globl _baz +-_baz: +- calll _foobar +- retl +- + .data + .globl _rust_crate # @rust_crate + .align 4 +@@ -39,15 +39,6 @@ _rust_crate: + .long _foobar-_rust_crate + .long _foobar-_rust_crate + +-// Even though _baz and _foobar are in the same .text section, we keep the +-// relocation for compatibility with the VC linker's /guard:cf and /incremental +-// flags, even on mingw. +- +-// CHECK: Name: .text +-// CHECK: Relocations [ +-// CHECK-NEXT: 0x12 IMAGE_REL_I386_REL32 _foobar +-// CHECK-NEXT: ] +- + // CHECK: Name: .data + // CHECK: Relocations [ + // CHECK-NEXT: 0x4 IMAGE_REL_I386_DIR32 _foobar +-- +2.17.1 + diff --git a/deps/patches/llvm7-symver-jlprefix.patch b/deps/patches/llvm7-symver-jlprefix.patch new file mode 100644 index 0000000..bdfb8e5 --- /dev/null +++ b/deps/patches/llvm7-symver-jlprefix.patch @@ -0,0 +1,18 @@ +From f23277bb91a4925ba8763337137a3123a7600557 Mon Sep 17 00:00:00 2001 +From: Valentin Churavy +Date: Tue, 16 Jan 2018 17:29:05 -0500 +Subject: [PATCH] add JL prefix to all LLVM version suffixes + +--- + tools/llvm-shlib/simple_version_script.map.in | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tools/llvm-shlib/simple_version_script.map.in b/tools/llvm-shlib/simple_version_script.map.in +index e9515fe7862..af082581627 100644 +--- a/tools/llvm-shlib/simple_version_script.map.in ++++ b/tools/llvm-shlib/simple_version_script.map.in +@@ -1 +1 @@ +-LLVM_@LLVM_VERSION_MAJOR@ { global: *; }; ++JL_LLVM_@LLVM_VERSION_MAJOR@.@LLVM_VERSION_MINOR@ { global: *; }; +-- +2.15.1 diff --git a/deps/patches/llvm7-windows-race.patch b/deps/patches/llvm7-windows-race.patch new file mode 100644 index 0000000..b6ae6ba --- /dev/null +++ b/deps/patches/llvm7-windows-race.patch @@ -0,0 +1,15 @@ +diff --git a/tools/llvm-config/CMakeLists.txt b/tools/llvm-config/CMakeLists.txt +index f59402ac4b0..5de4c6febe7 100644 +--- a/tools/llvm-config/CMakeLists.txt ++++ b/tools/llvm-config/CMakeLists.txt +@@ -77,5 +77,10 @@ if(CMAKE_CROSSCOMPILING AND NOT LLVM_CONFIG_PATH) + add_custom_target(NativeLLVMConfig DEPENDS ${LLVM_CONFIG_PATH}) + add_dependencies(NativeLLVMConfig CONFIGURE_LLVM_NATIVE) + ++ # Add a dependency on the host tblgen, which uses the same working ++ # directory and with which we're otherwise racing to build some ++ # of the utility libraries. ++ add_dependencies(NativeLLVMConfig LLVM-tablegen-host) ++ + add_dependencies(llvm-config NativeLLVMConfig) + endif() diff --git a/deps/patches/llvm8-D34078-vectorize-fdiv.patch b/deps/patches/llvm8-D34078-vectorize-fdiv.patch new file mode 100644 index 0000000..c386d04 --- /dev/null +++ b/deps/patches/llvm8-D34078-vectorize-fdiv.patch @@ -0,0 +1,42 @@ +diff --git a/lib/Analysis/IVDescriptors.cpp b/lib/Analysis/IVDescriptors.cpp +index aaebc4a481e..91fe4c0003c 100644 +--- a/lib/Analysis/IVDescriptors.cpp ++++ b/lib/Analysis/IVDescriptors.cpp +@@ -571,6 +571,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 ++} diff --git a/deps/patches/llvm9-D50010-VNCoercion-ni.patch b/deps/patches/llvm9-D50010-VNCoercion-ni.patch new file mode 100644 index 0000000..988d669 --- /dev/null +++ b/deps/patches/llvm9-D50010-VNCoercion-ni.patch @@ -0,0 +1,64 @@ +diff --git a/lib/Transforms/Utils/VNCoercion.cpp b/lib/Transforms/Utils/VNCoercion.cpp +--- a/lib/Transforms/Utils/VNCoercion.cpp ++++ b/lib/Transforms/Utils/VNCoercion.cpp +@@ -34,17 +34,22 @@ + if (StoreSize < DL.getTypeSizeInBits(LoadTy)) + return false; + ++ bool StoredNI = DL.isNonIntegralPointerType(StoredTy->getScalarType()); ++ bool LoadNI = DL.isNonIntegralPointerType(LoadTy->getScalarType()); + // Don't coerce non-integral pointers to integers or vice versa. +- if (DL.isNonIntegralPointerType(StoredVal->getType()->getScalarType()) != +- DL.isNonIntegralPointerType(LoadTy->getScalarType())) { ++ if (StoredNI != LoadNI) { + // As a special case, allow coercion of memset used to initialize + // an array w/null. Despite non-integral pointers not generally having a + // specific bit pattern, we do assume null is zero. + if (auto *CI = dyn_cast(StoredVal)) + return CI->isNullValue(); + return false; ++ } else if (StoredNI && LoadNI && ++ cast(StoredTy)->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 +--- a/test/Transforms/GVN/non-integral-pointers.ll ++++ b/test/Transforms/GVN/non-integral-pointers.ll +@@ -1,7 +1,7 @@ + ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py + ; 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) { +@@ -285,3 +285,21 @@ + %ref = load i8 addrspace(4)*, i8 addrspace(4)* addrspace(4)* %loc.off + ret i8 addrspace(4)* %ref + } ++ ++ 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/llvm9-D71443-PPC-MC-redef-symbol.patch b/deps/patches/llvm9-D71443-PPC-MC-redef-symbol.patch new file mode 100644 index 0000000..904514a --- /dev/null +++ b/deps/patches/llvm9-D71443-PPC-MC-redef-symbol.patch @@ -0,0 +1,47 @@ +From 5cd52dbfa9c60cfd12676924bed97701ee9bc4ef Mon Sep 17 00:00:00 2001 +From: Fangrui Song +Date: Thu, 12 Dec 2019 16:18:57 -0800 +Subject: [PATCH] [MC][PowerPC] Fix a crash when redefining a symbol after .set + +Fix PR44284. This is probably not valid assembly but we should not crash. + +Reviewed By: luporl, #powerpc, steven.zhang + +Differential Revision: https://reviews.llvm.org/D71443 + +(cherry picked from commit f99eedeb72644671cd584f48e4c136d47f6b0020) +--- + llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCTargetDesc.cpp | 3 ++- + llvm/test/MC/PowerPC/ppc64-localentry-symbols.s | 5 +++++ + 2 files changed, 7 insertions(+), 1 deletion(-) + +diff --git llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCTargetDesc.cpp llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCTargetDesc.cpp +index 90c3c8d20ed..71f926c265e 100644 +--- llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCTargetDesc.cpp ++++ llvm/lib/Target/PowerPC/MCTargetDesc/PPCMCTargetDesc.cpp +@@ -196,7 +196,8 @@ public: + + void finish() override { + for (auto *Sym : UpdateOther) +- copyLocalEntry(Sym, Sym->getVariableValue()); ++ if (Sym->isVariable()) ++ copyLocalEntry(Sym, Sym->getVariableValue()); + } + + private: +diff --git llvm/test/MC/PowerPC/ppc64-localentry-symbols.s llvm/test/MC/PowerPC/ppc64-localentry-symbols.s +index f1d5c5d0ab1..a663af57ad4 100644 +--- llvm/test/MC/PowerPC/ppc64-localentry-symbols.s ++++ llvm/test/MC/PowerPC/ppc64-localentry-symbols.s +@@ -32,3 +32,8 @@ func: + nop + nop + .localentry func, 8 ++ ++## PR44284 Don't crash if err is redefined after .set ++.set err, _err ++.globl err ++err: +-- +2.26.0 + diff --git a/deps/patches/openblas-fix-initialization-to-zero-arm64.patch b/deps/patches/openblas-fix-initialization-to-zero-arm64.patch new file mode 100644 index 0000000..2ed2373 --- /dev/null +++ b/deps/patches/openblas-fix-initialization-to-zero-arm64.patch @@ -0,0 +1,67 @@ +From 2c87b7f3892710e082e0c4dfea9bc234a38dc160 Mon Sep 17 00:00:00 2001 +From: Martin Kroeker +Date: Tue, 31 Mar 2020 16:53:56 +0200 +Subject: [PATCH] Fix zero initialization arm64 + +--- + kernel/arm64/dgemm_beta.S | 16 ++++++++-------- + kernel/arm64/sgemm_beta.S | 16 ++++++++-------- + 2 files changed, 16 insertions(+), 16 deletions(-) + +diff --git a/kernel/arm64/dgemm_beta.S b/kernel/arm64/dgemm_beta.S +index 20011c34..7d21525c 100644 +--- a/kernel/arm64/dgemm_beta.S ++++ b/kernel/arm64/dgemm_beta.S +@@ -81,14 +81,14 @@ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + .endm + + .macro INIT_ZERO +- fmul v0.2d, v0.2d, betaV0 +- fmul v1.2d, v1.2d, betaV0 +- fmul v2.2d, v2.2d, betaV0 +- fmul v3.2d, v3.2d, betaV0 +- fmul v4.2d, v4.2d, betaV0 +- fmul v5.2d, v5.2d, betaV0 +- fmul v6.2d, v6.2d, betaV0 +- fmul v7.2d, v7.2d, betaV0 ++ movi v0.2d, #0000000000000000 ++ movi v1.2d, #0000000000000000 ++ movi v2.2d, #0000000000000000 ++ movi v3.2d, #0000000000000000 ++ movi v4.2d, #0000000000000000 ++ movi v5.2d, #0000000000000000 ++ movi v6.2d, #0000000000000000 ++ movi v7.2d, #0000000000000000 + .endm + + /************************************************************************************** +diff --git a/kernel/arm64/sgemm_beta.S b/kernel/arm64/sgemm_beta.S +index a3b97e23..574485bc 100755 +--- a/kernel/arm64/sgemm_beta.S ++++ b/kernel/arm64/sgemm_beta.S +@@ -81,14 +81,14 @@ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + .endm + + .macro INIT_ZERO +- fmul v0.4s, v0.4s, betaV0 +- fmul v1.4s, v1.4s, betaV0 +- fmul v2.4s, v2.4s, betaV0 +- fmul v3.4s, v3.4s, betaV0 +- fmul v4.4s, v4.4s, betaV0 +- fmul v5.4s, v5.4s, betaV0 +- fmul v6.4s, v6.4s, betaV0 +- fmul v7.4s, v7.4s, betaV0 ++ movi v0.4s, #0x0 ++ movi v1.4s, #0x0 ++ movi v2.4s, #0x0 ++ movi v3.4s, #0x0 ++ movi v4.4s, #0x0 ++ movi v5.4s, #0x0 ++ movi v6.4s, #0x0 ++ movi v7.4s, #0x0 + .endm + + /************************************************************************************** +-- +2.25.1 + diff --git a/deps/patches/openblas-ofast-power.patch b/deps/patches/openblas-ofast-power.patch new file mode 100644 index 0000000..3d777eb --- /dev/null +++ b/deps/patches/openblas-ofast-power.patch @@ -0,0 +1,33 @@ + Makefile.power | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/Makefile.power b/Makefile.power +index 24d8aa8a..e53a243a 100644 +--- a/Makefile.power ++++ b/Makefile.power +@@ -11,20 +11,20 @@ endif + + ifeq ($(CORE), POWER9) + ifeq ($(USE_OPENMP), 1) +-COMMON_OPT += -Ofast -mcpu=power9 -mtune=power9 -mvsx -malign-power -DUSE_OPENMP -fno-fast-math -fopenmp ++COMMON_OPT += -mcpu=power9 -mtune=power9 -mvsx -malign-power -DUSE_OPENMP -fno-fast-math -fopenmp + FCOMMON_OPT += -O2 -frecursive -mcpu=power9 -mtune=power9 -malign-power -DUSE_OPENMP -fno-fast-math -fopenmp + else +-COMMON_OPT += -Ofast -mcpu=power9 -mtune=power9 -mvsx -malign-power -fno-fast-math ++COMMON_OPT += -mcpu=power9 -mtune=power9 -mvsx -malign-power -fno-fast-math + FCOMMON_OPT += -O2 -frecursive -mcpu=power9 -mtune=power9 -malign-power -fno-fast-math + endif + endif + + ifeq ($(CORE), POWER8) + ifeq ($(USE_OPENMP), 1) +-COMMON_OPT += -Ofast -mcpu=power8 -mtune=power8 -mvsx -malign-power -DUSE_OPENMP -fno-fast-math -fopenmp ++COMMON_OPT += -mcpu=power8 -mtune=power8 -mvsx -malign-power -DUSE_OPENMP -fno-fast-math -fopenmp + FCOMMON_OPT += -O2 -frecursive -mcpu=power8 -mtune=power8 -malign-power -DUSE_OPENMP -fno-fast-math -fopenmp + else +-COMMON_OPT += -Ofast -mcpu=power8 -mtune=power8 -mvsx -malign-power -fno-fast-math ++COMMON_OPT += -mcpu=power8 -mtune=power8 -mvsx -malign-power -fno-fast-math + FCOMMON_OPT += -O2 -frecursive -mcpu=power8 -mtune=power8 -malign-power -fno-fast-math + endif + endif + diff --git a/deps/patches/openblas-winexit.patch b/deps/patches/openblas-winexit.patch new file mode 100644 index 0000000..8db2fbf --- /dev/null +++ b/deps/patches/openblas-winexit.patch @@ -0,0 +1,178 @@ +From f919c3301fabbaa5d965dcc7b1c3d6892a8c730a Mon Sep 17 00:00:00 2001 +From: Keno Fischer +Date: Sat, 14 Mar 2020 12:05:19 +0100 + +--- + driver/others/memory.c | 131 +---------------------------------------- + 1 file changed, 2 insertions(+), 129 deletions(-) + +diff --git a/driver/others/memory.c b/driver/others/memory.c +index 62a5a021..23f8fe65 100644 +--- a/driver/others/memory.c ++++ b/driver/others/memory.c +@@ -1510,7 +1510,7 @@ void CONSTRUCTOR gotoblas_init(void) { + + } + +-void DESTRUCTOR gotoblas_quit(void) { ++void gotoblas_quit(void) { + + if (gotoblas_initialized == 0) return; + +@@ -1547,74 +1547,12 @@ void DESTRUCTOR gotoblas_quit(void) { + #endif + } + +-#if defined(_MSC_VER) && !defined(__clang__) +-BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) +-{ +- switch (ul_reason_for_call) +- { +- case DLL_PROCESS_ATTACH: +- gotoblas_init(); +- break; +- case DLL_THREAD_ATTACH: +- break; +- case DLL_THREAD_DETACH: +-#if defined(SMP) +- blas_thread_memory_cleanup(); +-#endif +- break; +- case DLL_PROCESS_DETACH: +- gotoblas_quit(); +- break; +- default: +- break; +- } +- return TRUE; +-} +- +-/* +- This is to allow static linking. +- Code adapted from Google performance tools: +- https://gperftools.googlecode.com/git-history/perftools-1.0/src/windows/port.cc +- Reference: +- https://sourceware.org/ml/pthreads-win32/2008/msg00028.html +- http://ci.boost.org/svn-trac/browser/trunk/libs/thread/src/win32/tss_pe.cpp +-*/ +-static int on_process_term(void) +-{ +- gotoblas_quit(); +- return 0; +-} + #ifdef _WIN64 + #pragma comment(linker, "/INCLUDE:_tls_used") + #else + #pragma comment(linker, "/INCLUDE:__tls_used") + #endif + +-#ifdef _WIN64 +-#pragma const_seg(".CRT$XLB") +-#else +-#pragma data_seg(".CRT$XLB") +-#endif +-static void (APIENTRY *dll_callback)(HINSTANCE h, DWORD ul_reason_for_call, PVOID pv) = DllMain; +-#ifdef _WIN64 +-#pragma const_seg() +-#else +-#pragma data_seg() +-#endif +- +-#ifdef _WIN64 +-#pragma const_seg(".CRT$XTU") +-#else +-#pragma data_seg(".CRT$XTU") +-#endif +-static int(*p_process_term)(void) = on_process_term; +-#ifdef _WIN64 +-#pragma const_seg() +-#else +-#pragma data_seg() +-#endif +-#endif +- + #if (defined(C_PGI) || (!defined(C_SUN) && defined(F_INTERFACE_SUN))) && (defined(ARCH_X86) || defined(ARCH_X86_64)) + /* Don't call me; this is just work around for PGI / Sun bug */ + void gotoblas_dummy_for_PGI(void) { +@@ -3104,7 +3042,7 @@ void CONSTRUCTOR gotoblas_init(void) { + + } + +-void DESTRUCTOR gotoblas_quit(void) { ++void gotoblas_quit(void) { + + if (gotoblas_initialized == 0) return; + +@@ -3133,71 +3071,6 @@ void DESTRUCTOR gotoblas_quit(void) { + #endif + } + +-#if defined(_MSC_VER) && !defined(__clang__) +-BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) +-{ +- switch (ul_reason_for_call) +- { +- case DLL_PROCESS_ATTACH: +- gotoblas_init(); +- break; +- case DLL_THREAD_ATTACH: +- break; +- case DLL_THREAD_DETACH: +- break; +- case DLL_PROCESS_DETACH: +- gotoblas_quit(); +- break; +- default: +- break; +- } +- return TRUE; +-} +- +-/* +- This is to allow static linking. +- Code adapted from Google performance tools: +- https://gperftools.googlecode.com/git-history/perftools-1.0/src/windows/port.cc +- Reference: +- https://sourceware.org/ml/pthreads-win32/2008/msg00028.html +- http://ci.boost.org/svn-trac/browser/trunk/libs/thread/src/win32/tss_pe.cpp +-*/ +-static int on_process_term(void) +-{ +- gotoblas_quit(); +- return 0; +-} +-#ifdef _WIN64 +-#pragma comment(linker, "/INCLUDE:_tls_used") +-#else +-#pragma comment(linker, "/INCLUDE:__tls_used") +-#endif +- +-#ifdef _WIN64 +-#pragma const_seg(".CRT$XLB") +-#else +-#pragma data_seg(".CRT$XLB") +-#endif +-static void (APIENTRY *dll_callback)(HINSTANCE h, DWORD ul_reason_for_call, PVOID pv) = DllMain; +-#ifdef _WIN64 +-#pragma const_seg() +-#else +-#pragma data_seg() +-#endif +- +-#ifdef _WIN64 +-#pragma const_seg(".CRT$XTU") +-#else +-#pragma data_seg(".CRT$XTU") +-#endif +-static int(*p_process_term)(void) = on_process_term; +-#ifdef _WIN64 +-#pragma const_seg() +-#else +-#pragma data_seg() +-#endif +-#endif +- + #if (defined(C_PGI) || (!defined(C_SUN) && defined(F_INTERFACE_SUN))) && (defined(ARCH_X86) || defined(ARCH_X86_64)) + /* Don't call me; this is just work around for PGI / Sun bug */ + void gotoblas_dummy_for_PGI(void) { + diff --git a/deps/patches/p7zip-12-CVE-2016-9296.patch b/deps/patches/p7zip-12-CVE-2016-9296.patch new file mode 100644 index 0000000..42245c9 --- /dev/null +++ b/deps/patches/p7zip-12-CVE-2016-9296.patch @@ -0,0 +1,23 @@ +From: Robert Luberda +Date: Sat, 19 Nov 2016 08:48:08 +0100 +Subject: Fix nullptr dereference (CVE-2016-9296) + +Patch taken from https://sourceforge.net/p/p7zip/bugs/185/ +--- + CPP/7zip/Archive/7z/7zIn.cpp | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/CPP/7zip/Archive/7z/7zIn.cpp b/CPP/7zip/Archive/7z/7zIn.cpp +index b0c6b98..7c6dde2 100644 +--- a/CPP/7zip/Archive/7z/7zIn.cpp ++++ b/CPP/7zip/Archive/7z/7zIn.cpp +@@ -1097,7 +1097,8 @@ HRESULT CInArchive::ReadAndDecodePackedStreams( + if (CrcCalc(data, unpackSize) != folders.FolderCRCs.Vals[i]) + ThrowIncorrect(); + } +- HeadersSize += folders.PackPositions[folders.NumPackStreams]; ++ if (folders.PackPositions) ++ HeadersSize += folders.PackPositions[folders.NumPackStreams]; + return S_OK; + } + diff --git a/deps/patches/p7zip-13-CVE-2017-17969.patch b/deps/patches/p7zip-13-CVE-2017-17969.patch new file mode 100644 index 0000000..a9787c4 --- /dev/null +++ b/deps/patches/p7zip-13-CVE-2017-17969.patch @@ -0,0 +1,35 @@ +From: =?utf-8?q?Antoine_Beaupr=C3=A9?= +Date: Fri, 2 Feb 2018 11:11:41 +0100 +Subject: Heap-based buffer overflow in 7zip/Compress/ShrinkDecoder.cpp + +Origin: vendor, https://sourceforge.net/p/p7zip/bugs/_discuss/thread/0920f369/27d7/attachment/CVE-2017-17969.patch +Forwarded: https://sourceforge.net/p/p7zip/bugs/_discuss/thread/0920f369/#27d7 +Bug: https://sourceforge.net/p/p7zip/bugs/204/ +Bug-Debian: https://bugs.debian.org/888297 +Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2017-17969 +Reviewed-by: Salvatore Bonaccorso +Last-Update: 2018-02-01 +Applied-Upstream: 18.00-beta +--- + CPP/7zip/Compress/ShrinkDecoder.cpp | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/CPP/7zip/Compress/ShrinkDecoder.cpp b/CPP/7zip/Compress/ShrinkDecoder.cpp +index 80b7e67..ca37764 100644 +--- a/CPP/7zip/Compress/ShrinkDecoder.cpp ++++ b/CPP/7zip/Compress/ShrinkDecoder.cpp +@@ -121,8 +121,13 @@ HRESULT CDecoder::CodeReal(ISequentialInStream *inStream, ISequentialOutStream * + { + _stack[i++] = _suffixes[cur]; + cur = _parents[cur]; ++ if (cur >= kNumItems || i >= kNumItems) ++ break; + } +- ++ ++ if (cur >= kNumItems || i >= kNumItems) ++ break; ++ + _stack[i++] = (Byte)cur; + lastChar2 = (Byte)cur; + diff --git a/deps/patches/p7zip-15-Enhanced-encryption-strength.patch b/deps/patches/p7zip-15-Enhanced-encryption-strength.patch new file mode 100644 index 0000000..ab1cfb9 --- /dev/null +++ b/deps/patches/p7zip-15-Enhanced-encryption-strength.patch @@ -0,0 +1,298 @@ +From ea31bbe661abef761e49983b56923e6523b9463a Mon Sep 17 00:00:00 2001 +From: aone +Date: Thu, 7 Mar 2019 10:06:16 +0100 +Subject: [PATCH] Enhanced encryption strength from 7-Zip 19.00 + +https://github.com/aonez/Keka/issues/379 +https://sourceforge.net/p/sevenzip/bugs/2176 +--- + .../CPP/7zip/Archive/Wim/WimHandlerOut.cpp | 2 +- + Bin/p7zip/source/CPP/7zip/Crypto/7zAes.cpp | 4 +- + Bin/p7zip/source/CPP/7zip/Crypto/RandGen.cpp | 135 ++++++++++++++++-- + Bin/p7zip/source/CPP/7zip/Crypto/RandGen.h | 19 +++ + Bin/p7zip/source/CPP/7zip/Crypto/WzAes.cpp | 2 +- + .../source/CPP/7zip/Crypto/ZipCrypto.cpp | 2 +- + 6 files changed, 146 insertions(+), 18 deletions(-) + +diff --git a/Bin/p7zip/source/CPP/7zip/Archive/Wim/WimHandlerOut.cpp b/Bin/p7zip/source/CPP/7zip/Archive/Wim/WimHandlerOut.cpp +index 1d198df0..39679883 100644 +--- a/Bin/p7zip/source/CPP/7zip/Archive/Wim/WimHandlerOut.cpp ++++ b/Bin/p7zip/source/CPP/7zip/Archive/Wim/WimHandlerOut.cpp +@@ -671,7 +671,7 @@ void CHeader::SetDefaultFields(bool useLZX) + ChunkSize = kChunkSize; + ChunkSizeBits = kChunkSizeBits; + } +- g_RandomGenerator.Generate(Guid, 16); ++ MY_RAND_GEN(Guid, 16); + PartNumber = 1; + NumParts = 1; + NumImages = 1; +diff --git a/Bin/p7zip/source/CPP/7zip/Crypto/7zAes.cpp b/Bin/p7zip/source/CPP/7zip/Crypto/7zAes.cpp +index d33b562a..2ed69bad 100644 +--- a/Bin/p7zip/source/CPP/7zip/Crypto/7zAes.cpp ++++ b/Bin/p7zip/source/CPP/7zip/Crypto/7zAes.cpp +@@ -164,8 +164,8 @@ STDMETHODIMP CEncoder::ResetInitVector() + { + for (unsigned i = 0; i < sizeof(_iv); i++) + _iv[i] = 0; +- _ivSize = 8; +- g_RandomGenerator.Generate(_iv, _ivSize); ++ _ivSize = 16; ++ MY_RAND_GEN(_iv, _ivSize); + return S_OK; + } + +diff --git a/Bin/p7zip/source/CPP/7zip/Crypto/RandGen.cpp b/Bin/p7zip/source/CPP/7zip/Crypto/RandGen.cpp +index f5ea31f0..a70f4ec8 100644 +--- a/Bin/p7zip/source/CPP/7zip/Crypto/RandGen.cpp ++++ b/Bin/p7zip/source/CPP/7zip/Crypto/RandGen.cpp +@@ -2,14 +2,44 @@ + + #include "StdAfx.h" + ++#include "RandGen.h" ++ ++#ifndef USE_STATIC_SYSTEM_RAND ++ + #ifndef _7ZIP_ST + #include "../../Windows/Synchronization.h" + #endif + +-#include "RandGen.h" + +-#ifndef _WIN32 ++#ifdef _WIN32 ++ ++#ifdef _WIN64 ++#define USE_STATIC_RtlGenRandom ++#endif ++ ++#ifdef USE_STATIC_RtlGenRandom ++ ++#include ++ ++EXTERN_C_BEGIN ++#ifndef RtlGenRandom ++ #define RtlGenRandom SystemFunction036 ++ BOOLEAN WINAPI RtlGenRandom(PVOID RandomBuffer, ULONG RandomBufferLength); ++#endif ++EXTERN_C_END ++ ++#else ++EXTERN_C_BEGIN ++typedef BOOLEAN (WINAPI * Func_RtlGenRandom)(PVOID RandomBuffer, ULONG RandomBufferLength); ++EXTERN_C_END ++#endif ++ ++ ++#else + #include ++#include ++#include ++#include + #define USE_POSIX_TIME + #define USE_POSIX_TIME2 + #endif +@@ -21,11 +51,9 @@ + #endif + #endif + +-// This is not very good random number generator. +-// Please use it only for salt. +-// First generated data block depends from timer and processID. ++// The seed and first generated data block depend from processID, ++// theadID, timer and system random generator, if available. + // Other generated data blocks depend from previous state +-// Maybe it's possible to restore original timer value from generated value. + + #define HASH_UPD(x) Sha256_Update(&hash, (const Byte *)&x, sizeof(x)); + +@@ -34,25 +62,102 @@ void CRandomGenerator::Init() + CSha256 hash; + Sha256_Init(&hash); + ++ unsigned numIterations = 1000; ++ ++ { ++ #ifndef UNDER_CE ++ const unsigned kNumIterations_Small = 100; ++ const unsigned kBufSize = 32; ++ Byte buf[kBufSize]; ++ #endif ++ + #ifdef _WIN32 ++ + DWORD w = ::GetCurrentProcessId(); + HASH_UPD(w); + w = ::GetCurrentThreadId(); + HASH_UPD(w); ++ ++ #ifdef UNDER_CE ++ /* ++ if (CeGenRandom(kBufSize, buf)) ++ { ++ numIterations = kNumIterations_Small; ++ Sha256_Update(&hash, buf, kBufSize); ++ } ++ */ ++ #elif defined(USE_STATIC_RtlGenRandom) ++ if (RtlGenRandom(buf, kBufSize)) ++ { ++ numIterations = kNumIterations_Small; ++ Sha256_Update(&hash, buf, kBufSize); ++ } + #else ++ { ++ HMODULE hModule = ::LoadLibrary(TEXT("Advapi32.dll")); ++ if (hModule) ++ { ++ // SystemFunction036() is real name of RtlGenRandom() function ++ Func_RtlGenRandom my_RtlGenRandom = (Func_RtlGenRandom)GetProcAddress(hModule, "SystemFunction036"); ++ if (my_RtlGenRandom) ++ { ++ if (my_RtlGenRandom(buf, kBufSize)) ++ { ++ numIterations = kNumIterations_Small; ++ Sha256_Update(&hash, buf, kBufSize); ++ } ++ } ++ ::FreeLibrary(hModule); ++ } ++ } ++ #endif ++ ++ #else ++ + pid_t pid = getpid(); + HASH_UPD(pid); + pid = getppid(); + HASH_UPD(pid); ++ ++ { ++ int f = open("/dev/urandom", O_RDONLY); ++ unsigned numBytes = kBufSize; ++ if (f >= 0) ++ { ++ do ++ { ++ int n = read(f, buf, numBytes); ++ if (n <= 0) ++ break; ++ Sha256_Update(&hash, buf, n); ++ numBytes -= n; ++ } ++ while (numBytes); ++ close(f); ++ if (numBytes == 0) ++ numIterations = kNumIterations_Small; ++ } ++ } ++ /* ++ { ++ int n = getrandom(buf, kBufSize, 0); ++ if (n > 0) ++ { ++ Sha256_Update(&hash, buf, n); ++ if (n == kBufSize) ++ numIterations = kNumIterations_Small; ++ } ++ } ++ */ ++ ++ #endif ++ } ++ ++ #ifdef _DEBUG ++ numIterations = 2; + #endif + +- for (unsigned i = 0; i < +- #ifdef _DEBUG +- 2; +- #else +- 1000; +- #endif +- i++) ++ do + { + #ifdef _WIN32 + LARGE_INTEGER v; +@@ -83,6 +188,8 @@ void CRandomGenerator::Init() + Sha256_Update(&hash, _buff, SHA256_DIGEST_SIZE); + } + } ++ while (--numIterations); ++ + Sha256_Final(&hash, _buff); + _needInit = false; + } +@@ -120,3 +227,5 @@ void CRandomGenerator::Generate(Byte *data, unsigned size) + } + + CRandomGenerator g_RandomGenerator; ++ ++#endif +diff --git a/Bin/p7zip/source/CPP/7zip/Crypto/RandGen.h b/Bin/p7zip/source/CPP/7zip/Crypto/RandGen.h +index cfdcd60d..5122ec4b 100644 +--- a/Bin/p7zip/source/CPP/7zip/Crypto/RandGen.h ++++ b/Bin/p7zip/source/CPP/7zip/Crypto/RandGen.h +@@ -5,6 +5,21 @@ + + #include "../../../C/Sha256.h" + ++#ifdef _WIN64 ++// #define USE_STATIC_SYSTEM_RAND ++#endif ++ ++#ifdef USE_STATIC_SYSTEM_RAND ++ ++#ifdef _WIN32 ++#include ++#define MY_RAND_GEN(data, size) RtlGenRandom(data, size) ++#else ++#define MY_RAND_GEN(data, size) getrandom(data, size, 0) ++#endif ++ ++#else ++ + class CRandomGenerator + { + Byte _buff[SHA256_DIGEST_SIZE]; +@@ -18,4 +33,8 @@ public: + + extern CRandomGenerator g_RandomGenerator; + ++#define MY_RAND_GEN(data, size) g_RandomGenerator.Generate(data, size) ++ ++#endif ++ + #endif +diff --git a/Bin/p7zip/source/CPP/7zip/Crypto/WzAes.cpp b/Bin/p7zip/source/CPP/7zip/Crypto/WzAes.cpp +index 4572f06e..d415ab84 100644 +--- a/Bin/p7zip/source/CPP/7zip/Crypto/WzAes.cpp ++++ b/Bin/p7zip/source/CPP/7zip/Crypto/WzAes.cpp +@@ -96,7 +96,7 @@ STDMETHODIMP CBaseCoder::Init() + HRESULT CEncoder::WriteHeader(ISequentialOutStream *outStream) + { + unsigned saltSize = _key.GetSaltSize(); +- g_RandomGenerator.Generate(_key.Salt, saltSize); ++ MY_RAND_GEN(_key.Salt, saltSize); + Init2(); + RINOK(WriteStream(outStream, _key.Salt, saltSize)); + return WriteStream(outStream, _key.PwdVerifComputed, kPwdVerifSize); +diff --git a/Bin/p7zip/source/CPP/7zip/Crypto/ZipCrypto.cpp b/Bin/p7zip/source/CPP/7zip/Crypto/ZipCrypto.cpp +index ae715063..8610297a 100644 +--- a/Bin/p7zip/source/CPP/7zip/Crypto/ZipCrypto.cpp ++++ b/Bin/p7zip/source/CPP/7zip/Crypto/ZipCrypto.cpp +@@ -49,7 +49,7 @@ HRESULT CEncoder::WriteHeader_Check16(ISequentialOutStream *outStream, UInt16 cr + PKZIP 2.0+ used 1 byte CRC check. It's more secure. + We also use 1 byte CRC. */ + +- g_RandomGenerator.Generate(h, kHeaderSize - 1); ++ MY_RAND_GEN(h, kHeaderSize - 1); + // h[kHeaderSize - 2] = (Byte)(crc); + h[kHeaderSize - 1] = (Byte)(crc >> 8); + +-- +2.17.1 + diff --git a/deps/patches/p7zip-Windows_ErrorMsg.patch b/deps/patches/p7zip-Windows_ErrorMsg.patch new file mode 100644 index 0000000..71de3e9 --- /dev/null +++ b/deps/patches/p7zip-Windows_ErrorMsg.patch @@ -0,0 +1,33 @@ +This fixes the build with Clang 6.0: + + ../../../../CPP/Windows/ErrorMsg.cpp:24:10: error: case value evaluates to -2147024809, which cannot be narrowed to type 'DWORD' (aka 'unsigned int') [-Wc++11-narrowing] + case E_INVALIDARG : txt = "E_INVALIDARG"; break ; + ^ + ../../../../CPP/Common/MyWindows.h:89:22: note: expanded from macro 'E_INVALIDARG' + #define E_INVALIDARG ((HRESULT)0x80070057L) + ^ + +The HRESULT cast in the macro causes the value to be read as signed int. +--- CPP/Windows/ErrorMsg.cpp.orig 2015-01-18 18:20:28 UTC ++++ CPP/Windows/ErrorMsg.cpp +@@ -15,13 +15,13 @@ UString MyFormatMessage(DWORD errorCode) + + switch(errorCode) { + case ERROR_NO_MORE_FILES : txt = "No more files"; break ; +- case E_NOTIMPL : txt = "E_NOTIMPL"; break ; +- case E_NOINTERFACE : txt = "E_NOINTERFACE"; break ; +- case E_ABORT : txt = "E_ABORT"; break ; +- case E_FAIL : txt = "E_FAIL"; break ; +- case STG_E_INVALIDFUNCTION : txt = "STG_E_INVALIDFUNCTION"; break ; +- case E_OUTOFMEMORY : txt = "E_OUTOFMEMORY"; break ; +- case E_INVALIDARG : txt = "E_INVALIDARG"; break ; ++ case (DWORD)(E_NOTIMPL) : txt = "E_NOTIMPL"; break ; ++ case (DWORD)(E_NOINTERFACE) : txt = "E_NOINTERFACE"; break ; ++ case (DWORD)(E_ABORT) : txt = "E_ABORT"; break ; ++ case (DWORD)(E_FAIL) : txt = "E_FAIL"; break ; ++ case (DWORD)(STG_E_INVALIDFUNCTION) : txt = "STG_E_INVALIDFUNCTION"; break ; ++ case (DWORD)(E_OUTOFMEMORY) : txt = "E_OUTOFMEMORY"; break ; ++ case (DWORD)(E_INVALIDARG) : txt = "E_INVALIDARG"; break ; + case ERROR_DIRECTORY : txt = "Error Directory"; break ; + default: + txt = strerror(errorCode); diff --git a/deps/pcre.mk b/deps/pcre.mk new file mode 100644 index 0000000..d7832ad --- /dev/null +++ b/deps/pcre.mk @@ -0,0 +1,63 @@ +## PCRE ## + +ifneq ($(USE_BINARYBUILDER_PCRE),1) +# Force optimization for PCRE flags (Issue #11668) +PCRE_CFLAGS := -O3 +PCRE_LDFLAGS := $(RPATH_ESCAPED_ORIGIN) + +$(SRCCACHE)/pcre2-$(PCRE_VER).tar.bz2: | $(SRCCACHE) + $(JLDOWNLOAD) $@ https://ftp.pcre.org/pub/pcre/pcre2-$(PCRE_VER).tar.bz2 + +$(SRCCACHE)/pcre2-$(PCRE_VER)/source-extracted: $(SRCCACHE)/pcre2-$(PCRE_VER).tar.bz2 + $(JLCHECKSUM) $< + cd $(dir $<) && $(TAR) jxf $(notdir $<) + cp $(SRCDIR)/patches/config.sub $(SRCCACHE)/pcre2-$(PCRE_VER)/config.sub + touch -c $(SRCCACHE)/pcre2-$(PCRE_VER)/configure # old target + echo $1 > $@ + +$(BUILDDIR)/pcre2-$(PCRE_VER)/build-configured: $(SRCCACHE)/pcre2-$(PCRE_VER)/source-extracted + mkdir -p $(dir $@) + cd $(dir $@) && \ + $(dir $<)/configure $(CONFIGURE_COMMON) --enable-jit --includedir=$(build_includedir) CFLAGS="$(CFLAGS) $(PCRE_CFLAGS)" LDFLAGS="$(LDFLAGS) $(PCRE_LDFLAGS)" + echo 1 > $@ + +$(BUILDDIR)/pcre2-$(PCRE_VER)/build-compiled: $(BUILDDIR)/pcre2-$(PCRE_VER)/build-configured + $(MAKE) -C $(dir $<) $(LIBTOOL_CCLD) + echo 1 > $@ + +$(BUILDDIR)/pcre2-$(PCRE_VER)/build-checked: $(BUILDDIR)/pcre2-$(PCRE_VER)/build-compiled +ifeq ($(OS),$(BUILD_OS)) +ifneq ($(OS),WINNT) + $(MAKE) -C $(dir $@) check -j1 +endif +endif + echo 1 > $@ + +$(eval $(call staged-install, \ + pcre,pcre2-$$(PCRE_VER), \ + MAKE_INSTALL,$$(LIBTOOL_CCLD),, \ + rm $$(build_shlibdir)/libpcre2-posix.* && \ + $$(INSTALL_NAME_CMD)libpcre2-8.$$(SHLIB_EXT) $$(build_shlibdir)/libpcre2-8.$$(SHLIB_EXT))) + +clean-pcre: + -rm $(BUILDDIR)/pcre2-$(PCRE_VER)/build-configured $(BUILDDIR)/pcre2-$(PCRE_VER)/build-compiled + -$(MAKE) -C $(BUILDDIR)/pcre2-$(PCRE_VER) clean + +distclean-pcre: + -rm -rf $(SRCCACHE)/pcre2-$(PCRE_VER).tar.bz2 $(SRCCACHE)/pcre2-$(PCRE_VER) $(BUILDDIR)/pcre2-$(PCRE_VER) + + +get-pcre: $(SRCCACHE)/pcre2-$(PCRE_VER).tar.bz2 +extract-pcre: $(SRCCACHE)/pcre2-$(PCRE_VER)/source-extracted +configure-pcre: $(BUILDDIR)/pcre2-$(PCRE_VER)/build-configured +compile-pcre: $(BUILDDIR)/pcre2-$(PCRE_VER)/build-compiled +fastcheck-pcre: check-pcre +check-pcre: $(BUILDDIR)/pcre2-$(PCRE_VER)/build-checked + +else # USE_BINARYBUILDER_PCRE +PCRE_BB_URL_BASE := https://github.com/JuliaPackaging/Yggdrasil/releases/download/PCRE2-v$(PCRE_VER)-$(PCRE_BB_REL) +PCRE_BB_NAME := PCRE2.v$(PCRE_VER).0 + +$(eval $(call bb-install,pcre,PCRE,false)) + +endif diff --git a/deps/suitesparse.mk b/deps/suitesparse.mk new file mode 100644 index 0000000..b6fe277 --- /dev/null +++ b/deps/suitesparse.mk @@ -0,0 +1,157 @@ +## SUITESPARSE ## + +ifeq ($(USE_BLAS64), 1) +UMFPACK_CONFIG := -DLONGBLAS='long long' +CHOLMOD_CONFIG := -DLONGBLAS='long long' +SPQR_CONFIG := -DLONGBLAS='long long' +ifeq ($(OPENBLAS_SYMBOLSUFFIX), 64_) +UMFPACK_CONFIG += -DSUN64 +CHOLMOD_CONFIG += -DSUN64 +SPQR_CONFIG += -DSUN64 +endif +endif + +# Disable trying to link against libmetis +CHOLMOD_CONFIG += -DNPARTITION + +ifneq ($(USE_BINARYBUILDER_SUITESPARSE), 1) + +SUITESPARSE_PROJECTS := AMD BTF CAMD CCOLAMD COLAMD CHOLMOD LDL KLU UMFPACK RBio SPQR +SUITESPARSE_LIBS := $(addsuffix .*$(SHLIB_EXT)*,suitesparseconfig amd btf camd ccolamd colamd cholmod klu umfpack rbio spqr) + +SUITE_SPARSE_LIB := $(LDFLAGS) -L"$(abspath $(BUILDDIR))/SuiteSparse-$(SUITESPARSE_VER)/lib" +ifeq ($(OS), Darwin) +SUITE_SPARSE_LIB += $(RPATH_ESCAPED_ORIGIN) +endif +SUITESPARSE_MFLAGS := CC="$(CC)" CXX="$(CXX)" F77="$(FC)" AR="$(AR)" RANLIB="$(RANLIB)" BLAS="$(LIBBLAS)" LAPACK="$(LIBLAPACK)" \ + LDFLAGS="$(SUITE_SPARSE_LIB)" CFOPENMP="" CUDA=no CUDA_PATH="" \ + UMFPACK_CONFIG="$(UMFPACK_CONFIG)" CHOLMOD_CONFIG="$(CHOLMOD_CONFIG)" SPQR_CONFIG="$(SPQR_CONFIG)" +ifeq ($(OS),WINNT) +SUITESPARSE_MFLAGS += UNAME=Windows +else +SUITESPARSE_MFLAGS += UNAME=$(OS) +endif + +$(SRCCACHE)/SuiteSparse-$(SUITESPARSE_VER).tar.gz: | $(SRCCACHE) + $(JLDOWNLOAD) $@ https://github.com/DrTimothyAldenDavis/SuiteSparse/archive/v$(SUITESPARSE_VER).tar.gz + +$(BUILDDIR)/SuiteSparse-$(SUITESPARSE_VER)/source-extracted: $(SRCCACHE)/SuiteSparse-$(SUITESPARSE_VER).tar.gz + $(JLCHECKSUM) $< + mkdir -p $(dir $@) + $(TAR) -C $(dir $@) --strip-components 1 -zxf $< + echo 1 > $@ + +$(BUILDDIR)/SuiteSparse-$(SUITESPARSE_VER)/SuiteSparse-winclang.patch-applied: $(BUILDDIR)/SuiteSparse-$(SUITESPARSE_VER)/source-extracted + cd $(dir $@) && patch -p0 < $(SRCDIR)/patches/SuiteSparse-winclang.patch + echo 1 > $@ +$(BUILDDIR)/SuiteSparse-$(SUITESPARSE_VER)/SuiteSparse-shlib.patch-applied: $(BUILDDIR)/SuiteSparse-$(SUITESPARSE_VER)/source-extracted + cd $(dir $@) && patch -p1 < $(SRCDIR)/patches/SuiteSparse-shlib.patch + echo 1 > $@ +$(BUILDDIR)/SuiteSparse-$(SUITESPARSE_VER)/build-compiled: $(BUILDDIR)/SuiteSparse-$(SUITESPARSE_VER)/SuiteSparse-winclang.patch-applied +$(BUILDDIR)/SuiteSparse-$(SUITESPARSE_VER)/build-compiled: $(BUILDDIR)/SuiteSparse-$(SUITESPARSE_VER)/SuiteSparse-shlib.patch-applied + +ifeq ($(USE_SYSTEM_BLAS), 0) +$(BUILDDIR)/SuiteSparse-$(SUITESPARSE_VER)/build-compiled: | $(build_prefix)/manifest/openblas +else ifeq ($(USE_SYSTEM_LAPACK), 0) +$(BUILDDIR)/SuiteSparse-$(SUITESPARSE_VER)/build-compiled: | $(build_prefix)/manifest/lapack +endif + +$(BUILDDIR)/SuiteSparse-$(SUITESPARSE_VER)/build-compiled: $(BUILDDIR)/SuiteSparse-$(SUITESPARSE_VER)/source-extracted + $(MAKE) -C $(dir $<)SuiteSparse_config library config $(SUITESPARSE_MFLAGS) + $(INSTALL_NAME_CMD)libsuitesparseconfig.$(SHLIB_EXT) $(dir $<)lib/libsuitesparseconfig.$(SHLIB_EXT) + for PROJ in $(SUITESPARSE_PROJECTS); do \ + $(MAKE) -C $(dir $<)$${PROJ} library $(SUITESPARSE_MFLAGS) || exit 1; \ + $(INSTALL_NAME_CMD)lib`echo $${PROJ} | tr A-Z a-z`.$(SHLIB_EXT) $(dir $<)lib/lib`echo $${PROJ} | tr A-Z a-z`.$(SHLIB_EXT) || exit 1; \ + done + echo 1 > $@ + +ifeq ($(OS),WINNT) +SUITESPARSE_SHLIB_ENV:=PATH="$(abspath $(dir $<))lib:$(build_bindir):$(PATH)" +else +SUITESPARSE_SHLIB_ENV:=LD_LIBRARY_PATH="$(build_shlibdir)" +endif +$(BUILDDIR)/SuiteSparse-$(SUITESPARSE_VER)/build-checked: $(BUILDDIR)/SuiteSparse-$(SUITESPARSE_VER)/build-compiled + for PROJ in $(SUITESPARSE_PROJECTS); do \ + $(SUITESPARSE_SHLIB_ENV) $(MAKE) -C $(dir $<)$${PROJ} default $(SUITESPARSE_MFLAGS) || exit 1; \ + done + echo 1 > $@ + +$(build_prefix)/manifest/suitesparse: $(BUILDDIR)/SuiteSparse-$(SUITESPARSE_VER)/build-compiled | $(build_prefix)/manifest $(build_shlibdir) + for lib in $(SUITESPARSE_LIBS); do \ + cp -a $(dir $<)lib/lib$${lib} $(build_shlibdir) || exit 1; \ + done + #cp -a $(dir $<)lib/* $(build_shlibdir) + #cp -a $(dir $<)include/* $(build_includedir) + echo $(SUITESPARSE_VER) > $@ + +uninstall-suitesparse: + -rm $(build_prefix)/manifest/suitesparse + -rm $(addprefix $(build_shlibdir)/lib, $(SUITESPARSE_LIBS)) + +clean-suitesparse: clean-suitesparse-wrapper uninstall-suitesparse + -rm $(BUILDDIR)/SuiteSparse-$(SUITESPARSE_VER)/build-compiled + -rm -fr $(BUILDDIR)/SuiteSparse-$(SUITESPARSE_VER)/lib + -rm -fr $(BUILDDIR)/SuiteSparse-$(SUITESPARSE_VER)/include + -$(MAKE) -C $(BUILDDIR)/SuiteSparse-$(SUITESPARSE_VER) clean + +distclean-suitesparse: clean-suitesparse-wrapper + -rm -rf $(SRCCACHE)/SuiteSparse-$(SUITESPARSE_VER).tar.gz \ + $(BUILDDIR)/SuiteSparse-$(SUITESPARSE_VER) + +get-suitesparse: $(SRCCACHE)/SuiteSparse-$(SUITESPARSE_VER).tar.gz +extract-suitesparse: $(BUILDDIR)/SuiteSparse-$(SUITESPARSE_VER)/source-extracted +configure-suitesparse: extract-suitesparse +compile-suitesparse: $(BUILDDIR)/SuiteSparse-$(SUITESPARSE_VER)/build-compiled +fastcheck-suitesparse: #none +check-suitesparse: $(BUILDDIR)/SuiteSparse-$(SUITESPARSE_VER)/build-checked +install-suitesparse: $(build_prefix)/manifest/suitesparse install-suitesparse-wrapper + +# SUITESPARSE WRAPPER + +ifeq ($(USE_SYSTEM_SUITESPARSE), 1) +SUITESPARSE_INC := -I $(LOCALBASE)/include/suitesparse +SUITESPARSE_LIB := -lumfpack -lcholmod -lamd -lcamd -lcolamd -lspqr +else +SUITESPARSE_INC := -I $(BUILDDIR)/SuiteSparse-$(SUITESPARSE_VER)/CHOLMOD/Include -I $(BUILDDIR)/SuiteSparse-$(SUITESPARSE_VER)/SuiteSparse_config -I $(BUILDDIR)/SuiteSparse-$(SUITESPARSE_VER)/SPQR/Include +SUITESPARSE_LIB := -L$(build_shlibdir) -lcholmod -lumfpack -lspqr $(RPATH_ORIGIN) +$(build_shlibdir)/libsuitesparse_wrapper.$(SHLIB_EXT): $(build_prefix)/manifest/suitesparse +endif + +$(build_shlibdir)/libsuitesparse_wrapper.$(SHLIB_EXT): $(SRCDIR)/SuiteSparse_wrapper.c + mkdir -p $(build_shlibdir) + $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -O2 -shared $(fPIC) $(SUITESPARSE_INC) $< -o $@ $(SUITESPARSE_LIB) + $(INSTALL_NAME_CMD)libsuitesparse_wrapper.$(SHLIB_EXT) $@ + touch -c $@ + +clean-suitesparse-wrapper: + -rm -f $(build_shlibdir)/libsuitesparse_wrapper.$(SHLIB_EXT) + +distclean-suitesparse-wrapper: clean-suitesparse-wrapper + +get-suitesparse-wrapper: +extract-suitesparse-wrapper: +configure-suitesparse-wrapper: +compile-suitesparse-wrapper: +fastcheck-suitesparse-wrapper: #none +check-suitesparse-wrapper: +install-suitesparse-wrapper: $(build_shlibdir)/libsuitesparse_wrapper.$(SHLIB_EXT) + +else # USE_BINARYBUILDER_SUITESPARSE + +SUITESPARSE_BB_URL_BASE := https://github.com/JuliaBinaryWrappers/SuiteSparse_jll.jl/releases/download/SuiteSparse-v$(SUITESPARSE_VER)+$(SUITESPARSE_BB_REL) +SUITESPARSE_BB_NAME := SuiteSparse.v$(SUITESPARSE_VER) + +$(eval $(call bb-install,suitesparse,SUITESPARSE,false)) +get-suitesparse-wrapper: get-suitesparse +extract-suitesparse-wrapper: extract-suitesparse +configure-suitesparse-wrapper: configure-suitesparse +compile-suitesparse-wrapper: compile-suitesparse +fastcheck-suitesparse-wrapper: fastcheck-suitesparse +check-suitesparse-wrapper: check-suitesparse +clean-suitesparse-wrapper: clean-suitesparse +distclean-suitesparse-wrapper: distclean-suitesparse +install-suitesparse-wrapper: install-suitesparse + +# suitesparse depends on OpenBLAS +compile-suitesparse: | $(build_prefix)/manifest/openblas +endif diff --git a/deps/tools/bb-install.mk b/deps/tools/bb-install.mk new file mode 100644 index 0000000..67bead5 --- /dev/null +++ b/deps/tools/bb-install.mk @@ -0,0 +1,68 @@ +#$(call bb-install, \ +# 1 target, \ # name (lowercase) +# 2 variable, \ # name (uppercase) +# 3 gfortran, \ # signifies a GCC ABI (e.g. libgfortran version) dependency +# 4 cxx11) # signifies a cxx11 ABI dependency + +# Auto-detect triplet once, create different versions that we use as defaults below for each BB install target +BB_TRIPLET_LIBGFORTRAN_CXXABI := $(shell $(call invoke_python,$(JULIAHOME)/contrib/normalize_triplet.py) $(or $(XC_HOST),$(XC_HOST),$(BUILD_MACHINE)) "$(shell $(FC) --version | head -1)" "$(or $(shell echo '\#include ' | $(CXX) $(CXXFLAGS) -x c++ -dM -E - | grep _GLIBCXX_USE_CXX11_ABI | awk '{ print $$3 }' ),1)") +BB_TRIPLET_LIBGFORTRAN := $(subst $(SPACE),-,$(filter-out cxx%,$(subst -,$(SPACE),$(BB_TRIPLET_LIBGFORTRAN_CXXABI)))) +BB_TRIPLET_CXXABI := $(subst $(SPACE),-,$(filter-out libgfortran%,$(subst -,$(SPACE),$(BB_TRIPLET_LIBGFORTRAN_CXXABI)))) +BB_TRIPLET := $(subst $(SPACE),-,$(filter-out cxx%,$(filter-out libgfortran%,$(subst -,$(SPACE),$(BB_TRIPLET_LIBGFORTRAN_CXXABI))))) + +define bb-install +TRIPLET_VAR := BB_TRIPLET +ifeq ($(3),true) +TRIPLET_VAR := $$(TRIPLET_VAR)_LIBGFORTRAN +endif +ifeq ($(4),true) +TRIPLET_VAR := $$(TRIPLET_VAR)_CXXABI +endif +$(2)_BB_TRIPLET := $$($$(TRIPLET_VAR)) +$(2)_BB_URL := $$($(2)_BB_URL_BASE)/$$($(2)_BB_NAME).$$($(2)_BB_TRIPLET).tar.gz +$(2)_BB_BASENAME := $$($(2)_BB_NAME)-$$($(2)_BB_REL).$$($(2)_BB_TRIPLET).tar.gz + +$$(BUILDDIR)/$$($(2)_BB_NAME): + mkdir -p $$@ + +$$(SRCCACHE)/$$($(2)_BB_BASENAME): | $$(SRCCACHE) + $$(JLDOWNLOAD) $$@ $$($(2)_BB_URL) + +stage-$(strip $1): $$(SRCCACHE)/$$($(2)_BB_BASENAME) +install-$(strip $1): $$(build_prefix)/manifest/$(strip $1) + +reinstall-$(strip $1): + +$$(MAKE) uninstall-$(strip $1) + +$$(MAKE) stage-$(strip $1) + +$$(MAKE) install-$(strip $1) + +UNINSTALL_$(strip $1) := $$($(2)_BB_BASENAME:.tar.gz=) bb-uninstaller + +$$(build_prefix)/manifest/$(strip $1): $$(SRCCACHE)/$$($(2)_BB_BASENAME) | $(build_prefix)/manifest + -+[ ! -e $$@ ] || $$(MAKE) uninstall-$(strip $1) + $$(JLCHECKSUM) $$< + mkdir -p $$(build_prefix) + $(UNTAR) $$< -C $$(build_prefix) + echo '$$(UNINSTALL_$(strip $1))' > $$@ + +clean-bb-download-$(1): + rm -f $$(SRCCACHE)/$$($(2)_BB_BASENAME) + +clean-$(1): +distclean-$(1): clean-bb-download-$(1) +get-$(1): $$(SRCCACHE)/$$($(2)_BB_BASENAME) +extract-$(1): +configure-$(1): +compile-$(1): get-$(1) +fastcheck-$(1): +check-$(1): + +.PHONY: clean-bb-$(1) + +endef + +define bb-uninstaller +uninstall-$(strip $1): + -cd $$(build_prefix) && rm -fdv -- $$$$($$(TAR) -tzf $$(SRCCACHE)/$2.tar.gz --exclude './$$$$') + -rm $$(build_prefix)/manifest/$(strip $1) +endef diff --git a/deps/tools/common.mk b/deps/tools/common.mk new file mode 100644 index 0000000..9af4966 --- /dev/null +++ b/deps/tools/common.mk @@ -0,0 +1,239 @@ +## Some shared configuration options ## + +# NOTE: Do not make RPATH changes in CMAKE_COMMON on platforms other than FreeBSD, since +# it will make its way into the LLVM build flags, and LLVM is picky about RPATH (though +# apparently not on FreeBSD). Ref PR #22352 + +CONFIGURE_COMMON := --prefix=$(abspath $(build_prefix)) --build=$(BUILD_MACHINE) --libdir=$(abspath $(build_libdir)) --bindir=$(abspath $(build_depsbindir)) $(CUSTOM_LD_LIBRARY_PATH) +ifneq ($(XC_HOST),) +CONFIGURE_COMMON += --host=$(XC_HOST) +endif +ifeq ($(OS),WINNT) +ifneq ($(USEMSVC), 1) +CONFIGURE_COMMON += LDFLAGS="$(LDFLAGS) -Wl,--stack,8388608" +endif +else +CONFIGURE_COMMON += LDFLAGS="$(LDFLAGS) $(RPATH_ESCAPED_ORIGIN)" +endif +CONFIGURE_COMMON += F77="$(FC)" CC="$(CC)" CXX="$(CXX)" LD="$(LD)" + +CMAKE_CC_ARG := $(CC_ARG) +CMAKE_CXX_ARG := $(CXX_ARG) + +CMAKE_COMMON := -DCMAKE_INSTALL_PREFIX:PATH=$(build_prefix) -DCMAKE_PREFIX_PATH=$(build_prefix) +CMAKE_COMMON += -DCMAKE_INSTALL_LIBDIR=$(build_libdir) -DCMAKE_INSTALL_BINDIR=$(build_bindir) +CMAKE_COMMON += -DLIB_INSTALL_DIR=$(build_shlibdir) +ifneq ($(VERBOSE), 0) +CMAKE_COMMON += -DCMAKE_VERBOSE_MAKEFILE=ON +endif +# The call to which here is to work around https://cmake.org/Bug/view.php?id=14366 +CMAKE_COMMON += -DCMAKE_C_COMPILER="$$(which $(CC_BASE))" +ifneq ($(strip $(CMAKE_CC_ARG)),) +CMAKE_COMMON += -DCMAKE_C_COMPILER_ARG1="$(CMAKE_CC_ARG)" +endif +CMAKE_COMMON += -DCMAKE_CXX_COMPILER="$(CXX_BASE)" +ifneq ($(strip $(CMAKE_CXX_ARG)),) +CMAKE_COMMON += -DCMAKE_CXX_COMPILER_ARG1="$(CMAKE_CXX_ARG)" +endif +CMAKE_COMMON += -DCMAKE_LINKER="$(LD)" -DCMAKE_AR="$(shell which $(AR))" -DCMAKE_RANLIB="$(shell which $(RANLIB))" + +ifeq ($(OS),WINNT) +CMAKE_COMMON += -DCMAKE_SYSTEM_NAME=Windows +ifneq ($(BUILD_OS),WINNT) +CMAKE_COMMON += -DCMAKE_RC_COMPILER="$$(which $(CROSS_COMPILE)windres)" +endif +endif + +# For now this is LLVM specific, but I expect it won't be in the future +ifeq ($(CMAKE_GENERATOR),Ninja) +CMAKE_GENERATOR_COMMAND := -G Ninja +else ifeq ($(CMAKE_GENERATOR),make) +CMAKE_GENERATOR_COMMAND := -G "Unix Makefiles" +else +$(error Unknown CMake generator '$(CMAKE_GENERATOR)'. Options are 'Ninja' and 'make') +endif + +# If the top-level Makefile is called with environment variables, +# they will override the values passed above to ./configure +MAKE_COMMON := DESTDIR="" prefix=$(build_prefix) bindir=$(build_depsbindir) libdir=$(build_libdir) shlibdir=$(build_shlibdir) libexecdir=$(build_libexecdir) datarootdir=$(build_datarootdir) includedir=$(build_includedir) sysconfdir=$(build_sysconfdir) O= + +#Platform specific flags + +ifeq ($(OS), WINNT) +LIBTOOL_CCLD := CCLD="$(CC) -no-undefined -avoid-version" +endif + +# Cross-deps flags + +USE_BLAS_FFLAGS := +ifeq ($(USE_BLAS64), 1) +ifeq ($(USEIFC),1) +USE_BLAS_FFLAGS += -i8 +else +USE_BLAS_FFLAGS += -fdefault-integer-8 +endif +endif + +ifeq ($(USE_INTEL_MKL),1) +# We want to test if gfortran is used but currently only gfortran and ifort are supported +# so not ifort is the same as gfortran. If support for new Fortran compilers is added +# then this should be adjusted +ifneq ($(USEIFC),1) +USE_BLAS_FFLAGS += -ff2c +endif +endif + +ifeq ($(OS),Darwin) +ifeq ($(USE_SYSTEM_BLAS),1) +ifeq ($(USE_SYSTEM_LAPACK),0) +USE_BLAS_FFLAGS += -cpp -ffree-line-length-0 -ffixed-line-length-0 \ + -Dsasum=sasum_gfort -Dscasum=scasum_gfort \ + -Dscnrm2=scnrm2_gfort -Dsdot=sdot_gfort \ + -Dsdsdot=sdsdot_gfort -Dsnrm2=snrm2_gfort \ + -Dcdotc=cdotc_gfort -Dcdotu=cdotu_gfort \ + -Dzdotc=zdotc_gfort -Dzdotu=zdotu_gfort \ + \ + -DSASUM=SASUM_GFORT -DSCASUM=SCASUM_GFORT \ + -DSCNRM2=SCNRM2_GFORT -DSDOT=SDOT_GFORT \ + -DSDSDOT=SDSDOT_GFORT -DSNRM2=SNRM2_GFORT \ + -DCDOTC=CDOTC_GFORT -DCDOTU=CDOTU_GFORT \ + -DZDOTC=ZDOTC_GFORT -DZDOTU=ZDOTU_GFORT +endif +endif +endif + + +## PATHS ## +# sort is used to remove potential duplicates +DIRS := $(sort $(build_bindir) $(build_depsbindir) $(build_libdir) $(build_includedir) $(build_sysconfdir) $(build_datarootdir) $(build_staging) $(build_prefix)/manifest) + +$(foreach dir,$(DIRS),$(eval $(call dir_target,$(dir)))) + +$(build_prefix): | $(DIRS) +$(eval $(call dir_target,$(SRCCACHE))) + + +upper = $(shell echo $1 | tr a-z A-Z) + + +## A rule for calling `make install` ## +# example usage: +# $(call staged-install, \ +# 1 target, \ # name +# 2 rel-build-directory, \ # BUILDDIR-relative path to binaries +# 3 MAKE_INSTALL, \ # will be called with args SRCDIR,DESTDIR,$4 +# 4 add-args, \ # extra args for $3 +# 5 (unused), \ +# 6 post-install) # post-install commands +# +# this rule ensures that make install is more nearly atomic +# so it's harder to get half-installed (or half-reinstalled) dependencies +# # and enables sharing deps compiles, uninstall, and fast reinstall +MAKE_INSTALL = +$$(MAKE) -C $1 install $$(MAKE_COMMON) $3 DESTDIR="$2" + +define SHLIBFILE_INSTALL + mkdir -p $2/$$(build_shlibdir) + cp $3 $2/$$(build_shlibdir) +endef + +define BINFILE_INSTALL + mkdir -p $2/$$(build_depsbindir) + cp $3 $2/$$(build_depsbindir) +endef + +define staged-install +stage-$(strip $1): $$(build_staging)/$2.tgz +install-$(strip $1): $$(build_prefix)/manifest/$(strip $1) + +ifeq (exists, $$(shell [ -e $$(build_staging)/$2.tgz ] && echo exists )) +# clean depends on uninstall only if the staged file exists +distclean-$(strip $1) clean-$(strip $1): uninstall-$(strip $1) +else +# uninstall depends on staging only if the staged file doesn't exist +# otherwise, uninstall doesn't actually want the file to be updated first +uninstall-$(strip $1): | $$(build_staging)/$2.tgz +endif + +reinstall-$(strip $1): + +$$(MAKE) uninstall-$(strip $1) + -rm $$(build_staging)/$2.tgz + +$$(MAKE) stage-$(strip $1) + +$$(MAKE) install-$(strip $1) + +$$(build_staging)/$2.tgz: $$(BUILDDIR)/$2/build-compiled + rm -rf $$(build_staging)/$2 + mkdir -p $$(build_staging)/$2$$(build_prefix) + $(call $3,$$(BUILDDIR)/$2,$$(build_staging)/$2,$4) + cd $$(build_staging)/$2$$(build_prefix) && tar -czf $$@.tmp . + rm -rf $$(build_staging)/$2 + mv $$@.tmp $$@ + +UNINSTALL_$(strip $1) := $2 staged-uninstaller + +$$(build_prefix)/manifest/$(strip $1): $$(build_staging)/$2.tgz | $(build_prefix)/manifest + -+[ ! -e $$@ ] || $$(MAKE) uninstall-$(strip $1) + mkdir -p $$(build_prefix) + $(UNTAR) $$< -C $$(build_prefix) + $6 + echo '$$(UNINSTALL_$(strip $1))' > $$@ +endef + +define staged-uninstaller +uninstall-$(strip $1): + -cd $$(build_prefix) && rm -fdv -- $$$$($$(TAR) -tzf $$(build_staging)/$2.tgz --exclude './$$$$') + -rm $$(build_prefix)/manifest/$(strip $1) +endef + + +## A rule for "installing" via a symlink ## +# example usage: +# $(call symlink_install, \ +# 1 target, \ # name +# 2 rel-build-directory, \ # BUILDDIR-relative path to content folder +# 3 abs-target-directory) # absolute path to installation folder for symlink `name` +define symlink_install # (target-name, rel-from, abs-to) +clean-$1: uninstall-$1 +install-$1: $$(build_prefix)/manifest/$1 +reinstall-$1: install-$1 + +UNINSTALL_$(strip $1) := $2 symlink-uninstaller $3 + +$$(build_prefix)/manifest/$1: $$(BUILDDIR)/$2/build-compiled | $3 $$(build_prefix)/manifest + -+[ ! \( -e $3/$1 -o -h $3/$1 \) ] || $$(MAKE) uninstall-$1 +ifeq ($$(BUILD_OS), WINNT) + cmd //C mklink //J $$(call mingw_to_dos,$3/$1,cd $3 &&) $$(call mingw_to_dos,$$(BUILDDIR)/$2,) +else ifneq (,$$(findstring CYGWIN,$$(BUILD_OS))) + cmd /C mklink /J $$(call cygpath_w,$3/$1) $$(call cygpath_w,$$(BUILDDIR)/$2) +else ifdef JULIA_VAGRANT_BUILD + cp -R $$(BUILDDIR)/$2 $3/$1 +else + ln -sf $$(abspath $$(BUILDDIR)/$2) $3/$1 +endif + echo '$$(UNINSTALL_$(strip $1))' > $$@ +endef + +define symlink-uninstaller +uninstall-$1: +ifeq ($$(BUILD_OS), WINNT) + -cmd //C rmdir $$(call mingw_to_dos,$3/$1,cd $3 &&) +else + -rm -r $3/$1 +endif + -rm $$(build_prefix)/manifest/$1 +endef + + +ifneq (bsdtar,$(findstring bsdtar,$(TAR_TEST))) +#gnu tar +UNTAR = $(TAR) -xmzf +else +#bsd tar +UNTAR = $(TAR) -xmUzf +endif + + +## phony targets ## + +.PHONY: default get extract configure compile fastcheck check install uninstall reinstall cleanall distcleanall \ + get-* extract-* configure-* compile-* fastcheck-* check-* install-* uninstall-* reinstall-* clean-* distclean-* \ + update-llvm diff --git a/deps/tools/git-external.mk b/deps/tools/git-external.mk new file mode 100644 index 0000000..7a88f1a --- /dev/null +++ b/deps/tools/git-external.mk @@ -0,0 +1,75 @@ +## A rule for making a git-external dependency ## +# call syntax: +# $(eval $(call git-external,dirname,VARNAME,file_from_download,file_from_compile,SRCDIR) +# dirname is the folder name to create +# VARNAME is the uppercased variable name prefix +# file_from_download (deprecated) +# file_from_compile (deprecated) +# SRCDIR is either $(SRCCACHE) or $(BUILDDIR), depending on whether the target supports out-of-tree builds +# +# also, in a file named dirname.version, define variables VARNAME_BRANCH and VARNAME_SHA1 +# +# also, define a Makefile variable VARNAME_GIT_URL to use as the clone origin +# and a Makefile variable VARNAME_TAR_URL to use as the clone origin when DEPS_GIT is set +# this will pass along the VARNAME_SHA1 target in $1 for its use +# +# this defines rules for: +# VARNAME_SRC_DIR = source directory for the output, relative to $(SRCCACHE) or $(BUILDDIR) +# VARNAME_SRC_FILE = target file for make get-VARNAME target +# dirname: +# dirname/source-extracted: +# distclean-dirname: +# +define git-external +include $$(SRCDIR)/$1.version +$2_SHA1 := $$(strip $$($2_SHA1)) +$2_BRANCH := $$(strip $$($2_BRANCH)) + +ifneq (,$$(filter $1 1,$$(DEPS_GIT))) +$2_SRC_DIR := $1 +$2_SRC_FILE := $$(SRCCACHE)/$1.git +$$($2_SRC_FILE)/HEAD: | $$(SRCCACHE) + git clone -q --mirror --branch $$($2_BRANCH) $$($2_GIT_URL) $$(dir $$@) +$5/$1/.git/HEAD: | $$($2_SRC_FILE)/HEAD + # try to update the cache, if that fails, attempt to continue anyways (the ref might already be local) + -cd $$($2_SRC_FILE) && git fetch -q $$($2_GIT_URL) $$($2_BRANCH):remotes/origin/$$($2_BRANCH) + git clone -q --depth=10 --branch $$($2_BRANCH) $$($2_SRC_FILE) $5/$1 + cd $5/$1 && git remote set-url origin $$($2_GIT_URL) +#ifneq ($3,) + touch -c $5/$1/$3 # old target +#endif + echo 1 > $5/$1/source-extracted +ifneq ($5,$$(BUILDDIR)) +$$(BUILDDIR)/$1: + mkdir -p $$@ +$5/$1/source-extracted: | $$(BUILDDIR)/$1 +endif +$5/$1/source-extracted: $$(SRCDIR)/$1.version | $5/$1/.git/HEAD + # try to update the cache, if that fails, attempt to continue anyways (the ref might already be local) + -cd $$(SRCCACHE)/$1.git && git fetch -q $$($2_GIT_URL) $$($2_BRANCH):remotes/origin/$$($2_BRANCH) + cd $5/$1 && git fetch -q $$(SRCCACHE)/$1.git remotes/origin/$$($2_BRANCH):remotes/origin/$$($2_BRANCH) + cd $5/$1 && git checkout -q --detach $$($2_SHA1) + @[ '$$($2_SHA1)' = "$$$$(cd $5/$1 && git show -s --format='%H' HEAD)" ] || echo $$(WARNCOLOR)'==> warning: SHA1 hash did not match $1.version file'$$(ENDCOLOR) + echo 1 > $$@ +$5/$1/source-compiled: $5/$1/.git/HEAD +$$($2_SRC_FILE): | $$($2_SRC_FILE)/HEAD + touch -c $$@ + +else # DEPS_GIT=0 + +$2_SRC_DIR := $1-$$($2_SHA1) +$2_SRC_FILE := $$(SRCCACHE)/$$($2_SRC_DIR).tar.gz +$$($2_SRC_FILE): | $$(SRCCACHE) + $$(JLDOWNLOAD) $$@ $$(call $2_TAR_URL,$$($2_SHA1)) +$5/$$($2_SRC_DIR)/source-extracted: $$($2_SRC_FILE) + $$(JLCHECKSUM) $$< + -[ ! \( -e $$(dir $$@) -o -h $$(dir $$@) \) ] || rm -r $$(dir $$@) + mkdir -p $$(dir $$@) + $(TAR) -C $$(dir $$@) --strip-components 1 -xf $$< + echo 1 > $$@ +endif # DEPS_GIT + +$$(build_prefix)/manifest/$1: $$(SRCDIR)/$1.version # make the manifest stale if the version file is touched (causing re-install for compliant targets) +distclean-$1: + -rm -rf $5/$$($2_SRC_DIR) $$($2_SRC_FILE) $$(BUILDDIR)/$$($2_SRC_DIR) +endef diff --git a/deps/tools/jlchecksum b/deps/tools/jlchecksum new file mode 100755 index 0000000..93ef872 --- /dev/null +++ b/deps/tools/jlchecksum @@ -0,0 +1,108 @@ +#!/bin/sh +# +# usage: jlchecksum +# + +if [ -z "$1" ]; then + echo "Usage: ./jlchecksum " >&2 + exit 1 +fi + +# Get the deps directory, one level up from this script +DEPSDIR="$( cd "$( dirname "$0" )"/.. && pwd )" + +# Get the basename of the file we're trying to checksum +ARG1=$1 +BASENAME=$(basename $1) + +# Print out a hash, and wrap around if we're longer than 64 characters +print_hash() +{ + if [ ${#1} -gt 64 ]; then + NUM_LINES=$(( (${#1} + 63) / 64)) + for i in `seq 0 1 $((NUM_LINES - 1))`; do + str_piece=$(echo "$1" | awk "{ string=substr(\$0, $((i*64 + 1)), $(((i+1)*64))); print string; }") + echo " $str_piece" + done + else + echo " $1" >&2 + fi +} + +checksum_error() +{ + echo "===============================================================================" >&2 + echo " ERROR: $CHECKSUM_TYPE checksum failure on $BASENAME, should be:" >&2 + print_hash "$TRUE_CHECKSUM" + echo " But \`$CHECKSUM_PROG\` results in:" >&2 + print_hash "$CURR_CHECKSUM" + echo " This can happen due to bad downloads or network proxies, please check your" >&2 + echo " network proxy/firewall settings and delete" >&2 + echo " $(realpath $ARG1)" >&2 + echo " to force a redownload when you are ready" >&2 + echo "===============================================================================" >&2 + exit 2 +} + +find_checksum() +{ + if [ ! -f "$DEPSDIR/checksums/$BASENAME/$CHECKSUM_TYPE" ]; then + echo "WARNING: $CHECKSUM_TYPE checksum for $BASENAME not found in deps/checksums/, autogenerating..." >&2 + + # Generate as many checksum types as we can + mkdir -p "$DEPSDIR/checksums/$BASENAME" + if [ ! -z "$MD5_PROG" ]; then + echo $(eval $MD5_PROG) > "$DEPSDIR/checksums/$BASENAME/md5" + fi + if [ ! -z "$SHA512_PROG" ]; then + echo $(eval $SHA512_PROG) > "$DEPSDIR/checksums/$BASENAME/sha512" + fi + fi + + TRUE_CHECKSUM=$(cat "$DEPSDIR/checksums/$BASENAME/$CHECKSUM_TYPE") +} + +# These are the programs we run to hash a file +SHA512_PROG="" +MD5_PROG="" +find_checksum_progs() +{ + if [ ! -z $(which sha512sum) ]; then + SHA512_PROG="sha512sum $ARG1 | awk '{ print \$1; }'" + elif [ ! -z $(which shasum) ]; then + SHA512_PROG="shasum -a 512 $ARG1 | awk '{ print \$1; }'" + fi + + if [ ! -z $(which md5sum) ]; then + MD5_PROG="md5sum $ARG1 | awk '{ print \$1; }'" + elif [ ! -z $(which md5) ]; then + MD5_PROG="md5 -q $ARG1" + fi +} + + +# Search for checksum programs and store them into SHA512_PROG, MD5_PROG, etc... +find_checksum_progs + +# Choose final checksumming program, preferring SHA512 over MD5 +if [ ! -z "$SHA512_PROG" ]; then + CHECKSUM_TYPE="sha512" + CHECKSUM_PROG=$SHA512_PROG +elif [ ! -z "$MD5_PROG" ]; then + CHECKSUM_TYPE="md5" + CHECKSUM_PROG=$MD5_PROG +else + echo "WARNING: No checksumming programs found, cannot verify downloads" >&2 + exit 0 +fi + +# Find true checksum +find_checksum "$CHECKSUM_TYPE" + +# Calculate actual checksum +CURR_CHECKSUM=$(eval $CHECKSUM_PROG) + +# Make sure they're the same. If not, error out! +if [ "$TRUE_CHECKSUM" != "$CURR_CHECKSUM" ]; then + checksum_error +fi diff --git a/deps/tools/jldownload b/deps/tools/jldownload new file mode 100755 index 0000000..9982219 --- /dev/null +++ b/deps/tools/jldownload @@ -0,0 +1,47 @@ +#!/bin/sh +# +# usage: jldownload [] +# + +CACHE_HOST=https://cache.julialang.org + +WGET=$(which wget 2>/dev/null) +CURL=$(which curl 2>/dev/null) +FETCH=$(which fetch 2>/dev/null) + +TIMEOUT=15 # seconds +WGET_OPTS="--no-check-certificate --tries=1 --timeout=$TIMEOUT" +CURL_OPTS="-fkL --connect-timeout $TIMEOUT -y $TIMEOUT" +FETCH_OPTS="-T $TIMEOUT" + +if [ $# -eq 1 ]; then + CURL_OPTS="$CURL_OPTS -O" + URL=$1 +elif [ $# -eq 2 ]; then + WGET_OPTS="$WGET_OPTS -O $1" + CURL_OPTS="$CURL_OPTS -o $1" + FETCH_OPTS="$FETCH_OPTS -o $1" + URL=$2 +else + exit 1 +fi + +CACHE_URL="$CACHE_HOST/$URL" + +if [ -x "$CURL" ] && $CURL -V >/dev/null; then + GETURL="$CURL $CURL_OPTS" +elif [ -x "$WGET" ] && $WGET --help >/dev/null 2>&1; then + GETURL="$WGET $WGET_OPTS" +elif [ -x "$FETCH" ]; then + GETURL="$FETCH $FETCH_OPTS" +else + echo "Could not find working curl, wget, or fetch." + echo "You need to install one of these to download dependencies." + exit 1 +fi + +# Try to get from the cache if it is possible. Note that the cache will +# forward to the original URL if it has not cached this download yet, or +# if the URL is not cacheable. We fallback to directly querying the +# uncached URL to protect against cache service downtime +$GETURL $CACHE_URL || $GETURL $URL diff --git a/deps/tools/stdlib-external.mk b/deps/tools/stdlib-external.mk new file mode 100644 index 0000000..043a533 --- /dev/null +++ b/deps/tools/stdlib-external.mk @@ -0,0 +1,27 @@ +# Define a set of targets for downloading, caching, and building the source of +# a stdlib library. The commit and git branch should be defined in a file +# $stdlib_name.version in the current directory. See the git-external macro for +# additional documentation. +# +# Parameters to the stdlib-external macro: +# +# $1 = stdlib_name +# $2 = var_prefix (by convention, use upper cased stdlib_name) + +include $(JULIAHOME)/deps/tools/git-external.mk + +define stdlib-external + +$$(eval $$(call git-external,$1,$2,,,$$(BUILDDIR))) +$$(BUILDDIR)/$$($2_SRC_DIR)/build-compiled: $$(BUILDDIR)/$$($2_SRC_DIR)/source-extracted + @# no build steps + echo 1 > $$@ +$$(eval $$(call symlink_install,$1,$$$$($2_SRC_DIR),$$$$(build_datarootdir)/julia/stdlib/$$$$(VERSDIR))) +clean-$1: + -rm $$(BUILDDIR)/$$($2_SRC_DIR)/build-compiled +get-$1: $$($2_SRC_FILE) +extract-$1: $$(BUILDDIR)/$$($2_SRC_DIR)/source-extracted +configure-$1: extract-$1 +compile-$1: $$(BUILDDIR)/$$($2_SRC_DIR)/build-compiled + +endef diff --git a/deps/tools/uninstallers.mk b/deps/tools/uninstallers.mk new file mode 100644 index 0000000..421626f --- /dev/null +++ b/deps/tools/uninstallers.mk @@ -0,0 +1,31 @@ +# include after all other files: +# defines uninstallers and version-checks +# based on the contents of the UNINSTALL_* variables and the manifest files + +install: version-check +version-check: $(addprefix version-check-, $(DEP_LIBS_STAGED)) +uninstall: $(addprefix uninstall-, $(DEP_LIBS_STAGED)) + +## read 'uninstall-*' definition from either the manifest or the current session +define define-uninstaller +MANIFEST_$1 := $$(shell [ -e $$(build_prefix)/manifest/$1 ] && cat $$(build_prefix)/manifest/$1) +ifeq (undefined,$$(flavor $$(word 2,$$(MANIFEST_$1)))) +MANIFEST_$1 := $$(UNINSTALL_$1) +endif +UNINST_HOW_$1 := $$(word 2,$$(MANIFEST_$1)) +ifneq ($$(UNINST_HOW_$1),) +UNINST_WHO_$1 := $$(firstword $$(MANIFEST_$1)) +UNINST_WHERE_$1 := $$(wordlist 3,99,$$(MANIFEST_$1)) +$$(eval $$(call $$(UNINST_HOW_$1),$1,$$(UNINST_WHO_$1),$$(UNINST_WHERE_$1))) +endif +endef +$(foreach dep,$(DEP_LIBS_STAGED),$(eval $(call define-uninstaller,$(dep)))) + +# for each subproject with a manifest, keep the user aware if something is not the expected version +$(addprefix version-check-,$(DEP_LIBS_STAGED)) : version-check-% : install-% + @if [ ! -e $(build_prefix)/manifest/$* ] || ( \ + [ "1" != "`wc -w $(build_prefix)/manifest/$* | cut -f 1 -d ' '`" ] && \ + [ "$(UNINSTALL_$*)" != "`cat $(build_prefix)/manifest/$*`" ]) ; then \ + echo "WARNING: using mismatched version for $$(cat $(build_prefix)/manifest/$*):" ; \ + echo " want $(UNINSTALL_$*)" ; \ + fi diff --git a/deps/unwind.mk b/deps/unwind.mk new file mode 100644 index 0000000..08d8990 --- /dev/null +++ b/deps/unwind.mk @@ -0,0 +1,117 @@ +## UNWIND ## + +ifneq ($(USE_BINARYBUILDER_LIBUNWIND),1) +LIBUNWIND_CFLAGS := -U_FORTIFY_SOURCE $(fPIC) +LIBUNWIND_CPPFLAGS := + +$(SRCCACHE)/libunwind-$(UNWIND_VER).tar.gz: | $(SRCCACHE) + $(JLDOWNLOAD) $@ https://github.com/libunwind/libunwind/releases/download/v$(UNWIND_VER)/libunwind-$(UNWIND_VER).tar.gz + +$(SRCCACHE)/libunwind-$(UNWIND_VER)/source-extracted: $(SRCCACHE)/libunwind-$(UNWIND_VER).tar.gz + $(JLCHECKSUM) $< + cd $(dir $<) && $(TAR) xfz $< + touch -c $(SRCCACHE)/libunwind-$(UNWIND_VER)/configure # old target + echo 1 > $@ + +$(SRCCACHE)/libunwind-$(UNWIND_VER)/libunwind-prefer-extbl.patch-applied: $(SRCCACHE)/libunwind-$(UNWIND_VER)/source-extracted + cd $(SRCCACHE)/libunwind-$(UNWIND_VER) && patch -p1 -f < $(SRCDIR)/patches/libunwind-prefer-extbl.patch + echo 1 > $@ + +$(SRCCACHE)/libunwind-$(UNWIND_VER)/libunwind-static-arm.patch-applied: $(SRCCACHE)/libunwind-$(UNWIND_VER)/libunwind-prefer-extbl.patch-applied + cd $(SRCCACHE)/libunwind-$(UNWIND_VER) && patch -p1 -f < $(SRCDIR)/patches/libunwind-static-arm.patch + echo 1 > $@ + +$(BUILDDIR)/libunwind-$(UNWIND_VER)/build-configured: $(SRCCACHE)/libunwind-$(UNWIND_VER)/source-extracted $(SRCCACHE)/libunwind-$(UNWIND_VER)/libunwind-static-arm.patch-applied + mkdir -p $(dir $@) + cd $(dir $@) && \ + $(dir $<)/configure $(CONFIGURE_COMMON) CPPFLAGS="$(CPPFLAGS) $(LIBUNWIND_CPPFLAGS)" CFLAGS="$(CFLAGS) $(LIBUNWIND_CFLAGS)" --disable-shared --disable-minidebuginfo --disable-tests + echo 1 > $@ + +$(BUILDDIR)/libunwind-$(UNWIND_VER)/build-compiled: $(BUILDDIR)/libunwind-$(UNWIND_VER)/build-configured + $(MAKE) -C $(dir $<) + echo 1 > $@ + +$(BUILDDIR)/libunwind-$(UNWIND_VER)/build-checked: $(BUILDDIR)/libunwind-$(UNWIND_VER)/build-compiled +ifeq ($(OS),$(BUILD_OS)) + $(MAKE) -C $(dir $@) check +endif + echo 1 > $@ + +$(eval $(call staged-install, \ + unwind,libunwind-$(UNWIND_VER), \ + MAKE_INSTALL,,,)) + +clean-unwind: + -rm $(BUILDDIR)/libunwind-$(UNWIND_VER)/build-configured $(BUILDDIR)/libunwind-$(UNWIND_VER)/build-compiled + -$(MAKE) -C $(BUILDDIR)/libunwind-$(UNWIND_VER) clean + +distclean-unwind: + -rm -rf $(SRCCACHE)/libunwind-$(UNWIND_VER).tar.gz \ + $(SRCCACHE)/libunwind-$(UNWIND_VER) \ + $(BUILDDIR)/libunwind-$(UNWIND_VER) + +get-unwind: $(SRCCACHE)/libunwind-$(UNWIND_VER).tar.gz +extract-unwind: $(SRCCACHE)/libunwind-$(UNWIND_VER)/source-extracted +configure-unwind: $(BUILDDIR)/libunwind-$(UNWIND_VER)/build-configured +compile-unwind: $(BUILDDIR)/libunwind-$(UNWIND_VER)/build-compiled +#todo: libunwind tests are known to fail, so they aren't run +fastcheck-unwind: #none +check-unwind: $(BUILDDIR)/libunwind-$(UNWIND_VER)/build-checked + + +## OS X Unwind ## + +OSXUNWIND_FLAGS := ARCH="$(ARCH)" CC="$(CC)" FC="$(FC)" AR="$(AR)" OS="$(OS)" USECLANG=$(USECLANG) USEGCC=$(USEGCC) CFLAGS="$(CFLAGS) -ggdb3 -O0" CXXFLAGS="$(CXXFLAGS) -ggdb3 -O0" SFLAGS="-ggdb3" LDFLAGS="$(LDFLAGS) -Wl,-macosx_version_min,10.7" + +$(SRCCACHE)/libosxunwind-$(OSXUNWIND_VER).tar.gz: | $(SRCCACHE) + $(JLDOWNLOAD) $@ https://github.com/JuliaLang/libosxunwind/archive/v$(OSXUNWIND_VER).tar.gz + +$(BUILDDIR)/libosxunwind-$(OSXUNWIND_VER)/source-extracted: $(SRCCACHE)/libosxunwind-$(OSXUNWIND_VER).tar.gz + $(JLCHECKSUM) $< + mkdir -p $(BUILDDIR) + cd $(BUILDDIR) && $(TAR) xfz $< + echo 1 > $@ + +$(BUILDDIR)/libosxunwind-$(OSXUNWIND_VER)/build-compiled: $(BUILDDIR)/libosxunwind-$(OSXUNWIND_VER)/source-extracted + $(MAKE) -C $(dir $<) $(OSXUNWIND_FLAGS) + echo 1 > $@ + +$(build_prefix)/manifest/osxunwind: $(BUILDDIR)/libosxunwind-$(OSXUNWIND_VER)/build-compiled | $(build_libdir) $(build_shlibdir) $(build_includedir) $(build_prefix)/manifest + cp $(dir $<)/libosxunwind.a $(build_libdir)/libosxunwind.a + cp $(dir $<)/libosxunwind.$(SHLIB_EXT) $(build_shlibdir)/libosxunwind.$(SHLIB_EXT) + cp -R $(dir $<)/include/* $(build_includedir) + $(INSTALL_NAME_CMD)libosxunwind.$(SHLIB_EXT) $(build_shlibdir)/libosxunwind.$(SHLIB_EXT) + echo $(OSXUNWIND_VER) > $(build_prefix)/manifest/osxunwind + +clean-osxunwind: + -rm $(build_prefix)/manifest/osxunwind $(BUILDDIR)/libosxunwind-$(OSXUNWIND_VER)/build-compiled + -rm -r $(build_libdir)/libosxunwind.a $(build_shlibdir)/libosxunwind.$(SHLIB_EXT) \ + $(build_includedir)/mach-o/ $(build_includedir)/unwind.h $(build_includedir)/libunwind.h + -$(MAKE) -C $(BUILDDIR)/libosxunwind-$(OSXUNWIND_VER) clean $(OSXUNWIND_FLAGS) + +distclean-osxunwind: + -rm -rf $(SRCCACHE)/libosxunwind-$(OSXUNWIND_VER).tar.gz \ + $(BUILDDIR)/libosxunwind-$(OSXUNWIND_VER) + + +get-osxunwind: $(SRCCACHE)/libosxunwind-$(OSXUNWIND_VER).tar.gz +extract-osxunwind: $(BUILDDIR)/libosxunwind-$(OSXUNWIND_VER)/source-extracted +configure-osxunwind: extract-osxunwind +compile-osxunwind: $(BUILDDIR)/libosxunwind-$(OSXUNWIND_VER)/build-compiled +fastcheck-osxunwind: check-osxunwind +check-osxunwind: compile-osxunwind +install-osxunwind: $(build_prefix)/manifest/osxunwind + +else # USE_BINARYBUILDER_LIBUNWIND + +UNWIND_BB_URL_BASE := https://github.com/JuliaPackaging/Yggdrasil/releases/download/LibUnwind-v$(UNWIND_VER)+$(UNWIND_BB_REL) +UNWIND_BB_NAME := LibUnwind.v$(UNWIND_VER) + +$(eval $(call bb-install,unwind,UNWIND,false)) + +OSXUNWIND_BB_URL_BASE := https://github.com/JuliaBinaryWrappers/LibOSXUnwind_jll.jl/releases/download/LibOSXUnwind-v$(OSXUNWIND_VER)+$(OSXUNWIND_BB_REL) +OSXUNWIND_BB_NAME := LibOSXUnwind.v$(OSXUNWIND_VER) + +$(eval $(call bb-install,osxunwind,OSXUNWIND,false)) + +endif diff --git a/deps/utf8proc.mk b/deps/utf8proc.mk new file mode 100644 index 0000000..b368e1c --- /dev/null +++ b/deps/utf8proc.mk @@ -0,0 +1,41 @@ +## UTF8PROC ## +UTF8PROC_GIT_URL := git://github.com/JuliaLang/utf8proc.git +UTF8PROC_TAR_URL = https://api.github.com/repos/JuliaLang/utf8proc/tarball/$1 +$(eval $(call git-external,utf8proc,UTF8PROC,,,$(BUILDDIR))) + +UTF8PROC_OBJ_LIB := $(build_libdir)/libutf8proc.a +UTF8PROC_OBJ_HEADER := $(build_includedir)/utf8proc.h +UTF8PROC_CFLAGS := -O2 +UTF8PROC_MFLAGS := CC="$(CC)" CFLAGS="$(CFLAGS) $(UTF8PROC_CFLAGS)" PICFLAG="$(fPIC)" AR="$(AR)" +UTF8PROC_BUILDDIR := $(BUILDDIR)/$(UTF8PROC_SRC_DIR) + +$(UTF8PROC_BUILDDIR)/build-compiled: $(UTF8PROC_BUILDDIR)/source-extracted + $(MAKE) -C $(dir $<) $(UTF8PROC_MFLAGS) libutf8proc.a + echo 1 > $@ + +$(UTF8PROC_BUILDDIR)/build-checked: $(UTF8PROC_BUILDDIR)/build-compiled +ifeq ($(OS),$(BUILD_OS)) + $(MAKE) -C $(dir $@) $(UTF8PROC_MFLAGS) check +endif + echo 1 > $@ + +define UTF8PROC_INSTALL + mkdir -p $2/$$(build_includedir) $2/$$(build_libdir) + cp $1/utf8proc.h $2/$$(build_includedir) + cp $1/libutf8proc.a $2/$$(build_libdir) +endef +$(eval $(call staged-install, \ + utf8proc,$(UTF8PROC_SRC_DIR), \ + UTF8PROC_INSTALL,,,)) + +clean-utf8proc: + -rm $(BUILDDIR)/$(UTF8PROC_SRC_DIR)/build-compiled + -$(MAKE) -C $(BUILDDIR)/$(UTF8PROC_SRC_DIR) clean + +get-utf8proc: $(UTF8PROC_SRC_FILE) +extract-utf8proc: $(UTF8PROC_BUILDDIR)/source-extracted +configure-utf8proc: extract-utf8proc +compile-utf8proc: $(UTF8PROC_BUILDDIR)/build-compiled +# utf8proc tests disabled since they require a download +fastcheck-utf8proc: #check-utf8proc +check-utf8proc: $(UTF8PROC_BUILDDIR)/build-checked diff --git a/deps/utf8proc.version b/deps/utf8proc.version new file mode 100644 index 0000000..32a970d --- /dev/null +++ b/deps/utf8proc.version @@ -0,0 +1,2 @@ +UTF8PROC_BRANCH=v2.5.0 +UTF8PROC_SHA1=0890a538bf8238cded9be0c81171f57e43f2c755 diff --git a/deps/valgrind/valgrind.h b/deps/valgrind/valgrind.h new file mode 100644 index 0000000..2e07a49 --- /dev/null +++ b/deps/valgrind/valgrind.h @@ -0,0 +1,6587 @@ +/* -*- c -*- + ---------------------------------------------------------------- + + Notice that the following BSD-style license applies to this one + file (valgrind.h) only. The rest of Valgrind is licensed under the + terms of the GNU General Public License, version 2, unless + otherwise indicated. See the COPYING file in the source + distribution for details. + + ---------------------------------------------------------------- + + This file is part of Valgrind, a dynamic binary instrumentation + framework. + + Copyright (C) 2000-2013 Julian Seward. 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. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + + ---------------------------------------------------------------- + + Notice that the above BSD-style license applies to this one file + (valgrind.h) only. The entire rest of Valgrind is licensed under + the terms of the GNU General Public License, version 2. See the + COPYING file in the source distribution for details. + + ---------------------------------------------------------------- +*/ + + +/* This file is for inclusion into client (your!) code. + + You can use these macros to manipulate and query Valgrind's + execution inside your own programs. + + The resulting executables will still run without Valgrind, just a + little bit more slowly than they otherwise would, but otherwise + unchanged. When not running on valgrind, each client request + consumes very few (eg. 7) instructions, so the resulting performance + loss is negligible unless you plan to execute client requests + millions of times per second. Nevertheless, if that is still a + problem, you can compile with the NVALGRIND symbol defined (gcc + -DNVALGRIND) so that client requests are not even compiled in. */ + +#ifndef __VALGRIND_H +#define __VALGRIND_H + + +/* ------------------------------------------------------------------ */ +/* VERSION NUMBER OF VALGRIND */ +/* ------------------------------------------------------------------ */ + +/* Specify Valgrind's version number, so that user code can + conditionally compile based on our version number. Note that these + were introduced at version 3.6 and so do not exist in version 3.5 + or earlier. The recommended way to use them to check for "version + X.Y or later" is (eg) + +#if defined(__VALGRIND_MAJOR__) && defined(__VALGRIND_MINOR__) \ + && (__VALGRIND_MAJOR__ > 3 \ + || (__VALGRIND_MAJOR__ == 3 && __VALGRIND_MINOR__ >= 6)) +*/ +#define __VALGRIND_MAJOR__ 3 +#define __VALGRIND_MINOR__ 10 + + +#include + +/* Nb: this file might be included in a file compiled with -ansi. So + we can't use C++ style "//" comments nor the "asm" keyword (instead + use "__asm__"). */ + +/* Derive some tags indicating what the target platform is. Note + that in this file we're using the compiler's CPP symbols for + identifying architectures, which are different to the ones we use + within the rest of Valgrind. Note, __powerpc__ is active for both + 32 and 64-bit PPC, whereas __powerpc64__ is only active for the + latter (on Linux, that is). + + Misc note: how to find out what's predefined in gcc by default: + gcc -Wp,-dM somefile.c +*/ +#undef PLAT_x86_darwin +#undef PLAT_amd64_darwin +#undef PLAT_x86_win32 +#undef PLAT_amd64_win64 +#undef PLAT_x86_linux +#undef PLAT_amd64_linux +#undef PLAT_ppc32_linux +#undef PLAT_ppc64be_linux +#undef PLAT_ppc64le_linux +#undef PLAT_arm_linux +#undef PLAT_arm64_linux +#undef PLAT_s390x_linux +#undef PLAT_mips32_linux +#undef PLAT_mips64_linux + + +#if defined(__APPLE__) && defined(__i386__) +# define PLAT_x86_darwin 1 +#elif defined(__APPLE__) && defined(__x86_64__) +# define PLAT_amd64_darwin 1 +#elif (defined(__MINGW32__) && !defined(__MINGW64__)) \ + || defined(__CYGWIN32__) \ + || (defined(_WIN32) && defined(_M_IX86)) +# define PLAT_x86_win32 1 +#elif defined(__MINGW64__) \ + || (defined(_WIN64) && defined(_M_X64)) +# define PLAT_amd64_win64 1 +#elif defined(__linux__) && defined(__i386__) +# define PLAT_x86_linux 1 +#elif defined(__linux__) && defined(__x86_64__) +# define PLAT_amd64_linux 1 +#elif defined(__linux__) && defined(__powerpc__) && !defined(__powerpc64__) +# define PLAT_ppc32_linux 1 +#elif defined(__linux__) && defined(__powerpc__) && defined(__powerpc64__) && _CALL_ELF != 2 +/* Big Endian uses ELF version 1 */ +# define PLAT_ppc64be_linux 1 +#elif defined(__linux__) && defined(__powerpc__) && defined(__powerpc64__) && _CALL_ELF == 2 +/* Little Endian uses ELF version 2 */ +# define PLAT_ppc64le_linux 1 +#elif defined(__linux__) && defined(__arm__) && !defined(__aarch64__) +# define PLAT_arm_linux 1 +#elif defined(__linux__) && defined(__aarch64__) && !defined(__arm__) +# define PLAT_arm64_linux 1 +#elif defined(__linux__) && defined(__s390__) && defined(__s390x__) +# define PLAT_s390x_linux 1 +#elif defined(__linux__) && defined(__mips__) && (__mips==64) +# define PLAT_mips64_linux 1 +#elif defined(__linux__) && defined(__mips__) && (__mips!=64) +# define PLAT_mips32_linux 1 +#else +/* If we're not compiling for our target platform, don't generate + any inline asms. */ +# if !defined(NVALGRIND) +# define NVALGRIND 1 +# endif +#endif + + +/* ------------------------------------------------------------------ */ +/* ARCHITECTURE SPECIFICS for SPECIAL INSTRUCTIONS. There is nothing */ +/* in here of use to end-users -- skip to the next section. */ +/* ------------------------------------------------------------------ */ + +/* + * VALGRIND_DO_CLIENT_REQUEST(): a statement that invokes a Valgrind client + * request. Accepts both pointers and integers as arguments. + * + * VALGRIND_DO_CLIENT_REQUEST_STMT(): a statement that invokes a Valgrind + * client request that does not return a value. + + * VALGRIND_DO_CLIENT_REQUEST_EXPR(): a C expression that invokes a Valgrind + * client request and whose value equals the client request result. Accepts + * both pointers and integers as arguments. Note that such calls are not + * necessarily pure functions -- they may have side effects. + */ + +#define VALGRIND_DO_CLIENT_REQUEST(_zzq_rlval, _zzq_default, \ + _zzq_request, _zzq_arg1, _zzq_arg2, \ + _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + do { (_zzq_rlval) = VALGRIND_DO_CLIENT_REQUEST_EXPR((_zzq_default), \ + (_zzq_request), (_zzq_arg1), (_zzq_arg2), \ + (_zzq_arg3), (_zzq_arg4), (_zzq_arg5)); } while (0) + +#define VALGRIND_DO_CLIENT_REQUEST_STMT(_zzq_request, _zzq_arg1, \ + _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + do { (void) VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ + (_zzq_request), (_zzq_arg1), (_zzq_arg2), \ + (_zzq_arg3), (_zzq_arg4), (_zzq_arg5)); } while (0) + +#if defined(NVALGRIND) + +/* Define NVALGRIND to completely remove the Valgrind magic sequence + from the compiled code (analogous to NDEBUG's effects on + assert()) */ +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + (_zzq_default) + +#else /* ! NVALGRIND */ + +/* The following defines the magic code sequences which the JITter + spots and handles magically. Don't look too closely at them as + they will rot your brain. + + The assembly code sequences for all architectures is in this one + file. This is because this file must be stand-alone, and we don't + want to have multiple files. + + For VALGRIND_DO_CLIENT_REQUEST, we must ensure that the default + value gets put in the return slot, so that everything works when + this is executed not under Valgrind. Args are passed in a memory + block, and so there's no intrinsic limit to the number that could + be passed, but it's currently five. + + The macro args are: + _zzq_rlval result lvalue + _zzq_default default value (result returned when running on real CPU) + _zzq_request request code + _zzq_arg1..5 request params + + The other two macros are used to support function wrapping, and are + a lot simpler. VALGRIND_GET_NR_CONTEXT returns the value of the + guest's NRADDR pseudo-register and whatever other information is + needed to safely run the call original from the wrapper: on + ppc64-linux, the R2 value at the divert point is also needed. This + information is abstracted into a user-visible type, OrigFn. + + VALGRIND_CALL_NOREDIR_* behaves the same as the following on the + guest, but guarantees that the branch instruction will not be + redirected: x86: call *%eax, amd64: call *%rax, ppc32/ppc64: + branch-and-link-to-r11. VALGRIND_CALL_NOREDIR is just text, not a + complete inline asm, since it needs to be combined with more magic + inline asm stuff to be useful. +*/ + +/* ------------------------- x86-{linux,darwin} ---------------- */ + +#if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin) \ + || (defined(PLAT_x86_win32) && defined(__GNUC__)) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "roll $3, %%edi ; roll $13, %%edi\n\t" \ + "roll $29, %%edi ; roll $19, %%edi\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + __extension__ \ + ({volatile unsigned int _zzq_args[6]; \ + volatile unsigned int _zzq_result; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %EDX = client_request ( %EAX ) */ \ + "xchgl %%ebx,%%ebx" \ + : "=d" (_zzq_result) \ + : "a" (&_zzq_args[0]), "0" (_zzq_default) \ + : "cc", "memory" \ + ); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %EAX = guest_NRADDR */ \ + "xchgl %%ecx,%%ecx" \ + : "=a" (__addr) \ + : \ + : "cc", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_EAX \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* call-noredir *%EAX */ \ + "xchgl %%edx,%%edx\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "xchgl %%edi,%%edi\n\t" \ + : : : "cc", "memory" \ + ); \ + } while (0) + +#endif /* PLAT_x86_linux || PLAT_x86_darwin || (PLAT_x86_win32 && __GNUC__) */ + +/* ------------------------- x86-Win32 ------------------------- */ + +#if defined(PLAT_x86_win32) && !defined(__GNUC__) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +#if defined(_MSC_VER) + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + __asm rol edi, 3 __asm rol edi, 13 \ + __asm rol edi, 29 __asm rol edi, 19 + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + valgrind_do_client_request_expr((uintptr_t)(_zzq_default), \ + (uintptr_t)(_zzq_request), (uintptr_t)(_zzq_arg1), \ + (uintptr_t)(_zzq_arg2), (uintptr_t)(_zzq_arg3), \ + (uintptr_t)(_zzq_arg4), (uintptr_t)(_zzq_arg5)) + +static __inline uintptr_t +valgrind_do_client_request_expr(uintptr_t _zzq_default, uintptr_t _zzq_request, + uintptr_t _zzq_arg1, uintptr_t _zzq_arg2, + uintptr_t _zzq_arg3, uintptr_t _zzq_arg4, + uintptr_t _zzq_arg5) +{ + volatile uintptr_t _zzq_args[6]; + volatile unsigned int _zzq_result; + _zzq_args[0] = (uintptr_t)(_zzq_request); + _zzq_args[1] = (uintptr_t)(_zzq_arg1); + _zzq_args[2] = (uintptr_t)(_zzq_arg2); + _zzq_args[3] = (uintptr_t)(_zzq_arg3); + _zzq_args[4] = (uintptr_t)(_zzq_arg4); + _zzq_args[5] = (uintptr_t)(_zzq_arg5); + __asm { __asm lea eax, _zzq_args __asm mov edx, _zzq_default + __SPECIAL_INSTRUCTION_PREAMBLE + /* %EDX = client_request ( %EAX ) */ + __asm xchg ebx,ebx + __asm mov _zzq_result, edx + } + return _zzq_result; +} + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned int __addr; \ + __asm { __SPECIAL_INSTRUCTION_PREAMBLE \ + /* %EAX = guest_NRADDR */ \ + __asm xchg ecx,ecx \ + __asm mov __addr, eax \ + } \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_EAX ERROR + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm { __SPECIAL_INSTRUCTION_PREAMBLE \ + __asm xchg edi,edi \ + } \ + } while (0) + +#else +#error Unsupported compiler. +#endif + +#endif /* PLAT_x86_win32 */ + +/* ------------------------ amd64-{linux,darwin} --------------- */ + +#if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin) \ + || (defined(PLAT_amd64_win64) && defined(__GNUC__)) + +typedef + struct { + unsigned long int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rolq $3, %%rdi ; rolq $13, %%rdi\n\t" \ + "rolq $61, %%rdi ; rolq $51, %%rdi\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + __extension__ \ + ({ volatile unsigned long int _zzq_args[6]; \ + volatile unsigned long int _zzq_result; \ + _zzq_args[0] = (unsigned long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long int)(_zzq_arg5); \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %RDX = client_request ( %RAX ) */ \ + "xchgq %%rbx,%%rbx" \ + : "=d" (_zzq_result) \ + : "a" (&_zzq_args[0]), "0" (_zzq_default) \ + : "cc", "memory" \ + ); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %RAX = guest_NRADDR */ \ + "xchgq %%rcx,%%rcx" \ + : "=a" (__addr) \ + : \ + : "cc", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_RAX \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* call-noredir *%RAX */ \ + "xchgq %%rdx,%%rdx\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "xchgq %%rdi,%%rdi\n\t" \ + : : : "cc", "memory" \ + ); \ + } while (0) + +#endif /* PLAT_amd64_linux || PLAT_amd64_darwin */ + +/* ------------------------- amd64-Win64 ------------------------- */ + +#if defined(PLAT_amd64_win64) && !defined(__GNUC__) + +#error Unsupported compiler. + +#endif /* PLAT_amd64_win64 */ + +/* ------------------------ ppc32-linux ------------------------ */ + +#if defined(PLAT_ppc32_linux) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rlwinm 0,0,3,0,31 ; rlwinm 0,0,13,0,31\n\t" \ + "rlwinm 0,0,29,0,31 ; rlwinm 0,0,19,0,31\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + __extension__ \ + ({ unsigned int _zzq_args[6]; \ + unsigned int _zzq_result; \ + unsigned int* _zzq_ptr; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + _zzq_ptr = _zzq_args; \ + __asm__ volatile("mr 3,%1\n\t" /*default*/ \ + "mr 4,%2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = client_request ( %R4 ) */ \ + "or 1,1,1\n\t" \ + "mr %0,3" /*result*/ \ + : "=b" (_zzq_result) \ + : "b" (_zzq_default), "b" (_zzq_ptr) \ + : "cc", "memory", "r3", "r4"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + unsigned int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR */ \ + "or 2,2,2\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R11 */ \ + "or 3,3,3\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "or 5,5,5\n\t" \ + ); \ + } while (0) + +#endif /* PLAT_ppc32_linux */ + +/* ------------------------ ppc64-linux ------------------------ */ + +#if defined(PLAT_ppc64be_linux) + +typedef + struct { + unsigned long int nraddr; /* where's the code? */ + unsigned long int r2; /* what tocptr do we need? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rotldi 0,0,3 ; rotldi 0,0,13\n\t" \ + "rotldi 0,0,61 ; rotldi 0,0,51\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + __extension__ \ + ({ unsigned long int _zzq_args[6]; \ + unsigned long int _zzq_result; \ + unsigned long int* _zzq_ptr; \ + _zzq_args[0] = (unsigned long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long int)(_zzq_arg5); \ + _zzq_ptr = _zzq_args; \ + __asm__ volatile("mr 3,%1\n\t" /*default*/ \ + "mr 4,%2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = client_request ( %R4 ) */ \ + "or 1,1,1\n\t" \ + "mr %0,3" /*result*/ \ + : "=b" (_zzq_result) \ + : "b" (_zzq_default), "b" (_zzq_ptr) \ + : "cc", "memory", "r3", "r4"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + unsigned long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR */ \ + "or 2,2,2\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->nraddr = __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR_GPR2 */ \ + "or 4,4,4\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->r2 = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R11 */ \ + "or 3,3,3\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "or 5,5,5\n\t" \ + ); \ + } while (0) + +#endif /* PLAT_ppc64be_linux */ + +#if defined(PLAT_ppc64le_linux) + +typedef + struct { + unsigned long int nraddr; /* where's the code? */ + unsigned long int r2; /* what tocptr do we need? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "rotldi 0,0,3 ; rotldi 0,0,13\n\t" \ + "rotldi 0,0,61 ; rotldi 0,0,51\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + __extension__ \ + ({ unsigned long int _zzq_args[6]; \ + unsigned long int _zzq_result; \ + unsigned long int* _zzq_ptr; \ + _zzq_args[0] = (unsigned long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long int)(_zzq_arg5); \ + _zzq_ptr = _zzq_args; \ + __asm__ volatile("mr 3,%1\n\t" /*default*/ \ + "mr 4,%2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = client_request ( %R4 ) */ \ + "or 1,1,1\n\t" \ + "mr %0,3" /*result*/ \ + : "=b" (_zzq_result) \ + : "b" (_zzq_default), "b" (_zzq_ptr) \ + : "cc", "memory", "r3", "r4"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + unsigned long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR */ \ + "or 2,2,2\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->nraddr = __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %R3 = guest_NRADDR_GPR2 */ \ + "or 4,4,4\n\t" \ + "mr %0,3" \ + : "=b" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->r2 = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R12 */ \ + "or 3,3,3\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "or 5,5,5\n\t" \ + ); \ + } while (0) + +#endif /* PLAT_ppc64le_linux */ + +/* ------------------------- arm-linux ------------------------- */ + +#if defined(PLAT_arm_linux) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "mov r12, r12, ror #3 ; mov r12, r12, ror #13 \n\t" \ + "mov r12, r12, ror #29 ; mov r12, r12, ror #19 \n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + __extension__ \ + ({volatile unsigned int _zzq_args[6]; \ + volatile unsigned int _zzq_result; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + __asm__ volatile("mov r3, %1\n\t" /*default*/ \ + "mov r4, %2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* R3 = client_request ( R4 ) */ \ + "orr r10, r10, r10\n\t" \ + "mov %0, r3" /*result*/ \ + : "=r" (_zzq_result) \ + : "r" (_zzq_default), "r" (&_zzq_args[0]) \ + : "cc","memory", "r3", "r4"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + unsigned int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* R3 = guest_NRADDR */ \ + "orr r11, r11, r11\n\t" \ + "mov %0, r3" \ + : "=r" (__addr) \ + : \ + : "cc", "memory", "r3" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir *%R4 */ \ + "orr r12, r12, r12\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "orr r9, r9, r9\n\t" \ + : : : "cc", "memory" \ + ); \ + } while (0) + +#endif /* PLAT_arm_linux */ + +/* ------------------------ arm64-linux ------------------------- */ + +#if defined(PLAT_arm64_linux) + +typedef + struct { + unsigned long int nraddr; /* where's the code? */ + } + OrigFn; + +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "ror x12, x12, #3 ; ror x12, x12, #13 \n\t" \ + "ror x12, x12, #51 ; ror x12, x12, #61 \n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + \ + __extension__ \ + ({volatile unsigned long int _zzq_args[6]; \ + volatile unsigned long int _zzq_result; \ + _zzq_args[0] = (unsigned long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long int)(_zzq_arg5); \ + __asm__ volatile("mov x3, %1\n\t" /*default*/ \ + "mov x4, %2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* X3 = client_request ( X4 ) */ \ + "orr x10, x10, x10\n\t" \ + "mov %0, x3" /*result*/ \ + : "=r" (_zzq_result) \ + : "r" (_zzq_default), "r" (&_zzq_args[0]) \ + : "cc","memory", "x3", "x4"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + unsigned long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* X3 = guest_NRADDR */ \ + "orr x11, x11, x11\n\t" \ + "mov %0, x3" \ + : "=r" (__addr) \ + : \ + : "cc", "memory", "x3" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* branch-and-link-to-noredir X8 */ \ + "orr x12, x12, x12\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "orr x9, x9, x9\n\t" \ + : : : "cc", "memory" \ + ); \ + } while (0) + +#endif /* PLAT_arm64_linux */ + +/* ------------------------ s390x-linux ------------------------ */ + +#if defined(PLAT_s390x_linux) + +typedef + struct { + unsigned long int nraddr; /* where's the code? */ + } + OrigFn; + +/* __SPECIAL_INSTRUCTION_PREAMBLE will be used to identify Valgrind specific + * code. This detection is implemented in platform specific toIR.c + * (e.g. VEX/priv/guest_s390_decoder.c). + */ +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "lr 15,15\n\t" \ + "lr 1,1\n\t" \ + "lr 2,2\n\t" \ + "lr 3,3\n\t" + +#define __CLIENT_REQUEST_CODE "lr 2,2\n\t" +#define __GET_NR_CONTEXT_CODE "lr 3,3\n\t" +#define __CALL_NO_REDIR_CODE "lr 4,4\n\t" +#define __VEX_INJECT_IR_CODE "lr 5,5\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + __extension__ \ + ({volatile unsigned long int _zzq_args[6]; \ + volatile unsigned long int _zzq_result; \ + _zzq_args[0] = (unsigned long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long int)(_zzq_arg5); \ + __asm__ volatile(/* r2 = args */ \ + "lgr 2,%1\n\t" \ + /* r3 = default */ \ + "lgr 3,%2\n\t" \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + __CLIENT_REQUEST_CODE \ + /* results = r3 */ \ + "lgr %0, 3\n\t" \ + : "=d" (_zzq_result) \ + : "a" (&_zzq_args[0]), "0" (_zzq_default) \ + : "cc", "2", "3", "memory" \ + ); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + __GET_NR_CONTEXT_CODE \ + "lgr %0, 3\n\t" \ + : "=a" (__addr) \ + : \ + : "cc", "3", "memory" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_R1 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + __CALL_NO_REDIR_CODE + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + __VEX_INJECT_IR_CODE); \ + } while (0) + +#endif /* PLAT_s390x_linux */ + +/* ------------------------- mips32-linux ---------------- */ + +#if defined(PLAT_mips32_linux) + +typedef + struct { + unsigned int nraddr; /* where's the code? */ + } + OrigFn; + +/* .word 0x342 + * .word 0x742 + * .word 0xC2 + * .word 0x4C2*/ +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "srl $0, $0, 13\n\t" \ + "srl $0, $0, 29\n\t" \ + "srl $0, $0, 3\n\t" \ + "srl $0, $0, 19\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + __extension__ \ + ({ volatile unsigned int _zzq_args[6]; \ + volatile unsigned int _zzq_result; \ + _zzq_args[0] = (unsigned int)(_zzq_request); \ + _zzq_args[1] = (unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned int)(_zzq_arg5); \ + __asm__ volatile("move $11, %1\n\t" /*default*/ \ + "move $12, %2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* T3 = client_request ( T4 ) */ \ + "or $13, $13, $13\n\t" \ + "move %0, $11\n\t" /*result*/ \ + : "=r" (_zzq_result) \ + : "r" (_zzq_default), "r" (&_zzq_args[0]) \ + : "$11", "$12"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* %t9 = guest_NRADDR */ \ + "or $14, $14, $14\n\t" \ + "move %0, $11" /*result*/ \ + : "=r" (__addr) \ + : \ + : "$11" \ + ); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_T9 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* call-noredir *%t9 */ \ + "or $15, $15, $15\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "or $11, $11, $11\n\t" \ + ); \ + } while (0) + + +#endif /* PLAT_mips32_linux */ + +/* ------------------------- mips64-linux ---------------- */ + +#if defined(PLAT_mips64_linux) + +typedef + struct { + unsigned long nraddr; /* where's the code? */ + } + OrigFn; + +/* dsll $0,$0, 3 + * dsll $0,$0, 13 + * dsll $0,$0, 29 + * dsll $0,$0, 19*/ +#define __SPECIAL_INSTRUCTION_PREAMBLE \ + "dsll $0,$0, 3 ; dsll $0,$0,13\n\t" \ + "dsll $0,$0,29 ; dsll $0,$0,19\n\t" + +#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + _zzq_default, _zzq_request, \ + _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ + __extension__ \ + ({ volatile unsigned long int _zzq_args[6]; \ + volatile unsigned long int _zzq_result; \ + _zzq_args[0] = (unsigned long int)(_zzq_request); \ + _zzq_args[1] = (unsigned long int)(_zzq_arg1); \ + _zzq_args[2] = (unsigned long int)(_zzq_arg2); \ + _zzq_args[3] = (unsigned long int)(_zzq_arg3); \ + _zzq_args[4] = (unsigned long int)(_zzq_arg4); \ + _zzq_args[5] = (unsigned long int)(_zzq_arg5); \ + __asm__ volatile("move $11, %1\n\t" /*default*/ \ + "move $12, %2\n\t" /*ptr*/ \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* $11 = client_request ( $12 ) */ \ + "or $13, $13, $13\n\t" \ + "move %0, $11\n\t" /*result*/ \ + : "=r" (_zzq_result) \ + : "r" (_zzq_default), "r" (&_zzq_args[0]) \ + : "$11", "$12"); \ + _zzq_result; \ + }) + +#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ + { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ + volatile unsigned long int __addr; \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + /* $11 = guest_NRADDR */ \ + "or $14, $14, $14\n\t" \ + "move %0, $11" /*result*/ \ + : "=r" (__addr) \ + : \ + : "$11"); \ + _zzq_orig->nraddr = __addr; \ + } + +#define VALGRIND_CALL_NOREDIR_T9 \ + __SPECIAL_INSTRUCTION_PREAMBLE \ + /* call-noredir $25 */ \ + "or $15, $15, $15\n\t" + +#define VALGRIND_VEX_INJECT_IR() \ + do { \ + __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ + "or $11, $11, $11\n\t" \ + ); \ + } while (0) + +#endif /* PLAT_mips64_linux */ + +/* Insert assembly code for other platforms here... */ + +#endif /* NVALGRIND */ + + +/* ------------------------------------------------------------------ */ +/* PLATFORM SPECIFICS for FUNCTION WRAPPING. This is all very */ +/* ugly. It's the least-worst tradeoff I can think of. */ +/* ------------------------------------------------------------------ */ + +/* This section defines magic (a.k.a appalling-hack) macros for doing + guaranteed-no-redirection macros, so as to get from function + wrappers to the functions they are wrapping. The whole point is to + construct standard call sequences, but to do the call itself with a + special no-redirect call pseudo-instruction that the JIT + understands and handles specially. This section is long and + repetitious, and I can't see a way to make it shorter. + + The naming scheme is as follows: + + CALL_FN_{W,v}_{v,W,WW,WWW,WWWW,5W,6W,7W,etc} + + 'W' stands for "word" and 'v' for "void". Hence there are + different macros for calling arity 0, 1, 2, 3, 4, etc, functions, + and for each, the possibility of returning a word-typed result, or + no result. +*/ + +/* Use these to write the name of your wrapper. NOTE: duplicates + VG_WRAP_FUNCTION_Z{U,Z} in pub_tool_redir.h. NOTE also: inserts + the default behaviour equivalance class tag "0000" into the name. + See pub_tool_redir.h for details -- normally you don't need to + think about this, though. */ + +/* Use an extra level of macroisation so as to ensure the soname/fnname + args are fully macro-expanded before pasting them together. */ +#define VG_CONCAT4(_aa,_bb,_cc,_dd) _aa##_bb##_cc##_dd + +#define I_WRAP_SONAME_FNNAME_ZU(soname,fnname) \ + VG_CONCAT4(_vgw00000ZU_,soname,_,fnname) + +#define I_WRAP_SONAME_FNNAME_ZZ(soname,fnname) \ + VG_CONCAT4(_vgw00000ZZ_,soname,_,fnname) + +/* Use this macro from within a wrapper function to collect the + context (address and possibly other info) of the original function. + Once you have that you can then use it in one of the CALL_FN_ + macros. The type of the argument _lval is OrigFn. */ +#define VALGRIND_GET_ORIG_FN(_lval) VALGRIND_GET_NR_CONTEXT(_lval) + +/* Also provide end-user facilities for function replacement, rather + than wrapping. A replacement function differs from a wrapper in + that it has no way to get hold of the original function being + called, and hence no way to call onwards to it. In a replacement + function, VALGRIND_GET_ORIG_FN always returns zero. */ + +#define I_REPLACE_SONAME_FNNAME_ZU(soname,fnname) \ + VG_CONCAT4(_vgr00000ZU_,soname,_,fnname) + +#define I_REPLACE_SONAME_FNNAME_ZZ(soname,fnname) \ + VG_CONCAT4(_vgr00000ZZ_,soname,_,fnname) + +/* Derivatives of the main macros below, for calling functions + returning void. */ + +#define CALL_FN_v_v(fnptr) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_v(_junk,fnptr); } while (0) + +#define CALL_FN_v_W(fnptr, arg1) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_W(_junk,fnptr,arg1); } while (0) + +#define CALL_FN_v_WW(fnptr, arg1,arg2) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_WW(_junk,fnptr,arg1,arg2); } while (0) + +#define CALL_FN_v_WWW(fnptr, arg1,arg2,arg3) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_WWW(_junk,fnptr,arg1,arg2,arg3); } while (0) + +#define CALL_FN_v_WWWW(fnptr, arg1,arg2,arg3,arg4) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_WWWW(_junk,fnptr,arg1,arg2,arg3,arg4); } while (0) + +#define CALL_FN_v_5W(fnptr, arg1,arg2,arg3,arg4,arg5) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_5W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5); } while (0) + +#define CALL_FN_v_6W(fnptr, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_6W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5,arg6); } while (0) + +#define CALL_FN_v_7W(fnptr, arg1,arg2,arg3,arg4,arg5,arg6,arg7) \ + do { volatile unsigned long _junk; \ + CALL_FN_W_7W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5,arg6,arg7); } while (0) + +/* ------------------------- x86-{linux,darwin} ---------------- */ + +#if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin) + +/* These regs are trashed by the hidden call. No need to mention eax + as gcc can already see that, plus causes gcc to bomb. */ +#define __CALLER_SAVED_REGS /*"eax"*/ "ecx", "edx" + +/* Macros to save and align the stack before making a function + call and restore it afterwards as gcc may not keep the stack + pointer aligned if it doesn't realise calls are being made + to other functions. */ + +#define VALGRIND_ALIGN_STACK \ + "movl %%esp,%%edi\n\t" \ + "andl $0xfffffff0,%%esp\n\t" +#define VALGRIND_RESTORE_STACK \ + "movl %%edi,%%esp\n\t" + +/* These CALL_FN_ macros assume that on x86-linux, sizeof(unsigned + long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $12, %%esp\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $8, %%esp\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $4, %%esp\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $12, %%esp\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $8, %%esp\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $4, %%esp\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $12, %%esp\n\t" \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $8, %%esp\n\t" \ + "pushl 40(%%eax)\n\t" \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "subl $4, %%esp\n\t" \ + "pushl 44(%%eax)\n\t" \ + "pushl 40(%%eax)\n\t" \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "pushl 48(%%eax)\n\t" \ + "pushl 44(%%eax)\n\t" \ + "pushl 40(%%eax)\n\t" \ + "pushl 36(%%eax)\n\t" \ + "pushl 32(%%eax)\n\t" \ + "pushl 28(%%eax)\n\t" \ + "pushl 24(%%eax)\n\t" \ + "pushl 20(%%eax)\n\t" \ + "pushl 16(%%eax)\n\t" \ + "pushl 12(%%eax)\n\t" \ + "pushl 8(%%eax)\n\t" \ + "pushl 4(%%eax)\n\t" \ + "movl (%%eax), %%eax\n\t" /* target->%eax */ \ + VALGRIND_CALL_NOREDIR_EAX \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_x86_linux || PLAT_x86_darwin */ + +/* ------------------------ amd64-{linux,darwin} --------------- */ + +#if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin) + +/* ARGREGS: rdi rsi rdx rcx r8 r9 (the rest on stack in R-to-L order) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS /*"rax",*/ "rcx", "rdx", "rsi", \ + "rdi", "r8", "r9", "r10", "r11" + +/* This is all pretty complex. It's so as to make stack unwinding + work reliably. See bug 243270. The basic problem is the sub and + add of 128 of %rsp in all of the following macros. If gcc believes + the CFA is in %rsp, then unwinding may fail, because what's at the + CFA is not what gcc "expected" when it constructs the CFIs for the + places where the macros are instantiated. + + But we can't just add a CFI annotation to increase the CFA offset + by 128, to match the sub of 128 from %rsp, because we don't know + whether gcc has chosen %rsp as the CFA at that point, or whether it + has chosen some other register (eg, %rbp). In the latter case, + adding a CFI annotation to change the CFA offset is simply wrong. + + So the solution is to get hold of the CFA using + __builtin_dwarf_cfa(), put it in a known register, and add a + CFI annotation to say what the register is. We choose %rbp for + this (perhaps perversely), because: + + (1) %rbp is already subject to unwinding. If a new register was + chosen then the unwinder would have to unwind it in all stack + traces, which is expensive, and + + (2) %rbp is already subject to precise exception updates in the + JIT. If a new register was chosen, we'd have to have precise + exceptions for it too, which reduces performance of the + generated code. + + However .. one extra complication. We can't just whack the result + of __builtin_dwarf_cfa() into %rbp and then add %rbp to the + list of trashed registers at the end of the inline assembly + fragments; gcc won't allow %rbp to appear in that list. Hence + instead we need to stash %rbp in %r15 for the duration of the asm, + and say that %r15 is trashed instead. gcc seems happy to go with + that. + + Oh .. and this all needs to be conditionalised so that it is + unchanged from before this commit, when compiled with older gccs + that don't support __builtin_dwarf_cfa. Furthermore, since + this header file is freestanding, it has to be independent of + config.h, and so the following conditionalisation cannot depend on + configure time checks. + + Although it's not clear from + 'defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM)', + this expression excludes Darwin. + .cfi directives in Darwin assembly appear to be completely + different and I haven't investigated how they work. + + For even more entertainment value, note we have to use the + completely undocumented __builtin_dwarf_cfa(), which appears to + really compute the CFA, whereas __builtin_frame_address(0) claims + to but actually doesn't. See + https://bugs.kde.org/show_bug.cgi?id=243270#c47 +*/ +#if defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM) +# define __FRAME_POINTER \ + ,"r"(__builtin_dwarf_cfa()) +# define VALGRIND_CFI_PROLOGUE \ + "movq %%rbp, %%r15\n\t" \ + "movq %2, %%rbp\n\t" \ + ".cfi_remember_state\n\t" \ + ".cfi_def_cfa rbp, 0\n\t" +# define VALGRIND_CFI_EPILOGUE \ + "movq %%r15, %%rbp\n\t" \ + ".cfi_restore_state\n\t" +#else +# define __FRAME_POINTER +# define VALGRIND_CFI_PROLOGUE +# define VALGRIND_CFI_EPILOGUE +#endif + +/* Macros to save and align the stack before making a function + call and restore it afterwards as gcc may not keep the stack + pointer aligned if it doesn't realise calls are being made + to other functions. */ + +#define VALGRIND_ALIGN_STACK \ + "movq %%rsp,%%r14\n\t" \ + "andq $0xfffffffffffffff0,%%rsp\n\t" +#define VALGRIND_RESTORE_STACK \ + "movq %%r14,%%rsp\n\t" + +/* These CALL_FN_ macros assume that on amd64-linux, sizeof(unsigned + long) == 8. */ + +/* NB 9 Sept 07. There is a nasty kludge here in all these CALL_FN_ + macros. In order not to trash the stack redzone, we need to drop + %rsp by 128 before the hidden call, and restore afterwards. The + nastiness is that it is only by luck that the stack still appears + to be unwindable during the hidden call - since then the behaviour + of any routine using this macro does not match what the CFI data + says. Sigh. + + Why is this important? Imagine that a wrapper has a stack + allocated local, and passes to the hidden call, a pointer to it. + Because gcc does not know about the hidden call, it may allocate + that local in the redzone. Unfortunately the hidden call may then + trash it before it comes to use it. So we must step clear of the + redzone, for the duration of the hidden call, to make it safe. + + Probably the same problem afflicts the other redzone-style ABIs too + (ppc64-linux); but for those, the stack is + self describing (none of this CFI nonsense) so at least messing + with the stack pointer doesn't give a danger of non-unwindable + stack. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $136,%%rsp\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $136,%%rsp\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "pushq 80(%%rax)\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $136,%%rsp\n\t" \ + "pushq 88(%%rax)\n\t" \ + "pushq 80(%%rax)\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + VALGRIND_ALIGN_STACK \ + "subq $128,%%rsp\n\t" \ + "pushq 96(%%rax)\n\t" \ + "pushq 88(%%rax)\n\t" \ + "pushq 80(%%rax)\n\t" \ + "pushq 72(%%rax)\n\t" \ + "pushq 64(%%rax)\n\t" \ + "pushq 56(%%rax)\n\t" \ + "movq 48(%%rax), %%r9\n\t" \ + "movq 40(%%rax), %%r8\n\t" \ + "movq 32(%%rax), %%rcx\n\t" \ + "movq 24(%%rax), %%rdx\n\t" \ + "movq 16(%%rax), %%rsi\n\t" \ + "movq 8(%%rax), %%rdi\n\t" \ + "movq (%%rax), %%rax\n\t" /* target->%rax */ \ + VALGRIND_CALL_NOREDIR_RAX \ + VALGRIND_RESTORE_STACK \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=a" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_amd64_linux || PLAT_amd64_darwin */ + +/* ------------------------ ppc32-linux ------------------------ */ + +#if defined(PLAT_ppc32_linux) + +/* This is useful for finding out about the on-stack stuff: + + extern int f9 ( int,int,int,int,int,int,int,int,int ); + extern int f10 ( int,int,int,int,int,int,int,int,int,int ); + extern int f11 ( int,int,int,int,int,int,int,int,int,int,int ); + extern int f12 ( int,int,int,int,int,int,int,int,int,int,int,int ); + + int g9 ( void ) { + return f9(11,22,33,44,55,66,77,88,99); + } + int g10 ( void ) { + return f10(11,22,33,44,55,66,77,88,99,110); + } + int g11 ( void ) { + return f11(11,22,33,44,55,66,77,88,99,110,121); + } + int g12 ( void ) { + return f12(11,22,33,44,55,66,77,88,99,110,121,132); + } +*/ + +/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "lr", "ctr", "xer", \ + "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ + "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ + "r11", "r12", "r13" + +/* Macros to save and align the stack before making a function + call and restore it afterwards as gcc may not keep the stack + pointer aligned if it doesn't realise calls are being made + to other functions. */ + +#define VALGRIND_ALIGN_STACK \ + "mr 28,1\n\t" \ + "rlwinm 1,1,0,0,27\n\t" +#define VALGRIND_RESTORE_STACK \ + "mr 1,28\n\t" + +/* These CALL_FN_ macros assume that on ppc32-linux, + sizeof(unsigned long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "addi 1,1,-16\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "addi 1,1,-16\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,12(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + _argvec[11] = (unsigned long)arg11; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "addi 1,1,-32\n\t" \ + /* arg11 */ \ + "lwz 3,44(11)\n\t" \ + "stw 3,16(1)\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,12(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + _argvec[11] = (unsigned long)arg11; \ + _argvec[12] = (unsigned long)arg12; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "addi 1,1,-32\n\t" \ + /* arg12 */ \ + "lwz 3,48(11)\n\t" \ + "stw 3,20(1)\n\t" \ + /* arg11 */ \ + "lwz 3,44(11)\n\t" \ + "stw 3,16(1)\n\t" \ + /* arg10 */ \ + "lwz 3,40(11)\n\t" \ + "stw 3,12(1)\n\t" \ + /* arg9 */ \ + "lwz 3,36(11)\n\t" \ + "stw 3,8(1)\n\t" \ + /* args1-8 */ \ + "lwz 3,4(11)\n\t" /* arg1->r3 */ \ + "lwz 4,8(11)\n\t" \ + "lwz 5,12(11)\n\t" \ + "lwz 6,16(11)\n\t" /* arg4->r6 */ \ + "lwz 7,20(11)\n\t" \ + "lwz 8,24(11)\n\t" \ + "lwz 9,28(11)\n\t" \ + "lwz 10,32(11)\n\t" /* arg8->r10 */ \ + "lwz 11,0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + VALGRIND_RESTORE_STACK \ + "mr %0,3" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_ppc32_linux */ + +/* ------------------------ ppc64-linux ------------------------ */ + +#if defined(PLAT_ppc64be_linux) + +/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "lr", "ctr", "xer", \ + "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ + "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ + "r11", "r12", "r13" + +/* Macros to save and align the stack before making a function + call and restore it afterwards as gcc may not keep the stack + pointer aligned if it doesn't realise calls are being made + to other functions. */ + +#define VALGRIND_ALIGN_STACK \ + "mr 28,1\n\t" \ + "rldicr 1,1,0,59\n\t" +#define VALGRIND_RESTORE_STACK \ + "mr 1,28\n\t" + +/* These CALL_FN_ macros assume that on ppc64-linux, sizeof(unsigned + long) == 8. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+0]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+1]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+2]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+3]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+4]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+5]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+6]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+7]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+8]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+9]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-128\n\t" /* expand stack frame */ \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+10]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-128\n\t" /* expand stack frame */ \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+11]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-144\n\t" /* expand stack frame */ \ + /* arg11 */ \ + "ld 3,88(11)\n\t" \ + "std 3,128(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+12]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + _argvec[2+12] = (unsigned long)arg12; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 11,%1\n\t" \ + "std 2,-16(11)\n\t" /* save tocptr */ \ + "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-144\n\t" /* expand stack frame */ \ + /* arg12 */ \ + "ld 3,96(11)\n\t" \ + "std 3,136(1)\n\t" \ + /* arg11 */ \ + "ld 3,88(11)\n\t" \ + "std 3,128(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(11)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(11)\n\t" \ + "std 3,112(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(11)\n\t" /* arg1->r3 */ \ + "ld 4, 16(11)\n\t" /* arg2->r4 */ \ + "ld 5, 24(11)\n\t" /* arg3->r5 */ \ + "ld 6, 32(11)\n\t" /* arg4->r6 */ \ + "ld 7, 40(11)\n\t" /* arg5->r7 */ \ + "ld 8, 48(11)\n\t" /* arg6->r8 */ \ + "ld 9, 56(11)\n\t" /* arg7->r9 */ \ + "ld 10, 64(11)\n\t" /* arg8->r10 */ \ + "ld 11, 0(11)\n\t" /* target->r11 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ + "mr 11,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(11)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_ppc64be_linux */ + +/* ------------------------- ppc64le-linux ----------------------- */ +#if defined(PLAT_ppc64le_linux) + +/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "lr", "ctr", "xer", \ + "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ + "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ + "r11", "r12", "r13" + +/* Macros to save and align the stack before making a function + call and restore it afterwards as gcc may not keep the stack + pointer aligned if it doesn't realise calls are being made + to other functions. */ + +#define VALGRIND_ALIGN_STACK \ + "mr 28,1\n\t" \ + "rldicr 1,1,0,59\n\t" +#define VALGRIND_RESTORE_STACK \ + "mr 1,28\n\t" + +/* These CALL_FN_ macros assume that on ppc64-linux, sizeof(unsigned + long) == 8. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+0]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+1]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+2]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+3]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+4]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+5]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+6]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+7]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 9, 56(12)\n\t" /* arg7->r9 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+8]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 9, 56(12)\n\t" /* arg7->r9 */ \ + "ld 10, 64(12)\n\t" /* arg8->r10 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+9]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-128\n\t" /* expand stack frame */ \ + /* arg9 */ \ + "ld 3,72(12)\n\t" \ + "std 3,96(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 9, 56(12)\n\t" /* arg7->r9 */ \ + "ld 10, 64(12)\n\t" /* arg8->r10 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+10]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-128\n\t" /* expand stack frame */ \ + /* arg10 */ \ + "ld 3,80(12)\n\t" \ + "std 3,104(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(12)\n\t" \ + "std 3,96(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 9, 56(12)\n\t" /* arg7->r9 */ \ + "ld 10, 64(12)\n\t" /* arg8->r10 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+11]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-144\n\t" /* expand stack frame */ \ + /* arg11 */ \ + "ld 3,88(12)\n\t" \ + "std 3,112(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(12)\n\t" \ + "std 3,104(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(12)\n\t" \ + "std 3,96(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 9, 56(12)\n\t" /* arg7->r9 */ \ + "ld 10, 64(12)\n\t" /* arg8->r10 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3+12]; \ + volatile unsigned long _res; \ + /* _argvec[0] holds current r2 across the call */ \ + _argvec[1] = (unsigned long)_orig.r2; \ + _argvec[2] = (unsigned long)_orig.nraddr; \ + _argvec[2+1] = (unsigned long)arg1; \ + _argvec[2+2] = (unsigned long)arg2; \ + _argvec[2+3] = (unsigned long)arg3; \ + _argvec[2+4] = (unsigned long)arg4; \ + _argvec[2+5] = (unsigned long)arg5; \ + _argvec[2+6] = (unsigned long)arg6; \ + _argvec[2+7] = (unsigned long)arg7; \ + _argvec[2+8] = (unsigned long)arg8; \ + _argvec[2+9] = (unsigned long)arg9; \ + _argvec[2+10] = (unsigned long)arg10; \ + _argvec[2+11] = (unsigned long)arg11; \ + _argvec[2+12] = (unsigned long)arg12; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "mr 12,%1\n\t" \ + "std 2,-16(12)\n\t" /* save tocptr */ \ + "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ + "addi 1,1,-144\n\t" /* expand stack frame */ \ + /* arg12 */ \ + "ld 3,96(12)\n\t" \ + "std 3,120(1)\n\t" \ + /* arg11 */ \ + "ld 3,88(12)\n\t" \ + "std 3,112(1)\n\t" \ + /* arg10 */ \ + "ld 3,80(12)\n\t" \ + "std 3,104(1)\n\t" \ + /* arg9 */ \ + "ld 3,72(12)\n\t" \ + "std 3,96(1)\n\t" \ + /* args1-8 */ \ + "ld 3, 8(12)\n\t" /* arg1->r3 */ \ + "ld 4, 16(12)\n\t" /* arg2->r4 */ \ + "ld 5, 24(12)\n\t" /* arg3->r5 */ \ + "ld 6, 32(12)\n\t" /* arg4->r6 */ \ + "ld 7, 40(12)\n\t" /* arg5->r7 */ \ + "ld 8, 48(12)\n\t" /* arg6->r8 */ \ + "ld 9, 56(12)\n\t" /* arg7->r9 */ \ + "ld 10, 64(12)\n\t" /* arg8->r10 */ \ + "ld 12, 0(12)\n\t" /* target->r12 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ + "mr 12,%1\n\t" \ + "mr %0,3\n\t" \ + "ld 2,-16(12)\n\t" /* restore tocptr */ \ + VALGRIND_RESTORE_STACK \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[2]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_ppc64le_linux */ + +/* ------------------------- arm-linux ------------------------- */ + +#if defined(PLAT_arm_linux) + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS "r0", "r1", "r2", "r3","r4","r14" + +/* Macros to save and align the stack before making a function + call and restore it afterwards as gcc may not keep the stack + pointer aligned if it doesn't realise calls are being made + to other functions. */ + +/* This is a bit tricky. We store the original stack pointer in r10 + as it is callee-saves. gcc doesn't allow the use of r11 for some + reason. Also, we can't directly "bic" the stack pointer in thumb + mode since r13 isn't an allowed register number in that context. + So use r4 as a temporary, since that is about to get trashed + anyway, just after each use of this macro. Side effect is we need + to be very careful about any future changes, since + VALGRIND_ALIGN_STACK simply assumes r4 is usable. */ +#define VALGRIND_ALIGN_STACK \ + "mov r10, sp\n\t" \ + "mov r4, sp\n\t" \ + "bic r4, r4, #7\n\t" \ + "mov sp, r4\n\t" +#define VALGRIND_RESTORE_STACK \ + "mov sp, r10\n\t" + +/* These CALL_FN_ macros assume that on arm-linux, sizeof(unsigned + long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #4 \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "push {r0} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "push {r0, r1} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #4 \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "push {r0, r1, r2} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "push {r0, r1, r2, r3} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #4 \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "ldr r4, [%1, #36] \n\t" \ + "push {r0, r1, r2, r3, r4} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #40] \n\t" \ + "push {r0} \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "ldr r4, [%1, #36] \n\t" \ + "push {r0, r1, r2, r3, r4} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #4 \n\t" \ + "ldr r0, [%1, #40] \n\t" \ + "ldr r1, [%1, #44] \n\t" \ + "push {r0, r1} \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "ldr r4, [%1, #36] \n\t" \ + "push {r0, r1, r2, r3, r4} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr r0, [%1, #40] \n\t" \ + "ldr r1, [%1, #44] \n\t" \ + "ldr r2, [%1, #48] \n\t" \ + "push {r0, r1, r2} \n\t" \ + "ldr r0, [%1, #20] \n\t" \ + "ldr r1, [%1, #24] \n\t" \ + "ldr r2, [%1, #28] \n\t" \ + "ldr r3, [%1, #32] \n\t" \ + "ldr r4, [%1, #36] \n\t" \ + "push {r0, r1, r2, r3, r4} \n\t" \ + "ldr r0, [%1, #4] \n\t" \ + "ldr r1, [%1, #8] \n\t" \ + "ldr r2, [%1, #12] \n\t" \ + "ldr r3, [%1, #16] \n\t" \ + "ldr r4, [%1] \n\t" /* target->r4 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ + VALGRIND_RESTORE_STACK \ + "mov %0, r0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_arm_linux */ + +/* ------------------------ arm64-linux ------------------------ */ + +#if defined(PLAT_arm64_linux) + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS \ + "x0", "x1", "x2", "x3","x4", "x5", "x6", "x7", "x8", "x9", \ + "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", \ + "x18", "x19", "x20", "x30", \ + "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8", "v9", \ + "v10", "v11", "v12", "v13", "v14", "v15", "v16", "v17", \ + "v18", "v19", "v20", "v21", "v22", "v23", "v24", "v25", \ + "v26", "v27", "v28", "v29", "v30", "v31" + +/* x21 is callee-saved, so we can use it to save and restore SP around + the hidden call. */ +#define VALGRIND_ALIGN_STACK \ + "mov x21, sp\n\t" \ + "bic sp, x21, #15\n\t" +#define VALGRIND_RESTORE_STACK \ + "mov sp, x21\n\t" + +/* These CALL_FN_ macros assume that on arm64-linux, + sizeof(unsigned long) == 8. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x6, [%1, #56] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x6, [%1, #56] \n\t" \ + "ldr x7, [%1, #64] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #0x20 \n\t" \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x6, [%1, #56] \n\t" \ + "ldr x7, [%1, #64] \n\t" \ + "ldr x8, [%1, #72] \n\t" \ + "str x8, [sp, #0] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #0x20 \n\t" \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x6, [%1, #56] \n\t" \ + "ldr x7, [%1, #64] \n\t" \ + "ldr x8, [%1, #72] \n\t" \ + "str x8, [sp, #0] \n\t" \ + "ldr x8, [%1, #80] \n\t" \ + "str x8, [sp, #8] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #0x30 \n\t" \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x6, [%1, #56] \n\t" \ + "ldr x7, [%1, #64] \n\t" \ + "ldr x8, [%1, #72] \n\t" \ + "str x8, [sp, #0] \n\t" \ + "ldr x8, [%1, #80] \n\t" \ + "str x8, [sp, #8] \n\t" \ + "ldr x8, [%1, #88] \n\t" \ + "str x8, [sp, #16] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10,arg11, \ + arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + VALGRIND_ALIGN_STACK \ + "sub sp, sp, #0x30 \n\t" \ + "ldr x0, [%1, #8] \n\t" \ + "ldr x1, [%1, #16] \n\t" \ + "ldr x2, [%1, #24] \n\t" \ + "ldr x3, [%1, #32] \n\t" \ + "ldr x4, [%1, #40] \n\t" \ + "ldr x5, [%1, #48] \n\t" \ + "ldr x6, [%1, #56] \n\t" \ + "ldr x7, [%1, #64] \n\t" \ + "ldr x8, [%1, #72] \n\t" \ + "str x8, [sp, #0] \n\t" \ + "ldr x8, [%1, #80] \n\t" \ + "str x8, [sp, #8] \n\t" \ + "ldr x8, [%1, #88] \n\t" \ + "str x8, [sp, #16] \n\t" \ + "ldr x8, [%1, #96] \n\t" \ + "str x8, [sp, #24] \n\t" \ + "ldr x8, [%1] \n\t" /* target->x8 */ \ + VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ + VALGRIND_RESTORE_STACK \ + "mov %0, x0" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_arm64_linux */ + +/* ------------------------- s390x-linux ------------------------- */ + +#if defined(PLAT_s390x_linux) + +/* Similar workaround as amd64 (see above), but we use r11 as frame + pointer and save the old r11 in r7. r11 might be used for + argvec, therefore we copy argvec in r1 since r1 is clobbered + after the call anyway. */ +#if defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM) +# define __FRAME_POINTER \ + ,"d"(__builtin_dwarf_cfa()) +# define VALGRIND_CFI_PROLOGUE \ + ".cfi_remember_state\n\t" \ + "lgr 1,%1\n\t" /* copy the argvec pointer in r1 */ \ + "lgr 7,11\n\t" \ + "lgr 11,%2\n\t" \ + ".cfi_def_cfa r11, 0\n\t" +# define VALGRIND_CFI_EPILOGUE \ + "lgr 11, 7\n\t" \ + ".cfi_restore_state\n\t" +#else +# define __FRAME_POINTER +# define VALGRIND_CFI_PROLOGUE \ + "lgr 1,%1\n\t" +# define VALGRIND_CFI_EPILOGUE +#endif + +/* Nb: On s390 the stack pointer is properly aligned *at all times* + according to the s390 GCC maintainer. (The ABI specification is not + precise in this regard.) Therefore, VALGRIND_ALIGN_STACK and + VALGRIND_RESTORE_STACK are not defined here. */ + +/* These regs are trashed by the hidden call. Note that we overwrite + r14 in s390_irgen_noredir (VEX/priv/guest_s390_irgen.c) to give the + function a proper return address. All others are ABI defined call + clobbers. */ +#define __CALLER_SAVED_REGS "0","1","2","3","4","5","14", \ + "f0","f1","f2","f3","f4","f5","f6","f7" + +/* Nb: Although r11 is modified in the asm snippets below (inside + VALGRIND_CFI_PROLOGUE) it is not listed in the clobber section, for + two reasons: + (1) r11 is restored in VALGRIND_CFI_EPILOGUE, so effectively it is not + modified + (2) GCC will complain that r11 cannot appear inside a clobber section, + when compiled with -O -fno-omit-frame-pointer + */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-160\n\t" \ + "lg 1, 0(1)\n\t" /* target->r1 */ \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,160\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "d" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +/* The call abi has the arguments in r2-r6 and stack */ +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-160\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,160\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1, arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-160\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,160\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1, arg2, arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-160\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,160\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1, arg2, arg3, arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-160\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,160\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1, arg2, arg3, arg4, arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-160\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,160\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-168\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,168\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-176\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "mvc 168(8,15), 56(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,176\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7 ,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-184\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "mvc 168(8,15), 56(1)\n\t" \ + "mvc 176(8,15), 64(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,184\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7 ,arg8, arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-192\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "mvc 168(8,15), 56(1)\n\t" \ + "mvc 176(8,15), 64(1)\n\t" \ + "mvc 184(8,15), 72(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,192\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7 ,arg8, arg9, arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-200\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "mvc 168(8,15), 56(1)\n\t" \ + "mvc 176(8,15), 64(1)\n\t" \ + "mvc 184(8,15), 72(1)\n\t" \ + "mvc 192(8,15), 80(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,200\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7 ,arg8, arg9, arg10, arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + _argvec[11] = (unsigned long)arg11; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-208\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "mvc 168(8,15), 56(1)\n\t" \ + "mvc 176(8,15), 64(1)\n\t" \ + "mvc 184(8,15), 72(1)\n\t" \ + "mvc 192(8,15), 80(1)\n\t" \ + "mvc 200(8,15), 88(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,208\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7 ,arg8, arg9, arg10, arg11, arg12)\ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)arg1; \ + _argvec[2] = (unsigned long)arg2; \ + _argvec[3] = (unsigned long)arg3; \ + _argvec[4] = (unsigned long)arg4; \ + _argvec[5] = (unsigned long)arg5; \ + _argvec[6] = (unsigned long)arg6; \ + _argvec[7] = (unsigned long)arg7; \ + _argvec[8] = (unsigned long)arg8; \ + _argvec[9] = (unsigned long)arg9; \ + _argvec[10] = (unsigned long)arg10; \ + _argvec[11] = (unsigned long)arg11; \ + _argvec[12] = (unsigned long)arg12; \ + __asm__ volatile( \ + VALGRIND_CFI_PROLOGUE \ + "aghi 15,-216\n\t" \ + "lg 2, 8(1)\n\t" \ + "lg 3,16(1)\n\t" \ + "lg 4,24(1)\n\t" \ + "lg 5,32(1)\n\t" \ + "lg 6,40(1)\n\t" \ + "mvc 160(8,15), 48(1)\n\t" \ + "mvc 168(8,15), 56(1)\n\t" \ + "mvc 176(8,15), 64(1)\n\t" \ + "mvc 184(8,15), 72(1)\n\t" \ + "mvc 192(8,15), 80(1)\n\t" \ + "mvc 200(8,15), 88(1)\n\t" \ + "mvc 208(8,15), 96(1)\n\t" \ + "lg 1, 0(1)\n\t" \ + VALGRIND_CALL_NOREDIR_R1 \ + "lgr %0, 2\n\t" \ + "aghi 15,216\n\t" \ + VALGRIND_CFI_EPILOGUE \ + : /*out*/ "=d" (_res) \ + : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ + : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + + +#endif /* PLAT_s390x_linux */ + +/* ------------------------- mips32-linux ----------------------- */ + +#if defined(PLAT_mips32_linux) + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS "$2", "$3", "$4", "$5", "$6", \ +"$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", \ +"$25", "$31" + +/* These CALL_FN_ macros assume that on mips-linux, sizeof(unsigned + long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "subu $29, $29, 16 \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 16\n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "subu $29, $29, 16 \n\t" \ + "lw $4, 4(%1) \n\t" /* arg1*/ \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 16 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "subu $29, $29, 16 \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 16 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "subu $29, $29, 16 \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 16 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "subu $29, $29, 16 \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 16 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 24\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 24 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 32\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "nop\n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 32 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 32\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 28(%1) \n\t" \ + "sw $4, 24($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 32 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 40\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 28(%1) \n\t" \ + "sw $4, 24($29) \n\t" \ + "lw $4, 32(%1) \n\t" \ + "sw $4, 28($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 40 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 40\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 28(%1) \n\t" \ + "sw $4, 24($29) \n\t" \ + "lw $4, 32(%1) \n\t" \ + "sw $4, 28($29) \n\t" \ + "lw $4, 36(%1) \n\t" \ + "sw $4, 32($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 40 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 48\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 28(%1) \n\t" \ + "sw $4, 24($29) \n\t" \ + "lw $4, 32(%1) \n\t" \ + "sw $4, 28($29) \n\t" \ + "lw $4, 36(%1) \n\t" \ + "sw $4, 32($29) \n\t" \ + "lw $4, 40(%1) \n\t" \ + "sw $4, 36($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 48 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 48\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 28(%1) \n\t" \ + "sw $4, 24($29) \n\t" \ + "lw $4, 32(%1) \n\t" \ + "sw $4, 28($29) \n\t" \ + "lw $4, 36(%1) \n\t" \ + "sw $4, 32($29) \n\t" \ + "lw $4, 40(%1) \n\t" \ + "sw $4, 36($29) \n\t" \ + "lw $4, 44(%1) \n\t" \ + "sw $4, 40($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 48 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + "subu $29, $29, 8 \n\t" \ + "sw $28, 0($29) \n\t" \ + "sw $31, 4($29) \n\t" \ + "lw $4, 20(%1) \n\t" \ + "subu $29, $29, 56\n\t" \ + "sw $4, 16($29) \n\t" \ + "lw $4, 24(%1) \n\t" \ + "sw $4, 20($29) \n\t" \ + "lw $4, 28(%1) \n\t" \ + "sw $4, 24($29) \n\t" \ + "lw $4, 32(%1) \n\t" \ + "sw $4, 28($29) \n\t" \ + "lw $4, 36(%1) \n\t" \ + "sw $4, 32($29) \n\t" \ + "lw $4, 40(%1) \n\t" \ + "sw $4, 36($29) \n\t" \ + "lw $4, 44(%1) \n\t" \ + "sw $4, 40($29) \n\t" \ + "lw $4, 48(%1) \n\t" \ + "sw $4, 44($29) \n\t" \ + "lw $4, 4(%1) \n\t" \ + "lw $5, 8(%1) \n\t" \ + "lw $6, 12(%1) \n\t" \ + "lw $7, 16(%1) \n\t" \ + "lw $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "addu $29, $29, 56 \n\t" \ + "lw $28, 0($29) \n\t" \ + "lw $31, 4($29) \n\t" \ + "addu $29, $29, 8 \n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_mips32_linux */ + +/* ------------------------- mips64-linux ------------------------- */ + +#if defined(PLAT_mips64_linux) + +/* These regs are trashed by the hidden call. */ +#define __CALLER_SAVED_REGS "$2", "$3", "$4", "$5", "$6", \ +"$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", \ +"$25", "$31" + +/* These CALL_FN_ macros assume that on mips-linux, sizeof(unsigned + long) == 4. */ + +#define CALL_FN_W_v(lval, orig) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[1]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + __asm__ volatile( \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "0" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_W(lval, orig, arg1) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[2]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" /* arg1*/ \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WW(lval, orig, arg1,arg2) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[3]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[4]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[5]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[6]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[7]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[8]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $10, 56(%1)\n\t" \ + "ld $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[9]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + __asm__ volatile( \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $10, 56(%1)\n\t" \ + "ld $11, 64(%1)\n\t" \ + "ld $25, 0(%1) \n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[10]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + __asm__ volatile( \ + "dsubu $29, $29, 8\n\t" \ + "ld $4, 72(%1)\n\t" \ + "sd $4, 0($29)\n\t" \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $10, 56(%1)\n\t" \ + "ld $11, 64(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "daddu $29, $29, 8\n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ + arg7,arg8,arg9,arg10) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[11]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + __asm__ volatile( \ + "dsubu $29, $29, 16\n\t" \ + "ld $4, 72(%1)\n\t" \ + "sd $4, 0($29)\n\t" \ + "ld $4, 80(%1)\n\t" \ + "sd $4, 8($29)\n\t" \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $10, 56(%1)\n\t" \ + "ld $11, 64(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "daddu $29, $29, 16\n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[12]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + __asm__ volatile( \ + "dsubu $29, $29, 24\n\t" \ + "ld $4, 72(%1)\n\t" \ + "sd $4, 0($29)\n\t" \ + "ld $4, 80(%1)\n\t" \ + "sd $4, 8($29)\n\t" \ + "ld $4, 88(%1)\n\t" \ + "sd $4, 16($29)\n\t" \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $10, 56(%1)\n\t" \ + "ld $11, 64(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "daddu $29, $29, 24\n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ + arg6,arg7,arg8,arg9,arg10, \ + arg11,arg12) \ + do { \ + volatile OrigFn _orig = (orig); \ + volatile unsigned long _argvec[13]; \ + volatile unsigned long _res; \ + _argvec[0] = (unsigned long)_orig.nraddr; \ + _argvec[1] = (unsigned long)(arg1); \ + _argvec[2] = (unsigned long)(arg2); \ + _argvec[3] = (unsigned long)(arg3); \ + _argvec[4] = (unsigned long)(arg4); \ + _argvec[5] = (unsigned long)(arg5); \ + _argvec[6] = (unsigned long)(arg6); \ + _argvec[7] = (unsigned long)(arg7); \ + _argvec[8] = (unsigned long)(arg8); \ + _argvec[9] = (unsigned long)(arg9); \ + _argvec[10] = (unsigned long)(arg10); \ + _argvec[11] = (unsigned long)(arg11); \ + _argvec[12] = (unsigned long)(arg12); \ + __asm__ volatile( \ + "dsubu $29, $29, 32\n\t" \ + "ld $4, 72(%1)\n\t" \ + "sd $4, 0($29)\n\t" \ + "ld $4, 80(%1)\n\t" \ + "sd $4, 8($29)\n\t" \ + "ld $4, 88(%1)\n\t" \ + "sd $4, 16($29)\n\t" \ + "ld $4, 96(%1)\n\t" \ + "sd $4, 24($29)\n\t" \ + "ld $4, 8(%1)\n\t" \ + "ld $5, 16(%1)\n\t" \ + "ld $6, 24(%1)\n\t" \ + "ld $7, 32(%1)\n\t" \ + "ld $8, 40(%1)\n\t" \ + "ld $9, 48(%1)\n\t" \ + "ld $10, 56(%1)\n\t" \ + "ld $11, 64(%1)\n\t" \ + "ld $25, 0(%1)\n\t" /* target->t9 */ \ + VALGRIND_CALL_NOREDIR_T9 \ + "daddu $29, $29, 32\n\t" \ + "move %0, $2\n" \ + : /*out*/ "=r" (_res) \ + : /*in*/ "r" (&_argvec[0]) \ + : /*trash*/ "memory", __CALLER_SAVED_REGS \ + ); \ + lval = (__typeof__(lval)) _res; \ + } while (0) + +#endif /* PLAT_mips64_linux */ + + +/* ------------------------------------------------------------------ */ +/* ARCHITECTURE INDEPENDENT MACROS for CLIENT REQUESTS. */ +/* */ +/* ------------------------------------------------------------------ */ + +/* Some request codes. There are many more of these, but most are not + exposed to end-user view. These are the public ones, all of the + form 0x1000 + small_number. + + Core ones are in the range 0x00000000--0x0000ffff. The non-public + ones start at 0x2000. +*/ + +/* These macros are used by tools -- they must be public, but don't + embed them into other programs. */ +#define VG_USERREQ_TOOL_BASE(a,b) \ + ((unsigned int)(((a)&0xff) << 24 | ((b)&0xff) << 16)) +#define VG_IS_TOOL_USERREQ(a, b, v) \ + (VG_USERREQ_TOOL_BASE(a,b) == ((v) & 0xffff0000)) + +/* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !! + This enum comprises an ABI exported by Valgrind to programs + which use client requests. DO NOT CHANGE THE ORDER OF THESE + ENTRIES, NOR DELETE ANY -- add new ones at the end. */ +typedef + enum { VG_USERREQ__RUNNING_ON_VALGRIND = 0x1001, + VG_USERREQ__DISCARD_TRANSLATIONS = 0x1002, + + /* These allow any function to be called from the simulated + CPU but run on the real CPU. Nb: the first arg passed to + the function is always the ThreadId of the running + thread! So CLIENT_CALL0 actually requires a 1 arg + function, etc. */ + VG_USERREQ__CLIENT_CALL0 = 0x1101, + VG_USERREQ__CLIENT_CALL1 = 0x1102, + VG_USERREQ__CLIENT_CALL2 = 0x1103, + VG_USERREQ__CLIENT_CALL3 = 0x1104, + + /* Can be useful in regression testing suites -- eg. can + send Valgrind's output to /dev/null and still count + errors. */ + VG_USERREQ__COUNT_ERRORS = 0x1201, + + /* Allows the client program and/or gdbserver to execute a monitor + command. */ + VG_USERREQ__GDB_MONITOR_COMMAND = 0x1202, + + /* These are useful and can be interpreted by any tool that + tracks malloc() et al, by using vg_replace_malloc.c. */ + VG_USERREQ__MALLOCLIKE_BLOCK = 0x1301, + VG_USERREQ__RESIZEINPLACE_BLOCK = 0x130b, + VG_USERREQ__FREELIKE_BLOCK = 0x1302, + /* Memory pool support. */ + VG_USERREQ__CREATE_MEMPOOL = 0x1303, + VG_USERREQ__DESTROY_MEMPOOL = 0x1304, + VG_USERREQ__MEMPOOL_ALLOC = 0x1305, + VG_USERREQ__MEMPOOL_FREE = 0x1306, + VG_USERREQ__MEMPOOL_TRIM = 0x1307, + VG_USERREQ__MOVE_MEMPOOL = 0x1308, + VG_USERREQ__MEMPOOL_CHANGE = 0x1309, + VG_USERREQ__MEMPOOL_EXISTS = 0x130a, + + /* Allow printfs to valgrind log. */ + /* The first two pass the va_list argument by value, which + assumes it is the same size as or smaller than a UWord, + which generally isn't the case. Hence are deprecated. + The second two pass the vargs by reference and so are + immune to this problem. */ + /* both :: char* fmt, va_list vargs (DEPRECATED) */ + VG_USERREQ__PRINTF = 0x1401, + VG_USERREQ__PRINTF_BACKTRACE = 0x1402, + /* both :: char* fmt, va_list* vargs */ + VG_USERREQ__PRINTF_VALIST_BY_REF = 0x1403, + VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF = 0x1404, + + /* Stack support. */ + VG_USERREQ__STACK_REGISTER = 0x1501, + VG_USERREQ__STACK_DEREGISTER = 0x1502, + VG_USERREQ__STACK_CHANGE = 0x1503, + + /* Wine support */ + VG_USERREQ__LOAD_PDB_DEBUGINFO = 0x1601, + + /* Querying of debug info. */ + VG_USERREQ__MAP_IP_TO_SRCLOC = 0x1701, + + /* Disable/enable error reporting level. Takes a single + Word arg which is the delta to this thread's error + disablement indicator. Hence 1 disables or further + disables errors, and -1 moves back towards enablement. + Other values are not allowed. */ + VG_USERREQ__CHANGE_ERR_DISABLEMENT = 0x1801, + + /* Initialise IR injection */ + VG_USERREQ__VEX_INIT_FOR_IRI = 0x1901 + } Vg_ClientRequest; + +#if !defined(__GNUC__) +# define __extension__ /* */ +#endif + + +/* Returns the number of Valgrinds this code is running under. That + is, 0 if running natively, 1 if running under Valgrind, 2 if + running under Valgrind which is running under another Valgrind, + etc. */ +#define RUNNING_ON_VALGRIND \ + (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* if not */, \ + VG_USERREQ__RUNNING_ON_VALGRIND, \ + 0, 0, 0, 0, 0) \ + + +/* Discard translation of code in the range [_qzz_addr .. _qzz_addr + + _qzz_len - 1]. Useful if you are debugging a JITter or some such, + since it provides a way to make sure valgrind will retranslate the + invalidated area. Returns no value. */ +#define VALGRIND_DISCARD_TRANSLATIONS(_qzz_addr,_qzz_len) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DISCARD_TRANSLATIONS, \ + _qzz_addr, _qzz_len, 0, 0, 0) + + +/* These requests are for getting Valgrind itself to print something. + Possibly with a backtrace. This is a really ugly hack. The return value + is the number of characters printed, excluding the "**** " part at the + start and the backtrace (if present). */ + +#if defined(__GNUC__) || defined(__INTEL_COMPILER) && !defined(_MSC_VER) +/* Modern GCC will optimize the static routine out if unused, + and unused attribute will shut down warnings about it. */ +static int VALGRIND_PRINTF(const char *format, ...) + __attribute__((format(__printf__, 1, 2), __unused__)); +#endif +static int +#if defined(_MSC_VER) +__inline +#endif +VALGRIND_PRINTF(const char *format, ...) +{ +#if defined(NVALGRIND) + return 0; +#else /* NVALGRIND */ +#if defined(_MSC_VER) || defined(__MINGW64__) + uintptr_t _qzz_res; +#else + unsigned long _qzz_res; +#endif + va_list vargs; + va_start(vargs, format); +#if defined(_MSC_VER) || defined(__MINGW64__) + _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0, + VG_USERREQ__PRINTF_VALIST_BY_REF, + (uintptr_t)format, + (uintptr_t)&vargs, + 0, 0, 0); +#else + _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0, + VG_USERREQ__PRINTF_VALIST_BY_REF, + (unsigned long)format, + (unsigned long)&vargs, + 0, 0, 0); +#endif + va_end(vargs); + return (int)_qzz_res; +#endif /* NVALGRIND */ +} + +#if defined(__GNUC__) || defined(__INTEL_COMPILER) && !defined(_MSC_VER) +static int VALGRIND_PRINTF_BACKTRACE(const char *format, ...) + __attribute__((format(__printf__, 1, 2), __unused__)); +#endif +static int +#if defined(_MSC_VER) +__inline +#endif +VALGRIND_PRINTF_BACKTRACE(const char *format, ...) +{ +#if defined(NVALGRIND) + return 0; +#else /* NVALGRIND */ +#if defined(_MSC_VER) || defined(__MINGW64__) + uintptr_t _qzz_res; +#else + unsigned long _qzz_res; +#endif + va_list vargs; + va_start(vargs, format); +#if defined(_MSC_VER) || defined(__MINGW64__) + _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0, + VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF, + (uintptr_t)format, + (uintptr_t)&vargs, + 0, 0, 0); +#else + _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0, + VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF, + (unsigned long)format, + (unsigned long)&vargs, + 0, 0, 0); +#endif + va_end(vargs); + return (int)_qzz_res; +#endif /* NVALGRIND */ +} + + +/* These requests allow control to move from the simulated CPU to the + real CPU, calling an arbitrary function. + + Note that the current ThreadId is inserted as the first argument. + So this call: + + VALGRIND_NON_SIMD_CALL2(f, arg1, arg2) + + requires f to have this signature: + + Word f(Word tid, Word arg1, Word arg2) + + where "Word" is a word-sized type. + + Note that these client requests are not entirely reliable. For example, + if you call a function with them that subsequently calls printf(), + there's a high chance Valgrind will crash. Generally, your prospects of + these working are made higher if the called function does not refer to + any global variables, and does not refer to any libc or other functions + (printf et al). Any kind of entanglement with libc or dynamic linking is + likely to have a bad outcome, for tricky reasons which we've grappled + with a lot in the past. +*/ +#define VALGRIND_NON_SIMD_CALL0(_qyy_fn) \ + VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ + VG_USERREQ__CLIENT_CALL0, \ + _qyy_fn, \ + 0, 0, 0, 0) + +#define VALGRIND_NON_SIMD_CALL1(_qyy_fn, _qyy_arg1) \ + VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ + VG_USERREQ__CLIENT_CALL1, \ + _qyy_fn, \ + _qyy_arg1, 0, 0, 0) + +#define VALGRIND_NON_SIMD_CALL2(_qyy_fn, _qyy_arg1, _qyy_arg2) \ + VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ + VG_USERREQ__CLIENT_CALL2, \ + _qyy_fn, \ + _qyy_arg1, _qyy_arg2, 0, 0) + +#define VALGRIND_NON_SIMD_CALL3(_qyy_fn, _qyy_arg1, _qyy_arg2, _qyy_arg3) \ + VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ + VG_USERREQ__CLIENT_CALL3, \ + _qyy_fn, \ + _qyy_arg1, _qyy_arg2, \ + _qyy_arg3, 0) + + +/* Counts the number of errors that have been recorded by a tool. Nb: + the tool must record the errors with VG_(maybe_record_error)() or + VG_(unique_error)() for them to be counted. */ +#define VALGRIND_COUNT_ERRORS \ + (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR( \ + 0 /* default return */, \ + VG_USERREQ__COUNT_ERRORS, \ + 0, 0, 0, 0, 0) + +/* Several Valgrind tools (Memcheck, Massif, Helgrind, DRD) rely on knowing + when heap blocks are allocated in order to give accurate results. This + happens automatically for the standard allocator functions such as + malloc(), calloc(), realloc(), memalign(), new, new[], free(), delete, + delete[], etc. + + But if your program uses a custom allocator, this doesn't automatically + happen, and Valgrind will not do as well. For example, if you allocate + superblocks with mmap() and then allocates chunks of the superblocks, all + Valgrind's observations will be at the mmap() level and it won't know that + the chunks should be considered separate entities. In Memcheck's case, + that means you probably won't get heap block overrun detection (because + there won't be redzones marked as unaddressable) and you definitely won't + get any leak detection. + + The following client requests allow a custom allocator to be annotated so + that it can be handled accurately by Valgrind. + + VALGRIND_MALLOCLIKE_BLOCK marks a region of memory as having been allocated + by a malloc()-like function. For Memcheck (an illustrative case), this + does two things: + + - It records that the block has been allocated. This means any addresses + within the block mentioned in error messages will be + identified as belonging to the block. It also means that if the block + isn't freed it will be detected by the leak checker. + + - It marks the block as being addressable and undefined (if 'is_zeroed' is + not set), or addressable and defined (if 'is_zeroed' is set). This + controls how accesses to the block by the program are handled. + + 'addr' is the start of the usable block (ie. after any + redzone), 'sizeB' is its size. 'rzB' is the redzone size if the allocator + can apply redzones -- these are blocks of padding at the start and end of + each block. Adding redzones is recommended as it makes it much more likely + Valgrind will spot block overruns. `is_zeroed' indicates if the memory is + zeroed (or filled with another predictable value), as is the case for + calloc(). + + VALGRIND_MALLOCLIKE_BLOCK should be put immediately after the point where a + heap block -- that will be used by the client program -- is allocated. + It's best to put it at the outermost level of the allocator if possible; + for example, if you have a function my_alloc() which calls + internal_alloc(), and the client request is put inside internal_alloc(), + stack traces relating to the heap block will contain entries for both + my_alloc() and internal_alloc(), which is probably not what you want. + + For Memcheck users: if you use VALGRIND_MALLOCLIKE_BLOCK to carve out + custom blocks from within a heap block, B, that has been allocated with + malloc/calloc/new/etc, then block B will be *ignored* during leak-checking + -- the custom blocks will take precedence. + + VALGRIND_FREELIKE_BLOCK is the partner to VALGRIND_MALLOCLIKE_BLOCK. For + Memcheck, it does two things: + + - It records that the block has been deallocated. This assumes that the + block was annotated as having been allocated via + VALGRIND_MALLOCLIKE_BLOCK. Otherwise, an error will be issued. + + - It marks the block as being unaddressable. + + VALGRIND_FREELIKE_BLOCK should be put immediately after the point where a + heap block is deallocated. + + VALGRIND_RESIZEINPLACE_BLOCK informs a tool about reallocation. For + Memcheck, it does four things: + + - It records that the size of a block has been changed. This assumes that + the block was annotated as having been allocated via + VALGRIND_MALLOCLIKE_BLOCK. Otherwise, an error will be issued. + + - If the block shrunk, it marks the freed memory as being unaddressable. + + - If the block grew, it marks the new area as undefined and defines a red + zone past the end of the new block. + + - The V-bits of the overlap between the old and the new block are preserved. + + VALGRIND_RESIZEINPLACE_BLOCK should be put after allocation of the new block + and before deallocation of the old block. + + In many cases, these three client requests will not be enough to get your + allocator working well with Memcheck. More specifically, if your allocator + writes to freed blocks in any way then a VALGRIND_MAKE_MEM_UNDEFINED call + will be necessary to mark the memory as addressable just before the zeroing + occurs, otherwise you'll get a lot of invalid write errors. For example, + you'll need to do this if your allocator recycles freed blocks, but it + zeroes them before handing them back out (via VALGRIND_MALLOCLIKE_BLOCK). + Alternatively, if your allocator reuses freed blocks for allocator-internal + data structures, VALGRIND_MAKE_MEM_UNDEFINED calls will also be necessary. + + Really, what's happening is a blurring of the lines between the client + program and the allocator... after VALGRIND_FREELIKE_BLOCK is called, the + memory should be considered unaddressable to the client program, but the + allocator knows more than the rest of the client program and so may be able + to safely access it. Extra client requests are necessary for Valgrind to + understand the distinction between the allocator and the rest of the + program. + + Ignored if addr == 0. +*/ +#define VALGRIND_MALLOCLIKE_BLOCK(addr, sizeB, rzB, is_zeroed) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MALLOCLIKE_BLOCK, \ + addr, sizeB, rzB, is_zeroed, 0) + +/* See the comment for VALGRIND_MALLOCLIKE_BLOCK for details. + Ignored if addr == 0. +*/ +#define VALGRIND_RESIZEINPLACE_BLOCK(addr, oldSizeB, newSizeB, rzB) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__RESIZEINPLACE_BLOCK, \ + addr, oldSizeB, newSizeB, rzB, 0) + +/* See the comment for VALGRIND_MALLOCLIKE_BLOCK for details. + Ignored if addr == 0. +*/ +#define VALGRIND_FREELIKE_BLOCK(addr, rzB) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__FREELIKE_BLOCK, \ + addr, rzB, 0, 0, 0) + +/* Create a memory pool. */ +#define VALGRIND_CREATE_MEMPOOL(pool, rzB, is_zeroed) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CREATE_MEMPOOL, \ + pool, rzB, is_zeroed, 0, 0) + +/* Destroy a memory pool. */ +#define VALGRIND_DESTROY_MEMPOOL(pool) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DESTROY_MEMPOOL, \ + pool, 0, 0, 0, 0) + +/* Associate a piece of memory with a memory pool. */ +#define VALGRIND_MEMPOOL_ALLOC(pool, addr, size) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_ALLOC, \ + pool, addr, size, 0, 0) + +/* Disassociate a piece of memory from a memory pool. */ +#define VALGRIND_MEMPOOL_FREE(pool, addr) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_FREE, \ + pool, addr, 0, 0, 0) + +/* Disassociate any pieces outside a particular range. */ +#define VALGRIND_MEMPOOL_TRIM(pool, addr, size) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_TRIM, \ + pool, addr, size, 0, 0) + +/* Resize and/or move a piece associated with a memory pool. */ +#define VALGRIND_MOVE_MEMPOOL(poolA, poolB) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MOVE_MEMPOOL, \ + poolA, poolB, 0, 0, 0) + +/* Resize and/or move a piece associated with a memory pool. */ +#define VALGRIND_MEMPOOL_CHANGE(pool, addrA, addrB, size) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_CHANGE, \ + pool, addrA, addrB, size, 0) + +/* Return 1 if a mempool exists, else 0. */ +#define VALGRIND_MEMPOOL_EXISTS(pool) \ + (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ + VG_USERREQ__MEMPOOL_EXISTS, \ + pool, 0, 0, 0, 0) + +/* Mark a piece of memory as being a stack. Returns a stack id. + start is the lowest addressable stack byte, end is the highest + addressable stack byte. */ +#define VALGRIND_STACK_REGISTER(start, end) \ + (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ + VG_USERREQ__STACK_REGISTER, \ + start, end, 0, 0, 0) + +/* Unmark the piece of memory associated with a stack id as being a + stack. */ +#define VALGRIND_STACK_DEREGISTER(id) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__STACK_DEREGISTER, \ + id, 0, 0, 0, 0) + +/* Change the start and end address of the stack id. + start is the new lowest addressable stack byte, end is the new highest + addressable stack byte. */ +#define VALGRIND_STACK_CHANGE(id, start, end) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__STACK_CHANGE, \ + id, start, end, 0, 0) + +/* Load PDB debug info for Wine PE image_map. */ +#define VALGRIND_LOAD_PDB_DEBUGINFO(fd, ptr, total_size, delta) \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__LOAD_PDB_DEBUGINFO, \ + fd, ptr, total_size, delta, 0) + +/* Map a code address to a source file name and line number. buf64 + must point to a 64-byte buffer in the caller's address space. The + result will be dumped in there and is guaranteed to be zero + terminated. If no info is found, the first byte is set to zero. */ +#define VALGRIND_MAP_IP_TO_SRCLOC(addr, buf64) \ + (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ + VG_USERREQ__MAP_IP_TO_SRCLOC, \ + addr, buf64, 0, 0, 0) + +/* Disable error reporting for this thread. Behaves in a stack like + way, so you can safely call this multiple times provided that + VALGRIND_ENABLE_ERROR_REPORTING is called the same number of times + to re-enable reporting. The first call of this macro disables + reporting. Subsequent calls have no effect except to increase the + number of VALGRIND_ENABLE_ERROR_REPORTING calls needed to re-enable + reporting. Child threads do not inherit this setting from their + parents -- they are always created with reporting enabled. */ +#define VALGRIND_DISABLE_ERROR_REPORTING \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CHANGE_ERR_DISABLEMENT, \ + 1, 0, 0, 0, 0) + +/* Re-enable error reporting, as per comments on + VALGRIND_DISABLE_ERROR_REPORTING. */ +#define VALGRIND_ENABLE_ERROR_REPORTING \ + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CHANGE_ERR_DISABLEMENT, \ + -1, 0, 0, 0, 0) + +/* Execute a monitor command from the client program. + If a connection is opened with GDB, the output will be sent + according to the output mode set for vgdb. + If no connection is opened, output will go to the log output. + Returns 1 if command not recognised, 0 otherwise. */ +#define VALGRIND_MONITOR_COMMAND(command) \ + VALGRIND_DO_CLIENT_REQUEST_EXPR(0, VG_USERREQ__GDB_MONITOR_COMMAND, \ + command, 0, 0, 0, 0) + + +#undef PLAT_x86_darwin +#undef PLAT_amd64_darwin +#undef PLAT_x86_win32 +#undef PLAT_amd64_win64 +#undef PLAT_x86_linux +#undef PLAT_amd64_linux +#undef PLAT_ppc32_linux +#undef PLAT_ppc64be_linux +#undef PLAT_ppc64le_linux +#undef PLAT_arm_linux +#undef PLAT_s390x_linux +#undef PLAT_mips32_linux +#undef PLAT_mips64_linux + +#endif /* __VALGRIND_H */ diff --git a/deps/zlib.mk b/deps/zlib.mk new file mode 100644 index 0000000..cdefe1b --- /dev/null +++ b/deps/zlib.mk @@ -0,0 +1,37 @@ +ZLIB_GIT_URL := git://github.com/madler/zlib.git +ZLIB_TAR_URL = https://api.github.com/repos/madler/zlib/tarball/$1 +$(eval $(call git-external,zlib,ZLIB,,,$(SRCCACHE))) + +ifneq ($(USE_BINARYBUILDER_ZLIB), 1) +$(BUILDDIR)/$(ZLIB_SRC_DIR)/build-configured: $(SRCCACHE)/$(ZLIB_SRC_DIR)/source-extracted + mkdir -p $(dir $@) + cd $(dir $@) && $(CMAKE) -DCMAKE_INSTALL_PREFIX=$(abspath $(build_prefix)) -DCMAKE_BUILD_TYPE=Release -DUNIX=true $(dir $<) + echo 1 > $@ + +$(BUILDDIR)/$(ZLIB_SRC_DIR)/build-compiled: $(BUILDDIR)/$(ZLIB_SRC_DIR)/build-configured + $(MAKE) -C $(dir $<) $(MAKE_COMMON) + echo 1 > $@ + +$(eval $(call staged-install, \ + zlib,$(ZLIB_SRC_DIR), \ + MAKE_INSTALL,,, \ + $(INSTALL_NAME_CMD)libz.$(SHLIB_EXT) $(build_shlibdir)/libz.$(SHLIB_EXT))) + +clean-zlib: + -rm $(BUILDDIR)/$(ZLIB_SRC_DIR)/build-compiled $(build_libdir)/libz.a* $(build_libdir)/libz.so* $(build_includedir)/zlib.h $(build_includedir)/zconf.h + -$(MAKE) -C $(BUILDDIR)/$(ZLIB_SRC_DIR) distclean $(ZLIB_FLAGS) + +get-zlib: $(ZLIB_SRC_FILE) +extract-zlib: $(BUILDDIR)/$(ZLIB_SRC_DIR)/source-extracted +configure-zlib: extract-zlib +compile-zlib: $(BUILDDIR)/$(ZLIB_SRC_DIR)/build-compiled +fastcheck-zlib: check-zlib +check-zlib: compile-zlib + +else # USE_BINARYBUILDER_ZLIB + +ZLIB_BB_URL_BASE := https://github.com/JuliaBinaryWrappers/Zlib_jll.jl/releases/download/Zlib-v$(ZLIB_VER)+$(ZLIB_BB_REL) +ZLIB_BB_NAME := Zlib.v$(ZLIB_VER) +$(eval $(call bb-install,zlib,ZLIB,false)) + +endif # USE_BINARYBUILDER_ZLIB diff --git a/deps/zlib.version b/deps/zlib.version new file mode 100644 index 0000000..e363169 --- /dev/null +++ b/deps/zlib.version @@ -0,0 +1,2 @@ +ZLIB_BRANCH=v1.2.11 +ZLIB_SHA1=cacf7f1d4e3d44d871b605da3b647f07d718623f diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 0000000..4e9962d --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1,6 @@ +deps/ +_build/ +UnicodeData.txt +src/stdlib/ +src/NEWS.md + diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..3bf710c --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,55 @@ +# Makefile for building documentation + +default: html + +# You can set these variables from the command line. +SRCDIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) +JULIAHOME := $(abspath $(SRCDIR)/..) +SRCCACHE := $(abspath $(JULIAHOME)/deps/srccache) +include $(JULIAHOME)/Make.inc +JULIA_EXECUTABLE := $(call spawn,$(build_bindir)/julia) --startup-file=no + +.PHONY: help clean cleanall html pdf deps deploy + +help: + @echo "Please use 'make ' where is one of" + @echo " html to make standalone HTML files" + @echo " pdf to make standalone PDF file" + @echo + @echo "To run linkcheck, use 'make linkcheck=true'" + @echo "To run doctests, use 'make doctest=true'" + @echo "To fix outdated doctests, use 'make doctest=fix'" + + +DOCUMENTER_OPTIONS := linkcheck=$(linkcheck) doctest=$(doctest) buildroot=$(call cygpath_w,$(BUILDROOT)) \ + texplatform=$(texplatform) + +UNICODE_DATA_VERSION=13.0.0 +$(SRCCACHE)/UnicodeData-$(UNICODE_DATA_VERSION).txt: + @mkdir -p "$(SRCCACHE)" + $(JLDOWNLOAD) "$@" http://www.unicode.org/Public/$(UNICODE_DATA_VERSION)/ucd/UnicodeData.txt + +deps: $(SRCCACHE)/UnicodeData-$(UNICODE_DATA_VERSION).txt + $(JLCHECKSUM) "$<" + cp "$<" UnicodeData.txt + +clean: + -rm -rf _build/* deps/* docbuild.log UnicodeData.txt + +cleanall: clean + +html: deps + @echo "Building HTML documentation." + $(JULIA_EXECUTABLE) --color=yes $(call cygpath_w,$(SRCDIR)/make.jl) $(DOCUMENTER_OPTIONS) + @echo "Build finished. The HTML pages are in _build/html." + +pdf: deps + @echo "Building PDF documentation." + $(JULIA_EXECUTABLE) --color=yes $(call cygpath_w,$(SRCDIR)/make.jl) -- pdf $(DOCUMENTER_OPTIONS) + @echo "Build finished." + +# The deploy target should only be called in Travis builds +deploy: deps + @echo "Deploying HTML documentation." + $(JULIA_EXECUTABLE) --color=yes $(call cygpath_w,$(SRCDIR)/make.jl) -- deploy $(DOCUMENTER_OPTIONS) + @echo "Build & deploy of docs finished." diff --git a/doc/Manifest.toml b/doc/Manifest.toml new file mode 100644 index 0000000..5410f3c --- /dev/null +++ b/doc/Manifest.toml @@ -0,0 +1,99 @@ +# This file is machine-generated - editing it directly is not advised + +[[Base64]] +uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" + +[[Dates]] +deps = ["Printf"] +uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" + +[[Distributed]] +deps = ["Random", "Serialization", "Sockets"] +uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" + +[[DocStringExtensions]] +deps = ["LibGit2", "Markdown", "Pkg", "Test"] +git-tree-sha1 = "c5714d9bcdba66389612dc4c47ed827c64112997" +uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" +version = "0.8.2" + +[[Documenter]] +deps = ["Base64", "Dates", "DocStringExtensions", "InteractiveUtils", "JSON", "LibGit2", "Logging", "Markdown", "REPL", "Test", "Unicode"] +git-tree-sha1 = "1c593d1efa27437ed9dd365d1143c594b563e138" +uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +version = "0.25.1" + +[[DocumenterLaTeX]] +deps = ["Documenter", "Test"] +git-tree-sha1 = "653299370be20ff580bccd707dc9f360c0852d7f" +uuid = "cd674d7a-5f81-5cf3-af33-235ef1834b99" +version = "0.2.0" + +[[InteractiveUtils]] +deps = ["Markdown"] +uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" + +[[JSON]] +deps = ["Dates", "Mmap", "Parsers", "Unicode"] +git-tree-sha1 = "b34d7cef7b337321e97d22242c3c2b91f476748e" +uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" +version = "0.21.0" + +[[LibGit2]] +deps = ["Printf"] +uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" + +[[Libdl]] +uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" + +[[Logging]] +uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" + +[[Markdown]] +deps = ["Base64"] +uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" + +[[Mmap]] +uuid = "a63ad114-7e13-5084-954f-fe012c677804" + +[[Parsers]] +deps = ["Dates", "Test"] +git-tree-sha1 = "8077624b3c450b15c087944363606a6ba12f925e" +uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" +version = "1.0.10" + +[[Pkg]] +deps = ["Dates", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "UUIDs"] +uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" + +[[Printf]] +deps = ["Unicode"] +uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" + +[[REPL]] +deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] +uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" + +[[Random]] +deps = ["Serialization"] +uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" + +[[SHA]] +uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" + +[[Serialization]] +uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" + +[[Sockets]] +uuid = "6462fe0b-24de-5631-8697-dd941f90decc" + +[[Test]] +deps = ["Distributed", "InteractiveUtils", "Logging", "Random"] +uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[[UUIDs]] +deps = ["Random", "SHA"] +uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" + +[[Unicode]] +uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" diff --git a/doc/NEWS-update.jl b/doc/NEWS-update.jl new file mode 100644 index 0000000..4014c67 --- /dev/null +++ b/doc/NEWS-update.jl @@ -0,0 +1,19 @@ +# Script to automatically insert Markdown footnotes for all [#xxxx] issue +# cross-references in the NEWS file. + +NEWS = get(ARGS, 1, "NEWS.md") + +s = read(NEWS, String) + +m = match(r"\[#[0-9]+\]:", s) +if m !== nothing + s = s[1:m.offset-1] +end + +footnote(n) = "[#$n]: https://github.com/JuliaLang/julia/issues/$n" +N = map(m -> parse(Int,m.captures[1]), eachmatch(r"\[#([0-9]+)\]", s)) +foots = join(map(footnote, sort!(unique(N))), "\n") + +open(NEWS, "w") do f + println(f, s, foots) +end diff --git a/doc/Project.toml b/doc/Project.toml new file mode 100644 index 0000000..c09e74d --- /dev/null +++ b/doc/Project.toml @@ -0,0 +1,3 @@ +[deps] +Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +DocumenterLaTeX = "cd674d7a-5f81-5cf3-af33-235ef1834b99" diff --git a/doc/README.md b/doc/README.md new file mode 100644 index 0000000..be54260 --- /dev/null +++ b/doc/README.md @@ -0,0 +1,29 @@ +# Julia Documentation README + +Julia's documentation is written in Markdown. A reference of all supported syntax can be found in the [manual](https://docs.julialang.org/en/v1/stdlib/Markdown/). All documentation can be found in the Markdown files in `doc/src/` and the docstrings in Julia source files in `base/` and `stdlib/`. + +## Requirements + +This documentation is built using the [Documenter.jl](https://github.com/JuliaDocs/Documenter.jl) package. + +All dependencies are automatically installed into a sandboxed package directory in `doc/deps/` to avoid interfering with user-installed packages. + +## Building + +To build Julia's documentation run + +```sh +$ make docs +``` + +from the root directory. This will build the HTML documentation and output it to the `doc/_build/` folder. + +## Testing + +To run the doctests found in the manual run + +```sh +$ make -C doc doctest=true +``` + +from the root directory. diff --git a/doc/build/README.md b/doc/build/README.md new file mode 100644 index 0000000..83c822c --- /dev/null +++ b/doc/build/README.md @@ -0,0 +1,25 @@ + +

+ +This directory contains various details related to building Julia: + +* [Detailed build instructions](build.md) + +Notes for various OSes: + +* [Linux](linux.md) +* [macOS](macos.md) +* [Windows](windows.md) +* [FreeBSD](freebsd.md) + +Notes for various architectures: + +* [ARM](arm.md) + +Notes for building Julia for distribution: + +* [Distribution Notes](distributing.md) diff --git a/doc/build/arm.md b/doc/build/arm.md new file mode 100644 index 0000000..9268da3 --- /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 `MCPU=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: + +``` +MCPU=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..03bc157 --- /dev/null +++ b/doc/build/build.md @@ -0,0 +1,273 @@ +# 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]** (9.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. + +If you are building Julia for the sole purpose of incorporating Intel MKL, it may be beneficial to first try [MKL.jl](https://github.com/JuliaComputing/MKL.jl). This package will automatically download MKL and rebuild Julia's system image against it, sidestepping the need to set up a working build environment just to add MKL functionality. + +## Source distributions of releases + +Each pre-release and release of Julia has a "full" source distribution and a "light" source +distribution. + +The full source distribution contains the source code for Julia and all dependencies so +that it can be built from source without an internet connection. The light source +distribution does not include the source code of dependencies. + +For example, `julia-1.0.0.tar.gz` is the light source distribution for the `v1.0.0` release +of Julia, while `julia-1.0.0-full.tar.gz` is the full source distribution. 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..bbe3649 --- /dev/null +++ b/doc/build/macos.md @@ -0,0 +1,10 @@ +## 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. + +The dependent libraries are now built with [BinaryBuilder](https://binarybuilder.org) and will be automatically downloaded. This is the preferred way to build Julia source. In case you want to build them all on your own, you will need a 64-bit gfortran to compile Julia dependencies. +```bash +brew install gcc +``` + +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..12e56d3 --- /dev/null +++ b/doc/build/windows.md @@ -0,0 +1,317 @@ +# 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 = <