From 3a3b935b6710e6f4183a58425776f29a2d1b4640 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Fri, 29 Oct 2021 21:22:16 +0800 Subject: [PATCH] Squashed commit of the following: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit c054dbc6d4e03d7168864fed018e3635b546d251 Author: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Fri Oct 29 01:31:55 2021 +0900 optimizer: eliminate allocations (#42833) commit 6a9737d9c06da0d5a705346cd66e7b785bdeef66 Author: Jeff Bezanson Date: Thu Oct 28 12:23:53 2021 -0400 fix #42659, move `jl_coverage_visit_line` to runtime library (#42810) commit c762f1050240aae2fa3fc90715c54daba8bb8b4c Author: Marc Ittel <35898736+MarcMush@users.noreply.github.com> Date: Thu Oct 28 12:19:13 2021 +0200 change `julia` to `julia-repl` in docstrings (#42824) Co-authored-by: Michael Abbott <32575566+mcabbott@users.noreply.github.com> commit 9f52ec0aabe0dec83423acea3dca911328119b10 Author: Dilum Aluthge Date: Thu Oct 28 05:30:11 2021 -0400 CI (Buildkite): Update all rootfs images to the latest versions (#42802) * CI (Buildkite): Update all rootfs images to the latest versions * Re-sign all of the signed pipelines commit 404e584165fdd83df977688f288ca4d35e0c85d1 Author: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Wed Oct 27 21:11:04 2021 -0400 πŸ€– Bump the Statistics stdlib from 74897fe to 5256d57 (#42826) Co-authored-by: Dilum Aluthge commit c74814e0ca7fd06ddbbbb828e43c6ccbcde909b2 Author: Jeff Bezanson Date: Wed Oct 27 16:34:46 2021 -0400 reset `RandomDevice` file from `__init__` (#42537) This prevents us from seeing an invalid `IOStream` object from a saved system image, and also ensures the files are opened once for all threads. commit 05ed3484e6f5801e2335d8e3cbf8c1013673a877 Author: Jeff Bezanson Date: Wed Oct 27 15:24:17 2021 -0400 only visit nonfunction_mt once when traversing method tables (#42821) commit d71b77d7b69b781d3eb29428c3bd406da76ebf34 Author: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Tue Oct 26 20:39:08 2021 -0400 πŸ€– Bump the Downloads stdlib from 5f1509d to dbb0625 (#42811) Co-authored-by: Dilum Aluthge commit b4fddc1c834143a98664a13eec69df2d99e0a86d Author: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Tue Oct 26 14:46:20 2021 -0400 πŸ€– Bump the Pkg stdlib from bc32103f to 26918395 (#42806) Co-authored-by: Dilum Aluthge commit 6a386de4b401968382ad0b480c52e6c6a46903b2 Author: Dilum Aluthge Date: Tue Oct 26 12:15:51 2021 -0400 CI (Buildkite): make sure to hit ignore any unencrypted repo keys, regardless of where they are located in the repository (#42803) commit 021a6b5ec99639a38a639b0df993259d58cdd57b Author: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Wed Oct 27 01:08:33 2021 +0900 optimizer: clean up inlining test code (#42804) commit 16eb196594a2ac76e8a758ede1cc8f4ee012e362 Merge: 21ebabf790 1510eaa93e Author: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Tue Oct 26 23:25:41 2021 +0900 Merge pull request #42766 from JuliaLang/avi/42754 optimizer: fix #42754, inline union-split const-prop'ed sources commit 21ebabf7904061b4283b4428efa8fa4d6e8d0bc6 Author: Kristoffer Carlsson Date: Tue Oct 26 16:11:32 2021 +0200 simplify code loading test now that TOML files are parsed with a real TOML parser (#42328) commit 1510eaa93e60c9c8d7e92fd1d296b78ded49d52e Author: Shuhei Kadowaki Date: Mon Oct 25 01:35:12 2021 +0900 optimizer: fix #42754, inline union-split const-prop'ed sources This commit complements #39754 and #39305: implements a logic to use constant-prop'ed results for inlining at union-split callsite. Currently it works only for cases when constant-prop' succeeded for all (union-split) signatures. > example ```julia julia> mutable struct X # NOTE in order to confuse `fieldtype_tfunc`, we need to have at least two fields with different types a::Union{Nothing, Int} b::Symbol end; julia> code_typed((X, Union{Nothing,Int})) do x, a # this `setproperty` call would be union-split and constant-prop will happen for # each signature: inlining would fail if we don't use constant-prop'ed source # since the approximated inlining cost of `convert(fieldtype(X, sym), a)` would # end up very high if we don't propagate `sym::Const(:a)` x.a = a x end |> only |> first ``` > before this commit ```julia CodeInfo( 1 ─ %1 = Base.setproperty!::typeof(setproperty!) β”‚ %2 = (isa)(a, Nothing)::Bool └── goto #3 if not %2 2 ─ %4 = Ο€ (a, Nothing) β”‚ invoke %1(_2::X, :a::Symbol, %4::Nothing)::Any └── goto #6 3 ─ %7 = (isa)(a, Int64)::Bool └── goto #5 if not %7 4 ─ %9 = Ο€ (a, Int64) β”‚ invoke %1(_2::X, :a::Symbol, %9::Int64)::Any └── goto #6 5 ─ Core.throw(ErrorException("fatal error in type inference (type bound)"))::Union{} └── unreachable 6 β”„ return x ) ``` > after this commit ```julia CodeInfo( 1 ─ %1 = (isa)(a, Nothing)::Bool └── goto #3 if not %1 2 ─ Base.setfield!(x, :a, nothing)::Nothing └── goto #6 3 ─ %5 = (isa)(a, Int64)::Bool └── goto #5 if not %5 4 ─ %7 = Ο€ (a, Int64) β”‚ Base.setfield!(x, :a, %7)::Int64 └── goto #6 5 ─ Core.throw(ErrorException("fatal error in type inference (type bound)"))::Union{} └── unreachable 6 β”„ return x ) ``` commit 4c3ae2011041670d1f1552da270e4195f4e689d8 Author: Chris Foster Date: Tue Oct 26 21:48:32 2021 +1000 Make Base.ifelse a generic function (#37343) Allow user code to directly extend `Base.ifelse` rather than needing a special package for it. commit 2e388e3731fcdd8d1db4c1aed5c6a39df3ef7153 Author: Shuhei Kadowaki Date: Mon Oct 25 01:30:09 2021 +0900 optimizer: eliminate excessive specialization in inlining code This commit includes several code quality improvements in inlining code: - eliminate excessive specializations around: * `item::Pair{Any, Any}` constructions * iterations on `Vector{Pair{Any, Any}}` - replace `Pair{Any, Any}` with new, more explicit data type `InliningCase` - remove dead code --- .buildkite/pipelines/main/misc/doctest.yml | 4 +- .buildkite/pipelines/main/misc/embedding.yml | 4 +- .buildkite/pipelines/main/misc/llvmpasses.yml | 8 +- .buildkite/pipelines/main/misc/sanitizers.yml | 8 +- .../misc/signed_pipeline_test.yml.signature | Bin 96 -> 96 bytes .buildkite/pipelines/main/misc/whitespace.yml | 4 +- .../main/platforms/package_linux.arches | 12 +- .../main/platforms/tester_linux.arches | 16 +- .../scheduled/coverage/coverage_linux64.yml | 5 +- .../coverage/coverage_linux64.yml.signature | Bin 96 -> 96 bytes .../no_bb/no_bb_package_linux.arches | 2 +- .../scheduled/no_bb/no_bb_tester_linux.arches | 2 +- .gitignore | 6 + base/boot.jl | 4 +- base/compiler/abstractinterpretation.jl | 2 +- base/compiler/optimize.jl | 2 +- base/compiler/ssair/inlining.jl | 325 ++++++++++-------- base/compiler/ssair/passes.jl | 36 +- base/compiler/tfuncs.jl | 2 +- base/docs/basedocs.jl | 2 +- base/essentials.jl | 16 + base/loading.jl | 2 +- base/operators.jl | 16 - base/stream.jl | 10 +- base/twiceprecision.jl | 6 +- .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 - .../sha512 | 1 - .../md5 | 1 + .../sha512 | 1 + .../md5 | 1 - .../sha512 | 1 - doc/src/devdocs/boundscheck.md | 3 +- doc/src/manual/functions.md | 6 +- doc/src/manual/modules.md | 4 +- doc/src/manual/workflow-tips.md | 4 +- src/Makefile | 2 +- src/codegen-stubs.c | 3 - src/codegen.cpp | 188 +--------- src/coverage.cpp | 214 ++++++++++++ src/gf.c | 3 +- src/jl_exported_funcs.inc | 3 - src/julia.expmap | 3 + stdlib/Downloads.version | 2 +- stdlib/LibGit2/src/types.jl | 2 +- stdlib/Pkg.version | 2 +- stdlib/REPL/docs/src/index.md | 4 +- .../REPL/src/TerminalMenus/MultiSelectMenu.jl | 2 +- stdlib/REPL/src/TerminalMenus/RadioMenu.jl | 2 +- stdlib/Random/src/RNGs.jl | 30 +- stdlib/Statistics.version | 2 +- test/compiler/inline.jl | 244 +++++++++---- test/loading.jl | 79 ++--- 58 files changed, 750 insertions(+), 558 deletions(-) delete mode 100644 deps/checksums/Downloads-5f1509da10cf22bb4fc59de707cb3455b6807d99.tar.gz/md5 delete mode 100644 deps/checksums/Downloads-5f1509da10cf22bb4fc59de707cb3455b6807d99.tar.gz/sha512 create mode 100644 deps/checksums/Downloads-dbb062584961888c6abddcb186d0248bec7763b8.tar.gz/md5 create mode 100644 deps/checksums/Downloads-dbb062584961888c6abddcb186d0248bec7763b8.tar.gz/sha512 create mode 100644 deps/checksums/Pkg-269183951b6532cbdb411fff2816c5bcf96ac495.tar.gz/md5 create mode 100644 deps/checksums/Pkg-269183951b6532cbdb411fff2816c5bcf96ac495.tar.gz/sha512 delete mode 100644 deps/checksums/Pkg-bc32103f6dead7d8016badc24a577e4a30014b49.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-bc32103f6dead7d8016badc24a577e4a30014b49.tar.gz/sha512 create mode 100644 deps/checksums/Statistics-5256d570d0a554780ed80949c79116f47eac6382.tar.gz/md5 create mode 100644 deps/checksums/Statistics-5256d570d0a554780ed80949c79116f47eac6382.tar.gz/sha512 delete mode 100644 deps/checksums/Statistics-74897fed33700dba92578aa0fefef5b99ba16086.tar.gz/md5 delete mode 100644 deps/checksums/Statistics-74897fed33700dba92578aa0fefef5b99ba16086.tar.gz/sha512 create mode 100644 src/coverage.cpp diff --git a/.buildkite/pipelines/main/misc/doctest.yml b/.buildkite/pipelines/main/misc/doctest.yml index 1f185284ec641c..b83139ddc1f9b7 100644 --- a/.buildkite/pipelines/main/misc/doctest.yml +++ b/.buildkite/pipelines/main/misc/doctest.yml @@ -12,8 +12,8 @@ steps: persist_depot_dirs: packages,artifacts,compiled version: '1.6' - staticfloat/sandbox#v1: - rootfs_url: https://github.com/JuliaCI/rootfs-images/releases/download/v3.1/package_linux.x86_64.tar.gz - rootfs_treehash: "8c33c341a864852629b8aac01a6eb6a79b73570e" + rootfs_url: https://github.com/JuliaCI/rootfs-images/releases/download/v4.8/package_linux.x86_64.tar.gz + rootfs_treehash: "2a058481b567f0e91b9aa3ce4ad4f09e6419355a" uid: 1000 gid: 1000 workspaces: diff --git a/.buildkite/pipelines/main/misc/embedding.yml b/.buildkite/pipelines/main/misc/embedding.yml index 45c740f4e8a6ef..bdd2a0a6065f5b 100644 --- a/.buildkite/pipelines/main/misc/embedding.yml +++ b/.buildkite/pipelines/main/misc/embedding.yml @@ -12,8 +12,8 @@ steps: persist_depot_dirs: packages,artifacts,compiled version: '1.6' - staticfloat/sandbox#v1: - rootfs_url: https://github.com/JuliaCI/rootfs-images/releases/download/v3.1/package_linux.x86_64.tar.gz - rootfs_treehash: "8c33c341a864852629b8aac01a6eb6a79b73570e" + rootfs_url: https://github.com/JuliaCI/rootfs-images/releases/download/v4.8/package_linux.x86_64.tar.gz + rootfs_treehash: "2a058481b567f0e91b9aa3ce4ad4f09e6419355a" uid: 1000 gid: 1000 workspaces: diff --git a/.buildkite/pipelines/main/misc/llvmpasses.yml b/.buildkite/pipelines/main/misc/llvmpasses.yml index 5b444a6db41aac..1f6d89014eb0dd 100644 --- a/.buildkite/pipelines/main/misc/llvmpasses.yml +++ b/.buildkite/pipelines/main/misc/llvmpasses.yml @@ -12,8 +12,8 @@ steps: persist_depot_dirs: packages,artifacts,compiled version: '1.6' - staticfloat/sandbox#v1: - rootfs_url: https://github.com/JuliaCI/rootfs-images/releases/download/v3.1/llvm_passes.x86_64.tar.gz - rootfs_treehash: "9dd715500b117a16fcfa419ea0bca0c0ca902cee" + rootfs_url: https://github.com/JuliaCI/rootfs-images/releases/download/v4.8/llvm_passes.x86_64.tar.gz + rootfs_treehash: "c7a289a8cc544b234b1e2d7cbcce3e6815359ecd" workspaces: # Include `/cache/repos` so that our `git` version introspection works. - "/cache/repos:/cache/repos" @@ -32,8 +32,8 @@ steps: persist_depot_dirs: packages,artifacts,compiled version: '1.6' - staticfloat/sandbox#v1: - rootfs_url: https://github.com/JuliaCI/rootfs-images/releases/download/v3.8/package_linux.x86_64.tar.gz - rootfs_treehash: "84a323ae8fcc724f8ea5aca5901bbbf4bda3e519" + rootfs_url: https://github.com/JuliaCI/rootfs-images/releases/download/v4.8/package_linux.x86_64.tar.gz + rootfs_treehash: "2a058481b567f0e91b9aa3ce4ad4f09e6419355a" uid: 1000 gid: 1000 workspaces: diff --git a/.buildkite/pipelines/main/misc/sanitizers.yml b/.buildkite/pipelines/main/misc/sanitizers.yml index 43beb05807a8ec..a0c40dda7e12f9 100644 --- a/.buildkite/pipelines/main/misc/sanitizers.yml +++ b/.buildkite/pipelines/main/misc/sanitizers.yml @@ -12,8 +12,8 @@ steps: persist_depot_dirs: packages,artifacts,compiled version: '1.6' - staticfloat/sandbox#v1: - rootfs_url: https://github.com/JuliaCI/rootfs-images/releases/download/v3.1/llvm_passes.x86_64.tar.gz - rootfs_treehash: "9dd715500b117a16fcfa419ea0bca0c0ca902cee" + rootfs_url: https://github.com/JuliaCI/rootfs-images/releases/download/v4.8/llvm_passes.x86_64.tar.gz + rootfs_treehash: "c7a289a8cc544b234b1e2d7cbcce3e6815359ecd" uid: 1000 gid: 1000 workspaces: @@ -33,8 +33,8 @@ steps: persist_depot_dirs: packages,artifacts,compiled version: '1.6' - staticfloat/sandbox#v1: - rootfs_url: https://github.com/JuliaCI/rootfs-images/releases/download/v3.1/llvm_passes.x86_64.tar.gz - rootfs_treehash: "9dd715500b117a16fcfa419ea0bca0c0ca902cee" + rootfs_url: https://github.com/JuliaCI/rootfs-images/releases/download/v4.8/llvm_passes.x86_64.tar.gz + rootfs_treehash: "c7a289a8cc544b234b1e2d7cbcce3e6815359ecd" uid: 1000 gid: 1000 workspaces: diff --git a/.buildkite/pipelines/main/misc/signed_pipeline_test.yml.signature b/.buildkite/pipelines/main/misc/signed_pipeline_test.yml.signature index 299f959c1db101e07ff0d5af706cea08748cca10..06cd41b71504d08c5154a7560214018e42ca73ba 100644 GIT binary patch literal 96 zcmV-m0H6O;VQh3|WM5wZjaf0UG-LO}IcTbI$3vIg;@1{@4Q^7L_@pqm3*V6j4D$O| zUBy+#RUTJFP%Kiy4&?+(g0r;Q;kca^g~(^l8w<;NoS+YJI^GWjHG7`ijnw0rWQ-qq CiYWo%_aU1s1^Q+#c~?GA3|JkZ1ns;=gAiTu;_EP+wlvOCFEPk(WAV3yT0?p&T9*Vio_a|sK zn~$9k&InHoe@z@p{ length(item.cases) - for ((metharg, case), next_cond_bb) in zip(item.cases, item.bbs) + local bb = compact.active_result_bb + @assert length(bbs) > length(cases) + for i in 1:length(cases) + ithcase = cases[i] + metharg = ithcase.sig + case = ithcase.item + next_cond_bb = bbs[i] @assert !isa(metharg, UnionAll) cond = true aparams, mparams = atype.parameters::SimpleVector, metharg.parameters::SimpleVector @@ -515,7 +521,7 @@ function ir_inline_unionsplit!(compact::IncrementalCompact, idx::Int, end bb += 1 # We're now in the fall through block, decide what to do - if item.fully_covered + if fully_covered e = Expr(:call, GlobalRef(Core, :throw), FATAL_TYPE_BOUND_ERROR) insert_node_here!(compact, NewInstruction(e, Union{}, line)) insert_node_here!(compact, NewInstruction(ReturnNode(), Union{}, line)) @@ -677,7 +683,8 @@ function rewrite_apply_exprargs!(ir::IRCode, todo::Vector{Pair{Int, Any}}, idx:: handled = false if isa(info, ConstCallInfo) if !is_stmt_noinline(flag) && maybe_handle_const_call!( - ir, state1.id, new_stmt, info, new_sig,call.rt, istate, flag, false, todo) + ir, state1.id, new_stmt, info, new_sig, + istate, flag, false, todo) handled = true else info = info.call @@ -687,8 +694,9 @@ function rewrite_apply_exprargs!(ir::IRCode, todo::Vector{Pair{Int, Any}}, idx:: info = isa(info, MethodMatchInfo) ? MethodMatchInfo[info] : info.matches # See if we can inline this call to `iterate` - analyze_single_call!(ir, todo, state1.id, new_stmt, - new_sig, call.rt, info, istate, flag) + analyze_single_call!( + ir, todo, state1.id, new_stmt, + new_sig, info, istate, flag) end if i != length(thisarginfo.each) valT = getfield_tfunc(call.rt, Const(1)) @@ -708,11 +716,13 @@ function rewrite_apply_exprargs!(ir::IRCode, todo::Vector{Pair{Int, Any}}, idx:: return new_argexprs, new_atypes end -function rewrite_invoke_exprargs!(argexprs::Vector{Any}) +function rewrite_invoke_exprargs!(expr::Expr) + argexprs = expr.args argexpr0 = argexprs[2] - argexprs = argexprs[4:end] - pushfirst!(argexprs, argexpr0) - return argexprs + argexprs = argexprs[3:end] + argexprs[1] = argexpr0 + expr.args = argexprs + return expr end function compileable_specialization(et::Union{EdgeTracker, Nothing}, match::MethodMatch) @@ -778,9 +788,15 @@ function resolve_todo(todo::InliningTodo, state::InliningState, flag::UInt8) return InliningTodo(mi, src) end -function resolve_todo(todo::UnionSplit, state::InliningState, flag::UInt8) - UnionSplit(todo.fully_covered, todo.atype, - Pair{Any,Any}[sig=>resolve_todo(item, state, flag) for (sig, item) in todo.cases]) +function resolve_todo((; fully_covered, atype, cases, #=bbs=#)::UnionSplit, state::InliningState, flag::UInt8) + ncases = length(cases) + newcases = Vector{InliningCase}(undef, ncases) + for i in 1:ncases + (; sig, item) = cases[i] + newitem = resolve_todo(item, state, flag) + push!(newcases, InliningCase(sig, newitem)) + end + return UnionSplit(fully_covered, atype, newcases) end function validate_sparams(sparams::SimpleVector) @@ -791,7 +807,7 @@ function validate_sparams(sparams::SimpleVector) end function analyze_method!(match::MethodMatch, atypes::Vector{Any}, - state::InliningState, @nospecialize(stmttyp), flag::UInt8) + state::InliningState, flag::UInt8) method = match.method methsig = method.sig @@ -821,7 +837,7 @@ function analyze_method!(match::MethodMatch, atypes::Vector{Any}, return compileable_specialization(et, match) end - todo = InliningTodo(mi, match, atypes, stmttyp) + todo = InliningTodo(mi, match, atypes) # If we don't have caches here, delay resolving this MethodInstance # until the batch inlining step (or an external post-processing pass) state.mi_cache === nothing && return todo @@ -846,17 +862,13 @@ function handle_single_case!(ir::IRCode, stmt::Expr, idx::Int, @nospecialize(cas if isa(case, ConstantCase) ir[SSAValue(idx)] = case.val elseif isa(case, MethodInstance) - if isinvoke - stmt.args = rewrite_invoke_exprargs!(stmt.args) - end + isinvoke && rewrite_invoke_exprargs!(stmt) stmt.head = :invoke pushfirst!(stmt.args, case) elseif case === nothing # Do, well, nothing else - if isinvoke - stmt.args = rewrite_invoke_exprargs!(stmt.args) - end + isinvoke && rewrite_invoke_exprargs!(stmt) push!(todo, idx=>(case::InliningTodo)) end nothing @@ -1005,7 +1017,6 @@ is_builtin(s::Signature) = function inline_invoke!(ir::IRCode, idx::Int, sig::Signature, (; match, result)::InvokeCallInfo, state::InliningState, todo::Vector{Pair{Int, Any}}, flag::UInt8) stmt = ir.stmts[idx][:inst] - calltype = ir.stmts[idx][:type] if !match.fully_covers # TODO: We could union split out the signature check and continue on @@ -1018,7 +1029,7 @@ function inline_invoke!(ir::IRCode, idx::Int, sig::Signature, (; match, result): pushfirst!(atypes, atype0) if isa(result, InferenceResult) && !is_stmt_noinline(flag) - (; mi) = item = InliningTodo(result, atypes, calltype) + (; mi) = item = InliningTodo(result, atypes) validate_sparams(mi.sparam_vals) || return nothing if argtypes_to_type(atypes) <: mi.def.sig state.mi_cache !== nothing && (item = resolve_todo(item, state, flag)) @@ -1027,7 +1038,7 @@ function inline_invoke!(ir::IRCode, idx::Int, sig::Signature, (; match, result): end end - result = analyze_method!(match, atypes, state, calltype, flag) + result = analyze_method!(match, atypes, state, flag) handle_single_case!(ir, stmt, idx, result, true, todo) return nothing end @@ -1136,13 +1147,13 @@ function process_simple!(ir::IRCode, todo::Vector{Pair{Int, Any}}, idx::Int, sta return sig end -function analyze_single_call!(ir::IRCode, todo::Vector{Pair{Int, Any}}, idx::Int, @nospecialize(stmt), - sig::Signature, @nospecialize(calltype), infos::Vector{MethodMatchInfo}, - state::InliningState, flag::UInt8) - cases = Pair{Any, Any}[] - signature_union = Union{} - only_method = nothing # keep track of whether there is one matching method - too_many = false +# TODO inline non-`isdispatchtuple`, union-split callsites +function analyze_single_call!( + ir::IRCode, todo::Vector{Pair{Int, Any}}, idx::Int, @nospecialize(stmt), + (; atypes, atype)::Signature, infos::Vector{MethodMatchInfo}, state::InliningState, flag::UInt8) + cases = InliningCase[] + local signature_union = Bottom + local only_method = nothing # keep track of whether there is one matching method local meth local fully_covered = true for i in 1:length(infos) @@ -1151,8 +1162,7 @@ function analyze_single_call!(ir::IRCode, todo::Vector{Pair{Int, Any}}, idx::Int if meth.ambig # Too many applicable methods # Or there is a (partial?) ambiguity - too_many = true - break + return elseif length(meth) == 0 # No applicable methods; try next union split continue @@ -1172,38 +1182,36 @@ function analyze_single_call!(ir::IRCode, todo::Vector{Pair{Int, Any}}, idx::Int fully_covered = false continue end - case = analyze_method!(match, sig.atypes, state, calltype, flag) - if case === nothing + item = analyze_method!(match, atypes, state, flag) + if item === nothing fully_covered = false continue - elseif _any(p->p[1] === spec_types, cases) + elseif _any(case->case.sig === spec_types, cases) continue end - push!(cases, Pair{Any,Any}(spec_types, case)) + push!(cases, InliningCase(spec_types, item)) end end - too_many && return - - signature_fully_covered = sig.atype <: signature_union - # 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 && only_method isa Method - if length(infos) > 1 - (metharg, methsp) = ccall(:jl_type_intersection_with_env, Any, (Any, Any), - sig.atype, only_method.sig)::SimpleVector - match = MethodMatch(metharg, methsp, only_method, true) - else - meth = meth::MethodLookupResult - @assert length(meth) == 1 - match = meth[1] + # if the signature is fully covered and there is only one applicable method, + # we can try to inline it even if the signature is not a dispatch tuple + if atype <: signature_union + if length(cases) == 0 && only_method isa Method + if length(infos) > 1 + (metharg, methsp) = ccall(:jl_type_intersection_with_env, Any, (Any, Any), + atype, only_method.sig)::SimpleVector + match = MethodMatch(metharg, methsp, only_method, true) + else + meth = meth::MethodLookupResult + @assert length(meth) == 1 + match = meth[1] + end + item = analyze_method!(match, atypes, state, flag) + item === nothing && return + push!(cases, InliningCase(match.spec_types, item)) + fully_covered = true end - fully_covered = true - case = analyze_method!(match, sig.atypes, state, calltype, flag) - case === nothing && return - push!(cases, Pair{Any,Any}(match.spec_types, case)) - end - if !signature_fully_covered + else fully_covered = false end @@ -1211,42 +1219,82 @@ function analyze_single_call!(ir::IRCode, todo::Vector{Pair{Int, Any}}, idx::Int # 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) - return + handle_single_case!(ir, stmt, idx, cases[1].item, false, todo) + elseif length(cases) > 0 + push!(todo, idx=>UnionSplit(fully_covered, atype, cases)) end - length(cases) == 0 && return - push!(todo, idx=>UnionSplit(fully_covered, sig.atype, cases)) return nothing end -function maybe_handle_const_call!(ir::IRCode, idx::Int, stmt::Expr, - info::ConstCallInfo, sig::Signature, @nospecialize(calltype), - state::InliningState, flag::UInt8, - isinvoke::Bool, todo::Vector{Pair{Int, Any}}) - # when multiple matches are found, bail out and later inliner will union-split this signature - # TODO effectively use multiple constant analysis results here - length(info.results) == 1 || return false - result = info.results[1] - isa(result, InferenceResult) || return false - - (; mi) = item = InliningTodo(result, sig.atypes, calltype) - validate_sparams(mi.sparam_vals) || return true - mthd_sig = mi.def.sig - mistypes = mi.specTypes - state.mi_cache !== nothing && (item = resolve_todo(item, state, flag)) - if sig.atype <: mthd_sig - handle_single_case!(ir, stmt, idx, item, isinvoke, todo) - return true - else - item === nothing && return true - # Union split out the error case - item = UnionSplit(false, sig.atype, Pair{Any, Any}[mistypes => item]) - if isinvoke - stmt.args = rewrite_invoke_exprargs!(stmt.args) +# try to create `InliningCase`s using constant-prop'ed results +# currently it works only when constant-prop' succeeded for all (union-split) signatures +# TODO use any of constant-prop'ed results, and leave the other unhandled cases to later +# TODO this function contains a lot of duplications with `analyze_single_call!`, factor them out +function maybe_handle_const_call!( + ir::IRCode, idx::Int, stmt::Expr, (; results)::ConstCallInfo, (; atypes, atype)::Signature, + state::InliningState, flag::UInt8, isinvoke::Bool, todo::Vector{Pair{Int, Any}}) + cases = InliningCase[] # TODO avoid this allocation for single cases ? + local fully_covered = true + local signature_union = Bottom + for result in results + isa(result, InferenceResult) || return false + (; mi) = item = InliningTodo(result, atypes) + spec_types = mi.specTypes + signature_union = Union{signature_union, spec_types} + if !isdispatchtuple(spec_types) + fully_covered = false + continue end - push!(todo, idx=>item) - return true + if !validate_sparams(mi.sparam_vals) + fully_covered = false + continue + end + state.mi_cache !== nothing && (item = resolve_todo(item, state, flag)) + if item === nothing + fully_covered = false + continue + end + push!(cases, InliningCase(spec_types, item)) + end + + # if the signature is fully covered and there is only one applicable method, + # we can try to inline it even if the signature is not a dispatch tuple + if atype <: signature_union + if length(cases) == 0 && length(results) == 1 + (; mi) = item = InliningTodo(results[1]::InferenceResult, atypes) + state.mi_cache !== nothing && (item = resolve_todo(item, state, flag)) + validate_sparams(mi.sparam_vals) || return true + item === nothing && return true + push!(cases, InliningCase(mi.specTypes, item)) + fully_covered = true + end + else + fully_covered = false 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].item, isinvoke, todo) + elseif length(cases) > 0 + isinvoke && rewrite_invoke_exprargs!(stmt) + push!(todo, idx=>UnionSplit(fully_covered, atype, cases)) + end + return true +end + +function handle_const_opaque_closure_call!( + ir::IRCode, idx::Int, stmt::Expr, (; results)::ConstCallInfo, + (; atypes)::Signature, state::InliningState, flag::UInt8, todo::Vector{Pair{Int, Any}}) + @assert length(results) == 1 + result = results[1]::InferenceResult + item = InliningTodo(result, atypes) + isdispatchtuple(item.mi.specTypes) || return + validate_sparams(item.mi.sparam_vals) || return + state.mi_cache !== nothing && (item = resolve_todo(item, state, flag)) + handle_single_case!(ir, stmt, idx, item, false, todo) + return nothing end function assemble_inline_todo!(ir::IRCode, state::InliningState) @@ -1258,11 +1306,11 @@ function assemble_inline_todo!(ir::IRCode, state::InliningState) sig === nothing && continue stmt = ir.stmts[idx][:inst] - calltype = ir.stmts[idx][:type] info = ir.stmts[idx][:info] # Check whether this call was @pure and evaluates to a constant if info isa MethodResultPure + calltype = ir.stmts[idx][:type] if calltype isa Const && is_inlineable_constant(calltype.val) ir.stmts[idx][:inst] = quoted(calltype.val) continue @@ -1278,21 +1326,28 @@ function assemble_inline_todo!(ir::IRCode, state::InliningState) continue end - # If inference arrived at this result by using constant propagation, - # it'll have performed a specialized analysis for just this case. Use its - # result. + # if inference arrived here with constant-prop'ed result(s), + # we can perform a specialized analysis for just this case if isa(info, ConstCallInfo) - if !is_stmt_noinline(flag) && maybe_handle_const_call!( - ir, idx, stmt, info, sig, calltype, state, flag, sig.f === Core.invoke, todo) - continue + if !is_stmt_noinline(flag) + if isa(info.call, OpaqueClosureCallInfo) + handle_const_opaque_closure_call!( + ir, idx, stmt, info, + sig, state, flag, todo) + continue + else + maybe_handle_const_call!( + ir, idx, stmt, info, sig, + state, flag, sig.f === Core.invoke, todo) && continue + end else info = info.call end end if isa(info, OpaqueClosureCallInfo) - result = analyze_method!(info.match, sig.atypes, state, calltype, flag) - handle_single_case!(ir, stmt, idx, result, false, todo) + item = analyze_method!(info.match, sig.atypes, state, flag) + handle_single_case!(ir, stmt, idx, item, false, todo) continue end @@ -1313,7 +1368,7 @@ function assemble_inline_todo!(ir::IRCode, state::InliningState) continue end - analyze_single_call!(ir, todo, idx, stmt, sig, calltype, infos, state, flag) + analyze_single_call!(ir, todo, idx, stmt, sig, infos, state, flag) end todo end diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index 8fc34e30c74d04..0ba1c38fc72bb7 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -76,24 +76,17 @@ 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 = let curblock = curblock - Int[stmt for stmt in du.defs if block_for_inst(ir.cfg, stmt) == curblock] - end - 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) + stmtblock = block_for_inst(ir.cfg, use_idx) + curblock = find_curblock(domtree, allblocks, stmtblock) + local def = 0 + for idx in du.defs + if block_for_inst(ir.cfg, idx) == curblock + if curblock != stmtblock + # Find the last def in this block + def = max(def, idx) + else + # Find the last def before our use + def = max(def, idx >= use_idx ? 0 : idx) end end end @@ -564,15 +557,10 @@ a result of dead code elimination. """ function getfield_elim_pass!(ir::IRCode) 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 field_ordering = :unspecified @@ -998,8 +986,6 @@ function adce_pass!(ir::IRCode) 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}() insts = ir.stmts for idx in 1:length(insts) diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index c2cab794ee6b30..cac266ff9ce6c2 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -231,7 +231,7 @@ function ifelse_tfunc(@nospecialize(cnd), @nospecialize(x), @nospecialize(y)) end return tmerge(x, y) end -add_tfunc(ifelse, 3, 3, ifelse_tfunc, 1) +add_tfunc(Core.ifelse, 3, 3, ifelse_tfunc, 1) function egal_tfunc(@nospecialize(x), @nospecialize(y)) xx = widenconditional(x) diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index fdfd1f1eeb965b..3cbe180233d9cc 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -992,7 +992,7 @@ then their contents are vertically concatenated together. In the standard REPL, typing `;` on an empty line will switch to shell mode. # Examples -```julia +```jldoctest julia> function foo() x = "Hello, "; x *= "World!" return x diff --git a/base/essentials.jl b/base/essentials.jl index 5280252f1946a2..cd4b618b5b0b1d 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -477,6 +477,22 @@ Stacktrace: """ sizeof(x) = Core.sizeof(x) +""" + ifelse(condition::Bool, x, y) + +Return `x` if `condition` is `true`, otherwise return `y`. This differs from `?` or `if` in +that it is an ordinary function, so all the arguments are evaluated first. In some cases, +using `ifelse` instead of an `if` statement can eliminate the branch in generated code and +provide higher performance in tight loops. + +# Examples +```jldoctest +julia> ifelse(1 > 2, 1, 2) +2 +``` +""" +ifelse(condition::Bool, x, y) = Core.ifelse(condition, x, y) + # simple Array{Any} operations needed for bootstrap @eval setindex!(A::Array{Any}, @nospecialize(x), i::Int) = arrayset($(Expr(:boundscheck)), A, x, i) diff --git a/base/loading.jl b/base/loading.jl index 30f7bd25a1160e..864ad6795f45dc 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -358,7 +358,7 @@ or `nothing` if `m` was not imported from a package. Optionally further path component strings can be provided to construct a path within the package root. -```julia +```julia-repl julia> pkgdir(Foo) "/path/to/Foo.jl" diff --git a/base/operators.jl b/base/operators.jl index ebd1d515f92b00..82b393e0a9e9e8 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -429,22 +429,6 @@ const β‰₯ = >= # which is more idiomatic: isless(x::Real, y::Real) = x ifelse(1 > 2, 1, 2) -2 -``` -""" -ifelse - """ cmp(x,y) diff --git a/base/stream.jl b/base/stream.jl index 05178a7f9ebcb3..cee4894b28c3c2 100644 --- a/base/stream.jl +++ b/base/stream.jl @@ -552,7 +552,7 @@ julia> withenv("LINES" => 30, "COLUMNS" => 100) do To get your TTY size, -```julia +```julia-repl julia> displaysize(stdout) (34, 147) ``` @@ -1328,7 +1328,7 @@ Possible values for each stream are: * `io` an `IOStream`, `TTY`, `Pipe`, socket, or `devnull`. # Examples -```julia +```julia-repl julia> redirect_stdio(stdout="stdout.txt", stderr="stderr.txt") do print("hello stdout") print(stderr, "hello stderr") @@ -1344,14 +1344,14 @@ julia> read("stderr.txt", String) # Edge cases It is possible to pass the same argument to `stdout` and `stderr`: -```julia +```julia-repl julia> redirect_stdio(stdout="log.txt", stderr="log.txt", stdin=devnull) do ... end ``` However it is not supported to pass two distinct descriptors of the same file. -```julia +```julia-repl julia> io1 = open("same/path", "w") julia> io2 = open("same/path", "w") @@ -1359,7 +1359,7 @@ julia> io2 = open("same/path", "w") julia> redirect_stdio(f, stdout=io1, stderr=io2) # not suppored ``` Also the `stdin` argument may not be the same descriptor as `stdout` or `stderr`. -```julia +```julia-repl julia> io = open(...) julia> redirect_stdio(f, stdout=io, stdin=io) # not supported diff --git a/base/twiceprecision.jl b/base/twiceprecision.jl index 7f338ce98a1f5d..84972d5c849eb5 100644 --- a/base/twiceprecision.jl +++ b/base/twiceprecision.jl @@ -63,7 +63,7 @@ representation, even though it is exact from the standpoint of binary representation. Example: -```julia +```julia-repl julia> 1.0 + 1.0001e-15 1.000000000000001 @@ -94,7 +94,7 @@ numbers. Mathematically, `zhi + zlo = x * y`, where `zhi` contains the most significant bits and `zlo` the least significant. Example: -```julia +```julia-repl julia> x = Float32(Ο€) 3.1415927f0 @@ -126,7 +126,7 @@ numbers. Mathematically, `zhi + zlo β‰ˆ x / y`, where `zhi` contains the most significant bits and `zlo` the least significant. Example: -```julia +```julia-repl julia> x, y = Float32(Ο€), 3.1f0 (3.1415927f0, 3.1f0) diff --git a/deps/checksums/Downloads-5f1509da10cf22bb4fc59de707cb3455b6807d99.tar.gz/md5 b/deps/checksums/Downloads-5f1509da10cf22bb4fc59de707cb3455b6807d99.tar.gz/md5 deleted file mode 100644 index 174aa11e6400ae..00000000000000 --- a/deps/checksums/Downloads-5f1509da10cf22bb4fc59de707cb3455b6807d99.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -f0659f9d0c7a8e1370ef79ce51f301f6 diff --git a/deps/checksums/Downloads-5f1509da10cf22bb4fc59de707cb3455b6807d99.tar.gz/sha512 b/deps/checksums/Downloads-5f1509da10cf22bb4fc59de707cb3455b6807d99.tar.gz/sha512 deleted file mode 100644 index dc1cea61be718c..00000000000000 --- a/deps/checksums/Downloads-5f1509da10cf22bb4fc59de707cb3455b6807d99.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -f66567bd01093c1c6f7d72888b7df71b259d00b583712a621b32a0d7971a380d25f723892d9431147824e08dcfba155d20f36bac626e33259b8f5124cf47b5f3 diff --git a/deps/checksums/Downloads-dbb062584961888c6abddcb186d0248bec7763b8.tar.gz/md5 b/deps/checksums/Downloads-dbb062584961888c6abddcb186d0248bec7763b8.tar.gz/md5 new file mode 100644 index 00000000000000..0bae33f0340acf --- /dev/null +++ b/deps/checksums/Downloads-dbb062584961888c6abddcb186d0248bec7763b8.tar.gz/md5 @@ -0,0 +1 @@ +84b074e6917d3507dd0f27d0206f9707 diff --git a/deps/checksums/Downloads-dbb062584961888c6abddcb186d0248bec7763b8.tar.gz/sha512 b/deps/checksums/Downloads-dbb062584961888c6abddcb186d0248bec7763b8.tar.gz/sha512 new file mode 100644 index 00000000000000..f84ec1ef5d6a8b --- /dev/null +++ b/deps/checksums/Downloads-dbb062584961888c6abddcb186d0248bec7763b8.tar.gz/sha512 @@ -0,0 +1 @@ +b5efe0b8958b13e12d68db340a6797e96508df3a6b97cb4a89ca400a78c3ba9e6826924caed5264b5dc773bb0bd9200c4cfdd44f4c47ca268270c4b1f22d36e6 diff --git a/deps/checksums/Pkg-269183951b6532cbdb411fff2816c5bcf96ac495.tar.gz/md5 b/deps/checksums/Pkg-269183951b6532cbdb411fff2816c5bcf96ac495.tar.gz/md5 new file mode 100644 index 00000000000000..347adfba558a39 --- /dev/null +++ b/deps/checksums/Pkg-269183951b6532cbdb411fff2816c5bcf96ac495.tar.gz/md5 @@ -0,0 +1 @@ +d8c921d8230a5607dd8721f835e7ca43 diff --git a/deps/checksums/Pkg-269183951b6532cbdb411fff2816c5bcf96ac495.tar.gz/sha512 b/deps/checksums/Pkg-269183951b6532cbdb411fff2816c5bcf96ac495.tar.gz/sha512 new file mode 100644 index 00000000000000..e4df8cd985f2c0 --- /dev/null +++ b/deps/checksums/Pkg-269183951b6532cbdb411fff2816c5bcf96ac495.tar.gz/sha512 @@ -0,0 +1 @@ +181e0d83a777df4f6fe10ad5841d9cec09f9f77e5624b0c4810cc84b9acbc484cb1d2420c25308351fea579d64310aabbfc770ec7069eda69a996d640aa10f78 diff --git a/deps/checksums/Pkg-bc32103f6dead7d8016badc24a577e4a30014b49.tar.gz/md5 b/deps/checksums/Pkg-bc32103f6dead7d8016badc24a577e4a30014b49.tar.gz/md5 deleted file mode 100644 index 316ab05c115d85..00000000000000 --- a/deps/checksums/Pkg-bc32103f6dead7d8016badc24a577e4a30014b49.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -ffb3a809aa10b13fba5a42eeb5af86fa diff --git a/deps/checksums/Pkg-bc32103f6dead7d8016badc24a577e4a30014b49.tar.gz/sha512 b/deps/checksums/Pkg-bc32103f6dead7d8016badc24a577e4a30014b49.tar.gz/sha512 deleted file mode 100644 index 874d2d3c8d464b..00000000000000 --- a/deps/checksums/Pkg-bc32103f6dead7d8016badc24a577e4a30014b49.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -04f9808b18bad02184e8e9ada147ef0af15c522e9d57586561c433fa60ae1ffbb97716378799a5f00fb5b83b801a95cef3df76a354f7b22fc4397759af74a70d diff --git a/deps/checksums/Statistics-5256d570d0a554780ed80949c79116f47eac6382.tar.gz/md5 b/deps/checksums/Statistics-5256d570d0a554780ed80949c79116f47eac6382.tar.gz/md5 new file mode 100644 index 00000000000000..cad5503d6fd27d --- /dev/null +++ b/deps/checksums/Statistics-5256d570d0a554780ed80949c79116f47eac6382.tar.gz/md5 @@ -0,0 +1 @@ +0b43a9ed3c1f081f9510dffd6697c825 diff --git a/deps/checksums/Statistics-5256d570d0a554780ed80949c79116f47eac6382.tar.gz/sha512 b/deps/checksums/Statistics-5256d570d0a554780ed80949c79116f47eac6382.tar.gz/sha512 new file mode 100644 index 00000000000000..e9aae6344832cb --- /dev/null +++ b/deps/checksums/Statistics-5256d570d0a554780ed80949c79116f47eac6382.tar.gz/sha512 @@ -0,0 +1 @@ +15b2ed0b269605b1dab150fccc8e202829278bc4920293b711c611627cd4a61767373895acd1d45a0b37557e8c0cd123673d75944cd08216cffa81dc5186c0fc diff --git a/deps/checksums/Statistics-74897fed33700dba92578aa0fefef5b99ba16086.tar.gz/md5 b/deps/checksums/Statistics-74897fed33700dba92578aa0fefef5b99ba16086.tar.gz/md5 deleted file mode 100644 index 5a2ea2906c1169..00000000000000 --- a/deps/checksums/Statistics-74897fed33700dba92578aa0fefef5b99ba16086.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -c6defdea70beb0f10b22c3f2770f93be diff --git a/deps/checksums/Statistics-74897fed33700dba92578aa0fefef5b99ba16086.tar.gz/sha512 b/deps/checksums/Statistics-74897fed33700dba92578aa0fefef5b99ba16086.tar.gz/sha512 deleted file mode 100644 index 537df2a1e0602c..00000000000000 --- a/deps/checksums/Statistics-74897fed33700dba92578aa0fefef5b99ba16086.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -1cf4cfd24f647c8a6f0937bf125bdd66fadc4951fa356bdce2e63091fc4f4ec4e080559526dbb5133ff8cb58f31ec5aa544fd8b5422355d07e3d112b9dc6e698 diff --git a/doc/src/devdocs/boundscheck.md b/doc/src/devdocs/boundscheck.md index ae62581902945e..f840a0283ea15e 100644 --- a/doc/src/devdocs/boundscheck.md +++ b/doc/src/devdocs/boundscheck.md @@ -54,8 +54,9 @@ end Which quietly assumes 1-based indexing and therefore exposes unsafe memory access when used with [`OffsetArrays`](@ref man-custom-indice): -```julia +```julia-repl julia> using OffsetArrays + julia> sum(OffsetArray([1,2,3], -10)) 9164911648 # inconsistent results or segfault ``` diff --git a/doc/src/manual/functions.md b/doc/src/manual/functions.md index 5fbca52bbfaad1..ec150b843e8611 100644 --- a/doc/src/manual/functions.md +++ b/doc/src/manual/functions.md @@ -481,7 +481,7 @@ Instead of destructuring based on iteration, the right side of assignments can a This follows the syntax for NamedTuples, and works by assigning to each variable on the left a property of the right side of the assignment with the same name using `getproperty`: -```julia +```jldoctest julia> (; b, a) = (a=1, b=2, c=3) (a = 1, b = 2, c = 3) @@ -498,7 +498,7 @@ The destructuring feature can also be used within a function argument. If a function argument name is written as a tuple (e.g. `(x, y)`) instead of just a symbol, then an assignment `(x, y) = argument` will be inserted for you: -```julia +```julia-repl julia> minmax(x, y) = (y < x) ? (y, x) : (x, y) julia> gap((min, max)) = max - min @@ -512,7 +512,7 @@ would be a two-argument function, and this example would not work. Similarly, property destructuring can also be used for function arguments: -```julia +```julia-repl julia> foo((; x, y)) = x + y foo (generic function with 1 method) diff --git a/doc/src/manual/modules.md b/doc/src/manual/modules.md index a8231be2a6afb8..d8242d1749f2dd 100644 --- a/doc/src/manual/modules.md +++ b/doc/src/manual/modules.md @@ -205,7 +205,7 @@ For example, `Base` exports the function name `read`, but the CSV.jl package als If we are going to invoke CSV reading many times, it would be convenient to drop the `CSV.` qualifier. But then it is ambiguous whether we are referring to `Base.read` or `CSV.read`: -```julia +```julia-repl julia> read; julia> import CSV: read @@ -214,7 +214,7 @@ WARNING: ignoring conflicting import of CSV.read into Main Renaming provides a solution: -```julia +```julia-repl julia> import CSV: read as rd ``` diff --git a/doc/src/manual/workflow-tips.md b/doc/src/manual/workflow-tips.md index 2f7abf5a6a0334..7ee4b6aefba770 100644 --- a/doc/src/manual/workflow-tips.md +++ b/doc/src/manual/workflow-tips.md @@ -104,7 +104,7 @@ the following modifications: Navigate to your temporary directory and launch Julia, then do the following: - ```julia + ```julia-repl pkg> generate MyPkg # type ] to enter pkg mode julia> push!(LOAD_PATH, pwd()) # hit backspace to exit pkg mode ``` @@ -123,7 +123,7 @@ the following modifications: Then navigate to the directory containing your test file (here assumed to be `"runtests.jl"`) and do the following: - ```julia + ```julia-repl julia> using MyPkg julia> include("runtests.jl") diff --git a/src/Makefile b/src/Makefile index 427d3b5c351c7e..17e70fe6d01f55 100644 --- a/src/Makefile +++ b/src/Makefile @@ -47,7 +47,7 @@ RUNTIME_SRCS := \ simplevector runtime_intrinsics precompile \ threading partr stackwalk gc gc-debug gc-pages gc-stacks method \ jlapi signal-handling safepoint timing subtype \ - crc32c APInt-C processor ircode opaque_closure codegen-stubs + crc32c APInt-C processor ircode opaque_closure codegen-stubs coverage SRCS := jloptions runtime_ccall rtutils ifeq ($(OS),WINNT) SRCS += win32_ucontext diff --git a/src/codegen-stubs.c b/src/codegen-stubs.c index c98d55f77c53f8..e95e2f4f46d320 100644 --- a/src/codegen-stubs.c +++ b/src/codegen-stubs.c @@ -14,10 +14,7 @@ JL_DLLEXPORT void jl_dump_native_fallback(void *native_code, const char *bc_fname, const char *unopt_bc_fname, const char *obj_fname, const char *asm_fname, const char *sysimg_data, size_t sysimg_len) UNAVAILABLE JL_DLLEXPORT int32_t jl_get_llvm_gv_fallback(void *native_code, jl_value_t *p) UNAVAILABLE -JL_DLLEXPORT void jl_write_malloc_log_fallback(void) UNAVAILABLE -JL_DLLEXPORT void jl_write_coverage_data_fallback(const char *output) UNAVAILABLE -JL_DLLEXPORT void jl_clear_malloc_data_fallback(void) UNAVAILABLE JL_DLLEXPORT int jl_extern_c_fallback(jl_function_t *f, jl_value_t *rt, jl_value_t *argt, char *name) UNAVAILABLE JL_DLLEXPORT jl_value_t *jl_dump_method_asm_fallback(jl_method_instance_t *linfo, size_t world, char raw_mc, char getwrapper, const char* asm_variant, const char *debuginfo, char binary) UNAVAILABLE diff --git a/src/codegen.cpp b/src/codegen.cpp index b09eb8db04e074..d1011f9139f566 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1803,28 +1803,12 @@ static std::pair uses_specsig(jl_method_instance_t *lam, jl_value_t // Logging for code coverage and memory allocation -const int logdata_blocksize = 32; // target getting nearby lines in the same general cache area and reducing calls to malloc by chunking -typedef uint64_t logdata_block[logdata_blocksize]; -typedef StringMap< std::vector > logdata_t; +JL_DLLEXPORT void jl_coverage_alloc_line(StringRef filename, int line); +JL_DLLEXPORT uint64_t *jl_coverage_data_pointer(StringRef filename, int line); +JL_DLLEXPORT uint64_t *jl_malloc_data_pointer(StringRef filename, int line); -static uint64_t *allocLine(std::vector &vec, int line) +static void visitLine(jl_codectx_t &ctx, uint64_t *ptr, Value *addend, const char *name) { - unsigned block = line / logdata_blocksize; - line = line % logdata_blocksize; - if (vec.size() <= block) - vec.resize(block + 1); - if (vec[block] == NULL) { - vec[block] = (logdata_block*)calloc(1, sizeof(logdata_block)); - } - logdata_block &data = *vec[block]; - if (data[line] == 0) - data[line] = 1; - return &data[line]; -} - -static void visitLine(jl_codectx_t &ctx, std::vector &vec, int line, Value *addend, const char* name) -{ - uint64_t *ptr = allocLine(vec, line); Value *pv = ConstantExpr::getIntToPtr( ConstantInt::get(T_size, (uintptr_t)ptr), T_pint64); @@ -1836,38 +1820,16 @@ static void visitLine(jl_codectx_t &ctx, std::vector &vec, int l // Code coverage -static logdata_t coverageData; - static void coverageVisitLine(jl_codectx_t &ctx, StringRef filename, int line) { assert(!imaging_mode); if (filename == "" || filename == "none" || filename == "no file" || filename == "" || line < 0) return; - visitLine(ctx, coverageData[filename], line, ConstantInt::get(T_int64, 1), "lcnt"); -} - -static void coverageAllocLine(StringRef filename, int line) -{ - assert(!imaging_mode); - if (filename == "" || filename == "none" || filename == "no file" || filename == "" || line < 0) - return; - allocLine(coverageData[filename], line); -} - -extern "C" JL_DLLEXPORT void jl_coverage_visit_line(const char* filename_, size_t len_filename, int line) -{ - StringRef filename = StringRef(filename_, len_filename); - if (imaging_mode || filename == "" || filename == "none" || filename == "no file" || filename == "" || line < 0) - return; - std::vector &vec = coverageData[filename]; - uint64_t *ptr = allocLine(vec, line); - (*ptr)++; + visitLine(ctx, jl_coverage_data_pointer(filename, line), ConstantInt::get(T_int64, 1), "lcnt"); } // Memory allocation log (malloc_log) -static logdata_t mallocData; - static void mallocVisitLine(jl_codectx_t &ctx, StringRef filename, int line, Value *sync) { assert(!imaging_mode); @@ -1876,143 +1838,7 @@ static void mallocVisitLine(jl_codectx_t &ctx, StringRef filename, int line, Val Value *addend = sync ? ctx.builder.CreateCall(prepare_call(sync_gc_total_bytes_func), {sync}) : ctx.builder.CreateCall(prepare_call(diff_gc_total_bytes_func), {}); - visitLine(ctx, mallocData[filename], line, addend, "bytecnt"); -} - -// Resets the malloc counts. -extern "C" JL_DLLEXPORT void jl_clear_malloc_data_impl(void) -{ - logdata_t::iterator it = mallocData.begin(); - for (; it != mallocData.end(); it++) { - std::vector &bytes = (*it).second; - std::vector::iterator itb; - for (itb = bytes.begin(); itb != bytes.end(); itb++) { - if (*itb) { - logdata_block &data = **itb; - for (int i = 0; i < logdata_blocksize; i++) { - if (data[i] > 0) - data[i] = 1; - } - } - } - } - jl_gc_sync_total_bytes(0); -} - -static void write_log_data(logdata_t &logData, const char *extension) -{ - std::string base = std::string(jl_options.julia_bindir); - base = base + "/../share/julia/base/"; - logdata_t::iterator it = logData.begin(); - for (; it != logData.end(); it++) { - std::string filename(it->first()); - std::vector &values = it->second; - if (!values.empty()) { - if (!jl_isabspath(filename.c_str())) - filename = base + filename; - std::ifstream inf(filename.c_str()); - if (!inf.is_open()) - continue; - std::string outfile = filename + extension; - std::ofstream outf(outfile.c_str(), std::ofstream::trunc | std::ofstream::out | std::ofstream::binary); - if (outf.is_open()) { - inf.exceptions(std::ifstream::badbit); - outf.exceptions(std::ifstream::failbit | std::ifstream::badbit); - char line[1024]; - int l = 1; - unsigned block = 0; - while (!inf.eof()) { - inf.getline(line, sizeof(line)); - if (inf.fail()) { - if (inf.eof()) - break; // no content on trailing line - // Read through lines longer than sizeof(line) - inf.clear(); - inf.ignore(std::numeric_limits::max(), '\n'); - } - logdata_block *data = NULL; - if (block < values.size()) { - data = values[block]; - } - uint64_t value = data ? (*data)[l] : 0; - if (++l >= logdata_blocksize) { - l = 0; - block++; - } - outf.width(9); - if (value == 0) - outf << '-'; - else - outf << (value - 1); - outf.width(0); - outf << " " << line << '\n'; - } - outf.close(); - } - inf.close(); - } - } -} - -static void write_lcov_data(logdata_t &logData, const std::string &outfile) -{ - std::ofstream outf(outfile.c_str(), std::ofstream::ate | std::ofstream::out | std::ofstream::binary); - //std::string base = std::string(jl_options.julia_bindir); - //base = base + "/../share/julia/base/"; - logdata_t::iterator it = logData.begin(); - for (; it != logData.end(); it++) { - StringRef filename = it->first(); - const std::vector &values = it->second; - if (!values.empty()) { - outf << "SF:" << filename.str() << '\n'; - size_t n_covered = 0; - size_t n_instrumented = 0; - size_t lno = 0; - for (auto &itv : values) { - if (itv) { - logdata_block &data = *itv; - for (int i = 0; i < logdata_blocksize; i++) { - auto cov = data[i]; - if (cov > 0) { - n_instrumented++; - if (cov > 1) - n_covered++; - outf << "DA:" << lno << ',' << (cov - 1) << '\n'; - } - lno++; - } - } - else { - lno += logdata_blocksize; - } - } - outf << "LH:" << n_covered << '\n'; - outf << "LF:" << n_instrumented << '\n'; - outf << "end_of_record\n"; - } - } - outf.close(); -} - -extern "C" JL_DLLEXPORT void jl_write_coverage_data_impl(const char *output) -{ - if (output) { - StringRef output_pattern(output); - if (output_pattern.endswith(".info")) - write_lcov_data(coverageData, jl_format_filename(output_pattern.str().c_str())); - } - else { - std::string stm; - raw_string_ostream(stm) << "." << jl_getpid() << ".cov"; - write_log_data(coverageData, stm.c_str()); - } -} - -extern "C" JL_DLLEXPORT void jl_write_malloc_log_impl(void) -{ - std::string stm; - raw_string_ostream(stm) << "." << jl_getpid() << ".mem"; - write_log_data(mallocData, stm.c_str()); + visitLine(ctx, jl_malloc_data_pointer(filename, line), addend, "bytecnt"); } // --- constant determination --- @@ -7073,7 +6899,7 @@ static std::pair, jl_llvm_functions_t> // record all lines that could be covered for (const auto &info : linetable) if (do_coverage(info.is_user_code)) - coverageAllocLine(info.file, info.line); + jl_coverage_alloc_line(info.file, info.line); } come_from_bb[0] = ctx.builder.GetInsertBlock(); diff --git a/src/coverage.cpp b/src/coverage.cpp new file mode 100644 index 00000000000000..4ce33c105691ce --- /dev/null +++ b/src/coverage.cpp @@ -0,0 +1,214 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + +#include +#include +#include +#include + +#include "llvm-version.h" +#include +#include +#include + +#include "julia.h" +#include "julia_internal.h" + +using namespace llvm; + +static int codegen_imaging_mode(void) +{ + return jl_options.image_codegen || (jl_generating_output() && !jl_options.incremental); +} + +// Logging for code coverage and memory allocation + +const int logdata_blocksize = 32; // target getting nearby lines in the same general cache area and reducing calls to malloc by chunking +typedef uint64_t logdata_block[logdata_blocksize]; +typedef StringMap< std::vector > logdata_t; + +static uint64_t *allocLine(std::vector &vec, int line) +{ + unsigned block = line / logdata_blocksize; + line = line % logdata_blocksize; + if (vec.size() <= block) + vec.resize(block + 1); + if (vec[block] == NULL) { + vec[block] = (logdata_block*)calloc(1, sizeof(logdata_block)); + } + logdata_block &data = *vec[block]; + if (data[line] == 0) + data[line] = 1; + return &data[line]; +} + +// Code coverage + +static logdata_t coverageData; + +JL_DLLEXPORT void jl_coverage_alloc_line(StringRef filename, int line) +{ + assert(!codegen_imaging_mode()); + if (filename == "" || filename == "none" || filename == "no file" || filename == "" || line < 0) + return; + allocLine(coverageData[filename], line); +} + +JL_DLLEXPORT uint64_t *jl_coverage_data_pointer(StringRef filename, int line) +{ + return allocLine(coverageData[filename], line); +} + +extern "C" JL_DLLEXPORT void jl_coverage_visit_line(const char *filename_, size_t len_filename, int line) +{ + StringRef filename = StringRef(filename_, len_filename); + if (codegen_imaging_mode() || filename == "" || filename == "none" || filename == "no file" || filename == "" || line < 0) + return; + std::vector &vec = coverageData[filename]; + uint64_t *ptr = allocLine(vec, line); + (*ptr)++; +} + +// Memory allocation log (malloc_log) + +static logdata_t mallocData; + +JL_DLLEXPORT uint64_t *jl_malloc_data_pointer(StringRef filename, int line) +{ + return allocLine(mallocData[filename], line); +} + +// Resets the malloc counts. +extern "C" JL_DLLEXPORT void jl_clear_malloc_data(void) +{ + logdata_t::iterator it = mallocData.begin(); + for (; it != mallocData.end(); it++) { + std::vector &bytes = (*it).second; + std::vector::iterator itb; + for (itb = bytes.begin(); itb != bytes.end(); itb++) { + if (*itb) { + logdata_block &data = **itb; + for (int i = 0; i < logdata_blocksize; i++) { + if (data[i] > 0) + data[i] = 1; + } + } + } + } + jl_gc_sync_total_bytes(0); +} + +static void write_log_data(logdata_t &logData, const char *extension) +{ + std::string base = std::string(jl_options.julia_bindir); + base = base + "/../share/julia/base/"; + logdata_t::iterator it = logData.begin(); + for (; it != logData.end(); it++) { + std::string filename(it->first()); + std::vector &values = it->second; + if (!values.empty()) { + if (!jl_isabspath(filename.c_str())) + filename = base + filename; + std::ifstream inf(filename.c_str()); + if (!inf.is_open()) + continue; + std::string outfile = filename + extension; + std::ofstream outf(outfile.c_str(), std::ofstream::trunc | std::ofstream::out | std::ofstream::binary); + if (outf.is_open()) { + inf.exceptions(std::ifstream::badbit); + outf.exceptions(std::ifstream::failbit | std::ifstream::badbit); + char line[1024]; + int l = 1; + unsigned block = 0; + while (!inf.eof()) { + inf.getline(line, sizeof(line)); + if (inf.fail()) { + if (inf.eof()) + break; // no content on trailing line + // Read through lines longer than sizeof(line) + inf.clear(); + inf.ignore(std::numeric_limits::max(), '\n'); + } + logdata_block *data = NULL; + if (block < values.size()) { + data = values[block]; + } + uint64_t value = data ? (*data)[l] : 0; + if (++l >= logdata_blocksize) { + l = 0; + block++; + } + outf.width(9); + if (value == 0) + outf << '-'; + else + outf << (value - 1); + outf.width(0); + outf << " " << line << '\n'; + } + outf.close(); + } + inf.close(); + } + } +} + +static void write_lcov_data(logdata_t &logData, const std::string &outfile) +{ + std::ofstream outf(outfile.c_str(), std::ofstream::ate | std::ofstream::out | std::ofstream::binary); + //std::string base = std::string(jl_options.julia_bindir); + //base = base + "/../share/julia/base/"; + logdata_t::iterator it = logData.begin(); + for (; it != logData.end(); it++) { + StringRef filename = it->first(); + const std::vector &values = it->second; + if (!values.empty()) { + outf << "SF:" << filename.str() << '\n'; + size_t n_covered = 0; + size_t n_instrumented = 0; + size_t lno = 0; + for (auto &itv : values) { + if (itv) { + logdata_block &data = *itv; + for (int i = 0; i < logdata_blocksize; i++) { + auto cov = data[i]; + if (cov > 0) { + n_instrumented++; + if (cov > 1) + n_covered++; + outf << "DA:" << lno << ',' << (cov - 1) << '\n'; + } + lno++; + } + } + else { + lno += logdata_blocksize; + } + } + outf << "LH:" << n_covered << '\n'; + outf << "LF:" << n_instrumented << '\n'; + outf << "end_of_record\n"; + } + } + outf.close(); +} + +extern "C" JL_DLLEXPORT void jl_write_coverage_data(const char *output) +{ + if (output) { + StringRef output_pattern(output); + if (output_pattern.endswith(".info")) + write_lcov_data(coverageData, jl_format_filename(output_pattern.str().c_str())); + } + else { + std::string stm; + raw_string_ostream(stm) << "." << jl_getpid() << ".cov"; + write_log_data(coverageData, stm.c_str()); + } +} + +extern "C" JL_DLLEXPORT void jl_write_malloc_log(void) +{ + std::string stm; + raw_string_ostream(stm) << "." << jl_getpid() << ".mem"; + write_log_data(mallocData, stm.c_str()); +} diff --git a/src/gf.c b/src/gf.c index bd2dc7b5469848..07594a217364f1 100644 --- a/src/gf.c +++ b/src/gf.c @@ -442,7 +442,7 @@ static void foreach_mtable_in_module( jl_typename_t *tn = ((jl_datatype_t*)v)->name; if (tn->module == m && tn->name == b->name) { jl_methtable_t *mt = tn->mt; - if (mt != NULL && (jl_value_t*)mt != jl_nothing && mt != jl_type_type_mt) { + if (mt != NULL && (jl_value_t*)mt != jl_nothing && mt != jl_type_type_mt && mt != jl_nonfunction_mt) { visit(mt, env); } } @@ -467,6 +467,7 @@ void jl_foreach_reachable_mtable(void (*visit)(jl_methtable_t *mt, void *env), v JL_GC_PUSH2(&visited, &mod_array); mod_array = jl_get_loaded_modules(); visit(jl_type_type_mt, env); + visit(jl_nonfunction_mt, env); if (mod_array) { int i; for (i = 0; i < jl_array_len(mod_array); i++) { diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index c2405d6e1a3891..50baad851da3a3 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -519,7 +519,6 @@ // use YY instead of XX to avoid jl -> ijl renaming in libjulia-codegen #define JL_CODEGEN_EXPORTED_FUNCS(YY) \ - YY(jl_clear_malloc_data) \ YY(jl_dump_function_ir) \ YY(jl_dump_method_asm) \ YY(jl_extern_c) \ @@ -530,8 +529,6 @@ YY(jl_get_LLVM_VERSION) \ YY(jl_dump_native) \ YY(jl_get_llvm_gv) \ - YY(jl_write_malloc_log) \ - YY(jl_write_coverage_data) \ YY(jl_dump_function_asm) \ YY(jl_LLVMCreateDisasm) \ YY(jl_LLVMDisasmInstruction) \ diff --git a/src/julia.expmap b/src/julia.expmap index 9c21fff9963f76..425fd9c9c1ee09 100644 --- a/src/julia.expmap +++ b/src/julia.expmap @@ -37,6 +37,9 @@ jlbacktracet; _IO_stdin_used; __ZN4llvm23createLowerSimdLoopPassEv; + _Z24jl_coverage_data_pointerN4llvm9StringRefEi; + _Z22jl_coverage_alloc_lineN4llvm9StringRefEi; + _Z22jl_malloc_data_pointerN4llvm9StringRefEi; LLVMExtra*; /* freebsd */ diff --git a/stdlib/Downloads.version b/stdlib/Downloads.version index c4c000b62506a6..2d94b19b59bdc3 100644 --- a/stdlib/Downloads.version +++ b/stdlib/Downloads.version @@ -1,4 +1,4 @@ DOWNLOADS_BRANCH = master -DOWNLOADS_SHA1 = 5f1509da10cf22bb4fc59de707cb3455b6807d99 +DOWNLOADS_SHA1 = dbb062584961888c6abddcb186d0248bec7763b8 DOWNLOADS_GIT_URL := git://github.com/JuliaLang/Downloads.jl.git DOWNLOADS_TAR_URL = https://api.github.com/repos/JuliaLang/Downloads.jl/tarball/$1 diff --git a/stdlib/LibGit2/src/types.jl b/stdlib/LibGit2/src/types.jl index 9ffcaa36461279..b68dbb7c0bf027 100644 --- a/stdlib/LibGit2/src/types.jl +++ b/stdlib/LibGit2/src/types.jl @@ -248,7 +248,7 @@ distinct payload. Each callback, when called, will receive `Dict` which will hol callback's custom payload which can be accessed using the callback name. # Examples -```julia +```julia-repl julia> c = LibGit2.Callbacks(:credentials => (LibGit2.credentials_cb(), LibGit2.CredentialPayload())); julia> LibGit2.clone(url, callbacks=c); diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 7583b2286c6c75..26115a0373e5c5 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = master -PKG_SHA1 = bc32103f6dead7d8016badc24a577e4a30014b49 +PKG_SHA1 = 269183951b6532cbdb411fff2816c5bcf96ac495 PKG_GIT_URL := git://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 diff --git a/stdlib/REPL/docs/src/index.md b/stdlib/REPL/docs/src/index.md index bd57c87ff3a211..1d1feea6d5a09f 100644 --- a/stdlib/REPL/docs/src/index.md +++ b/stdlib/REPL/docs/src/index.md @@ -669,7 +669,7 @@ v [ ] blueberry can instead be rendered with Unicode selection and navigation characters with -```julia +```julia-repl julia> menu = MultiSelectMenu(options, pagesize=5, charset=:unicode); julia> request(menu) @@ -683,7 +683,7 @@ julia> request(menu) More fine-grained configuration is also possible: -```julia +```julia-repl julia> menu = MultiSelectMenu(options, pagesize=5, charset=:unicode, checked="YEP!", unchecked="NOPE", cursor='⧐'); julia> request(menu) diff --git a/stdlib/REPL/src/TerminalMenus/MultiSelectMenu.jl b/stdlib/REPL/src/TerminalMenus/MultiSelectMenu.jl index bf686dec28d195..bcca3bd8d851e8 100644 --- a/stdlib/REPL/src/TerminalMenus/MultiSelectMenu.jl +++ b/stdlib/REPL/src/TerminalMenus/MultiSelectMenu.jl @@ -8,7 +8,7 @@ A menu that allows a user to select a multiple options from a list. # Sample Output -```julia +```julia-repl julia> request(MultiSelectMenu(options)) Select the fruits you like: [press: d=done, a=all, n=none] diff --git a/stdlib/REPL/src/TerminalMenus/RadioMenu.jl b/stdlib/REPL/src/TerminalMenus/RadioMenu.jl index 75c78def2bbebe..32a6373b719d70 100644 --- a/stdlib/REPL/src/TerminalMenus/RadioMenu.jl +++ b/stdlib/REPL/src/TerminalMenus/RadioMenu.jl @@ -8,7 +8,7 @@ A menu that allows a user to select a single option from a list. # Sample Output -```julia +```julia-repl julia> request(RadioMenu(options, pagesize=4)) Choose your favorite fruit: ^ grape diff --git a/stdlib/Random/src/RNGs.jl b/stdlib/Random/src/RNGs.jl index 141ec14f4ed31f..5c36b085cd5055 100644 --- a/stdlib/Random/src/RNGs.jl +++ b/stdlib/Random/src/RNGs.jl @@ -23,16 +23,26 @@ else # !windows rand(rd::RandomDevice, sp::SamplerBoolBitInteger) = read(getfile(rd), sp[]) rand(rd::RandomDevice, ::SamplerType{Bool}) = read(getfile(rd), UInt8) % Bool - function getfile(rd::RandomDevice) - devrandom = rd.unlimited ? DEV_URANDOM : DEV_RANDOM - # TODO: there is a data-race, this can leak up to nthreads() copies of the file descriptors, - # so use a "thread-once" utility once available - isassigned(devrandom) || (devrandom[] = open(rd.unlimited ? "/dev/urandom" : "/dev/random")) - devrandom[] + mutable struct FileRef + @atomic file::Union{IOStream, Nothing} end - const DEV_RANDOM = Ref{IOStream}() - const DEV_URANDOM = Ref{IOStream}() + const DEV_RANDOM = FileRef(nothing) + const DEV_URANDOM = FileRef(nothing) + + function getfile(rd::RandomDevice) + ref = rd.unlimited ? DEV_URANDOM : DEV_RANDOM + fd = ref.file + if fd === nothing + fd = open(rd.unlimited ? "/dev/urandom" : "/dev/random") + old, ok = @atomicreplace ref.file nothing => fd + if !ok + close(fd) + fd = old::IOStream + end + end + return fd + end end # os-test @@ -411,6 +421,10 @@ for T in BitInteger_types end function __init__() + @static if !Sys.iswindows() + @atomic DEV_RANDOM.file = nothing + @atomic DEV_URANDOM.file = nothing + end seed!(GLOBAL_RNG) end diff --git a/stdlib/Statistics.version b/stdlib/Statistics.version index c94fcf9d062ff6..b277db56bee4bf 100644 --- a/stdlib/Statistics.version +++ b/stdlib/Statistics.version @@ -1,4 +1,4 @@ STATISTICS_BRANCH = master -STATISTICS_SHA1 = 74897fed33700dba92578aa0fefef5b99ba16086 +STATISTICS_SHA1 = 5256d570d0a554780ed80949c79116f47eac6382 STATISTICS_GIT_URL := git://github.com/JuliaLang/Statistics.jl.git STATISTICS_TAR_URL = https://api.github.com/repos/JuliaLang/Statistics.jl/tarball/$1 diff --git a/test/compiler/inline.jl b/test/compiler/inline.jl index 6bdb71bf8f292c..c92eafe208afe2 100644 --- a/test/compiler/inline.jl +++ b/test/compiler/inline.jl @@ -380,15 +380,23 @@ using Base.Experimental: @opaque f_oc_getfield(x) = (@opaque ()->x)() @test fully_eliminated(f_oc_getfield, Tuple{Int}) -# check if `x` is a statically-resolved call of a function whose name is `sym` -isinvoke(@nospecialize(x), sym::Symbol) = isinvoke(x, mi->mi.def.name===sym) -function isinvoke(@nospecialize(x), pred) - if Meta.isexpr(x, :invoke) - return pred(x.args[1]::Core.MethodInstance) +import Core.Compiler: argextype +const EMPTY_SPTYPES = Core.Compiler.EMPTY_SLOTTYPES + +code_typed1(args...; kwargs...) = first(only(code_typed(args...; kwargs...)))::Core.CodeInfo +get_code(args...; kwargs...) = code_typed1(args...; kwargs...).code + +# check if `x` is a dynamic call of a given function +function iscall((src, f)::Tuple{Core.CodeInfo,Function}, @nospecialize(x)) + return iscall(x) do @nospecialize x + argextype(x, src, EMPTY_SPTYPES) === typeof(f) end - return false end -code_typed1(args...; kwargs...) = (first(only(code_typed(args...; kwargs...)))::Core.CodeInfo).code +iscall(pred, @nospecialize(x)) = Meta.isexpr(x, :call) && pred(x.args[1]) + +# check if `x` is a statically-resolved call of a function whose name is `sym` +isinvoke(sym::Symbol, @nospecialize(x)) = isinvoke(mi->mi.def.name===sym, x) +isinvoke(pred, @nospecialize(x)) = Meta.isexpr(x, :invoke) && pred(x.args[1]::Core.MethodInstance) @testset "@inline/@noinline annotation before definition" begin M = Module() @@ -413,25 +421,25 @@ code_typed1(args...; kwargs...) = (first(only(code_typed(args...; kwargs...))):: def_noinline_noconflict(x) = _def_noinline_noconflict(x) end - let code = code_typed1(M.def_inline, (Int,)) - @test all(code) do x - !isinvoke(x, :_def_inline) + let code = get_code(M.def_inline, (Int,)) + @test all(code) do @nospecialize x + !isinvoke(:_def_inline, x) end end - let code = code_typed1(M.def_noinline, (Int,)) - @test any(code) do x - isinvoke(x, :_def_noinline) + let code = get_code(M.def_noinline, (Int,)) + @test any(code) do @nospecialize x + isinvoke(:_def_noinline, x) end end # test that they don't conflict with other "before-definition" macros - let code = code_typed1(M.def_inline_noconflict, (Int,)) - @test all(code) do x - !isinvoke(x, :_def_inline_noconflict) + let code = get_code(M.def_inline_noconflict, (Int,)) + @test all(code) do @nospecialize x + !isinvoke(:_def_inline_noconflict, x) end end - let code = code_typed1(M.def_noinline_noconflict, (Int,)) - @test any(code) do x - isinvoke(x, :_def_noinline_noconflict) + let code = get_code(M.def_noinline_noconflict, (Int,)) + @test any(code) do @nospecialize x + isinvoke(:_def_noinline_noconflict, x) end end end @@ -470,29 +478,33 @@ end end end - let code = code_typed1(M.body_inline, (Int,)) - @test all(code) do x - !isinvoke(x, :_body_inline) + let code = get_code(M.body_inline, (Int,)) + @test all(code) do @nospecialize x + !isinvoke(:_body_inline, x) end end - let code = code_typed1(M.body_noinline, (Int,)) - @test any(code) do x - isinvoke(x, :_body_noinline) + let code = get_code(M.body_noinline, (Int,)) + @test any(code) do @nospecialize x + isinvoke(:_body_noinline, x) end end # test annotations for `do` blocks - let code = code_typed1(M.do_inline, (Int,)) + let code = get_code(M.do_inline, (Int,)) # what we test here is that both `simple_caller` and the anonymous function that the # `do` block creates should inlined away, and as a result there is only the unresolved call - @test all(code) do x - !isinvoke(x, :simple_caller) && - !isinvoke(x, mi->startswith(string(mi.def.name), '#')) + @test all(code) do @nospecialize x + !isinvoke(:simple_caller, x) && + !isinvoke(x) do mi + startswith(string(mi.def.name), '#') + end end end - let code = code_typed1(M.do_noinline, (Int,)) + let code = get_code(M.do_noinline, (Int,)) # the anonymous function that the `do` block created shouldn't be inlined here - @test any(code) do x - isinvoke(x, mi->startswith(string(mi.def.name), '#')) + @test any(code) do @nospecialize x + isinvoke(x) do mi + startswith(string(mi.def.name), '#') + end end end end @@ -520,14 +532,14 @@ end # test callsite annotations for constant-prop'ed calls @noinline Base.@constprop :aggressive noinlined_constprop_explicit(a) = a+g - force_inline_constprop_explicit() = @inline noinlined_constprop_explicit(0) + force_inline_constprop_explicit() = @inline noinlined_constprop_explicit(0) Base.@constprop :aggressive noinlined_constprop_implicit(a) = a+g - force_inline_constprop_implicit() = @inline noinlined_constprop_implicit(0) + force_inline_constprop_implicit() = @inline noinlined_constprop_implicit(0) @inline Base.@constprop :aggressive inlined_constprop_explicit(a) = a+g - force_noinline_constprop_explicit() = @noinline inlined_constprop_explicit(0) + force_noinline_constprop_explicit() = @noinline inlined_constprop_explicit(0) @inline Base.@constprop :aggressive inlined_constprop_implicit(a) = a+g - force_noinline_constprop_implicit() = @noinline inlined_constprop_implicit(0) + force_noinline_constprop_implicit() = @noinline inlined_constprop_implicit(0) @noinline notinlined(a) = a function nested(a0, b0) @@ -539,51 +551,75 @@ end end end - let code = code_typed1(M.force_inline_explicit, (Int,)) - @test all(x->!isinvoke(x, :noinlined_explicit), code) + let code = get_code(M.force_inline_explicit, (Int,)) + @test all(code) do @nospecialize x + !isinvoke(:noinlined_explicit, x) + end end - let code = code_typed1(M.force_inline_block_explicit, (Int,)) - @test all(code) do x - !isinvoke(x, :noinlined_explicit) && - !isinvoke(x, :(+)) + let code = get_code(M.force_inline_block_explicit, (Int,)) + @test all(code) do @nospecialize x + !isinvoke(:noinlined_explicit, x) && + !isinvoke(:(+), x) end end - let code = code_typed1(M.force_inline_implicit, (Int,)) - @test all(x->!isinvoke(x, :noinlined_implicit), code) + let code = get_code(M.force_inline_implicit, (Int,)) + @test all(code) do @nospecialize x + !isinvoke(:noinlined_implicit, x) + end end - let code = code_typed1(M.force_inline_block_implicit, (Int,)) - @test all(x->!isinvoke(x, :noinlined_explicit), code) + let code = get_code(M.force_inline_block_implicit, (Int,)) + @test all(code) do @nospecialize x + !isinvoke(:noinlined_explicit, x) + end end - let code = code_typed1(M.force_noinline_explicit, (Int,)) - @test any(x->isinvoke(x, :inlined_explicit), code) + let code = get_code(M.force_noinline_explicit, (Int,)) + @test any(code) do @nospecialize x + isinvoke(:inlined_explicit, x) + end end - let code = code_typed1(M.force_noinline_block_explicit, (Int,)) - @test count(x->isinvoke(x, :inlined_explicit), code) == 2 + let code = get_code(M.force_noinline_block_explicit, (Int,)) + @test count(code) do @nospecialize x + isinvoke(:inlined_explicit, x) + end == 2 end - let code = code_typed1(M.force_noinline_implicit, (Int,)) - @test any(x->isinvoke(x, :inlined_implicit), code) + let code = get_code(M.force_noinline_implicit, (Int,)) + @test any(code) do @nospecialize x + isinvoke(:inlined_implicit, x) + end end - let code = code_typed1(M.force_noinline_block_implicit, (Int,)) - @test count(x->isinvoke(x, :inlined_implicit), code) == 2 + let code = get_code(M.force_noinline_block_implicit, (Int,)) + @test count(code) do @nospecialize x + isinvoke(:inlined_implicit, x) + end == 2 end - let code = code_typed1(M.force_inline_constprop_explicit) - @test all(x->!isinvoke(x, :noinlined_constprop_explicit), code) + let code = get_code(M.force_inline_constprop_explicit) + @test all(code) do @nospecialize x + !isinvoke(:noinlined_constprop_explicit, x) + end end - let code = code_typed1(M.force_inline_constprop_implicit) - @test all(x->!isinvoke(x, :noinlined_constprop_implicit), code) + let code = get_code(M.force_inline_constprop_implicit) + @test all(code) do @nospecialize x + !isinvoke(:noinlined_constprop_implicit, x) + end end - let code = code_typed1(M.force_noinline_constprop_explicit) - @test any(x->isinvoke(x, :inlined_constprop_explicit), code) + let code = get_code(M.force_noinline_constprop_explicit) + @test any(code) do @nospecialize x + isinvoke(:inlined_constprop_explicit, x) + end end - let code = code_typed1(M.force_noinline_constprop_implicit) - @test any(x->isinvoke(x, :inlined_constprop_implicit), code) + let code = get_code(M.force_noinline_constprop_implicit) + @test any(code) do @nospecialize x + isinvoke(:inlined_constprop_implicit, x) + end end - let code = code_typed1(M.nested, (Int,Int)) - @test count(x->isinvoke(x, :notinlined), code) == 1 + let code = get_code(M.nested, (Int,Int)) + @test count(code) do @nospecialize x + isinvoke(:notinlined, x) + end == 1 end end @@ -604,10 +640,12 @@ let code = @eval Module() begin end end - $code_typed1(setter, (Vector{Foo},)) + $get_code(setter, (Vector{Foo},)) end - @test !any(x->isinvoke(x, :setproperty!), code) + @test !any(code) do @nospecialize x + isinvoke(:setproperty!, x) + end end # Issue #41299 - inlining deletes error check in :> @@ -624,10 +662,12 @@ end @noinline f42078(a) = sum(sincos(a)) let ninlined = let - code = code_typed1((Int,)) do a + code = get_code((Int,)) do a @inline f42078(a) end - @test all(x->!isinvoke(x, :f42078), code) + @test all(code) do @nospecialize x + !isinvoke(:f42078, x) + end length(code) end @@ -643,10 +683,12 @@ let end let # inference should re-infer `f42078(::Int)` and we should get the same code - code = code_typed1((Int,)) do a + code = get_code((Int,)) do a @inline f42078(a) end - @test all(x->!isinvoke(x, :f42078), code) + @test all(code) do @nospecialize x + !isinvoke(:f42078, x) + end @test ninlined == length(code) end end @@ -680,3 +722,65 @@ let f(x) = (x...,) # the the original apply call is not union-split, but the inserted `iterate` call is. @test code_typed(f, Tuple{Union{Int64, CartesianIndex{1}, CartesianIndex{3}}})[1][2] == Tuple{Int64} end + +# https://github.com/JuliaLang/julia/issues/42754 +# inline union-split constant-prop'ed sources +mutable struct X42754 + # NOTE in order to confuse `fieldtype_tfunc`, we need to have at least two fields with different types + a::Union{Nothing, Int} + b::Symbol +end +let src = code_typed1((X42754, Union{Nothing,Int})) do x, a + # this `setproperty` call would be union-split and constant-prop will happen for + # each signature: inlining would fail if we don't use constant-prop'ed source + # since the approximate inlining cost of `convert(fieldtype(X, sym), a)` would + # end up very high if we don't propagate `sym::Const(:a)` + x.a = a + x + end + @test all(src.code) do @nospecialize x + !(isinvoke(:setproperty!, x) || iscall((src, setproperty!), x)) + end +end + +import Base: @constprop + +# test single, non-dispatchtuple callsite inlining + +@constprop :none @inline test_single_nondispatchtuple(@nospecialize(t)) = + isa(t, DataType) && t.name === Type.body.name +let + src = code_typed1((Any,)) do x + test_single_nondispatchtuple(x) + end + @test all(src.code) do @nospecialize x + !(isinvoke(:test_single_nondispatchtuple, x) || iscall((src, test_single_nondispatchtuple), x)) + end +end + +@constprop :aggressive @inline test_single_nondispatchtuple(c, @nospecialize(t)) = + c && isa(t, DataType) && t.name === Type.body.name +let + src = code_typed1((Any,)) do x + test_single_nondispatchtuple(true, x) + end + @test all(src.code) do @nospecialize(x) + !(isinvoke(:test_single_nondispatchtuple, x) || iscall((src, test_single_nondispatchtuple), x)) + end +end + +# validate inlining processing + +@constprop :none @inline validate_unionsplit_inlining(@nospecialize(t)) = throw("invalid inlining processing detected") +@constprop :none @noinline validate_unionsplit_inlining(i::Integer) = (println(IOBuffer(), "prevent inlining"); false) +let + invoke(xs) = validate_unionsplit_inlining(xs[1]) + @test invoke(Any[10]) === false +end + +@constprop :aggressive @inline validate_unionsplit_inlining(c, @nospecialize(t)) = c && throw("invalid inlining processing detected") +@constprop :aggressive @noinline validate_unionsplit_inlining(c, i::Integer) = c && (println(IOBuffer(), "prevent inlining"); false) +let + invoke(xs) = validate_unionsplit_inlining(true, xs[1]) + @test invoke(Any[10]) === false +end diff --git a/test/loading.jl b/test/loading.jl index c56f6c463a21f5..c4cc23575e8d0b 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -132,18 +132,6 @@ end @test_throws ArgumentError parse(UUID, "not a UUID") @test tryparse(UUID, "either is this") === nothing -function subset(v::Vector{T}, m::Int) where T - T[v[j] for j = 1:length(v) if ((m >>> (j - 1)) & 1) == 1] -end - -function perm(p::Vector, i::Int) - for j = length(p):-1:1 - i, k = divrem(i, j) - p[j], p[k+1] = p[k+1], p[j] - end - return p -end - @testset "explicit_project_deps_get" begin mktempdir() do dir project_file = joinpath(dir, "Project.toml") @@ -152,39 +140,40 @@ end proj_uuid = dummy_uuid(project_file) root_uuid = uuid4() this_uuid = uuid4() - # project file to subset/permute - lines = split(""" - name = "Root" - uuid = "$root_uuid" - [deps] - This = "$this_uuid" - """, '\n') - N = length(lines) - # test every permutation of every subset of lines - for m = 0:2^N-1 - s = subset(lines, m) # each subset of lines - for i = 1:factorial(count_ones(m)) - p = perm(s, i) # each permutation of the subset - open(project_file, write=true) do io - for line in p - println(io, line) - end - end - # look at lines and their order - n = findfirst(line -> startswith(line, "name"), p) - u = findfirst(line -> startswith(line, "uuid"), p) - d = findfirst(line -> line == "[deps]", p) - t = findfirst(line -> startswith(line, "This"), p) - # look up various packages by name - root = Base.explicit_project_deps_get(project_file, "Root") - this = Base.explicit_project_deps_get(project_file, "This") - that = Base.explicit_project_deps_get(project_file, "That") - # test that the correct answers are given - @test root == (something(n, N+1) β‰₯ something(d, N+1) ? nothing : - something(u, N+1) < something(d, N+1) ? root_uuid : proj_uuid) - @test this == (something(d, N+1) < something(t, N+1) ≀ N ? this_uuid : nothing) - @test that == nothing - end + + old_load_path = copy(LOAD_PATH) + try + copy!(LOAD_PATH, [project_file]) + write(project_file, """ + name = "Root" + uuid = "$root_uuid" + [deps] + This = "$this_uuid" + """) + # look up various packages by name + root = Base.identify_package("Root") + this = Base.identify_package("This") + that = Base.identify_package("That") + + @test root.uuid == root_uuid + @test this.uuid == this_uuid + @test that == nothing + + write(project_file, """ + name = "Root" + This = "$this_uuid" + [deps] + """) + # look up various packages by name + root = Base.identify_package("Root") + this = Base.identify_package("This") + that = Base.identify_package("That") + + @test root.uuid == proj_uuid + @test this == nothing + @test that == nothing + finally + copy!(LOAD_PATH, old_load_path) end end end