From 84bde3f4b6cd3354d59e3b7de5109f426a927224 Mon Sep 17 00:00:00 2001 From: N5N3 <2642243996@qq.com> Date: Wed, 29 May 2024 07:33:13 +0800 Subject: [PATCH 01/71] typeintersect: fix incorrect innervar handling under circular env (#54545) The infinite loop encountered in #54516 has been traced back to a circular bound during `finish_unionall`. As we insert innervar more eagerly now, the direct `jl_has_typevar` could not find all circularity. To address this, `has_typevar_via_flatten_env` is added to perform thorough check. Although there is some code duplication with `reachable_var`, it could be improved in future refactoring. #54516 also highlighted another free var escaping regression since v1.10. This regression is not solely the result of incomplete checks, it is also caused by the missing final substitution of `vb`'s bound, which has now been corrected. At last, this PR adds an assertion of sorting complexity, which should facilitate the detection of similar issues by PkgEval. close #54516 (cherry picked from commit 92dfdca4f493ac5dd60e533259b6551642c67f8d) --- src/subtype.c | 124 +++++++++++++++++++++++++++++++++++++++--------- test/subtype.jl | 7 +++ 2 files changed, 108 insertions(+), 23 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index 688a39ca87ef0..3fdacfbb7a4bb 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -2814,6 +2814,50 @@ static jl_value_t *omit_bad_union(jl_value_t *u, jl_tvar_t *t) return res; } +// TODO: fuse with reachable_var? +static int has_typevar_via_flatten_env(jl_value_t *x, jl_tvar_t *t, jl_ivarbinding_t *allvars, int8_t *checked) { + if (jl_is_unionall(x)) { + jl_tvar_t *var = ((jl_unionall_t *)x)->var; + if (has_typevar_via_flatten_env(var->lb, t, allvars, checked) || + has_typevar_via_flatten_env(var->ub, t, allvars, checked)) + return 1; + return has_typevar_via_flatten_env(((jl_unionall_t *)x)->body, t, allvars, checked); + } + else if (jl_is_uniontype(x)) { + return has_typevar_via_flatten_env(((jl_uniontype_t *)x)->a, t, allvars, checked) || + has_typevar_via_flatten_env(((jl_uniontype_t *)x)->b, t, allvars, checked); + } + else if (jl_is_vararg(x)) { + jl_vararg_t *v = (jl_vararg_t *)x; + return (v->T && has_typevar_via_flatten_env(v->T, t, allvars, checked)) || + (v->N && has_typevar_via_flatten_env(v->N, t, allvars, checked)); + } + else if (jl_is_datatype(x)) { + for (size_t i = 0; i < jl_nparams(x); i++) { + if (has_typevar_via_flatten_env(jl_tparam(x, i), t, allvars, checked)) + return 1; + } + return 0; + } + else if (jl_is_typevar(x)) { + if (t == (jl_tvar_t *)x) + return 1; + size_t ind = 0; + jl_ivarbinding_t *itemp = allvars; + while (itemp && *itemp->var != (jl_tvar_t *)x) + { + ind++; + itemp = itemp->next; + } + if (itemp == NULL || checked[ind]) + return 0; + checked[ind] = 1; + return has_typevar_via_flatten_env(*itemp->lb, t, allvars, checked) || + has_typevar_via_flatten_env(*itemp->ub, t, allvars, checked); + } + return 0; +} + // Caller might not have rooted `res` static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbinding_t *vb, jl_unionall_t *u, jl_stenv_t *e) { @@ -2911,8 +2955,11 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind // remove/replace/rewrap free occurrences of this var in the environment int wrapped = 0; jl_ivarbinding_t *pwrap = NULL; + int vcount = icount + current_env_length(e); + int8_t *checked = (int8_t *)alloca(vcount); for (jl_ivarbinding_t *btemp = allvars, *pbtemp = NULL; btemp != NULL; btemp = btemp->next) { int bdepth0 = btemp->root->depth0; + int innerflag = 0; ivar = *btemp->var; ilb = *btemp->lb; iub = *btemp->ub; @@ -2934,18 +2981,9 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind else if (ilb == (jl_value_t*)vb->var) { *btemp->lb = vb->lb; } - else if (bdepth0 == vb->depth0 && !jl_has_typevar(vb->lb, ivar) && !jl_has_typevar(vb->ub, ivar)) { - // if our variable is T, and some outer variable has constraint S = Ref{T}, - // move the `where T` outside `where S` instead of putting it here. issue #21243. - if (newvar != vb->var) - *btemp->lb = jl_substitute_var(ilb, vb->var, (jl_value_t*)newvar); - if (!wrapped) pwrap = pbtemp; - wrapped = 1; - } else { - *btemp->lb = jl_new_struct(jl_unionall_type, vb->var, ilb); + innerflag |= 1; } - assert((jl_value_t*)ivar != *btemp->lb); } if (jl_has_typevar(iub, vb->var)) { assert(btemp->root->var == ivar || bdepth0 == vb->depth0); @@ -2971,14 +3009,35 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind // Tuple{Float64, T3, T4} where {S3, T3<:Tuple{S3}, T4<:S3} *btemp->ub = vb->ub; } - else if (bdepth0 == vb->depth0 && !jl_has_typevar(vb->lb, ivar) && !jl_has_typevar(vb->ub, ivar)) { - if (newvar != vb->var) - *btemp->ub = jl_substitute_var(iub, vb->var, (jl_value_t*)newvar); - if (!wrapped) pwrap = pbtemp; - wrapped = 1; + else { + innerflag |= 2; } - else - *btemp->ub = jl_new_struct(jl_unionall_type, vb->var, iub); + if (innerflag) { + memset(checked, 0, vcount); + if (bdepth0 != vb->depth0 || + has_typevar_via_flatten_env(vb->lb, ivar, allvars, checked) || + has_typevar_via_flatten_env(vb->ub, ivar, allvars, checked)) { + if (innerflag & 1) + *btemp->lb = jl_new_struct(jl_unionall_type, vb->var, ilb); + if (innerflag & 2) + *btemp->ub = jl_new_struct(jl_unionall_type, vb->var, iub); + } + else { + assert(btemp->root != vb); + // if our variable is T, and some outer variable has constraint S = Ref{T}, + // move the `where T` outside `where S` instead of putting it here. issue #21243. + if (newvar != vb->var) { + if (innerflag & 1) + *btemp->lb = jl_substitute_var(ilb, vb->var, (jl_value_t*)newvar); + if (innerflag & 2) + *btemp->ub = jl_substitute_var(iub, vb->var, (jl_value_t*)newvar); + } + if (!wrapped) + pwrap = pbtemp; + wrapped = 1; + } + } + assert((jl_value_t*)ivar != *btemp->lb); assert((jl_value_t*)ivar != *btemp->ub); } pbtemp = btemp; @@ -2997,6 +3056,7 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind pwrap->next = inew; else allvars = inew; + vcount++; } // Re-sort the innervar inside the (reversed) var list. @@ -3004,17 +3064,23 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind // If this is slow, we could possibly switch to a simpler graph sort, such as Tarjan's SCC. if (icount > 0) { jl_ivarbinding_t *pib1 = NULL; +#ifndef NDEBUG + size_t sort_count = 0; +#endif while (1) { jl_ivarbinding_t *ib1 = pib1 == NULL ? allvars : pib1->next; if (ib1 == NULL) break; - if (jl_has_free_typevars(*ib1->lb) || jl_has_free_typevars(*ib1->ub)) { + assert((++sort_count) <= (vcount * (vcount + 1)) >> 1); + int lbfree = jl_has_free_typevars(*ib1->lb); + int ubfree = jl_has_free_typevars(*ib1->ub); + if (lbfree || ubfree) { int changed = 0; jl_ivarbinding_t *pib2 = ib1, *ib2 = ib1->next; while (ib2 != NULL) { int isinnervar = ib2->root->var != *ib2->var; if (isinnervar && ib1->root->depth0 == ib2->root->depth0 && - (jl_has_typevar(*ib1->lb, *ib2->var) || - jl_has_typevar(*ib1->ub, *ib2->var))) { + ((lbfree && jl_has_typevar(*ib1->lb, *ib2->var)) || + (ubfree && jl_has_typevar(*ib1->ub, *ib2->var)))) { pib2->next = ib2->next; ib2->next = ib1; ib2->root = ib1->root; @@ -3052,6 +3118,13 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind if (jl_has_typevar(iub, ivar)) *btemp2->ub = jl_substitute_var(iub, ivar, nivar); } + if (!wrapped && !varval) { + // newvar also needs bounds substitution. + if (jl_has_typevar(vb->lb, ivar)) + vb->lb = jl_substitute_var(vb->lb, ivar, nivar); + if (jl_has_typevar(vb->ub, ivar)) + vb->ub = jl_substitute_var(vb->ub, ivar, nivar); + } *btemp1->var = (jl_tvar_t *)nivar; } } @@ -3067,11 +3140,13 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind } if (root != vb) icount--; if (root->innervars != NULL) { - size_t len = jl_array_nrows(root->innervars); + jl_array_t *rinnervars = root->innervars; + JL_GC_PROMISE_ROOTED(rinnervars); + size_t len = jl_array_nrows(rinnervars); if (icount > len) - jl_array_grow_end(root->innervars, icount - len); + jl_array_grow_end(rinnervars, icount - len); if (icount < len) - jl_array_del_end(root->innervars, len - icount); + jl_array_del_end(rinnervars, len - icount); } else if (icount > 0) { root->innervars = jl_alloc_array_1d(jl_array_any_type, icount); @@ -3104,6 +3179,9 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind res = jl_bottom_type; } else { + // re-fresh newvar if bounds changed. + if (vb->lb != newvar->lb || vb->ub != newvar->ub) + newvar = jl_new_typevar(newvar->name, vb->lb, vb->ub); if (newvar != vb->var) res = jl_substitute_var(res, vb->var, (jl_value_t*)newvar); varval = (jl_value_t*)newvar; diff --git a/test/subtype.jl b/test/subtype.jl index c65521d44ac5a..c26f4fc9d30e2 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -2650,3 +2650,10 @@ let S = Tuple{Val, Val{T}} where {T}, R = Tuple{Val{Val{T}}, Val{T}} where {T}, @testintersect(Tuple{Val{A}, A} where {B, A<:Union{Val{B}, GenericMemory{B}}}, S{1}, R{1}) @testintersect(Tuple{Val{A}, A} where {B, A<:Union{Val{B}, GenericMemory{:not_atomic,Int,B}}}, S{1}, R{1}) end + +#issue 54516 +let S = Tuple{Val{<:T}, Union{Int,T}} where {T}, + T = Tuple{Union{Int,T}, Val{<:T}} where {T} + @testintersect(S, T, !Union{}) + @test !Base.has_free_typevars(typeintersect(S, T)) +end From 862f863e0ff3e6e1880914a6bb3b2e47e8dc9bcb Mon Sep 17 00:00:00 2001 From: Eric Hanson <5846501+ericphanson@users.noreply.github.com> Date: Wed, 29 May 2024 12:49:12 +0200 Subject: [PATCH 02/71] [backport] v1.11: mark Lockable as `public` and update NEWS.md for Lockable not being exported (again) (#54612) --------- Co-authored-by: Fredrik Ekre --- NEWS.md | 2 +- base/exports.jl | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 1279f46df609e..adccd7b825a13 100644 --- a/NEWS.md +++ b/NEWS.md @@ -60,7 +60,7 @@ Multi-threading changes ----------------------- * `Threads.@threads` now supports the `:greedy` scheduler, intended for non-uniform workloads ([#52096]). -* A new exported struct `Lockable{T, L<:AbstractLock}` makes it easy to bundle a resource and its lock together ([#52898]). +* A new public (but unexported) struct `Base.Lockable{T, L<:AbstractLock}` makes it easy to bundle a resource and its lock together ([#52898]). New library functions --------------------- diff --git a/base/exports.jl b/base/exports.jl index 152831dc6043a..63e5b43216cca 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -1090,6 +1090,7 @@ public Fix2, Generator, ImmutableDict, + Lockable, OneTo, LogRange, AnnotatedString, From 0df6f80003eeee247db2b61ba51ab8dcc00e1f91 Mon Sep 17 00:00:00 2001 From: Alexis Montoison <35051714+amontoison@users.noreply.github.com> Date: Sun, 5 May 2024 10:40:17 -0400 Subject: [PATCH 03/71] [LBT] Upgrade to v5.9.0 (#54361) (cherry picked from commit 9d8e0e7016ad64890f81b1fd93c1d46de9588a02) --- deps/blastrampoline.version | 6 +- deps/checksums/blastrampoline | 68 +++++++++++------------ stdlib/libblastrampoline_jll/Project.toml | 4 +- 3 files changed, 39 insertions(+), 39 deletions(-) diff --git a/deps/blastrampoline.version b/deps/blastrampoline.version index 616300377e3e6..cd23d50285549 100644 --- a/deps/blastrampoline.version +++ b/deps/blastrampoline.version @@ -2,6 +2,6 @@ BLASTRAMPOLINE_JLL_NAME := libblastrampoline ## source build -BLASTRAMPOLINE_VER := 5.8.0 -BLASTRAMPOLINE_BRANCH=v5.8.0 -BLASTRAMPOLINE_SHA1=81316155d4838392e8462a92bcac3eebe9acd0c7 +BLASTRAMPOLINE_VER := 5.9.0 +BLASTRAMPOLINE_BRANCH=v5.9.0 +BLASTRAMPOLINE_SHA1=3c451b99639a984126c6a618d4394ee582e872a2 diff --git a/deps/checksums/blastrampoline b/deps/checksums/blastrampoline index d72a584fd1b0c..d62a7b5a69cc8 100644 --- a/deps/checksums/blastrampoline +++ b/deps/checksums/blastrampoline @@ -1,34 +1,34 @@ -blastrampoline-81316155d4838392e8462a92bcac3eebe9acd0c7.tar.gz/md5/0478361eac783b99002b1ad985182f05 -blastrampoline-81316155d4838392e8462a92bcac3eebe9acd0c7.tar.gz/sha512/2489ce5770a9861889a2d07e61440ba4f233a92efd4a3544747f83320e0e7a229a8fe01553d99f5f1d98713316f2506daf0adb7d024a46e32b3de1bb2966d637 -libblastrampoline.v5.8.0+1.aarch64-apple-darwin.tar.gz/md5/a28837b9838fef2b3831de3278ec7949 -libblastrampoline.v5.8.0+1.aarch64-apple-darwin.tar.gz/sha512/111ac2fe5f8f8102f2f7c9e9e6aa1d1a12d2db941238c949ff8e64b30335e8b2f6ecce0d5f577879c231eb839c06e259302b709f3d34e94a97047bfa984222f6 -libblastrampoline.v5.8.0+1.aarch64-linux-gnu.tar.gz/md5/9e781a026e03118df81347fb90f10d45 -libblastrampoline.v5.8.0+1.aarch64-linux-gnu.tar.gz/sha512/89469f32a666efd46437351a8fb16758c35e5aecc563d202b480c10ddf9fa5350a5a321076b79b0a1a07ec2cea0b73aa5c28979cc382a198fa96cca0b5899d25 -libblastrampoline.v5.8.0+1.aarch64-linux-musl.tar.gz/md5/b7acda2fdd157bbb183d0dd33643beef -libblastrampoline.v5.8.0+1.aarch64-linux-musl.tar.gz/sha512/cf4125a47334fe2ec0d5a4b11624b12e1366ec031500218f680ad5a53152b9d752c0c02a0b92d0e07f3eb21f2f8f58d0c587438a4869a72197bbd5e91531369d -libblastrampoline.v5.8.0+1.armv6l-linux-gnueabihf.tar.gz/md5/eafabd99fb1287d495acb8efb8091fde -libblastrampoline.v5.8.0+1.armv6l-linux-gnueabihf.tar.gz/sha512/63ff4e6bc400fa8ee713a1c5ae4af0a8e152d49860c6f5e94a17e426ad9f780d41cc0f84d33c75ea5347af1a53f07fc012798d603b6a94ea39f37cfd651a0719 -libblastrampoline.v5.8.0+1.armv6l-linux-musleabihf.tar.gz/md5/9788f74b375ef6b84c16c080f2be5bdd -libblastrampoline.v5.8.0+1.armv6l-linux-musleabihf.tar.gz/sha512/f00ebf794927404e2294a2fbb759b1e3e57836c7f683525fac0b2ac570da2c75904e43f154cf76fce310a624f9b35fbd40e6c7757882bb6f30db790f4221a543 -libblastrampoline.v5.8.0+1.armv7l-linux-gnueabihf.tar.gz/md5/4492bace63d8274d68ecdaa735e47e99 -libblastrampoline.v5.8.0+1.armv7l-linux-gnueabihf.tar.gz/sha512/8868283e6c5224b80145fdfd17f13f713053ba94e49c170f38f0cbf9f794185d7dec9c107ce65dc76121d3ac5b21d2f3857f619d8279bede86a906230ff59a71 -libblastrampoline.v5.8.0+1.armv7l-linux-musleabihf.tar.gz/md5/d66b6ed1d4e5f6a130f36791063e651d -libblastrampoline.v5.8.0+1.armv7l-linux-musleabihf.tar.gz/sha512/414ad07574a6e9aa670bbfea13eaea11da13129c9ccb4193cad708014c31493ff10ff427558b90cb16040fa64c8a325c2e375e3310c39fb37bb3e7fdb6a72a5f -libblastrampoline.v5.8.0+1.i686-linux-gnu.tar.gz/md5/595199a3a01174cfa4d9ce3407bf30dc -libblastrampoline.v5.8.0+1.i686-linux-gnu.tar.gz/sha512/02c3b0c3c0a411d5090a081f3bbbe38aaae40eaa5fe63d0690e0582e233cd9ce76483922557d4f65dc457e29a4e84d86ee5af20a60b082aec7bec4ca8607c1ca -libblastrampoline.v5.8.0+1.i686-linux-musl.tar.gz/md5/5832d0044842cb84f4e1e1b0a04b8205 -libblastrampoline.v5.8.0+1.i686-linux-musl.tar.gz/sha512/d28954d0feef6a33fa0bfeb59acb68821222d36a4e353eaf41936ee2c9aace719c2d0f0b0f080eafe2baecc67a29de4cacc0446aac776bbb615c4426d35c9c8f -libblastrampoline.v5.8.0+1.i686-w64-mingw32.tar.gz/md5/46391ac222980a0ad2c2d6d2b54db26d -libblastrampoline.v5.8.0+1.i686-w64-mingw32.tar.gz/sha512/6dd3434648a297639ef327efa5827d6aea70df551774e52ba395cdf187bfb603d365eed84780913fda5f3d12512ac54ccf784da3cf6317671ab346211f5984b3 -libblastrampoline.v5.8.0+1.powerpc64le-linux-gnu.tar.gz/md5/5f76f5c6a88c0caaa6419ba212f8cb94 -libblastrampoline.v5.8.0+1.powerpc64le-linux-gnu.tar.gz/sha512/785071e682075b2cebd992394e66169f4ee2db3a8e23affb88dc05d9abf55f49d597b2a7400a13c83ad106ad825b5ee666b01f8625e51aec267132573273991e -libblastrampoline.v5.8.0+1.x86_64-apple-darwin.tar.gz/md5/21beb51d448bd22e4608a16b3f4fde05 -libblastrampoline.v5.8.0+1.x86_64-apple-darwin.tar.gz/sha512/620ba64d93ef416e483f813617aa313957282d8361f920b5444702fa911ff0051d1f8a8814b5fa0b082fd4dc77d96cb8b763937c786959bbc97cbb6131617152 -libblastrampoline.v5.8.0+1.x86_64-linux-gnu.tar.gz/md5/14c1045ba4d400f490ddea5343a46f04 -libblastrampoline.v5.8.0+1.x86_64-linux-gnu.tar.gz/sha512/0fdae83f4df93b28951521cf426736367f568c1e76fb68eea42b045cc9a288b6836abb3206a6d61e4f88adcf198553e911c45231aecb0f552e06de28eb3bec54 -libblastrampoline.v5.8.0+1.x86_64-linux-musl.tar.gz/md5/59b110676fcb2fcfdcf670a5d435d555 -libblastrampoline.v5.8.0+1.x86_64-linux-musl.tar.gz/sha512/57a5022e9fabc0637a29f3c32f6180cb4f6a90282191232e299df6cea5265b535e4a0af4fde15c8fe80e5a59edea0fae96dd3a510f5720ecd78e85a2a9ffbfe0 -libblastrampoline.v5.8.0+1.x86_64-unknown-freebsd.tar.gz/md5/c22da112cfc7f9fa0f103d08f4b78965 -libblastrampoline.v5.8.0+1.x86_64-unknown-freebsd.tar.gz/sha512/ace02fac0dc6df472456007a081e0aaa85a6b17290321fb214349aac0f2d0f893df602dca28fc26ddfd4ed574fd9063bacff343249e5a1109f5d92dc9cb7a1d3 -libblastrampoline.v5.8.0+1.x86_64-w64-mingw32.tar.gz/md5/34fdc53745245887f968f420b2f02ed9 -libblastrampoline.v5.8.0+1.x86_64-w64-mingw32.tar.gz/sha512/bbf478736b7bd57b340ccd5b6744d526a7a95fc524d30fdf9af6e9d79285641be26fae5f9e5302d71a5be76b05c379e969a829e259d8100ba9c6ce202b632b3d +blastrampoline-3c451b99639a984126c6a618d4394ee582e872a2.tar.gz/md5/74f00893d201fa37910c52a1dfd96078 +blastrampoline-3c451b99639a984126c6a618d4394ee582e872a2.tar.gz/sha512/705a7169ce9743a08a88826a7f740ae80c660b396610a146acd6d3eceae09d0d5629e45ffd11ee8ed82b405e8648df705f2e0898b9a8c6f28cf1bb7618880549 +libblastrampoline.v5.9.0+0.aarch64-apple-darwin.tar.gz/md5/2d1fe8c420a126de2ee8328a77db23ab +libblastrampoline.v5.9.0+0.aarch64-apple-darwin.tar.gz/sha512/c91eead38796e3fa392287de9aef411617c2d13bb86d5f3849e24c0df71483595ad6495e983bdd17fb43a84765997b3f6ba6b08e4e43954faeb629de72c0cd8c +libblastrampoline.v5.9.0+0.aarch64-linux-gnu.tar.gz/md5/947a9b8ac0b4835a358d0b3837d227f2 +libblastrampoline.v5.9.0+0.aarch64-linux-gnu.tar.gz/sha512/b0d938dfb5c4314ec3625279a4e66283c983363ca7bd03e6626bef27d08d1566cc292b8043d30697f0f6e95299ea6be4b6b2f5acf55900767f622a9fe7f068d7 +libblastrampoline.v5.9.0+0.aarch64-linux-musl.tar.gz/md5/9c1482aaa2fa17c1bb30bcb1d26802e2 +libblastrampoline.v5.9.0+0.aarch64-linux-musl.tar.gz/sha512/c741d79a631550f7b8e5bf028f50551f3000515229306c88be6aa76de5aaf2b55bffd22c7f5f4015226a274c2f0cea8de6f7c3ea5010cad17ce242eb369c0336 +libblastrampoline.v5.9.0+0.armv6l-linux-gnueabihf.tar.gz/md5/f7b655a80c50848b15a7ab7dced9aede +libblastrampoline.v5.9.0+0.armv6l-linux-gnueabihf.tar.gz/sha512/29cc68195c3a10edd5965327d0c6a0ccb1ec818fdd8b57adeaa582cb0537eeabe0c019e55e048c2859292fa1532d6b6edeeb46f6adda97bae758305c9f99492e +libblastrampoline.v5.9.0+0.armv6l-linux-musleabihf.tar.gz/md5/0f3e0632cd4537ba5688912efd0f6a44 +libblastrampoline.v5.9.0+0.armv6l-linux-musleabihf.tar.gz/sha512/1446ee28607414169d24cfc965439380ef9de925b1327018c38d9aa981e6df6430b9e216bf489693c6e12b8b7baccfbfe5001884bbe07e76edd416157615c4a7 +libblastrampoline.v5.9.0+0.armv7l-linux-gnueabihf.tar.gz/md5/19a11466b7664e60028e24b39e79106b +libblastrampoline.v5.9.0+0.armv7l-linux-gnueabihf.tar.gz/sha512/01894533621c155e80b436251eab1185e6e38ec44d68154f24bd535fbeca2d9d5a9180f657f92da652db080526a4659a48282030e40e1d7b9e962a7b5643df5c +libblastrampoline.v5.9.0+0.armv7l-linux-musleabihf.tar.gz/md5/749654c9e8f333f2237649f6e5dc094d +libblastrampoline.v5.9.0+0.armv7l-linux-musleabihf.tar.gz/sha512/ee3f78d11c60c4c25297094ed8cce70baf830f78efd2a04be94398b5db457061cf8b443ca0f9508028c991bd927cb3aa076688e32b60a5079ad7b36a1699def6 +libblastrampoline.v5.9.0+0.i686-linux-gnu.tar.gz/md5/263a4f8bd1f269ef7f8f0d86a2ad88e9 +libblastrampoline.v5.9.0+0.i686-linux-gnu.tar.gz/sha512/74b49e2208bfdacfa9f51d8e7de29ec0de431009937e46f9bc6c4f24536eb39d54ed090ad120f5861ff9ccdeb97f3fce2df36bd203e454dd6b9f47083baadac4 +libblastrampoline.v5.9.0+0.i686-linux-musl.tar.gz/md5/38d066c1dd09eea0c922d6b695b39f8f +libblastrampoline.v5.9.0+0.i686-linux-musl.tar.gz/sha512/7c61739608e4bf5e6a08764efb1c2a8ea98ddcd54eb63bf5de627de208bcc3a69ce95bc7ae81722d2414fb33750eff47748fab93a903cc12f2eab1cff18d1df1 +libblastrampoline.v5.9.0+0.i686-w64-mingw32.tar.gz/md5/fd9678da295f74e49a519bb27cdf5ac3 +libblastrampoline.v5.9.0+0.i686-w64-mingw32.tar.gz/sha512/406f434a5595662cc0720818399cdc7b2377bea7053bc3791530fc789f34209d86988136820b304c9321d0d3cd9ac05f4f727683470be377ecbd3b33c812c451 +libblastrampoline.v5.9.0+0.powerpc64le-linux-gnu.tar.gz/md5/495cb39451d65b0d8cf8aa811d012b40 +libblastrampoline.v5.9.0+0.powerpc64le-linux-gnu.tar.gz/sha512/b465b4a41f19aeda0cc1298294e1f1b6bf21cb078d97d2c3df2618986fb65f3928608cdfcc762bb1d16c43755cd59e275912aaf528b643f137a240cd62ec825f +libblastrampoline.v5.9.0+0.x86_64-apple-darwin.tar.gz/md5/fa245d4f7aba107de9985d98813663d6 +libblastrampoline.v5.9.0+0.x86_64-apple-darwin.tar.gz/sha512/551a52a81460f494ff50c7b83a2aa73b788d0fc75fa25f99c2b694a9ec1eb69bdb9535bbd3710cdee8ddbe6a3369d6e7b3376e778f5bad5cde17439eb163d752 +libblastrampoline.v5.9.0+0.x86_64-linux-gnu.tar.gz/md5/e2a24428d271df5310e01b56aed41584 +libblastrampoline.v5.9.0+0.x86_64-linux-gnu.tar.gz/sha512/d38227a5d57f190be5c06d0d37d085256a48f86daf442496a33dec16fb1123c7626b90735932a597bd3cbaea9980d299a019b738618f7ac8e9dfc2e0274255f7 +libblastrampoline.v5.9.0+0.x86_64-linux-musl.tar.gz/md5/364c860a456b8700dd83056b76acdd63 +libblastrampoline.v5.9.0+0.x86_64-linux-musl.tar.gz/sha512/1716f805e24e660b4d831b59a0a15bde9e94f8660d797a729668f12520c777d9fbdbc8099d5851f89ffc0d6f69ddb097a00e8037dc061fb6bbb91d7463885aa4 +libblastrampoline.v5.9.0+0.x86_64-unknown-freebsd.tar.gz/md5/2c39cb711012e39f8b82de94beab5e2c +libblastrampoline.v5.9.0+0.x86_64-unknown-freebsd.tar.gz/sha512/748e119c12a440c50a49f0d471b61a32b84c9398192c17fbfa545d7d9d7e9cda68289a128f50b75499c752678c5f9dfb9bf8e5bf8394707d70eb26781f0e91f1 +libblastrampoline.v5.9.0+0.x86_64-w64-mingw32.tar.gz/md5/8c093a1273ce747d8073624a94e1d92b +libblastrampoline.v5.9.0+0.x86_64-w64-mingw32.tar.gz/sha512/98f14398581fcb14b5bc271033b070c07e03b771c5bdec80556d373a6197695a912a510e404bdb9e9be0d1e28df32217917dd2a3b50fe6dd68d422de142a0cc5 diff --git a/stdlib/libblastrampoline_jll/Project.toml b/stdlib/libblastrampoline_jll/Project.toml index 6ee704bc7e1c4..f33f89ff07802 100644 --- a/stdlib/libblastrampoline_jll/Project.toml +++ b/stdlib/libblastrampoline_jll/Project.toml @@ -1,13 +1,13 @@ name = "libblastrampoline_jll" uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" -version = "5.8.0+1" +version = "5.9.0+0" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" Artifacts = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" [compat] -julia = "1.10" +julia = "1.12" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" From b1a30d264ed1db9bfdb5b178319fab11427a5a2b Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Wed, 29 May 2024 17:43:24 -0400 Subject: [PATCH 04/71] CLI: Use `GetModuleHandleExW` to locate libjulia.dll (#54617) This should be a more reliable look-up, since this will directly report the path of the currently-executing libjulia.dll. Without this PR, `LoadLibraryW` depends on the system library search order. When the top-level executable is adjacent to `libjulia.dll` (as it is for our binary distribution usually), then that search should be OK. However, applications that use Julia as a library can end up searching the system PATH before making it to the correct `lib/julia` directory, causing us to load the wrong version of `libjulia.dll`. In many cases, that extra load is benign due to the stricter separation of libraries/symbols on Windows - However, in general it's likely to be the cause of subtle bugs. (cherry picked from commit c11245dc5e77d2bcd1dfc3ef4c3c6736dfbdda87) --- cli/loader_lib.c | 48 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/cli/loader_lib.c b/cli/loader_lib.c index 02030cf2717a5..9f52249362bf7 100644 --- a/cli/loader_lib.c +++ b/cli/loader_lib.c @@ -125,6 +125,32 @@ static void * lookup_symbol(const void * lib_handle, const char * symbol_name) { #endif } +#if defined(_OS_WINDOWS_) +void win32_formatmessage(DWORD code, char *reason, int len) { + DWORD res; + LPWSTR errmsg; + res = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_MAX_WIDTH_MASK, + NULL, code, + MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), + (LPWSTR)&errmsg, 0, NULL); + if (!res && (GetLastError() == ERROR_MUI_FILE_NOT_FOUND || + GetLastError() == ERROR_RESOURCE_TYPE_NOT_FOUND)) { + res = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_MAX_WIDTH_MASK, + NULL, code, + 0, (LPWSTR)&errmsg, 0, NULL); + } + res = WideCharToMultiByte(CP_UTF8, 0, errmsg, -1, reason, len, NULL, NULL); + reason[len - 1] = '\0'; + LocalFree(errmsg); +} +#endif + // Find the location of libjulia. char *lib_dir = NULL; JL_DLLEXPORT const char * jl_get_libdir() @@ -135,21 +161,21 @@ JL_DLLEXPORT const char * jl_get_libdir() } #if defined(_OS_WINDOWS_) // On Windows, we use GetModuleFileNameW - wchar_t *libjulia_path = utf8_to_wchar(LIBJULIA_NAME); HMODULE libjulia = NULL; - // Get a handle to libjulia. - if (!libjulia_path) { - jl_loader_print_stderr3("ERROR: Unable to convert path ", LIBJULIA_NAME, " to wide string!\n"); - exit(1); - } - libjulia = LoadLibraryW(libjulia_path); - if (libjulia == NULL) { - jl_loader_print_stderr3("ERROR: Unable to load ", LIBJULIA_NAME, "!\n"); + // Get a handle to libjulia + if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + (LPCWSTR)jl_get_libdir, &libjulia)) { + DWORD err = GetLastError(); + jl_loader_print_stderr3("ERROR: could not locate library \"", LIBJULIA_NAME, "\"\n"); + + char msg[2048]; + win32_formatmessage(err, msg, sizeof(msg)); + jl_loader_print_stderr(msg); exit(1); } - free(libjulia_path); - libjulia_path = (wchar_t*)malloc(32768 * sizeof(wchar_t)); // max long path length + + wchar_t *libjulia_path = (wchar_t*)malloc(32768 * sizeof(wchar_t)); // max long path length if (!GetModuleFileNameW(libjulia, libjulia_path, 32768)) { jl_loader_print_stderr("ERROR: GetModuleFileName() failed\n"); exit(1); From 9e2cb49714e5a3d634e8fa9e2367b28bb406ae83 Mon Sep 17 00:00:00 2001 From: "Viral B. Shah" Date: Wed, 29 May 2024 20:23:06 -0400 Subject: [PATCH 05/71] Allow libquadmath to also fail as it is not available on all systems (#54605) Fix #41613. Co-authored-by: FX Coudert (cherry picked from commit e5549c23045c2cc082d04b01a28b7c317f8aa877) --- base/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/Makefile b/base/Makefile index 6abf9b285ce34..c3899d5a90d47 100644 --- a/base/Makefile +++ b/base/Makefile @@ -249,12 +249,12 @@ endif ifneq (,$(LIBGFORTRAN_VERSION)) $(eval $(call symlink_system_library,CSL,libgfortran,$(LIBGFORTRAN_VERSION))) endif -$(eval $(call symlink_system_library,CSL,libquadmath,0)) $(eval $(call symlink_system_library,CSL,libstdc++,6)) -# We allow libssp, libatomic and libgomp to fail as they are not available on all systems +# We allow libssp, libatomic, libgomp and libquadmath to fail as they are not available on all systems $(eval $(call symlink_system_library,CSL,libssp,0,ALLOW_FAILURE)) $(eval $(call symlink_system_library,CSL,libatomic,1,ALLOW_FAILURE)) $(eval $(call symlink_system_library,CSL,libgomp,1,ALLOW_FAILURE)) +$(eval $(call symlink_system_library,CSL,libquadmath,0,ALLOW_FAILURE)) $(eval $(call symlink_system_library,PCRE,libpcre2-8)) $(eval $(call symlink_system_library,DSFMT,libdSFMT)) $(eval $(call symlink_system_library,LIBBLASTRAMPOLINE,libblastrampoline)) From 166a82c8446353e5180a48d2dc99edf518a362c8 Mon Sep 17 00:00:00 2001 From: Diogo Netto <61364108+d-netto@users.noreply.github.com> Date: Fri, 31 May 2024 16:51:48 -0300 Subject: [PATCH 06/71] ensure we set the right value to gc_first_tid (#54645) This may introduce a correctness issue in the work-stealing termination loop if we're using interactive threads and GC threads simultaneously. Indeed, if we forget to add `nthreadsi` to `nthreads`, then we're checking in the mark-loop termination protocol a range `[gc_first_tid, gc_first_tid + jl_n_markthreads)` of threads which is "shifted to the left" compared to what it should be. This implies that we will not be checking whether the GC threads with higher TID actually have terminated the mark-loop. (cherry picked from commit c52eee220654e4b4aca43edb07f5bcf896d3d9bf) --- src/threading.c | 2 +- test/gc.jl | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/threading.c b/src/threading.c index dcfc6ac7e93bb..4b1c622df2c07 100644 --- a/src/threading.c +++ b/src/threading.c @@ -732,7 +732,7 @@ void jl_init_threading(void) jl_atomic_store_release(&jl_all_tls_states, (jl_ptls_t*)calloc(jl_all_tls_states_size, sizeof(jl_ptls_t))); jl_atomic_store_release(&jl_n_threads, jl_all_tls_states_size); jl_n_gcthreads = ngcthreads; - gc_first_tid = nthreads; + gc_first_tid = nthreads + nthreadsi; } static uv_barrier_t thread_init_done; diff --git a/test/gc.jl b/test/gc.jl index d5990582cc00a..578fe799fb935 100644 --- a/test/gc.jl +++ b/test/gc.jl @@ -5,11 +5,13 @@ using Test function run_gctest(file) let cmd = `$(Base.julia_cmd()) --depwarn=error --rr-detach --startup-file=no $file` @testset for test_nthreads in (1, 2, 4) - @testset for concurrent_sweep in (0, 1) - new_env = copy(ENV) - new_env["JULIA_NUM_THREADS"] = string(test_nthreads) - new_env["JULIA_NUM_GC_THREADS"] = "$(test_nthreads),$(concurrent_sweep)" - @test success(run(pipeline(setenv(cmd, new_env), stdout = stdout, stderr = stderr))) + @testset for test_nithreads in (0, 1) + @testset for concurrent_sweep in (0, 1) + new_env = copy(ENV) + new_env["JULIA_NUM_THREADS"] = "$test_nthreads,$test_nithreads" + new_env["JULIA_NUM_GC_THREADS"] = "$(test_nthreads),$(concurrent_sweep)" + @test success(run(pipeline(setenv(cmd, new_env), stdout = stdout, stderr = stderr))) + end end end end From 7ed2a6dc827dff70aaeefb8563029a5442a91409 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Tue, 4 Jun 2024 08:44:00 +0530 Subject: [PATCH 07/71] Construct LazyString in error paths for tridiag (#54648) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similar to https://github.com/JuliaLang/julia/pull/54631, this would help reduce dynamic dispatches involved in concatenating a `String` and a `LazyString`. These show up in ```julia julia> @report_opt Tridiagonal(rand(1), rand(2), rand(1)) \ rand(2) [ Info: tracking Base ┌ Warning: skipping var"#sprint#594"(context, sizehint::Integer, ::typeof(sprint), f::Function, args...) @ Base strings/io.jl:107 to avoid parsing too much code └ @ Revise ~/.julia/packages/Revise/bAgL0/src/packagedef.jl:1092 ┌ Warning: skipping (::Base.var"#120#121")(io) @ Base strings/lazy.jl:84 to avoid parsing too much code └ @ Revise ~/.julia/packages/Revise/bAgL0/src/packagedef.jl:1092 ═════ 1 possible error found ═════ ┌ \(A::Tridiagonal{Float64, Vector{Float64}}, B::Vector{Float64}) @ LinearAlgebra /cache/build/builder-amdci4-5/julialang/julia-release-1-dot-11/usr/share/julia/stdlib/v1.11/LinearAlgebra/src/generic.jl:1132 │┌ lu(::Tridiagonal{Float64, Vector{Float64}}) @ LinearAlgebra /cache/build/builder-amdci4-5/julialang/julia-release-1-dot-11/usr/share/julia/stdlib/v1.11/LinearAlgebra/src/lu.jl:341 ││┌ lu(::Tridiagonal{Float64, Vector{Float64}}; kwargs::@Kwargs{}) @ LinearAlgebra /cache/build/builder-amdci4-5/julialang/julia-release-1-dot-11/usr/share/julia/stdlib/v1.11/LinearAlgebra/src/lu.jl:341 │││┌ _lucopy(A::Tridiagonal{Float64, Vector{Float64}}, T::Type{Float64}) @ LinearAlgebra /cache/build/builder-amdci4-5/julialang/julia-release-1-dot-11/usr/share/julia/stdlib/v1.11/LinearAlgebra/src/lu.jl:351 ││││┌ copymutable_oftype(A::Tridiagonal{Float64, Vector{Float64}}, ::Type{Float64}) @ LinearAlgebra /cache/build/builder-amdci4-5/julialang/julia-release-1-dot-11/usr/share/julia/stdlib/v1.11/LinearAlgebra/src/LinearAlgebra.jl:463 │││││┌ similar(M::Tridiagonal{Float64, Vector{Float64}}, ::Type{Float64}) @ LinearAlgebra /cache/build/builder-amdci4-5/julialang/julia-release-1-dot-11/usr/share/julia/stdlib/v1.11/LinearAlgebra/src/tridiag.jl:603 ││││││┌ Tridiagonal(dl::Vector{Float64}, d::Vector{Float64}, du::Vector{Float64}) @ LinearAlgebra /cache/build/builder-amdci4-5/julialang/julia-release-1-dot-11/usr/share/julia/stdlib/v1.11/LinearAlgebra/src/tridiag.jl:520 │││││││┌ Tridiagonal{Float64, Vector{Float64}}(dl::Vector{Float64}, d::Vector{Float64}, du::Vector{Float64}) @ LinearAlgebra /cache/build/builder-amdci4-5/julialang/julia-release-1-dot-11/usr/share/julia/stdlib/v1.11/LinearAlgebra/src/tridiag.jl:477 ││││││││┌ string(::String, ::String, ::LazyString) @ Base ./strings/io.jl:189 │││││││││┌ print_to_string(::String, ::String, ::LazyString) @ Base ./strings/io.jl:148 ││││││││││┌ print(io::IOBuffer, s::LazyString) @ Base ./strings/io.jl:195 │││││││││││┌ iterate(s::LazyString) @ Base ./strings/lazy.jl:94 ││││││││││││┌ String(l::LazyString) @ Base ./strings/lazy.jl:83 │││││││││││││┌ sprint(::Base.var"#120#121"{LazyString}) @ Base ./strings/io.jl:107 ││││││││││││││┌ sprint(::Base.var"#120#121"{LazyString}; context::Nothing, sizehint::Int64) @ Base ./strings/io.jl:114 │││││││││││││││┌ (::Base.var"#120#121"{LazyString})(io::IOBuffer) @ Base ./strings/lazy.jl:85 ││││││││││││││││ runtime dispatch detected: print(io::IOBuffer, %16::Any)::Any │││││││││││││││└──────────────────── ``` Co-authored-by: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Co-authored-by: Dilum Aluthge (cherry picked from commit 770a464cda238cb93a5a1bb3ac291e1e3d88f5e4) --- stdlib/LinearAlgebra/src/tridiag.jl | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/stdlib/LinearAlgebra/src/tridiag.jl b/stdlib/LinearAlgebra/src/tridiag.jl index 447d87f1c5efc..497e265b280bc 100644 --- a/stdlib/LinearAlgebra/src/tridiag.jl +++ b/stdlib/LinearAlgebra/src/tridiag.jl @@ -176,6 +176,12 @@ issymmetric(S::SymTridiagonal) = true tr(S::SymTridiagonal) = sum(S.dv) +@noinline function throw_diag_outofboundserror(n, sz) + sz1, sz2 = sz + throw(ArgumentError(LazyString(lazy"requested diagonal, $n, must be at least $(-sz1) ", + lazy"and at most $sz2 for an $(sz1)-by-$(sz2) matrix"))) +end + function diag(M::SymTridiagonal{T}, n::Integer=0) where T<:Number # every branch call similar(..., ::Int) to make sure the # same vector type is returned independent of n @@ -187,8 +193,7 @@ function diag(M::SymTridiagonal{T}, n::Integer=0) where T<:Number elseif absn <= size(M,1) return fill!(similar(M.dv, size(M,1)-absn), zero(T)) else - throw(ArgumentError(string(lazy"requested diagonal, $n, must be at least $(-size(M, 1)) ", - lazy"and at most $(size(M, 2)) for an $(size(M, 1))-by-$(size(M, 2)) matrix"))) + throw_diag_outofboundserror(n, size(M)) end end function diag(M::SymTridiagonal, n::Integer=0) @@ -203,8 +208,7 @@ function diag(M::SymTridiagonal, n::Integer=0) elseif n <= size(M,1) throw(ArgumentError("requested diagonal contains undefined zeros of an array type")) else - throw(ArgumentError(string(lazy"requested diagonal, $n, must be at least $(-size(M, 1)) ", - lazy"and at most $(size(M, 2)) for an $(size(M, 1))-by-$(size(M, 2)) matrix"))) + throw_diag_outofboundserror(n, size(M)) end end @@ -346,7 +350,7 @@ isdiag(M::SymTridiagonal) = iszero(_evview(M)) function tril!(M::SymTridiagonal{T}, k::Integer=0) where T n = length(M.dv) if !(-n - 1 <= k <= n - 1) - throw(ArgumentError(string(lazy"the requested diagonal, $k, must be at least ", + throw(ArgumentError(LazyString(lazy"the requested diagonal, $k, must be at least ", lazy"$(-n - 1) and at most $(n - 1) in an $n-by-$n matrix"))) elseif k < -1 fill!(M.ev, zero(T)) @@ -365,7 +369,7 @@ end function triu!(M::SymTridiagonal{T}, k::Integer=0) where T n = length(M.dv) if !(-n + 1 <= k <= n + 1) - throw(ArgumentError(string(lazy"the requested diagonal, $k, must be at least ", + throw(ArgumentError(LazyString(lazy"the requested diagonal, $k, must be at least ", lazy"$(-n + 1) and at most $(n + 1) in an $n-by-$n matrix"))) elseif k > 1 fill!(M.ev, zero(T)) @@ -474,7 +478,7 @@ struct Tridiagonal{T,V<:AbstractVector{T}} <: AbstractMatrix{T} require_one_based_indexing(dl, d, du) n = length(d) if (length(dl) != n-1 || length(du) != n-1) && !(length(d) == 0 && length(dl) == 0 && length(du) == 0) - throw(ArgumentError(string("cannot construct Tridiagonal from incompatible ", + throw(ArgumentError(LazyString("cannot construct Tridiagonal from incompatible ", "lengths of subdiagonal, diagonal and superdiagonal: ", lazy"($(length(dl)), $(length(d)), $(length(du)))"))) end @@ -642,7 +646,7 @@ function diag(M::Tridiagonal{T}, n::Integer=0) where T elseif abs(n) <= size(M,1) return fill!(similar(M.d, size(M,1)-abs(n)), zero(T)) else - throw(ArgumentError(string(lazy"requested diagonal, $n, must be at least $(-size(M, 1)) ", + throw(ArgumentError(LazyString(lazy"requested diagonal, $n, must be at least $(-size(M, 1)) ", lazy"and at most $(size(M, 2)) for an $(size(M, 1))-by-$(size(M, 2)) matrix"))) end end @@ -695,7 +699,7 @@ end elseif j - i == 1 @inbounds A.du[i] = x elseif !iszero(x) - throw(ArgumentError(string(lazy"cannot set entry ($i, $j) off ", + throw(ArgumentError(LazyString(lazy"cannot set entry ($i, $j) off ", lazy"the tridiagonal band to a nonzero value ($x)"))) end return x @@ -738,7 +742,7 @@ isdiag(M::Tridiagonal) = iszero(M.dl) && iszero(M.du) function tril!(M::Tridiagonal{T}, k::Integer=0) where T n = length(M.d) if !(-n - 1 <= k <= n - 1) - throw(ArgumentError(string(lazy"the requested diagonal, $k, must be at least ", + throw(ArgumentError(LazyString(lazy"the requested diagonal, $k, must be at least ", lazy"$(-n - 1) and at most $(n - 1) in an $n-by-$n matrix"))) elseif k < -1 fill!(M.dl, zero(T)) @@ -756,7 +760,7 @@ end function triu!(M::Tridiagonal{T}, k::Integer=0) where T n = length(M.d) if !(-n + 1 <= k <= n + 1) - throw(ArgumentError(string(lazy"the requested diagonal, $k, must be at least ", + throw(ArgumentError(LazyString(lazy"the requested diagonal, $k, must be at least ", lazy"$(-n + 1) and at most $(n + 1) in an $n-by-$n matrix"))) elseif k > 1 fill!(M.dl, zero(T)) From 2cea685c420d959608294687428b21e6467392b9 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Wed, 5 Jun 2024 10:11:55 +0900 Subject: [PATCH 08/71] inference: fix too conservative effects for recursive cycles (#54323) The `:terminates` effect bit must be conservatively tainted unless recursion cycle has been fully resolved. As for other effects, there's no need to taint them at this moment because they will be tainted as we try to resolve the cycle. - fixes #52938 - xref #51092 --- base/compiler/typeinfer.jl | 24 ++++++++++++++++++------ test/compiler/AbstractInterpreter.jl | 10 +++++++++- test/compiler/effects.jl | 12 ++++++------ test/compiler/inference.jl | 2 +- test/math.jl | 18 +++++++++++++----- 5 files changed, 47 insertions(+), 19 deletions(-) diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index fc1c537f740cd..fd45791b64a40 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -245,15 +245,21 @@ function _typeinf(interp::AbstractInterpreter, frame::InferenceState) # with no active ip's, frame is done frames = frame.callers_in_cycle isempty(frames) && push!(frames, frame) - valid_worlds = WorldRange() + cycle_valid_worlds = WorldRange() + cycle_effects = EFFECTS_TOTAL for caller in frames @assert !(caller.dont_work_on_me) caller.dont_work_on_me = true - # might might not fully intersect these earlier, so do that now - valid_worlds = intersect(caller.valid_worlds, valid_worlds) + # converge the world age range and effects for this cycle here: + # all frames in the cycle should have the same bits of `valid_worlds` and `effects` + # that are simply the intersection of each partial computation, without having + # dependencies on each other (unlike rt and exct) + cycle_valid_worlds = intersect(cycle_valid_worlds, caller.valid_worlds) + cycle_effects = merge_effects(cycle_effects, caller.ipo_effects) end for caller in frames - caller.valid_worlds = valid_worlds + caller.valid_worlds = cycle_valid_worlds + caller.ipo_effects = cycle_effects finish(caller, caller.interp) end for caller in frames @@ -872,7 +878,8 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize update_valid_age!(caller, frame.valid_worlds) isinferred = is_inferred(frame) edge = isinferred ? mi : nothing - effects = isinferred ? frame.result.ipo_effects : adjust_effects(Effects(), method) # effects are adjusted already within `finish` for ipo_effects + effects = isinferred ? frame.result.ipo_effects : # effects are adjusted already within `finish` for ipo_effects + adjust_effects(effects_for_cycle(frame.ipo_effects), method) exc_bestguess = refine_exception_type(frame.exc_bestguess, effects) # propagate newly inferred source to the inliner, allowing efficient inlining w/o deserialization: # note that this result is cached globally exclusively, we can use this local result destructively @@ -887,11 +894,16 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize # return the current knowledge about this cycle frame = frame::InferenceState update_valid_age!(caller, frame.valid_worlds) - effects = adjust_effects(Effects(), method) + effects = adjust_effects(effects_for_cycle(frame.ipo_effects), method) exc_bestguess = refine_exception_type(frame.exc_bestguess, effects) return EdgeCallResult(frame.bestguess, exc_bestguess, nothing, effects) end +# The `:terminates` effect bit must be conservatively tainted unless recursion cycle has +# been fully resolved. As for other effects, there's no need to taint them at this moment +# because they will be tainted as we try to resolve the cycle. +effects_for_cycle(effects::Effects) = Effects(effects; terminates=false) + function cached_return_type(code::CodeInstance) rettype = code.rettype isdefined(code, :rettype_const) || return rettype diff --git a/test/compiler/AbstractInterpreter.jl b/test/compiler/AbstractInterpreter.jl index 2068997c77c82..776b9fd69ce9b 100644 --- a/test/compiler/AbstractInterpreter.jl +++ b/test/compiler/AbstractInterpreter.jl @@ -134,10 +134,18 @@ function CC.concrete_eval_eligible(interp::Issue48097Interp, end @overlay Issue48097MT @noinline Core.throw_inexacterror(f::Symbol, ::Type{T}, val) where {T} = return issue48097(; kwargs...) = return 42 -@test_broken fully_eliminated(; interp=Issue48097Interp(), retval=42) do +@test fully_eliminated(; interp=Issue48097Interp(), retval=42) do issue48097(; a=1f0, b=1.0) end +# https://github.com/JuliaLang/julia/issues/52938 +@newinterp Issue52938Interp +@MethodTable ISSUE_52938_MT +CC.method_table(interp::Issue52938Interp) = CC.OverlayMethodTable(CC.get_inference_world(interp), ISSUE_52938_MT) +inner52938(x, types::Type, args...; kwargs...) = x +outer52938(x) = @inline inner52938(x, Tuple{}; foo=Ref(42), bar=1) +@test fully_eliminated(outer52938, (Any,); interp=Issue52938Interp(), retval=Argument(2)) + # Should not concrete-eval overlayed methods in semi-concrete interpretation @newinterp OverlaySinInterp @MethodTable OverlaySinMT diff --git a/test/compiler/effects.jl b/test/compiler/effects.jl index 36c28b6c844bf..0b458905e4217 100644 --- a/test/compiler/effects.jl +++ b/test/compiler/effects.jl @@ -89,13 +89,13 @@ Base.@assume_effects :terminates_globally function recur_termination1(x) 0 ≤ x < 20 || error("bad fact") return x * recur_termination1(x-1) end -@test_broken Core.Compiler.is_foldable(Base.infer_effects(recur_termination1, (Int,))) +@test Core.Compiler.is_foldable(Base.infer_effects(recur_termination1, (Int,))) @test Core.Compiler.is_terminates(Base.infer_effects(recur_termination1, (Int,))) function recur_termination2() Base.@assume_effects :total !:terminates_globally recur_termination1(12) end -@test_broken fully_eliminated(recur_termination2) +@test fully_eliminated(recur_termination2) @test fully_eliminated() do; recur_termination2(); end Base.@assume_effects :terminates_globally function recur_termination21(x) @@ -104,15 +104,15 @@ Base.@assume_effects :terminates_globally function recur_termination21(x) return recur_termination22(x) end recur_termination22(x) = x * recur_termination21(x-1) -@test_broken Core.Compiler.is_foldable(Base.infer_effects(recur_termination21, (Int,))) -@test_broken Core.Compiler.is_foldable(Base.infer_effects(recur_termination22, (Int,))) +@test Core.Compiler.is_foldable(Base.infer_effects(recur_termination21, (Int,))) +@test Core.Compiler.is_foldable(Base.infer_effects(recur_termination22, (Int,))) @test Core.Compiler.is_terminates(Base.infer_effects(recur_termination21, (Int,))) @test Core.Compiler.is_terminates(Base.infer_effects(recur_termination22, (Int,))) function recur_termination2x() Base.@assume_effects :total !:terminates_globally recur_termination21(12) + recur_termination22(12) end -@test_broken fully_eliminated(recur_termination2x) +@test fully_eliminated(recur_termination2x) @test fully_eliminated() do; recur_termination2x(); end # anonymous function support for `@assume_effects` @@ -970,7 +970,7 @@ unknown_sparam_nothrow2(x::Ref{Ref{T}}) where T = (T; nothing) abstractly_recursive1() = abstractly_recursive2() abstractly_recursive2() = (Core.Compiler._return_type(abstractly_recursive1, Tuple{}); 1) abstractly_recursive3() = abstractly_recursive2() -@test Core.Compiler.is_terminates(Base.infer_effects(abstractly_recursive3, ())) +@test_broken Core.Compiler.is_terminates(Base.infer_effects(abstractly_recursive3, ())) actually_recursive1(x) = actually_recursive2(x) actually_recursive2(x) = (x <= 0) ? 1 : actually_recursive1(x - 1) actually_recursive3(x) = actually_recursive2(x) diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index d546e70d7d749..d560d4362068f 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -5290,7 +5290,7 @@ end return r end foo51090(b) = return bar51090(b) -@test !fully_eliminated(foo51090, (Int,)) +@test_broken !fully_eliminated(foo51090, (Int,)) # exploit throwness from concrete eval for intrinsics @test Base.return_types() do diff --git a/test/math.jl b/test/math.jl index c88905018fb3a..be134adb15d34 100644 --- a/test/math.jl +++ b/test/math.jl @@ -1557,13 +1557,21 @@ end @testset let T = T for f = Any[sin, cos, tan, log, log2, log10, log1p, exponent, sqrt, cbrt, fourthroot, asin, atan, acos, sinh, cosh, tanh, asinh, acosh, atanh, exp, exp2, exp10, expm1] - @testset let f = f - @test Base.infer_return_type(f, (T,)) != Union{} - @test Core.Compiler.is_foldable(Base.infer_effects(f, (T,))) + @testset let f = f, + rt = Base.infer_return_type(f, (T,)), + effects = Base.infer_effects(f, (T,)) + @test rt != Union{} + @test Core.Compiler.is_foldable(effects) end end - @test Core.Compiler.is_foldable(Base.infer_effects(^, (T,Int))) - @test Core.Compiler.is_foldable(Base.infer_effects(^, (T,T))) + @static if !(Sys.iswindows()&&Int==Int32) # COMBAK debug this + @testset let effects = Base.infer_effects(^, (T,Int)) + @test Core.Compiler.is_foldable(effects) + end + end # @static + @testset let effects = Base.infer_effects(^, (T,T)) + @test Core.Compiler.is_foldable(effects) + end end end end; From c0f71e7dbb51b1f0a58359d404e9c559858f60b7 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Fri, 7 Jun 2024 11:14:08 +0900 Subject: [PATCH 09/71] inference: follow up #54323, override ssaflags with new cycle effects (#54689) JuliaLang/julia#54323 ensures that all frames within a cycle have the same, cycle valid effects. However, `src.ssaflags` is calculated using partial effects, so when the effects of a `frame` within the cycle are updated, there would be an inconsistency between `frame.ipo_effects` and `frame.src.ssaflags`. Due to this inconsistency, JuliaLang/julia#54323 breaks the test cases from JuliaLang/julia#51092, when backported to v1.11. On the surface this is because JuliaLang/julia#52999 hasn't been backported to v1.11, but the fundamental issue lies in this inconsistency between cycle effects and `ssaflags`. To resolve this issue, this commit traverses `cycle_backedges` to visit statements involved in the cycle, and updates each `ssaflags` according to new cycle valid effects if necessary. --- base/compiler/typeinfer.jl | 54 +++++++++++++++++++++++++++++++++----- test/compiler/inference.jl | 11 +++++++- 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index fd45791b64a40..8b049efa241e7 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -244,9 +244,36 @@ function _typeinf(interp::AbstractInterpreter, frame::InferenceState) typeinf_nocycle(interp, 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) + if isempty(frames) + push!(frames, frame) + finish_nocycle(interp, frame) + elseif length(frames) == 1 + @assert frames[1] === frame "invalid callers_in_cycle" + finish_nocycle(interp, frame) + else + finish_cycle(interp, frames) + end + empty!(frames) + return true +end + +function finish_nocycle(::AbstractInterpreter, frame::InferenceState) + frame.dont_work_on_me = true + finish(frame, frame.interp) + opt = frame.result.src + if opt isa OptimizationState # implies `may_optimize(caller.interp) === true` + optimize(frame.interp, opt, frame.result) + end + finish!(frame.interp, frame) + if is_cached(frame) + cache_result!(frame.interp, frame.result) + end + return nothing +end + +function finish_cycle(::AbstractInterpreter, frames::Vector{InferenceState}) cycle_valid_worlds = WorldRange() - cycle_effects = EFFECTS_TOTAL + cycle_valid_effects = EFFECTS_TOTAL for caller in frames @assert !(caller.dont_work_on_me) caller.dont_work_on_me = true @@ -255,11 +282,10 @@ function _typeinf(interp::AbstractInterpreter, frame::InferenceState) # that are simply the intersection of each partial computation, without having # dependencies on each other (unlike rt and exct) cycle_valid_worlds = intersect(cycle_valid_worlds, caller.valid_worlds) - cycle_effects = merge_effects(cycle_effects, caller.ipo_effects) + cycle_valid_effects = merge_effects(cycle_valid_effects, caller.ipo_effects) end for caller in frames - caller.valid_worlds = cycle_valid_worlds - caller.ipo_effects = cycle_effects + adjust_cycle_frame!(caller, cycle_valid_worlds, cycle_valid_effects) finish(caller, caller.interp) end for caller in frames @@ -274,8 +300,22 @@ function _typeinf(interp::AbstractInterpreter, frame::InferenceState) cache_result!(caller.interp, caller.result) end end - empty!(frames) - return true + return nothing +end + +function adjust_cycle_frame!(sv::InferenceState, cycle_valid_worlds::WorldRange, cycle_valid_effects::Effects) + sv.valid_worlds = cycle_valid_worlds + sv.ipo_effects = cycle_valid_effects + # traverse the callees of this cycle that are tracked within `sv.cycle_backedges` + # and adjust their statements so that they are consistent with the new `cycle_valid_effects` + new_flags = flags_for_effects(cycle_valid_effects) + for (callee, pc) in sv.cycle_backedges + old_currpc = callee.currpc + callee.currpc = pc + set_curr_ssaflag!(callee, new_flags, IR_FLAGS_EFFECTS) + callee.currpc = old_currpc + end + return nothing end function is_result_constabi_eligible(result::InferenceResult) diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index d560d4362068f..b661fcc384268 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -5290,7 +5290,16 @@ end return r end foo51090(b) = return bar51090(b) -@test_broken !fully_eliminated(foo51090, (Int,)) +@test !fully_eliminated(foo51090, (Int,)) + +Base.@assume_effects :terminates_globally @noinline function bar51090_terminates(b) + b == 0 && return + r = foo51090_terminates(b - 1) + Base.donotdelete(b) + return r +end +foo51090_terminates(b) = return bar51090_terminates(b) +@test !fully_eliminated(foo51090_terminates, (Int,)) # exploit throwness from concrete eval for intrinsics @test Base.return_types() do From 92f5aa5310ed1d198154405d1f329cf84d99e09c Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Tue, 4 Jun 2024 12:23:48 -0400 Subject: [PATCH 10/71] Switch to Pkg mode prompt immediately and load Pkg in the background (#54594) Because Pkg is now a pkgimage it can load slowly on slower machines, which is a bit frustrating in the first repl switch. This makes the repl immediately switch to a dummy prompt that looks like Pkg mode to allow the user to keep typing while Pkg loads. During which the keymap is disabled. It works best if julia has >1 thread, otherwise typing stalls during Pkg load. If Pkg takes longer to load than the user to type the command and press return, then the UX isn't great as it won't do anything. https://github.com/JuliaLang/julia/assets/1694067/1bf17323-441a-4db2-8a3b-4d571eac622f (cherry picked from commit 13635e1321991306aadab3f2eb4fac59ef9b3745) --- stdlib/REPL/src/REPL.jl | 42 +++++++++++++++++++++++++---------- stdlib/REPL/src/precompile.jl | 10 +++++++++ 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 8b540f0220a30..bf807f2a8a6c9 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -1041,9 +1041,6 @@ setup_interface( extra_repl_keymap::Any = repl.options.extra_keymap ) = setup_interface(repl, hascolor, extra_repl_keymap) -# we have to grab this after Pkg is loaded so cache it -pkg_mode::Union{Nothing,LineEdit.Prompt} = nothing - # This non keyword method can be precompiled which is important function setup_interface( repl::LineEditREPL, @@ -1189,11 +1186,17 @@ function setup_interface( end, ']' => function (s::MIState,o...) if isempty(s) || position(LineEdit.buffer(s)) == 0 - global pkg_mode - if pkg_mode === nothing + # print a dummy pkg prompt while Pkg loads + LineEdit.clear_line(LineEdit.terminal(s)) + # use 6 .'s here because its the same width as the most likely `@v1.xx` env name + print(LineEdit.terminal(s), styled"{blue,bold:({gray:......}) pkg> }") + pkg_mode = nothing + transition_finished = false + iolock = Base.ReentrantLock() # to avoid race between tasks reading stdin & input buffer + # spawn Pkg load to avoid blocking typing during loading. Typing will block if only 1 thread + t_replswitch = Threads.@spawn begin pkgid = Base.PkgId(Base.UUID("44cfe95a-1eb2-52ea-b672-e2afdf69b78f"), "Pkg") REPLExt = Base.require_stdlib(pkgid, "REPLExt") - pkg_mode = nothing if REPLExt isa Module && isdefined(REPLExt, :PkgCompletionProvider) for mode in repl.interface.modes if mode isa LineEdit.Prompt && mode.complete isa REPLExt.PkgCompletionProvider @@ -1202,16 +1205,31 @@ function setup_interface( end end end + if pkg_mode !== nothing + @lock iolock begin + buf = copy(LineEdit.buffer(s)) + transition(s, pkg_mode) do + LineEdit.state(s, pkg_mode).input_buffer = buf + end + if !isempty(s) + @invokelatest(LineEdit.check_for_hint(s)) && @invokelatest(LineEdit.refresh_line(s)) + end + transition_finished = true + end + end end - if pkg_mode !== nothing - buf = copy(LineEdit.buffer(s)) - transition(s, pkg_mode) do - LineEdit.state(s, pkg_mode).input_buffer = buf + Base.errormonitor(t_replswitch) + # while loading just accept all keys, no keymap functionality + while !istaskdone(t_replswitch) + # wait but only take if task is still running + peek(stdin, Char) + @lock iolock begin + transition_finished || edit_insert(s, read(stdin, Char)) end - return end + else + edit_insert(s, ']') end - edit_insert(s, ']') end, # Bracketed Paste Mode diff --git a/stdlib/REPL/src/precompile.jl b/stdlib/REPL/src/precompile.jl index 7299742eaef1c..3154642b74307 100644 --- a/stdlib/REPL/src/precompile.jl +++ b/stdlib/REPL/src/precompile.jl @@ -220,4 +220,14 @@ end generate_precompile_statements() precompile(Tuple{typeof(getproperty), REPL.REPLBackend, Symbol}) + +# Helps Pkg repl mode switch +precompile(Tuple{typeof(REPL.Terminals.clear_line), REPL.Terminals.TTYTerminal}) +precompile(Tuple{typeof(Base.print), REPL.Terminals.TTYTerminal, Base.AnnotatedString{String}}) +precompile(Tuple{typeof(Base.peek), Base.TTY, Type{Char}}) +precompile(Tuple{typeof(Base.similar), Array{String, 1}}) +precompile(Tuple{typeof(Base.Iterators.enumerate), Array{String, 1}}) +precompile(Tuple{typeof(Base.setindex!), Array{String, 1}, String, Int64}) +precompile(Tuple{typeof(Base.convert), Type{Base.Dict{String, Union{Array{String, 1}, String}}}, Base.Dict{String, Any}}) + end # Precompile From 10c2e6461b3671d44b3da5d8cc971a2b5d3a25c4 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Wed, 5 Jun 2024 02:36:53 +0530 Subject: [PATCH 11/71] Improve error message in inplace transpose (#54669) (cherry picked from commit 9eb7a0c479be95ffe21d4c7553c3f0c211e19c11) --- stdlib/LinearAlgebra/src/transpose.jl | 15 ++++++++++----- stdlib/LinearAlgebra/test/adjtrans.jl | 10 ++++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/stdlib/LinearAlgebra/src/transpose.jl b/stdlib/LinearAlgebra/src/transpose.jl index 8aa04f7d34b48..a36919b2e557a 100644 --- a/stdlib/LinearAlgebra/src/transpose.jl +++ b/stdlib/LinearAlgebra/src/transpose.jl @@ -74,27 +74,32 @@ julia> A ``` """ adjoint!(B::AbstractMatrix, A::AbstractMatrix) = transpose_f!(adjoint, B, A) + +@noinline function check_transpose_axes(axesA, axesB) + axesB == reverse(axesA) || throw(DimensionMismatch("axes of the destination are incompatible with that of the source")) +end + function transpose!(B::AbstractVector, A::AbstractMatrix) - axes(B,1) == axes(A,2) && axes(A,1) == 1:1 || throw(DimensionMismatch("transpose")) + check_transpose_axes((axes(B,1), axes(B,2)), axes(A)) copyto!(B, A) end function transpose!(B::AbstractMatrix, A::AbstractVector) - axes(B,2) == axes(A,1) && axes(B,1) == 1:1 || throw(DimensionMismatch("transpose")) + check_transpose_axes(axes(B), (axes(A,1), axes(A,2))) copyto!(B, A) end function adjoint!(B::AbstractVector, A::AbstractMatrix) - axes(B,1) == axes(A,2) && axes(A,1) == 1:1 || throw(DimensionMismatch("transpose")) + check_transpose_axes((axes(B,1), axes(B,2)), axes(A)) ccopy!(B, A) end function adjoint!(B::AbstractMatrix, A::AbstractVector) - axes(B,2) == axes(A,1) && axes(B,1) == 1:1 || throw(DimensionMismatch("transpose")) + check_transpose_axes(axes(B), (axes(A,1), axes(A,2))) ccopy!(B, A) end const transposebaselength=64 function transpose_f!(f, B::AbstractMatrix, A::AbstractMatrix) inds = axes(A) - axes(B,1) == inds[2] && axes(B,2) == inds[1] || throw(DimensionMismatch(string(f))) + check_transpose_axes(axes(B), inds) m, n = length(inds[1]), length(inds[2]) if m*n<=4*transposebaselength diff --git a/stdlib/LinearAlgebra/test/adjtrans.jl b/stdlib/LinearAlgebra/test/adjtrans.jl index 2c533af37f912..1a66c7430723e 100644 --- a/stdlib/LinearAlgebra/test/adjtrans.jl +++ b/stdlib/LinearAlgebra/test/adjtrans.jl @@ -703,4 +703,14 @@ end @test B == At end +@testset "error message in transpose" begin + v = zeros(2) + A = zeros(1,1) + B = zeros(2,3) + for (t1, t2) in Any[(A, v), (v, A), (A, B)] + @test_throws "axes of the destination are incompatible with that of the source" transpose!(t1, t2) + @test_throws "axes of the destination are incompatible with that of the source" adjoint!(t1, t2) + end +end + end # module TestAdjointTranspose From 9353607102bf7643478c28439ac5be1be92bff7f Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Tue, 4 Jun 2024 21:06:14 -0300 Subject: [PATCH 12/71] Add boundscheck in bindingkey_eq to avoid OOB access due to data race (#54671) The race here is that svec might be replaced and a new binding introduced into the keyset while we hold a reference to the old svec, which led to a OOB access on the svec with the index a binding introduced at the same time. This now introduces a bounds check which will force taking the lock if we fail the lookup i.e we had a data race. Fixes https://github.com/JuliaLang/julia/issues/54285 --------- Co-authored-by: Jameson Nash (cherry picked from commit 20f03ddcefffaf5e42d9060144a6cc3aa1313dd4) --- src/module.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/module.c b/src/module.c index 44b89bc236afc..3aadb181b5e97 100644 --- a/src/module.c +++ b/src/module.c @@ -701,13 +701,15 @@ JL_DLLEXPORT int jl_binding_resolved_p(jl_module_t *m, jl_sym_t *var) static uint_t bindingkey_hash(size_t idx, jl_value_t *data) { - jl_binding_t *b = (jl_binding_t*)jl_svecref(data, idx); + jl_binding_t *b = (jl_binding_t*)jl_svecref(data, idx); // This must always happen inside the lock jl_sym_t *var = b->globalref->name; return var->hash; } static int bindingkey_eq(size_t idx, const void *var, jl_value_t *data, uint_t hv) { + if (idx >= jl_svec_len(data)) + return 0; // We got a OOB access, probably due to a data race jl_binding_t *b = (jl_binding_t*)jl_svecref(data, idx); jl_sym_t *name = b->globalref->name; return var == name; From 60cc3aba36d3de826535ad8fc6b522a8d87398bf Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Tue, 4 Jun 2024 22:17:38 -0400 Subject: [PATCH 13/71] make: Fix `sed` command for LLVM libraries with no symbol versioning (#54672) If the LLVM library was built without symbol versioning, previously this would return the `nm` output in its entirety, instead of correctly reporting "" as the LLVM symbol version. (cherry picked from commit debaa73a93f6829cab34dcf896284fff36e28cd8) --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 5f58c7fa57627..5e1d150e8b06b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -127,7 +127,7 @@ ifneq ($(USE_SYSTEM_LLVM),0) # USE_SYSTEM_LLVM != 0 CG_LLVMLINK += $(LLVM_LDFLAGS) $(shell $(LLVM_CONFIG_HOST) --libs --system-libs) LLVM_SHLIB_SYMBOL_VERSION := $(shell nm -D --with-symbol-versions $(shell $(LLVM_CONFIG_HOST) --libfiles --link-shared | awk '{print $1; exit}') | \ - grep _ZN4llvm3Any6TypeId | head -n 1 | sed -e 's/.*@//') + grep _ZN4llvm3Any6TypeId | head -n 1 | sed -ne 's/.*@//p') # HACK: llvm-config doesn't correctly point to shared libs on all platforms # https://github.com/JuliaLang/julia/issues/29981 From 77f4bc6712e34456b6959e3ff56fcc1dab585832 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Fri, 7 Jun 2024 14:06:03 +0530 Subject: [PATCH 14/71] LazyString in reinterpretarray error messages (#54704) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This should remove the dynamic dispatch flagged by JET, e.g. in ```julia julia> using JET julia> @report_opt reinterpret(Float64, [1.0im;;]) [ Info: tracking Base ┌ Warning: skipping (::Base.var"#thrownonint#375")(S::Type, T::Type, dim) @ Base reinterpretarray.jl:68 to avoid parsing too much code └ @ Revise ~/.julia/packages/Revise/bAgL0/src/packagedef.jl:1092 ┌ Warning: skipping (::Base.var"#show_bound#661")(io::IO, b) @ Base show.jl:2777 to avoid parsing too much code └ @ Revise ~/.julia/packages/Revise/bAgL0/src/packagedef.jl:1092 ┌ Warning: skipping (::Base.var"#show_bound#661")(io::IO, b) @ Base show.jl:2777 to avoid parsing too much code └ @ Revise ~/.julia/packages/Revise/bAgL0/src/packagedef.jl:1092 ═════ 32 possible errors found ═════ ┌ reinterpret(::Type{Float64}, a::Matrix{ComplexF64}) @ Base ./reinterpretarray.jl:88 │┌ (::Base.var"#thrownonint#375")(S::Type{ComplexF64}, T::Type{Float64}, dim::Int64) @ Base ./reinterpretarray.jl:70 ││┌ string(::String, ::Type{ComplexF64}, ::String, ::Type{Float64}, ::String, ::Int64, ::String) @ Base ./strings/io.jl:189 │││┌ print_to_string(::String, ::Type{ComplexF64}, ::String, ::Type{Float64}, ::String, ::Int64, ::String) @ Base ./strings/io.jl:148 ││││┌ print(io::IOBuffer, x::DataType) @ Base ./strings/io.jl:35 │││││┌ show(io::IOBuffer, x::DataType) @ Base ./show.jl:970 ││││││┌ _show_type(io::IOBuffer, x::Type) @ Base ./show.jl:975 │││││││┌ show_typealias(io::IOBuffer, x::Type) @ Base ./show.jl:810 ││││││││┌ make_typealias(x::Type) @ Base ./show.jl:620 │││││││││┌ modulesof!(s::Set{Module}, x::Type) @ Base ./show.jl:595 ││││││││││ runtime dispatch detected: Base.modulesof!(s::Set{Module}, %20::Any)::Any │││││││││└──────────────────── │││││││││┌ modulesof!(s::Set{Module}, x::Type) @ Base ./show.jl:596 ││││││││││ runtime dispatch detected: Base.modulesof!(s::Set{Module}, %34::Any)::Any │││││││││└──────────────────── │││││││││┌ modulesof!(s::Set{Module}, x::TypeVar) @ Base ./show.jl:589 ││││││││││ runtime dispatch detected: Base.modulesof!(s::Set{Module}, %1::Any)::Set{Module} │││││││││└──────────────────── │││││││┌ show_typealias(io::IOBuffer, x::Type) @ Base ./show.jl:813 ││││││││┌ show_typealias(io::IOBuffer, name::GlobalRef, x::Type, env::Core.SimpleVector, wheres::Vector{TypeVar}) @ Base ./show.jl:760 │││││││││┌ show_typeparams(io::IOContext{IOBuffer}, env::Core.SimpleVector, orig::Core.SimpleVector, wheres::Vector{TypeVar}) @ Base ./show.jl:724 ││││││││││┌ show(io::IOContext{IOBuffer}, tv::TypeVar) @ Base ./show.jl:2788 │││││││││││┌ (::Base.var"#show_bound#661")(io::IOContext{IOBuffer}, b::Any) @ Base ./show.jl:2780 ││││││││││││ runtime dispatch detected: show(io::IOContext{IOBuffer}, b::Any)::Any │││││││││││└──────────────────── │││││││││┌ show_typeparams(io::IOContext{IOBuffer}, env::Core.SimpleVector, orig::Core.SimpleVector, wheres::Vector{TypeVar}) @ Base ./show.jl:719 ││││││││││ runtime dispatch detected: show(io::IOContext{IOBuffer}, %252::Any)::Any │││││││││└──────────────────── │││││││││┌ show_typeparams(io::IOContext{IOBuffer}, env::Core.SimpleVector, orig::Core.SimpleVector, wheres::Vector{TypeVar}) @ Base ./show.jl:722 ││││││││││ runtime dispatch detected: show(io::IOContext{IOBuffer}, %313::Any)::Any │││││││││└──────────────────── │││││││││┌ show_typeparams(io::IOContext{IOBuffer}, env::Core.SimpleVector, orig::Core.SimpleVector, wheres::Vector{TypeVar}) @ Base ./show.jl:727 ││││││││││ runtime dispatch detected: show(io::IOContext{IOBuffer}, %191::Any)::Any │││││││││└──────────────────── ││││││┌ _show_type(io::IOBuffer, x::Type) @ Base ./show.jl:978 │││││││┌ show_datatype(io::IOBuffer, x::DataType) @ Base ./show.jl:1094 ││││││││┌ show_datatype(io::IOBuffer, x::DataType, wheres::Vector{TypeVar}) @ Base ./show.jl:1097 │││││││││┌ maybe_kws_nt(x::DataType) @ Base ./show.jl:1085 ││││││││││ runtime dispatch detected: eltype(%76::DataType)::Any │││││││││└──────────────────── ││││││││┌ show_datatype(io::IOBuffer, x::DataType, wheres::Vector{TypeVar}) @ Base ./show.jl:1186 │││││││││┌ show_typeparams(io::IOBuffer, env::Core.SimpleVector, orig::Core.SimpleVector, wheres::Vector{TypeVar}) @ Base ./show.jl:724 ││││││││││┌ show(io::IOBuffer, tv::TypeVar) @ Base ./show.jl:2788 │││││││││││┌ (::Base.var"#show_bound#661")(io::IOBuffer, b::Any) @ Base ./show.jl:2780 ││││││││││││ runtime dispatch detected: show(io::IOBuffer, b::Any)::Any │││││││││││└──────────────────── │││││││││┌ show_typeparams(io::IOBuffer, env::Core.SimpleVector, orig::Core.SimpleVector, wheres::Vector{TypeVar}) @ Base ./show.jl:719 ││││││││││ runtime dispatch detected: show(io::IOBuffer, %250::Any)::Any │││││││││└──────────────────── │││││││││┌ show_typeparams(io::IOBuffer, env::Core.SimpleVector, orig::Core.SimpleVector, wheres::Vector{TypeVar}) @ Base ./show.jl:722 ││││││││││ runtime dispatch detected: show(io::IOBuffer, %310::Any)::Any │││││││││└──────────────────── │││││││││┌ show_typeparams(io::IOBuffer, env::Core.SimpleVector, orig::Core.SimpleVector, wheres::Vector{TypeVar}) @ Base ./show.jl:727 ││││││││││ runtime dispatch detected: show(io::IOBuffer, %190::Any)::Any │││││││││└──────────────────── ││││││││┌ show_datatype(io::IOBuffer, x::DataType, wheres::Vector{TypeVar}) @ Base ./show.jl:1157 │││││││││ runtime dispatch detected: show(io::IOBuffer, %224::Any)::Any ││││││││└──────────────────── ││││││││┌ show_datatype(io::IOBuffer, x::DataType, wheres::Vector{TypeVar}) @ Base ./show.jl:1162 │││││││││ runtime dispatch detected: show(io::IOBuffer, %54::Any)::Any ││││││││└──────────────────── ││││││││┌ show_datatype(io::IOBuffer, x::DataType, wheres::Vector{TypeVar}) @ Base ./show.jl:1148 │││││││││ runtime dispatch detected: show(io::IOBuffer, %57::Any)::Any ││││││││└──────────────────── ││││││││┌ show_datatype(io::IOBuffer, x::DataType, wheres::Vector{TypeVar}) @ Base ./show.jl:1150 │││││││││ runtime dispatch detected: show(io::IOBuffer, %54::Any)::Any ││││││││└──────────────────── ││││││││┌ show_datatype(io::IOBuffer, x::DataType, wheres::Vector{TypeVar}) @ Base ./show.jl:1172 │││││││││ runtime dispatch detected: Base.show_at_namedtuple(io::IOBuffer, %329::Tuple, %328::DataType)::Any ││││││││└──────────────────── ││││││┌ _show_type(io::IOBuffer, x::Type) @ Base ./show.jl:981 │││││││┌ show_unionaliases(io::IOBuffer, x::Union) @ Base ./show.jl:901 ││││││││┌ make_typealiases(x::Union) @ Base ./show.jl:822 │││││││││┌ modulesof!(s::Set{Module}, x::Union) @ Base ./show.jl:595 ││││││││││ runtime dispatch detected: Base.modulesof!(s::Set{Module}, %3::Any)::Any │││││││││└──────────────────── │││││││││┌ modulesof!(s::Set{Module}, x::Union) @ Base ./show.jl:596 ││││││││││ runtime dispatch detected: Base.modulesof!(s::Set{Module}, %17::Any)::Any │││││││││└──────────────────── │││││││┌ show_unionaliases(io::IOBuffer, x::Union) @ Base ./show.jl:914 ││││││││ runtime dispatch detected: show(io::IOBuffer, %89::Any)::Any │││││││└──────────────────── │││││││┌ show_unionaliases(io::IOBuffer, x::Union) @ Base ./show.jl:920 ││││││││ runtime dispatch detected: Base.show_typealias(io::IOBuffer, %206::Any, x::Union, %204::Core.SimpleVector, %205::Vector{TypeVar})::Any │││││││└──────────────────── │││││││┌ show_unionaliases(io::IOBuffer, x::Union) @ Base ./show.jl:928 ││││││││ runtime dispatch detected: Base.show_typealias(io::IOBuffer, %269::Any, x::Union, %267::Core.SimpleVector, %268::Vector{TypeVar})::Any │││││││└──────────────────── ││││││┌ _show_type(io::IOBuffer, x::Type) @ Base ./show.jl:985 │││││││┌ show_delim_array(io::IOBuffer, itr::Vector{Any}, op::Char, delim::Char, cl::Char, delim_one::Bool) @ Base ./show.jl:1392 ││││││││┌ show_delim_array(io::IOBuffer, itr::Vector{Any}, op::Char, delim::Char, cl::Char, delim_one::Bool, i1::Int64, l::Int64) @ Base ./show.jl:1403 │││││││││ runtime dispatch detected: show(%3::IOContext{IOBuffer}, %52::Any)::Any ││││││││└──────────────────── ││││││┌ _show_type(io::IOBuffer, x::Type) @ Base ./show.jl:1012 │││││││┌ show_datatype(io::IOContext{IOBuffer}, x::DataType, wheres::Vector{TypeVar}) @ Base ./show.jl:1185 ││││││││┌ show_type_name(io::IOContext{IOBuffer}, tn::Core.TypeName) @ Base ./show.jl:1059 │││││││││ runtime dispatch detected: Base.isvisible(%29::Symbol, %86::Module, %80::Any)::Bool ││││││││└──────────────────── │││││││┌ show_datatype(io::IOContext{IOBuffer}, x::DataType, wheres::Vector{TypeVar}) @ Base ./show.jl:1157 ││││││││ runtime dispatch detected: show(io::IOContext{IOBuffer}, %227::Any)::Any │││││││└──────────────────── │││││││┌ show_datatype(io::IOContext{IOBuffer}, x::DataType, wheres::Vector{TypeVar}) @ Base ./show.jl:1162 ││││││││ runtime dispatch detected: show(io::IOContext{IOBuffer}, %55::Any)::Any │││││││└──────────────────── │││││││┌ show_datatype(io::IOContext{IOBuffer}, x::DataType, wheres::Vector{TypeVar}) @ Base ./show.jl:1148 ││││││││ runtime dispatch detected: show(io::IOContext{IOBuffer}, %58::Any)::Any │││││││└──────────────────── │││││││┌ show_datatype(io::IOContext{IOBuffer}, x::DataType, wheres::Vector{TypeVar}) @ Base ./show.jl:1150 ││││││││ runtime dispatch detected: show(io::IOContext{IOBuffer}, %55::Any)::Any │││││││└──────────────────── │││││││┌ show_datatype(io::IOContext{IOBuffer}, x::DataType, wheres::Vector{TypeVar}) @ Base ./show.jl:1172 ││││││││ runtime dispatch detected: Base.show_at_namedtuple(io::IOContext{IOBuffer}, %338::Tuple, %337::DataType)::Any │││││││└──────────────────── │││││││┌ show_datatype(io::IOContext{IOBuffer}, x::DataType, wheres::Vector{TypeVar}) @ Base ./show.jl:1180 ││││││││ runtime dispatch detected: Base.show_at_namedtuple(io::IOContext{IOBuffer}, %387::Tuple, %391::DataType)::Any │││││││└──────────────────── ││││││┌ _show_type(io::IOBuffer, x::Type) @ Base ./show.jl:1014 │││││││ runtime dispatch detected: show(%98::IOContext{IOBuffer}, %99::Any)::Any ││││││└──────────────────── │││││┌ show(io::IOBuffer, x::DataType) @ Base ./show.jl:970 ││││││ runtime dispatch detected: Base._show_type(io::IOBuffer, %1::Any)::Nothing │││││└──────────────────── ``` (cherry picked from commit b54c688a3aa01445028a9a6cd61dfcac05e9b512) --- base/reinterpretarray.jl | 31 ++++++++++++++++++------------- test/reinterpretarray.jl | 5 ++--- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/base/reinterpretarray.jl b/base/reinterpretarray.jl index 8b4025e6903cd..d74a043293a3a 100644 --- a/base/reinterpretarray.jl +++ b/base/reinterpretarray.jl @@ -13,15 +13,16 @@ struct ReinterpretArray{T,N,S,A<:AbstractArray{S},IsReshaped} <: AbstractArray{T function throwbits(S::Type, T::Type, U::Type) @noinline - throw(ArgumentError("cannot reinterpret `$(S)` as `$(T)`, type `$(U)` is not a bits type")) + throw(ArgumentError(LazyString("cannot reinterpret `", S, "` as `", T, "`, type `", U, "` is not a bits type"))) end function throwsize0(S::Type, T::Type, msg) @noinline - throw(ArgumentError("cannot reinterpret a zero-dimensional `$(S)` array to `$(T)` which is of a $msg size")) + throw(ArgumentError(LazyString("cannot reinterpret a zero-dimensional `", S, "` array to `", T, + "` which is of a ", msg, " size"))) end function throwsingleton(S::Type, T::Type) @noinline - throw(ArgumentError("cannot reinterpret a `$(S)` array to `$(T)` which is a singleton type")) + throw(ArgumentError(LazyString("cannot reinterpret a `", S, "` array to `", T, "` which is a singleton type"))) end global reinterpret @@ -67,14 +68,14 @@ struct ReinterpretArray{T,N,S,A<:AbstractArray{S},IsReshaped} <: AbstractArray{T function reinterpret(::Type{T}, a::A) where {T,N,S,A<:AbstractArray{S, N}} function thrownonint(S::Type, T::Type, dim) @noinline - throw(ArgumentError(""" - cannot reinterpret an `$(S)` array to `$(T)` whose first dimension has size `$(dim)`. - The resulting array would have non-integral first dimension. - """)) + throw(ArgumentError(LazyString( + "cannot reinterpret an `", S, "` array to `", T, "` whose first dimension has size `", dim, + "`. The resulting array would have a non-integral first dimension."))) end function throwaxes1(S::Type, T::Type, ax1) @noinline - throw(ArgumentError("cannot reinterpret a `$(S)` array to `$(T)` when the first axis is $ax1. Try reshaping first.")) + throw(ArgumentError(LazyString("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) @@ -99,15 +100,19 @@ struct ReinterpretArray{T,N,S,A<:AbstractArray{S},IsReshaped} <: AbstractArray{T function reinterpret(::typeof(reshape), ::Type{T}, a::A) where {T,S,A<:AbstractArray{S}} function throwintmult(S::Type, T::Type) @noinline - throw(ArgumentError("`reinterpret(reshape, T, a)` requires that one of `sizeof(T)` (got $(sizeof(T))) and `sizeof(eltype(a))` (got $(sizeof(S))) be an integer multiple of the other")) + throw(ArgumentError(LazyString("`reinterpret(reshape, T, a)` requires that one of `sizeof(T)` (got ", + sizeof(T), ") and `sizeof(eltype(a))` (got ", sizeof(S), ") be an integer multiple of the other"))) end function throwsize1(a::AbstractArray, T::Type) @noinline - throw(ArgumentError("`reinterpret(reshape, $T, a)` where `eltype(a)` is $(eltype(a)) requires that `axes(a, 1)` (got $(axes(a, 1))) be equal to 1:$(sizeof(T) ÷ sizeof(eltype(a))) (from the ratio of element sizes)")) + throw(ArgumentError(LazyString("`reinterpret(reshape, ", T, ", a)` where `eltype(a)` is ", eltype(a), + " requires that `axes(a, 1)` (got ", axes(a, 1), ") be equal to 1:", + sizeof(T) ÷ sizeof(eltype(a)), " (from the ratio of element sizes)"))) end function throwfromsingleton(S, T) @noinline - throw(ArgumentError("`reinterpret(reshape, $T, a)` where `eltype(a)` is $S requires that $T be a singleton type, since $S is one")) + throw(ArgumentError(LazyString("`reinterpret(reshape, ", T, ", a)` where `eltype(a)` is ", S, + " requires that ", T, " be a singleton type, since ", S, " is one"))) end isbitstype(T) || throwbits(S, T, T) isbitstype(S) || throwbits(S, T, S) @@ -851,8 +856,8 @@ end inpackedsize = packedsize(In) outpackedsize = packedsize(Out) inpackedsize == outpackedsize || - throw(ArgumentError("Packed sizes of types $Out and $In do not match; got $outpackedsize \ - and $inpackedsize, respectively.")) + throw(ArgumentError(LazyString("Packed sizes of types ", Out, " and ", In, + " do not match; got ", outpackedsize, " and ", inpackedsize, ", respectively."))) in = Ref{In}(x) out = Ref{Out}() if struct_subpadding(Out, In) diff --git a/test/reinterpretarray.jl b/test/reinterpretarray.jl index 46ecbf6d06723..e6381329e4ec6 100644 --- a/test/reinterpretarray.jl +++ b/test/reinterpretarray.jl @@ -40,9 +40,8 @@ end @test_throws ArgumentError("cannot reinterpret `Vector{Int32}` as `Int32`, type `Vector{Int32}` is not a bits type") reinterpret(Int32, Av) @test_throws ArgumentError("cannot reinterpret a zero-dimensional `Int64` array to `Int32` which is of a different size") reinterpret(Int32, reshape([Int64(0)])) @test_throws ArgumentError("cannot reinterpret a zero-dimensional `Int32` array to `Int64` which is of a different size") reinterpret(Int64, reshape([Int32(0)])) -@test_throws ArgumentError("""cannot reinterpret an `$Int` array to `Tuple{$Int, $Int}` whose first dimension has size `5`. - The resulting array would have non-integral first dimension. - """) reinterpret(Tuple{Int,Int}, [1,2,3,4,5]) +@test_throws ArgumentError("cannot reinterpret an `$Int` array to `Tuple{$Int, $Int}` whose first dimension has size `5`."* + " The resulting array would have a non-integral first dimension.") reinterpret(Tuple{Int,Int}, [1,2,3,4,5]) @test_throws ArgumentError("`reinterpret(reshape, Complex{Int64}, a)` where `eltype(a)` is Int64 requires that `axes(a, 1)` (got Base.OneTo(4)) be equal to 1:2 (from the ratio of element sizes)") reinterpret(reshape, Complex{Int64}, A) @test_throws ArgumentError("`reinterpret(reshape, T, a)` requires that one of `sizeof(T)` (got 24) and `sizeof(eltype(a))` (got 16) be an integer multiple of the other") reinterpret(reshape, NTuple{3, Int64}, B) From 05f40c331205be002b04a13fe85a8c99815796f6 Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Thu, 23 May 2024 15:30:39 -0500 Subject: [PATCH 15/71] make `AbstractPipe` public (#54191) (cherry picked from commit cd41d6f8ef02604042a37f0889b70e2f055c9d70) --- base/exports.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/base/exports.jl b/base/exports.jl index 63e5b43216cca..6e09bfc4cc3ea 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -1083,6 +1083,7 @@ public # Types AbstractLock, + AbstractPipe, AsyncCondition, CodeUnits, Event, From 207ada24f46d6420785bb38be83461a4a2730252 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Fri, 31 May 2024 15:39:13 +0530 Subject: [PATCH 16/71] Aggressive constprop in trevc! to stabilize triangular eigvec (#54635) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We may use aggressive constprop in `trevc!` to eliminate branches, which makes the return type of `eigvec(::UpperTriangular)` concretely inferred. ```julia julia> @inferred eigvecs(UpperTriangular([1 0; 0 1])) 2×2 Matrix{Float32}: 1.0 -0.0 0.0 1.0 ``` (cherry picked from commit ea8b4aff3b5dcf78ce7a4fbe17c46ac8a51a7643) --- stdlib/LinearAlgebra/src/lapack.jl | 2 +- stdlib/LinearAlgebra/test/triangular.jl | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/stdlib/LinearAlgebra/src/lapack.jl b/stdlib/LinearAlgebra/src/lapack.jl index fc8d8d67e07b4..254425f8cd2bf 100644 --- a/stdlib/LinearAlgebra/src/lapack.jl +++ b/stdlib/LinearAlgebra/src/lapack.jl @@ -3701,7 +3701,7 @@ for (trcon, trevc, trrfs, elty) in # LOGICAL SELECT( * ) # DOUBLE PRECISION T( LDT, * ), VL( LDVL, * ), VR( LDVR, * ), #$ WORK( * ) - function trevc!(side::AbstractChar, howmny::AbstractChar, select::AbstractVector{BlasInt}, T::AbstractMatrix{$elty}, + Base.@constprop :aggressive function trevc!(side::AbstractChar, howmny::AbstractChar, select::AbstractVector{BlasInt}, T::AbstractMatrix{$elty}, VL::AbstractMatrix{$elty} = similar(T), VR::AbstractMatrix{$elty} = similar(T)) require_one_based_indexing(select, T, VL, VR) diff --git a/stdlib/LinearAlgebra/test/triangular.jl b/stdlib/LinearAlgebra/test/triangular.jl index 97bf756044c66..d4dff286c7997 100644 --- a/stdlib/LinearAlgebra/test/triangular.jl +++ b/stdlib/LinearAlgebra/test/triangular.jl @@ -1050,4 +1050,10 @@ end end end +@testset "type-stable eigvecs" begin + D = Float64[1 0; 0 2] + V = @inferred eigvecs(UpperTriangular(D)) + @test V == Diagonal([1, 1]) +end + end # module TestTriangular From 26053dcec28d96d53b92756e52c0e537ab482cdd Mon Sep 17 00:00:00 2001 From: Lilith Orion Hafner Date: Fri, 31 May 2024 15:35:00 -0500 Subject: [PATCH 17/71] make elsize public (#54554) It's part of the strided arrays API https://docs.julialang.org/en/v1/manual/interfaces/#man-interface-strided-arrays (cherry picked from commit 48964736fba36c8289749cc9a575b41fdda87dc8) --- base/exports.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/base/exports.jl b/base/exports.jl index 6e09bfc4cc3ea..ffbe0dd6830ba 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -1119,6 +1119,7 @@ public split_rest, tail, checked_length, + elsize, # Loading DL_LOAD_PATH, From baf763de64bb39bfa452cff7c44bbeec4564aee4 Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Tue, 4 Jun 2024 09:33:19 +0200 Subject: [PATCH 18/71] fix missing uuid check on extension when finding the location of an extension (#54658) in stacked environments with name collisions of extensions, this could compute the path for the wrong extension Fixes https://github.com/JuliaLang/Pkg.jl/issues/3906 (cherry picked from commit 5034e878e49b7cfd5f9d3e35407082dd23ab9cd9) --- base/loading.jl | 14 ++++++-------- test/loading.jl | 13 +++++++++++++ .../Extensions/ExtNameCollision_A/Project.toml | 9 +++++++++ .../Extensions/ExtNameCollision_A/ext/REPLExt.jl | 0 .../ExtNameCollision_A/src/ExtNameCollision_A.jl | 5 +++++ .../Extensions/ExtNameCollision_B/Project.toml | 9 +++++++++ .../Extensions/ExtNameCollision_B/ext/REPLExt.jl | 0 .../ExtNameCollision_B/src/ExtNameCollision_B.jl | 5 +++++ 8 files changed, 47 insertions(+), 8 deletions(-) create mode 100644 test/project/Extensions/ExtNameCollision_A/Project.toml create mode 100644 test/project/Extensions/ExtNameCollision_A/ext/REPLExt.jl create mode 100644 test/project/Extensions/ExtNameCollision_A/src/ExtNameCollision_A.jl create mode 100644 test/project/Extensions/ExtNameCollision_B/Project.toml create mode 100644 test/project/Extensions/ExtNameCollision_B/ext/REPLExt.jl create mode 100644 test/project/Extensions/ExtNameCollision_B/src/ExtNameCollision_B.jl diff --git a/base/loading.jl b/base/loading.jl index 8c5767df811d6..90302ef24c8fe 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -669,7 +669,7 @@ function manifest_uuid_path(env::String, pkg::PkgId)::Union{Nothing,String,Missi # if `pkg` matches the project, return the project itself return project_file_path(project_file) end - mby_ext = project_file_ext_path(project_file, pkg.name) + mby_ext = project_file_ext_path(project_file, pkg) mby_ext === nothing || return mby_ext # look for manifest file and `where` stanza return explicit_manifest_uuid_path(project_file, pkg) @@ -684,7 +684,7 @@ function manifest_uuid_path(env::String, pkg::PkgId)::Union{Nothing,String,Missi if parent_project_file !== nothing parentproj = project_file_name_uuid(parent_project_file, parentid.name) if parentproj == parentid - mby_ext = project_file_ext_path(parent_project_file, pkg.name) + mby_ext = project_file_ext_path(parent_project_file, pkg) mby_ext === nothing || return mby_ext end end @@ -700,13 +700,13 @@ function find_ext_path(project_path::String, extname::String) return joinpath(project_path, "ext", extname * ".jl") end -function project_file_ext_path(project_file::String, name::String) +function project_file_ext_path(project_file::String, ext::PkgId) d = parsed_toml(project_file) p = project_file_path(project_file) exts = get(d, "extensions", nothing)::Union{Dict{String, Any}, Nothing} if exts !== nothing - if name in keys(exts) - return find_ext_path(p, name) + if ext.name in keys(exts) && ext.uuid == uuid5(UUID(d["uuid"]::String), ext.name) + return find_ext_path(p, ext.name) end end return nothing @@ -792,9 +792,7 @@ function implicit_env_project_file_extension(dir::String, ext::PkgId) for pkg in readdir(dir; join=true) project_file = env_project_file(pkg) project_file isa String || continue - proj = project_file_name_uuid(project_file, "") - uuid5(proj.uuid, ext.name) == ext.uuid || continue - path = project_file_ext_path(project_file, ext.name) + path = project_file_ext_path(project_file, ext) if path !== nothing return path, project_file end diff --git a/test/loading.jl b/test/loading.jl index 53e05a4a76bbc..cda7139e2d875 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -1544,3 +1544,16 @@ end @test_throws SystemError("opening file $(repr(file))") include(file) end end + +@testset "extension path computation name collision" begin + old_load_path = copy(LOAD_PATH) + try + empty!(LOAD_PATH) + push!(LOAD_PATH, joinpath(@__DIR__, "project", "Extensions", "ExtNameCollision_A")) + push!(LOAD_PATH, joinpath(@__DIR__, "project", "Extensions", "ExtNameCollision_B")) + ext_B = Base.PkgId(Base.uuid5(Base.identify_package("ExtNameCollision_B").uuid, "REPLExt"), "REPLExt") + @test Base.locate_package(ext_B) == joinpath(@__DIR__, "project", "Extensions", "ExtNameCollision_B", "ext", "REPLExt.jl") + finally + copy!(LOAD_PATH, old_load_path) + end +end diff --git a/test/project/Extensions/ExtNameCollision_A/Project.toml b/test/project/Extensions/ExtNameCollision_A/Project.toml new file mode 100644 index 0000000000000..f4cc37786f508 --- /dev/null +++ b/test/project/Extensions/ExtNameCollision_A/Project.toml @@ -0,0 +1,9 @@ +name = "ExtNameCollision_A" +uuid = "9f48de98-8f56-4937-aa32-2a5530882eaa" +version = "0.1.0" + +[weakdeps] +REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" + +[extensions] +REPLExt = "REPL" diff --git a/test/project/Extensions/ExtNameCollision_A/ext/REPLExt.jl b/test/project/Extensions/ExtNameCollision_A/ext/REPLExt.jl new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/test/project/Extensions/ExtNameCollision_A/src/ExtNameCollision_A.jl b/test/project/Extensions/ExtNameCollision_A/src/ExtNameCollision_A.jl new file mode 100644 index 0000000000000..2f47a862dd9c5 --- /dev/null +++ b/test/project/Extensions/ExtNameCollision_A/src/ExtNameCollision_A.jl @@ -0,0 +1,5 @@ +module ExtNameCollision_A + +greet() = print("Hello World!") + +end # module ExtNameCollision_A diff --git a/test/project/Extensions/ExtNameCollision_B/Project.toml b/test/project/Extensions/ExtNameCollision_B/Project.toml new file mode 100644 index 0000000000000..ac52d64a82a7c --- /dev/null +++ b/test/project/Extensions/ExtNameCollision_B/Project.toml @@ -0,0 +1,9 @@ +name = "ExtNameCollision_B" +uuid = "597d654f-44d8-4443-9b1e-1f2f4b45906f" +version = "0.1.0" + +[weakdeps] +REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" + +[extensions] +REPLExt = "REPL" diff --git a/test/project/Extensions/ExtNameCollision_B/ext/REPLExt.jl b/test/project/Extensions/ExtNameCollision_B/ext/REPLExt.jl new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/test/project/Extensions/ExtNameCollision_B/src/ExtNameCollision_B.jl b/test/project/Extensions/ExtNameCollision_B/src/ExtNameCollision_B.jl new file mode 100644 index 0000000000000..e7665982a79b3 --- /dev/null +++ b/test/project/Extensions/ExtNameCollision_B/src/ExtNameCollision_B.jl @@ -0,0 +1,5 @@ +module ExtNameCollision_B + +greet() = print("Hello World!") + +end # module ExtNameCollision_B From ddd5c287364920ac685c665ab8a6ab0377d200da Mon Sep 17 00:00:00 2001 From: Frames White Date: Tue, 28 May 2024 19:04:01 +0800 Subject: [PATCH 19/71] Rename at-scriptdir project argument to at-script and search upwards for Project.toml (#53356) Closes https://github.com/JuliaLang/julia/issues/53352 I also noticed it wasn't mentioned in the NEWs.md and so I added it . Reconciling the relative path behavior, with the search upwads behavour requested in #53352 is a thing. (cherry picked from commit a60f22eeee30c84bac4744a97caf105e4cfb1175) --- NEWS.md | 1 + base/initdefs.jl | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index adccd7b825a13..927fa4ea5fb81 100644 --- a/NEWS.md +++ b/NEWS.md @@ -55,6 +55,7 @@ Command-line option changes difference between defining a `main` function and executing the code directly at the end of the script ([#50974]). * The `--compiled-modules` and `--pkgimages` flags can now be set to `existing`, which will cause Julia to consider loading existing cache files, but not to create new ones ([#50586], [#52573]). +* The `--project` argument now accepts `@script` to give a path to a directory with a Project.toml relative to the passed script file. `--project=@script/foo` for the `foo` subdirectory. If no path is given after (i.e. `--project=@script`) then (like `--project=@.`) the directory and its parents are searched for a Project.toml ([#50864] and [#53352]) Multi-threading changes ----------------------- diff --git a/base/initdefs.jl b/base/initdefs.jl index 56c2c0c587272..96bdc7957bcca 100644 --- a/base/initdefs.jl +++ b/base/initdefs.jl @@ -272,7 +272,7 @@ function load_path_expand(env::AbstractString)::Union{String, Nothing} env == "@" && return active_project(false) env == "@." && return current_project() env == "@stdlib" && return Sys.STDLIB - if startswith(env, "@scriptdir") + if startswith(env, "@script") if @isdefined(PROGRAM_FILE) dir = dirname(PROGRAM_FILE) else @@ -283,7 +283,12 @@ function load_path_expand(env::AbstractString)::Union{String, Nothing} end dir = dirname(ARGS[1]) end - return abspath(replace(env, "@scriptdir" => dir)) + if env == "@script" # complete match, not startswith, so search upwards + return current_project(dir) + else + # starts with, so assume relative path is after + return abspath(replace(env, "@script" => dir)) + end end env = replace(env, '#' => VERSION.major, count=1) env = replace(env, '#' => VERSION.minor, count=1) From 4bc3d0701f760d6251ac178c53c92e5fc457b9d3 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Wed, 22 May 2024 20:50:33 +0200 Subject: [PATCH 20/71] Fixes for bitcast bugs with LLVM 17 / opaque pointers (#54548) Skip setName on folded inputs, and ensure the correct pointer address space is used. (cherry picked from commit baca8baea0e62f06530a9640153d793a69eba7f7) --- src/intrinsics.cpp | 15 +++++++++++++-- test/intrinsics.jl | 13 +++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index 3a98850ddca68..aacf1e495beff 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -628,10 +628,21 @@ static jl_cgval_t generic_bitcast(jl_codectx_t &ctx, ArrayRef argv) setName(ctx.emission_context, vx, "bitcast_coercion"); } else if (!vxt->isPointerTy() && llvmt->isPointerTy()) { vx = emit_inttoptr(ctx, vx, llvmt); - setName(ctx.emission_context, vx, "bitcast_coercion"); + } else if (vxt->isPointerTy() && llvmt->isPointerTy()) { + // emit_bitcast preserves the origin address space, which we can't have here + #if JL_LLVM_VERSION >= 170000 + vx = ctx.builder.CreateAddrSpaceCast(vx, llvmt); + #else + vx = ctx.builder.CreatePointerBitCastOrAddrSpaceCast(vx, llvmt); + #endif + if (isa(vx) && !vx->hasName()) + // cast may have been folded + setName(ctx.emission_context, vx, "bitcast_coercion"); } else { vx = emit_bitcast(ctx, vx, llvmt); - setName(ctx.emission_context, vx, "bitcast_coercion"); + if (isa(vx) && !vx->hasName()) + // emit_bitcast may undo another bitcast + setName(ctx.emission_context, vx, "bitcast_coercion"); } } diff --git a/test/intrinsics.jl b/test/intrinsics.jl index 8e4ab932f5eb6..dc9ce1ecd0039 100644 --- a/test/intrinsics.jl +++ b/test/intrinsics.jl @@ -345,3 +345,16 @@ Base.show(io::IO, a::IntWrap) = print(io, "IntWrap(", a.x, ")") @test r2 isa IntWrap && r2.x === 103 === r[].x && r2 !== r[] end end)() + +@testset "issue #54548" begin + @inline passthrough(ptr::Core.LLVMPtr{T,A}) where {T,A} = Base.llvmcall((""" + define ptr addrspace(1) @entry(ptr addrspace(1) %0) #0 { + entry: + ret ptr addrspace(1) %0 + } + + attributes #0 = { alwaysinline }""", "entry"), + Core.LLVMPtr{T,A}, Tuple{Core.LLVMPtr{T,A}}, ptr) + f(gws) = passthrough(Core.bitcast(Core.LLVMPtr{UInt32,1}, gws)) + f(C_NULL) +end From 6b4b5efe97b7addfce1f71949aea69605daa381d Mon Sep 17 00:00:00 2001 From: Carsten Bauer Date: Sat, 25 May 2024 16:16:20 +0200 Subject: [PATCH 21/71] Add `jl_getaffinity` and `jl_setaffinity` (#53402) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR adds two functions `jl_getaffinity` and `jl_setaffinity` to the runtime, which are slim wrappers around `uv_thread_getaffinity` and `uv_thread_setaffinity` and can be used to set the affinity of Julia threads. This will * simplify thread pinning (ThreadPinning.jl currently pins threads by spawning tasks that run the necessary ccalls) and * enable users to also pin GC threads (or, more generally, all Julia threads). **Example:** ```julia bauerc@n2lcn0146 julia git:(cb/affinity) ➜ ./julia -q --startup-file=no --threads 2,3 --gcthreads 4,1 julia> cpumasksize = @ccall uv_cpumask_size()::Cint 1024 julia> mask = zeros(Cchar, cpumasksize); julia> jl_getaffinity(tid, mask, cpumasksize) = ccall(:jl_getaffinity, Int32, (Int16, Ptr{Cchar}, Int32), tid, mask, cpumasksize) jl_getaffinity (generic function with 1 method) julia> jl_getaffinity(1, mask, cpumasksize) 0 julia> print(mask[1:Sys.CPU_THREADS]) Int8[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] julia> mask[1] = 0; julia> jl_setaffinity(tid, mask, cpumasksize) = ccall(:jl_setaffinity, Int32, (Int16, Ptr{Cchar}, Int32), tid, mask, cpumasksize) jl_setaffinity (generic function with 1 method) julia> jl_setaffinity(1, mask, cpumasksize) 0 julia> fill!(mask, 0); julia> jl_getaffinity(1, mask, cpumasksize) 0 julia> print(mask[1:Sys.CPU_THREADS]) Int8[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] ``` (cc @vchuravy, @gbaraldi) Would be great to get this into 1.11 (despite feature freeze) because otherwise we won't be able to pin GC threads until 1.12 (likely not until the end of the year). Closes #53073 --------- Co-authored-by: Valentin Churavy Co-authored-by: Dilum Aluthge (cherry picked from commit 065aeb6c27ae2a0d5dc616f567053be4b028a565) --- src/jl_exported_funcs.inc | 2 ++ src/julia_threads.h | 3 +++ src/threading.c | 46 +++++++++++++++++++++++++++++++++++++++ test/threads.jl | 12 ++++++++++ 4 files changed, 63 insertions(+) diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index db23bcbc33f6c..5ecff60813a4f 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -194,6 +194,7 @@ XX(jl_generating_output) \ XX(jl_generic_function_def) \ XX(jl_gensym) \ + XX(jl_getaffinity) \ XX(jl_getallocationgranularity) \ XX(jl_getnameinfo) \ XX(jl_getpagesize) \ @@ -407,6 +408,7 @@ XX(jl_safepoint_suspend_thread) \ XX(jl_safepoint_resume_thread) \ XX(jl_SC_CLK_TCK) \ + XX(jl_setaffinity) \ XX(jl_set_ARGS) \ XX(jl_set_const) \ XX(jl_set_errno) \ diff --git a/src/julia_threads.h b/src/julia_threads.h index 3f8f5391919b4..e69e7fae9b4d9 100644 --- a/src/julia_threads.h +++ b/src/julia_threads.h @@ -363,6 +363,9 @@ JL_DLLEXPORT int8_t jl_gc_is_in_finalizer(void) JL_NOTSAFEPOINT; JL_DLLEXPORT void jl_wakeup_thread(int16_t tid); +JL_DLLEXPORT int jl_getaffinity(int16_t tid, char *mask, int cpumasksize); +JL_DLLEXPORT int jl_setaffinity(int16_t tid, char *mask, int cpumasksize); + #ifdef __cplusplus } #endif diff --git a/src/threading.c b/src/threading.c index 4b1c622df2c07..dfc75b0af581e 100644 --- a/src/threading.c +++ b/src/threading.c @@ -960,6 +960,52 @@ JL_DLLEXPORT int jl_alignment(size_t sz) return jl_gc_alignment(sz); } +// Return values: +// 0 == success +// 1 == invalid thread id provided +// 2 == ptls2 was NULL +// <0 == uv_thread_getaffinity exit code +JL_DLLEXPORT int jl_getaffinity(int16_t tid, char *mask, int cpumasksize) { + int nthreads = jl_atomic_load_acquire(&jl_n_threads); + if (tid < 0 || tid >= nthreads) + return 1; + + // TODO: use correct lock. system_id is only legal if the thread is alive. + jl_ptls_t ptls2 = jl_atomic_load_relaxed(&jl_all_tls_states)[tid]; + if (ptls2 == NULL) + return 2; + uv_thread_t uvtid = ptls2->system_id; + + int ret_uv = uv_thread_getaffinity(&uvtid, mask, cpumasksize); + if (ret_uv != 0) + return ret_uv; + + return 0; // success +} + +// Return values: +// 0 == success +// 1 == invalid thread id provided +// 2 == ptls2 was NULL +// <0 == uv_thread_getaffinity exit code +JL_DLLEXPORT int jl_setaffinity(int16_t tid, char *mask, int cpumasksize) { + int nthreads = jl_atomic_load_acquire(&jl_n_threads); + if (tid < 0 || tid >= nthreads) + return 1; + + // TODO: use correct lock. system_id is only legal if the thread is alive. + jl_ptls_t ptls2 = jl_atomic_load_relaxed(&jl_all_tls_states)[tid]; + if (ptls2 == NULL) + return 2; + uv_thread_t uvtid = ptls2->system_id; + + int ret_uv = uv_thread_setaffinity(&uvtid, mask, NULL, cpumasksize); + if (ret_uv != 0) + return ret_uv; + + return 0; // success +} + #ifdef __cplusplus } #endif diff --git a/test/threads.jl b/test/threads.jl index 307742a4c292b..ab35c327196ef 100644 --- a/test/threads.jl +++ b/test/threads.jl @@ -341,3 +341,15 @@ end @testset "Base.Threads docstrings" begin @test isempty(Docs.undocumented_names(Threads)) end + +@testset "jl_*affinity" begin + cpumasksize = @ccall uv_cpumask_size()::Cint + if !Sys.iswindows() && cpumasksize > 0 # otherwise affinities are not supported on the platform (UV_ENOTSUP) + mask = zeros(Cchar, cpumasksize); + jl_getaffinity = (tid, mask, cpumasksize) -> ccall(:jl_getaffinity, Int32, (Int16, Ptr{Cchar}, Int32), tid, mask, cpumasksize) + jl_setaffinity = (tid, mask, cpumasksize) -> ccall(:jl_setaffinity, Int32, (Int16, Ptr{Cchar}, Int32), tid, mask, cpumasksize) + @test jl_getaffinity(1, mask, cpumasksize) == 0 + fill!(mask, 1) + @test jl_setaffinity(1, mask, cpumasksize) == 0 + end +end From 90acf2a52d910b5b89f798767efdda46e6f20fab Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Tue, 28 May 2024 22:12:21 -0300 Subject: [PATCH 22/71] Set storage class of julia globals to dllimport on windows to avoid auto-import weirdness (#54586) Forward port of #54572 (cherry picked from commit efc35a69c5ef9515d75bf7c6f157477f0ac758ff) --- Make.inc | 2 +- base/linking.jl | 2 +- cli/Makefile | 3 +-- src/aotcompile.cpp | 17 +++++++++++------ src/codegen.cpp | 8 ++++---- src/staticdata.c | 6 +++--- sysimage.mk | 2 +- 7 files changed, 22 insertions(+), 18 deletions(-) diff --git a/Make.inc b/Make.inc index 2a23bd3d37b51..e0bc8ba224a6d 100644 --- a/Make.inc +++ b/Make.inc @@ -1393,7 +1393,7 @@ ifeq ($(OS), WINNT) HAVE_SSP := 1 OSLIBS += -Wl,--export-all-symbols -Wl,--version-script=$(BUILDROOT)/src/julia.expmap \ $(NO_WHOLE_ARCHIVE) -lpsapi -lkernel32 -lws2_32 -liphlpapi -lwinmm -ldbghelp -luserenv -lsecur32 -latomic -lole32 -JLDFLAGS += -Wl,--stack,8388608 +JLDFLAGS += -Wl,--stack,8388608 --disable-auto-import --disable-runtime-pseudo-reloc ifeq ($(ARCH),i686) JLDFLAGS += -Wl,--large-address-aware endif diff --git a/base/linking.jl b/base/linking.jl index 2d68ea730c0fb..4bf0198962346 100644 --- a/base/linking.jl +++ b/base/linking.jl @@ -110,7 +110,7 @@ function ld() # LLD supports mingw style linking flavor = "gnu" m = Sys.ARCH == :x86_64 ? "i386pep" : "i386pe" - default_args = `-m $m -Bdynamic --enable-auto-image-base --allow-multiple-definition` + default_args = `-m $m -Bdynamic --enable-auto-image-base --allow-multiple-definition --disable-auto-import --disable-runtime-pseudo-reloc` elseif Sys.isapple() flavor = "darwin" arch = Sys.ARCH == :aarch64 ? :arm64 : Sys.ARCH diff --git a/cli/Makefile b/cli/Makefile index 4e32c53b9a6f0..8b46067820d85 100644 --- a/cli/Makefile +++ b/cli/Makefile @@ -17,8 +17,7 @@ LOADER_CFLAGS += -DGLIBCXX_LEAST_VERSION_SYMBOL=\"$(shell echo "$(CSL_NEXT_GLIBC endif ifeq ($(OS),WINNT) -LOADER_LDFLAGS += -municode -mconsole -nostdlib --disable-auto-import \ - --disable-runtime-pseudo-reloc -lntdll -lkernel32 -lpsapi +LOADER_LDFLAGS += -municode -mconsole -nostdlib -lntdll -lkernel32 -lpsapi else ifeq ($(OS),Linux) # textoff and notext are aliases to the same option which suppress the TEXTREL warning for i686 LOADER_LDFLAGS += -Wl,--no-as-needed -ldl -lpthread -rdynamic -lc -Wl,--as-needed -Wl,-z,notext diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index c84ef4e756cb0..97734852f9479 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -1242,6 +1242,8 @@ static void materializePreserved(Module &M, Partition &partition) { GV.setInitializer(nullptr); GV.setLinkage(GlobalValue::ExternalLinkage); GV.setVisibility(GlobalValue::HiddenVisibility); + if (GV.getDLLStorageClass() != GlobalValue::DLLStorageClassTypes::DefaultStorageClass) + continue; // Don't mess with exported or imported globals GV.setDSOLocal(true); } @@ -1773,6 +1775,7 @@ void jl_dump_native_impl(void *native_code, if (jl_small_typeof_copy) { jl_small_typeof_copy->setVisibility(GlobalValue::HiddenVisibility); jl_small_typeof_copy->setDSOLocal(true); + jl_small_typeof_copy->setDLLStorageClass(GlobalValue::DLLStorageClassTypes::DefaultStorageClass); } } @@ -1804,16 +1807,18 @@ void jl_dump_native_impl(void *native_code, // reflect the address of the jl_RTLD_DEFAULT_handle variable // back to the caller, so that we can check for consistency issues GlobalValue *jlRTLD_DEFAULT_var = jl_emit_RTLD_DEFAULT_var(&metadataM); - addComdat(new GlobalVariable(metadataM, - jlRTLD_DEFAULT_var->getType(), - true, - GlobalVariable::ExternalLinkage, - jlRTLD_DEFAULT_var, - "jl_RTLD_DEFAULT_handle_pointer"), TheTriple); Type *T_size = DL.getIntPtrType(Context); Type *T_psize = T_size->getPointerTo(); + auto FT = FunctionType::get(Type::getInt8Ty(Context)->getPointerTo()->getPointerTo(), {}, false); + auto F = Function::Create(FT, Function::ExternalLinkage, "get_jl_RTLD_DEFAULT_handle_addr", metadataM); + llvm::IRBuilder<> builder(BasicBlock::Create(Context, "top", F)); + builder.CreateRet(jlRTLD_DEFAULT_var); + F->setLinkage(GlobalValue::ExternalLinkage); + if (TheTriple.isOSBinFormatCOFF()) + F->setDLLStorageClass(GlobalValue::DLLStorageClassTypes::DLLExportStorageClass); + if (TheTriple.isOSWindows()) { // Windows expect that the function `_DllMainStartup` is present in an dll. // Normal compilers use something like Zig's crtdll.c instead we provide a diff --git a/src/codegen.cpp b/src/codegen.cpp index cb1957abf9e04..0e874e8d44be8 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -536,9 +536,12 @@ struct JuliaVariable { if (GlobalValue *V = m->getNamedValue(name)) return cast(V); auto T_size = m->getDataLayout().getIntPtrType(m->getContext()); - return new GlobalVariable(*m, _type(T_size), + auto var = new GlobalVariable(*m, _type(T_size), isconst, GlobalVariable::ExternalLinkage, NULL, name); + if (Triple(m->getTargetTriple()).isOSWindows()) + var->setDLLStorageClass(GlobalValue::DLLStorageClassTypes::DLLImportStorageClass); // Cross-library imports must be explicit for COFF (Windows) + return var; } GlobalVariable *realize(jl_codectx_t &ctx); }; @@ -2129,9 +2132,6 @@ static inline GlobalVariable *prepare_global_in(Module *M, GlobalVariable *G) proto->setInitializer(G->getInitializer()); } proto->copyAttributesFrom(G); - // DLLImport only needs to be set for the shadow module - // it just gets annoying in the JIT - proto->setDLLStorageClass(GlobalValue::DefaultStorageClass); return proto; } return cast(local); diff --git a/src/staticdata.c b/src/staticdata.c index 261042b775c14..ed11bcfc15171 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -3042,10 +3042,10 @@ JL_DLLEXPORT void jl_preload_sysimg_so(const char *fname) // Allow passing in a module handle directly, rather than a path JL_DLLEXPORT void jl_set_sysimg_so(void *handle) { - void* *jl_RTLD_DEFAULT_handle_pointer; + void** (*get_jl_RTLD_DEFAULT_handle_addr)(void) = NULL; if (handle != jl_RTLD_DEFAULT_handle) { - int symbol_found = jl_dlsym(handle, "jl_RTLD_DEFAULT_handle_pointer", (void **)&jl_RTLD_DEFAULT_handle_pointer, 0); - if (!symbol_found || (void*)&jl_RTLD_DEFAULT_handle != *jl_RTLD_DEFAULT_handle_pointer) + int symbol_found = jl_dlsym(handle, "get_jl_RTLD_DEFAULT_handle_addr", (void **)&get_jl_RTLD_DEFAULT_handle_addr, 0); + if (!symbol_found || (void*)&jl_RTLD_DEFAULT_handle != (get_jl_RTLD_DEFAULT_handle_addr())) jl_error("System image file failed consistency check: maybe opened the wrong version?"); } if (jl_options.cpu_target == NULL) diff --git a/sysimage.mk b/sysimage.mk index b5f47be0160a4..6aa972ee1ea0a 100644 --- a/sysimage.mk +++ b/sysimage.mk @@ -17,7 +17,7 @@ $(build_private_libdir)/%.$(SHLIB_EXT): $(build_private_libdir)/%-o.a @$(call PRINT_LINK, $(CXX) $(LDFLAGS) -shared $(fPIC) -L$(build_private_libdir) -L$(build_libdir) -L$(build_shlibdir) -o $@ \ $(WHOLE_ARCHIVE) $< $(NO_WHOLE_ARCHIVE) \ $(if $(findstring -debug,$(notdir $@)),-ljulia-internal-debug -ljulia-debug,-ljulia-internal -ljulia) \ - $$([ $(OS) = WINNT ] && echo '' -lssp)) + $$([ $(OS) = WINNT ] && echo '' -lssp --disable-auto-import --disable-runtime-pseudo-reloc)) @$(INSTALL_NAME_CMD)$(notdir $@) $@ @$(DSYMUTIL) $@ From 45b38b3c11721bda4168198da36d0607852abcc8 Mon Sep 17 00:00:00 2001 From: Matt Bauman Date: Wed, 5 Jun 2024 04:22:47 -0400 Subject: [PATCH 23/71] more precise aliasing checks for SubArray (#54624) This avoids returning false positives where only the indices are shared. As the indices are not mutated by array assignments (and are explicitly warned against mutation in general), we can ignore the case where _only_ the indices are aliasing. Fix #54621 (cherry picked from commit 97bf1484bce0e15fe8c400bd338a68c9424508f8) --- base/multidimensional.jl | 41 ++++++++++++++++++++++++---------------- test/subarray.jl | 31 ++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 16 deletions(-) diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 146f8c160e8e9..80a76a5c34ef5 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -1044,25 +1044,34 @@ 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)) +function mightalias(A::SubArray, B::SubArray) + # There are three ways that SubArrays might _problematically_ alias one another: + # 1. The parents are the same we can conservatively check if the indices might overlap OR + # 2. The parents alias eachother in a more complicated manner (and we can't trace indices) OR + # 3. One's parent is used in the other's indices + # Note that it's ok for just the indices to alias each other as those should not be mutated, + # so we can always do better than the default !_isdisjoint(dataids(A), dataids(B)) + if isbits(A.parent) || isbits(B.parent) + return false # Quick out for immutables + elseif _parentsmatch(A.parent, B.parent) + # Each SubArray unaliases its own parent from its own indices upon construction, so if + # the two parents are the same, then by construction one cannot alias the other's indices + # and therefore this is the only test we need to perform: + return _indicesmightoverlap(A.indices, B.indices) + else + A_parent_ids = dataids(A.parent) + B_parent_ids = dataids(B.parent) + return !_isdisjoint(A_parent_ids, B_parent_ids) || + !_isdisjoint(A_parent_ids, _splatmap(dataids, B.indices)) || + !_isdisjoint(B_parent_ids, _splatmap(dataids, A.indices)) + end end +# Test if two arrays are backed by exactly the same memory in exactly the same order _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) +_parentsmatch(A::DenseArray, B::DenseArray) = elsize(A) == elsize(B) && pointer(A) == pointer(B) && size(A) == size(B) +_parentsmatch(A::StridedArray, B::StridedArray) = elsize(A) == elsize(B) && pointer(A) == pointer(B) && strides(A) == strides(B) +# Given two SubArrays with the same parent, check if the indices might overlap (returning true if unsure) _indicesmightoverlap(A::Tuple{}, B::Tuple{}) = true _indicesmightoverlap(A::Tuple{}, B::Tuple) = error("malformed subarray") _indicesmightoverlap(A::Tuple, B::Tuple{}) = error("malformed subarray") diff --git a/test/subarray.jl b/test/subarray.jl index 9fd59ef5a4156..d02da15dc3158 100644 --- a/test/subarray.jl +++ b/test/subarray.jl @@ -1075,3 +1075,34 @@ end @test !isassigned(v, 3, 3) # out-of-bounds end end + +@testset "aliasing checks with shared indices" begin + indices = [1,3] + a = rand(3) + av = @view a[indices] + b = rand(3) + bv = @view b[indices] + @test !Base.mightalias(av, bv) + @test Base.mightalias(a, av) + @test Base.mightalias(b, bv) + @test Base.mightalias(indices, av) + @test Base.mightalias(indices, bv) + @test Base.mightalias(view(indices, :), av) + @test Base.mightalias(view(indices, :), bv) +end + +@testset "aliasing checks with disjoint arrays" begin + A = rand(3,4,5) + @test Base.mightalias(view(A, :, :, 1), view(A, :, :, 1)) + @test !Base.mightalias(view(A, :, :, 1), view(A, :, :, 2)) + + B = reinterpret(UInt64, A) + @test Base.mightalias(view(B, :, :, 1), view(A, :, :, 1)) + @test !Base.mightalias(view(B, :, :, 1), view(A, :, :, 2)) + + C = reinterpret(UInt32, A) + @test Base.mightalias(view(C, :, :, 1), view(A, :, :, 1)) + @test Base.mightalias(view(C, :, :, 1), view(A, :, :, 2)) # This is overly conservative + @test Base.mightalias(@view(C[begin:2:end, :, 1]), view(A, :, :, 1)) + @test Base.mightalias(@view(C[begin:2:end, :, 1]), view(A, :, :, 2)) # This is overly conservative +end From 5b3a5e4ef6d876ad76c7149b783b2f7bd0be27bc Mon Sep 17 00:00:00 2001 From: DilumAluthgeBot <43731525+DilumAluthgeBot@users.noreply.github.com> Date: Wed, 5 Jun 2024 12:05:24 -0400 Subject: [PATCH 24/71] =?UTF-8?q?=F0=9F=A4=96=20[master]=20Bump=20the=20Di?= =?UTF-8?q?stributed=20stdlib=20from=206a07d98=20to=206c7cdb5=20(#54679)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stdlib: Distributed URL: https://github.com/JuliaLang/Distributed.jl Stdlib branch: master Julia branch: master Old commit: 6a07d98 New commit: 6c7cdb5 Julia version: 1.12.0-DEV Distributed version: 1.11.0(Does not match) Bump invoked by: @Keno Powered by: [BumpStdlibs.jl](https://github.com/JuliaLang/BumpStdlibs.jl) Diff: https://github.com/JuliaLang/Distributed.jl/compare/6a07d9853ab7686df7440a47d1b585c6c9f3be35...6c7cdb5860fa5cb9ca191ce9c52a3d25a9ab3781 ``` $ git log --oneline 6a07d98..6c7cdb5 6c7cdb5 Don't rely on implicit binding creation by setglobal (#102) 3b889ee Create dependabot.yml (#82) ``` Co-authored-by: Dilum Aluthge Co-authored-by: Keno Fischer (cherry picked from commit 06a90c53ad731a418598da77e9396c3670eeb9c8) --- .../md5 | 1 + .../sha512 | 1 + stdlib/Distributed.version | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 deps/checksums/Distributed-6c7cdb5860fa5cb9ca191ce9c52a3d25a9ab3781.tar.gz/md5 create mode 100644 deps/checksums/Distributed-6c7cdb5860fa5cb9ca191ce9c52a3d25a9ab3781.tar.gz/sha512 diff --git a/deps/checksums/Distributed-6c7cdb5860fa5cb9ca191ce9c52a3d25a9ab3781.tar.gz/md5 b/deps/checksums/Distributed-6c7cdb5860fa5cb9ca191ce9c52a3d25a9ab3781.tar.gz/md5 new file mode 100644 index 0000000000000..9904464c82b3b --- /dev/null +++ b/deps/checksums/Distributed-6c7cdb5860fa5cb9ca191ce9c52a3d25a9ab3781.tar.gz/md5 @@ -0,0 +1 @@ +390521058a478a131ca49d349c9b9383 diff --git a/deps/checksums/Distributed-6c7cdb5860fa5cb9ca191ce9c52a3d25a9ab3781.tar.gz/sha512 b/deps/checksums/Distributed-6c7cdb5860fa5cb9ca191ce9c52a3d25a9ab3781.tar.gz/sha512 new file mode 100644 index 0000000000000..a7fbe055c2251 --- /dev/null +++ b/deps/checksums/Distributed-6c7cdb5860fa5cb9ca191ce9c52a3d25a9ab3781.tar.gz/sha512 @@ -0,0 +1 @@ +7f0f414d94739a25b7d713c46887e26cd349329828d42297f44928204b36d15ba9163ad6f670aba72ed9229557bb0f35ab4686429975d1f349fe12b1ba2b189f diff --git a/stdlib/Distributed.version b/stdlib/Distributed.version index ca528066e9403..02eac7eadf0ad 100644 --- a/stdlib/Distributed.version +++ b/stdlib/Distributed.version @@ -1,4 +1,4 @@ DISTRIBUTED_BRANCH = master -DISTRIBUTED_SHA1 = 41c01069533e22a6ce6b794746e4b3aa9f5a25cd +DISTRIBUTED_SHA1 = 6c7cdb5860fa5cb9ca191ce9c52a3d25a9ab3781 DISTRIBUTED_GIT_URL := https://github.com/JuliaLang/Distributed.jl DISTRIBUTED_TAR_URL = https://api.github.com/repos/JuliaLang/Distributed.jl/tarball/$1 From bab6633bae650173cc5cdf55cf00c4fe8ceb7bf3 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Wed, 5 Jun 2024 16:00:11 -0300 Subject: [PATCH 25/71] Fix tbaa annotation on union selector bytes inside of structs (#54604) We currently cause a alias analysis contradiction by saying that the unionselbytes are on the stack, even if they are on a struct. LLVM is then able to figure out that we giving it a impossible alias situation (the object doesn't alias itself) and triggers UB. https://godbolt.org/z/ssEKMzsPf We may want to do a benchmarks run on this to see if anything too critical hasn't regressed. Fixes https://github.com/JuliaLang/julia/issues/54599 --------- Co-authored-by: Cody Tapscott <84105208+topolarity@users.noreply.github.com> (cherry picked from commit 30542e065b47c02d50e2b7f6f925943560324e7e) --- src/cgutils.cpp | 2 +- test/compiler/codegen.jl | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 998a8421c79f0..9fd7a368faf39 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -2740,7 +2740,7 @@ static jl_cgval_t emit_getfield_knownidx(jl_codectx_t &ctx, const jl_cgval_t &st else { ptindex = emit_struct_gep(ctx, cast(lt), staddr, byte_offset + fsz1); } - auto val = emit_unionload(ctx, addr, ptindex, jfty, fsz, al, tbaa, !jl_field_isconst(jt, idx), union_max, ctx.tbaa().tbaa_unionselbyte); + auto val = emit_unionload(ctx, addr, ptindex, jfty, fsz, al, tbaa, !jl_field_isconst(jt, idx), union_max, strct.tbaa); if (val.V && val.V != addr) { setNameWithField(ctx.emission_context, val.V, get_objname, jt, idx, Twine()); } diff --git a/test/compiler/codegen.jl b/test/compiler/codegen.jl index a95ef3225c3f3..19321cf9f21a7 100644 --- a/test/compiler/codegen.jl +++ b/test/compiler/codegen.jl @@ -887,3 +887,12 @@ end ex54166 = Union{Missing, Int64}[missing -2; missing -2]; dims54166 = (1,2) @test (minimum(ex54166; dims=dims54166)[1] === missing) + +function foo54599() + pkgid = Base.identify_package("Test") + println(devnull,pkgid) + println(devnull, pkgid.uuid) + pkgid.uuid +end + +@test foo54599() !== nothing From e8662da00a37dd863c842e94f62ff62cb5c5913f Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Thu, 6 Jun 2024 00:00:18 -0300 Subject: [PATCH 26/71] Fix assertion/crash when optimizing function with dead basic block (#54690) AllocOpt probably needs to handle that in other places more smartly but this seems to at least stop it crashing. Fixes issue found in https://github.com/JuliaLang/julia/pull/54604#issuecomment-2136320508 by @topolarity. (cherry picked from commit 5cb1107cce1f8361b44e8609f08d5e3b0e11c77d) --- src/llvm-alloc-opt.cpp | 2 + test/compiler/codegen.jl | 17 ++- test/llvmpasses/alloc-opt-pass.ll | 186 +++++++++++++++++------------- 3 files changed, 121 insertions(+), 84 deletions(-) diff --git a/src/llvm-alloc-opt.cpp b/src/llvm-alloc-opt.cpp index 5df4f52aca425..9638ca7e68c48 100644 --- a/src/llvm-alloc-opt.cpp +++ b/src/llvm-alloc-opt.cpp @@ -402,6 +402,8 @@ void Optimizer::insertLifetime(Value *ptr, Constant *sz, Instruction *orig) auto bb = use->getParent(); if (!bbs.insert(bb).second) continue; + if (pred_empty(bb)) + continue; // No predecessors so the block is dead assert(lifetime_stack.empty()); Lifetime::Frame cur{bb}; while (true) { diff --git a/test/compiler/codegen.jl b/test/compiler/codegen.jl index 19321cf9f21a7..e8f43236c1bd1 100644 --- a/test/compiler/codegen.jl +++ b/test/compiler/codegen.jl @@ -889,10 +889,17 @@ dims54166 = (1,2) @test (minimum(ex54166; dims=dims54166)[1] === missing) function foo54599() - pkgid = Base.identify_package("Test") - println(devnull,pkgid) - println(devnull, pkgid.uuid) - pkgid.uuid + pkginfo = @noinline bar54599() + pkgid = pkginfo !== nothing ? pkginfo[1] : nothing + @noinline println(devnull, pkgid) + pkgid.uuid !== nothing ? pkgid.uuid : false end -@test foo54599() !== nothing +#this function used to crash allocopt due to a no predecessors bug +barnopreds() = Base.inferencebarrier(true) ? (Base.PkgId(Test),1) : nothing +function foonopreds() + pkginfo = @noinline barnopreds() + pkgid = pkginfo !== nothing ? pkginfo[1] : nothing + pkgid.uuid !== nothing ? pkgid.uuid : false +end +@test foonopreds() !== nothing diff --git a/test/llvmpasses/alloc-opt-pass.ll b/test/llvmpasses/alloc-opt-pass.ll index 6bee0fd325105..d5adbb424f848 100644 --- a/test/llvmpasses/alloc-opt-pass.ll +++ b/test/llvmpasses/alloc-opt-pass.ll @@ -21,23 +21,23 @@ ; CHECK-NEXT: br label %L3 ; CHECK: L3: -define void @preserve_branches(i8* %fptr, i1 %b, i1 %b2) { - %pgcstack = call {}*** @julia.get_pgcstack() - %ptls = call {}*** @julia.ptls_states() - %ptls_i8 = bitcast {}*** %ptls to i8* +define void @preserve_branches(ptr %fptr, i1 %b, i1 %b2) { + %pgcstack = call ptr @julia.get_pgcstack() + %ptls = call ptr @julia.ptls_states() + %ptls_i8 = bitcast ptr %ptls to ptr br i1 %b, label %L1, label %L3 -L1: - %v = call noalias {} addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, i64 8, {} addrspace(10)* @tag) - %tok = call token (...) @llvm.julia.gc_preserve_begin({} addrspace(10)* nonnull %v) +L1: ; preds = %0 + %v = call noalias ptr addrspace(10) @julia.gc_alloc_obj(ptr %ptls_i8, i64 8, ptr addrspace(10) @tag) + %tok = call token (...) @llvm.julia.gc_preserve_begin(ptr addrspace(10) nonnull %v) call void @external_function() br i1 %b2, label %L2, label %L3 -L2: +L2: ; preds = %L1 call void @external_function() br label %L3 -L3: +L3: ; preds = %L2, %L1, %0 ret void } ; CHECK-LABEL: }{{$}} @@ -56,24 +56,24 @@ L3: ; CHECK-NEXT: br label %L3 ; CHECK: L3: -define void @preserve_branches2(i8* %fptr, i1 %b, i1 %b2) { - %pgcstack = call {}*** @julia.get_pgcstack() - %ptls = call {}*** @julia.ptls_states() - %ptls_i8 = bitcast {}*** %ptls to i8* - %v2 = call {} addrspace(10)* @external_function2() +define void @preserve_branches2(ptr %fptr, i1 %b, i1 %b2) { + %pgcstack = call ptr @julia.get_pgcstack() + %ptls = call ptr @julia.ptls_states() + %ptls_i8 = bitcast ptr %ptls to ptr + %v2 = call ptr addrspace(10) @external_function2() br i1 %b, label %L1, label %L3 -L1: - %v = call noalias {} addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, i64 8, {} addrspace(10)* @tag) - %tok = call token (...) @llvm.julia.gc_preserve_begin({} addrspace(10)* %v, {} addrspace(10)* nonnull %v2) +L1: ; preds = %0 + %v = call noalias ptr addrspace(10) @julia.gc_alloc_obj(ptr %ptls_i8, i64 8, ptr addrspace(10) @tag) + %tok = call token (...) @llvm.julia.gc_preserve_begin(ptr addrspace(10) %v, ptr addrspace(10) nonnull %v2) call void @external_function() br i1 %b2, label %L2, label %L3 -L2: +L2: ; preds = %L1 call void @external_function() br label %L3 -L3: +L3: ; preds = %L2, %L1, %0 ret void } ; CHECK-LABEL: }{{$}} @@ -84,26 +84,30 @@ L3: ; CHECK: store [12 x i8] zeroinitializer, ; CHECK: ret void define void @legal_int_types() { - %pgcstack = call {}*** @julia.get_pgcstack() - %ptls = call {}*** @julia.ptls_states() - %ptls_i8 = bitcast {}*** %ptls to i8* - %var1 = call {} addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, i64 12, {} addrspace(10)* @tag) - %var2 = addrspacecast {} addrspace(10)* %var1 to {} addrspace(11)* - %var3 = call {}* @julia.pointer_from_objref({} addrspace(11)* %var2) + %pgcstack = call ptr @julia.get_pgcstack() + %ptls = call ptr @julia.ptls_states() + %ptls_i8 = bitcast ptr %ptls to ptr + %var1 = call ptr addrspace(10) @julia.gc_alloc_obj(ptr %ptls_i8, i64 12, ptr addrspace(10) @tag) + %var2 = addrspacecast ptr addrspace(10) %var1 to ptr addrspace(11) + %var3 = call ptr @julia.pointer_from_objref(ptr addrspace(11) %var2) ret void } ; CHECK-LABEL: }{{$}} - declare void @external_function() -declare {} addrspace(10)* @external_function2() -declare {}*** @julia.ptls_states() -declare {}*** @julia.get_pgcstack() -declare noalias {} addrspace(10)* @julia.gc_alloc_obj(i8*, i64, {} addrspace(10)*) -declare {}* @julia.pointer_from_objref({} addrspace(11)*) -declare void @llvm.memcpy.p11i8.p0i8.i64(i8 addrspace(11)* nocapture writeonly, i8* nocapture readonly, i64, i32, i1) -declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture writeonly, i8* nocapture readonly, i64, i1) + +declare ptr addrspace(10) @external_function2() + +declare ptr @julia.ptls_states() + +declare ptr @julia.get_pgcstack() + +declare noalias ptr addrspace(10) @julia.gc_alloc_obj(ptr, i64, ptr addrspace(10)) + +declare ptr @julia.pointer_from_objref(ptr addrspace(11)) + declare token @llvm.julia.gc_preserve_begin(...) + declare void @llvm.julia.gc_preserve_end(token) ; CHECK-LABEL: @memref_collision @@ -120,24 +124,25 @@ declare void @llvm.julia.gc_preserve_end(token) ; CHECK: L2: ; CHECK: load i define void @memref_collision(i64 %x) { - %pgcstack = call {}*** @julia.get_pgcstack() - %ptls = call {}*** @julia.ptls_states() - %ptls_i8 = bitcast {}*** %ptls to i8* - %v = call noalias {} addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, i64 8, {} addrspace(10)* @tag) - %v_p = bitcast {} addrspace(10)* %v to i64 addrspace(10)* - store i64 %x, i64 addrspace(10)* %v_p - br i1 0, label %L1, label %L2 - -L1: - %v1 = bitcast {} addrspace(10)* %v to {} addrspace(10)* addrspace(10)* - %v1_x = load {} addrspace(10)*, {} addrspace(10)* addrspace(10)* %v1 + %pgcstack = call ptr @julia.get_pgcstack() + %ptls = call ptr @julia.ptls_states() + %ptls_i8 = bitcast ptr %ptls to ptr + %v = call noalias ptr addrspace(10) @julia.gc_alloc_obj(ptr %ptls_i8, i64 8, ptr addrspace(10) @tag) + %v_p = bitcast ptr addrspace(10) %v to ptr addrspace(10) + store i64 %x, ptr addrspace(10) %v_p, align 4 + br i1 false, label %L1, label %L2 + +L1: ; preds = %0 + %v1 = bitcast ptr addrspace(10) %v to ptr addrspace(10) + %v1_x = load ptr addrspace(10), ptr addrspace(10) %v1, align 8 ret void -L2: - %v2 = bitcast {} addrspace(10)* %v to i64 addrspace(10)* - %v2_x = load i64, i64 addrspace(10)* %v2 +L2: ; preds = %0 + %v2 = bitcast ptr addrspace(10) %v to ptr addrspace(10) + %v2_x = load i64, ptr addrspace(10) %v2, align 4 ret void } + ; CHECK-LABEL: }{{$}} ; CHECK-LABEL: @lifetime_no_preserve_end @@ -146,19 +151,19 @@ L2: ; CHECK: call void @llvm.lifetime.start ; CHECK: store [8 x i8] zeroinitializer, ; CHECK-NOT: call void @llvm.lifetime.end -define void @lifetime_no_preserve_end({}* noalias nocapture noundef nonnull sret({}) %0) { - %pgcstack = call {}*** @julia.get_pgcstack() - %ptls = call {}*** @julia.ptls_states() - %ptls_i8 = bitcast {}*** %ptls to i8* - %v = call noalias {} addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, i64 8, {} addrspace(10)* @tag) - %token = call token (...) @llvm.julia.gc_preserve_begin({} addrspace(10)* %v) - %v_derived = addrspacecast {} addrspace(10)* %v to {} addrspace(11)* - %ptr = call nonnull {}* @julia.pointer_from_objref({} addrspace(11)* %v_derived) - %ptr_raw = bitcast {}* %ptr to i8* - call void @external_function() ; safepoint - %ret_raw = bitcast {}* %0 to i8* - call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %ret_raw, i8 * align 8 %ptr_raw, i64 0, i1 false) - %ret_raw2 = bitcast {}* %0 to i8* +define void @lifetime_no_preserve_end(ptr noalias nocapture noundef nonnull sret({}) %0) { + %pgcstack = call ptr @julia.get_pgcstack() + %ptls = call ptr @julia.ptls_states() + %ptls_i8 = bitcast ptr %ptls to ptr + %v = call noalias ptr addrspace(10) @julia.gc_alloc_obj(ptr %ptls_i8, i64 8, ptr addrspace(10) @tag) + %token = call token (...) @llvm.julia.gc_preserve_begin(ptr addrspace(10) %v) + %v_derived = addrspacecast ptr addrspace(10) %v to ptr addrspace(11) + %ptr = call nonnull ptr @julia.pointer_from_objref(ptr addrspace(11) %v_derived) + %ptr_raw = bitcast ptr %ptr to ptr + call void @external_function() + %ret_raw = bitcast ptr %0 to ptr + call void @llvm.memcpy.p0.p0.i64(ptr align 8 %ret_raw, ptr align 8 %ptr_raw, i64 0, i1 false) + %ret_raw2 = bitcast ptr %0 to ptr ret void } ; CHECK-LABEL: }{{$}} @@ -175,26 +180,49 @@ define void @lifetime_no_preserve_end({}* noalias nocapture noundef nonnull sret ; CHECK-NOT: zeroinitializer ; CHECK: ret void define void @initializers() { - %pgcstack = call {}*** @julia.get_pgcstack() - %ptls = call {}*** @julia.ptls_states() - %ptls_i8 = bitcast {}*** %ptls to i8* - - %var1 = call {} addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, i64 1, {} addrspace(10)* @tag) #0 - %var2 = addrspacecast {} addrspace(10)* %var1 to {} addrspace(11)* - %var3 = call {}* @julia.pointer_from_objref({} addrspace(11)* %var2) - - %var4 = call {} addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, i64 2, {} addrspace(10)* @tag) #1 - %var5 = addrspacecast {} addrspace(10)* %var4 to {} addrspace(11)* - %var6 = call {}* @julia.pointer_from_objref({} addrspace(11)* %var5) - - %var7 = call {} addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, i64 3, {} addrspace(10)* @tag) #2 - %var8 = addrspacecast {} addrspace(10)* %var7 to {} addrspace(11)* - %var9 = call {}* @julia.pointer_from_objref({} addrspace(11)* %var8) - + %pgcstack = call ptr @julia.get_pgcstack() + %ptls = call ptr @julia.ptls_states() + %ptls_i8 = bitcast ptr %ptls to ptr + %var1 = call ptr addrspace(10) @julia.gc_alloc_obj(ptr %ptls_i8, i64 1, ptr addrspace(10) @tag) #1 + %var2 = addrspacecast ptr addrspace(10) %var1 to ptr addrspace(11) + %var3 = call ptr @julia.pointer_from_objref(ptr addrspace(11) %var2) + %var4 = call ptr addrspace(10) @julia.gc_alloc_obj(ptr %ptls_i8, i64 2, ptr addrspace(10) @tag) #2 + %var5 = addrspacecast ptr addrspace(10) %var4 to ptr addrspace(11) + %var6 = call ptr @julia.pointer_from_objref(ptr addrspace(11) %var5) + %var7 = call ptr addrspace(10) @julia.gc_alloc_obj(ptr %ptls_i8, i64 3, ptr addrspace(10) @tag) #3 + %var8 = addrspacecast ptr addrspace(10) %var7 to ptr addrspace(11) + %var9 = call ptr @julia.pointer_from_objref(ptr addrspace(11) %var8) ret void } ; CHECK-LABEL: }{{$}} -attributes #0 = { allockind("alloc") } -attributes #1 = { allockind("alloc,uninitialized") } -attributes #2 = { allockind("alloc,zeroed") } +; Test that the pass handles dead basic blocks with references to the allocation +; CHECK-LABEL: @nopreds +; CHECK: alloca i8, i64 0, align 1 +; CHECK: call void @llvm.lifetime.start +define swiftcc { ptr addrspace(10), i8 } @nopreds() { +top: + %0 = call ptr addrspace(10) @julia.gc_alloc_obj(ptr null, i64 0, ptr addrspace(10) null) + %1 = addrspacecast ptr addrspace(10) %0 to ptr addrspace(11) + br label %common.ret + +common.ret: ; preds = %union_move9, %top + ret { ptr addrspace(10), i8 } zeroinitializer + +union_move9: ; No predecessors! + call void @llvm.memcpy.p0.p11.i64(ptr null, ptr addrspace(11) %1, i64 0, i1 false) + br label %common.ret +} +; CHECK-LABEL: }{{$}} + +; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) +declare void @llvm.memcpy.p11.p0.i64(ptr addrspace(11) noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0 +; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) +declare void @llvm.memcpy.p0.p11.i64(ptr noalias nocapture writeonly, ptr addrspace(11) noalias nocapture readonly, i64, i1 immarg) #0 +; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) +declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0 + +attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } +attributes #1 = { allockind("alloc") } +attributes #2 = { allockind("alloc,uninitialized") } +attributes #3 = { allockind("alloc,zeroed") } From 065304448872c926c30501da8bfc9389e8ab591e Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Thu, 16 May 2024 02:30:53 +0530 Subject: [PATCH 27/71] Unalias source from dest in copytrito (#54474) (cherry picked from commit 72d644fd7dbc6e6b75518132b88cd848ab8e2d65) --- stdlib/LinearAlgebra/src/generic.jl | 1 + stdlib/LinearAlgebra/test/generic.jl | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/stdlib/LinearAlgebra/src/generic.jl b/stdlib/LinearAlgebra/src/generic.jl index f3d39824ad72c..81d092ca14060 100644 --- a/stdlib/LinearAlgebra/src/generic.jl +++ b/stdlib/LinearAlgebra/src/generic.jl @@ -1935,6 +1935,7 @@ function copytrito!(B::AbstractMatrix, A::AbstractMatrix, uplo::AbstractChar) m,n = size(A) m1,n1 = size(B) (m1 < m || n1 < n) && throw(DimensionMismatch(lazy"B of size ($m1,$n1) should have at least the same number of rows and columns than A of size ($m,$n)")) + A = Base.unalias(B, A) if uplo == 'U' for j=1:n for i=1:min(j,m) diff --git a/stdlib/LinearAlgebra/test/generic.jl b/stdlib/LinearAlgebra/test/generic.jl index 6318b8e405ede..ba4bdb1845255 100644 --- a/stdlib/LinearAlgebra/test/generic.jl +++ b/stdlib/LinearAlgebra/test/generic.jl @@ -654,6 +654,14 @@ end C = uplo == 'L' ? tril(A) : triu(A) @test B ≈ C end + @testset "aliasing" begin + M = Matrix(reshape(1:36, 6, 6)) + A = view(M, 1:5, 1:5) + A2 = Matrix(A) + B = view(M, 2:6, 2:6) + copytrito!(B, A, 'U') + @test UpperTriangular(B) == UpperTriangular(A2) + end end @testset "immutable arrays" begin From d07a863ce4ffe6f33ae2f241ac759ea19a2ce044 Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Wed, 29 May 2024 19:15:50 +0200 Subject: [PATCH 28/71] Accomodate for rectangular matrices in `copytrito!` (#54587) (cherry picked from commit fc54be6eac4409afd831b37d56d4a7796fdc3565) --- stdlib/LinearAlgebra/src/generic.jl | 25 ++++++++----- stdlib/LinearAlgebra/src/lapack.jl | 20 ++++++++-- stdlib/LinearAlgebra/test/generic.jl | 56 +++++++++++++++++++++++++--- stdlib/LinearAlgebra/test/lapack.jl | 20 +++++++++- 4 files changed, 101 insertions(+), 20 deletions(-) diff --git a/stdlib/LinearAlgebra/src/generic.jl b/stdlib/LinearAlgebra/src/generic.jl index 81d092ca14060..c2144bf85d024 100644 --- a/stdlib/LinearAlgebra/src/generic.jl +++ b/stdlib/LinearAlgebra/src/generic.jl @@ -1934,19 +1934,24 @@ function copytrito!(B::AbstractMatrix, A::AbstractMatrix, uplo::AbstractChar) BLAS.chkuplo(uplo) m,n = size(A) m1,n1 = size(B) - (m1 < m || n1 < n) && throw(DimensionMismatch(lazy"B of size ($m1,$n1) should have at least the same number of rows and columns than A of size ($m,$n)")) A = Base.unalias(B, A) if uplo == 'U' - for j=1:n - for i=1:min(j,m) - @inbounds B[i,j] = A[i,j] - end + if n < m + (m1 < n || n1 < n) && throw(DimensionMismatch(lazy"B of size ($m1,$n1) should have at least size ($n,$n)")) + else + (m1 < m || n1 < n) && throw(DimensionMismatch(lazy"B of size ($m1,$n1) should have at least size ($m,$n)")) end - else # uplo == 'L' - for j=1:n - for i=j:m - @inbounds B[i,j] = A[i,j] - end + for j in 1:n, i in 1:min(j,m) + @inbounds B[i,j] = A[i,j] + end + else # uplo == 'L' + if m < n + (m1 < m || n1 < m) && throw(DimensionMismatch(lazy"B of size ($m1,$n1) should have at least size ($m,$m)")) + else + (m1 < m || n1 < n) && throw(DimensionMismatch(lazy"B of size ($m1,$n1) should have at least size ($m,$n)")) + end + for j in 1:n, i in j:m + @inbounds B[i,j] = A[i,j] end end return B diff --git a/stdlib/LinearAlgebra/src/lapack.jl b/stdlib/LinearAlgebra/src/lapack.jl index 254425f8cd2bf..4b7bffcd50e89 100644 --- a/stdlib/LinearAlgebra/src/lapack.jl +++ b/stdlib/LinearAlgebra/src/lapack.jl @@ -7163,9 +7163,23 @@ for (fn, elty) in ((:dlacpy_, :Float64), function lacpy!(B::AbstractMatrix{$elty}, A::AbstractMatrix{$elty}, uplo::AbstractChar) require_one_based_indexing(A, B) chkstride1(A, B) - m,n = size(A) - m1,n1 = size(B) - (m1 < m || n1 < n) && throw(DimensionMismatch(lazy"B of size ($m1,$n1) should have at least the same number of rows and columns than A of size ($m,$n)")) + m, n = size(A) + m1, n1 = size(B) + if uplo == 'U' + if n < m + (m1 < n || n1 < n) && throw(DimensionMismatch(lazy"B of size ($m1,$n1) should have at least size ($n,$n)")) + else + (m1 < m || n1 < n) && throw(DimensionMismatch(lazy"B of size ($m1,$n1) should have at least size ($m,$n)")) + end + elseif uplo == 'L' + if m < n + (m1 < m || n1 < m) && throw(DimensionMismatch(lazy"B of size ($m1,$n1) should have at least size ($m,$m)")) + else + (m1 < m || n1 < n) && throw(DimensionMismatch(lazy"B of size ($m1,$n1) should have at least size ($m,$n)")) + end + else + (m1 < m || n1 < n) && throw(DimensionMismatch(lazy"B of size ($m1,$n1) should have at least size ($m,$n)")) + end lda = max(1, stride(A, 2)) ldb = max(1, stride(B, 2)) ccall((@blasfunc($fn), libblastrampoline), Cvoid, diff --git a/stdlib/LinearAlgebra/test/generic.jl b/stdlib/LinearAlgebra/test/generic.jl index ba4bdb1845255..fd464d6c0762c 100644 --- a/stdlib/LinearAlgebra/test/generic.jl +++ b/stdlib/LinearAlgebra/test/generic.jl @@ -647,12 +647,56 @@ end @testset "copytrito!" begin n = 10 - A = rand(n, n) - for uplo in ('L', 'U') - B = zeros(n, n) - copytrito!(B, A, uplo) - C = uplo == 'L' ? tril(A) : triu(A) - @test B ≈ C + @testset "square" begin + for A in (rand(n, n), rand(Int8, n, n)), uplo in ('L', 'U') + for AA in (A, view(A, reverse.(axes(A))...)) + C = uplo == 'L' ? tril(AA) : triu(AA) + for B in (zeros(n, n), zeros(n+1, n+2)) + copytrito!(B, AA, uplo) + @test view(B, 1:n, 1:n) == C + end + end + end + end + @testset "wide" begin + for A in (rand(n, 2n), rand(Int8, n, 2n)) + for AA in (A, view(A, reverse.(axes(A))...)) + C = tril(AA) + for (M, N) in ((n, n), (n+1, n), (n, n+1), (n+1, n+1)) + B = zeros(M, N) + copytrito!(B, AA, 'L') + @test view(B, 1:n, 1:n) == view(C, 1:n, 1:n) + end + @test_throws DimensionMismatch copytrito!(zeros(n-1, 2n), AA, 'L') + C = triu(AA) + for (M, N) in ((n, 2n), (n+1, 2n), (n, 2n+1), (n+1, 2n+1)) + B = zeros(M, N) + copytrito!(B, AA, 'U') + @test view(B, 1:n, 1:2n) == view(C, 1:n, 1:2n) + end + @test_throws DimensionMismatch copytrito!(zeros(n+1, 2n-1), AA, 'U') + end + end + end + @testset "tall" begin + for A in (rand(2n, n), rand(Int8, 2n, n)) + for AA in (A, view(A, reverse.(axes(A))...)) + C = triu(AA) + for (M, N) in ((n, n), (n+1, n), (n, n+1), (n+1, n+1)) + B = zeros(M, N) + copytrito!(B, AA, 'U') + @test view(B, 1:n, 1:n) == view(C, 1:n, 1:n) + end + @test_throws DimensionMismatch copytrito!(zeros(n-1, n+1), AA, 'U') + C = tril(AA) + for (M, N) in ((2n, n), (2n, n+1), (2n+1, n), (2n+1, n+1)) + B = zeros(M, N) + copytrito!(B, AA, 'L') + @test view(B, 1:2n, 1:n) == view(C, 1:2n, 1:n) + end + @test_throws DimensionMismatch copytrito!(zeros(n-1, n+1), AA, 'L') + end + end end @testset "aliasing" begin M = Matrix(reshape(1:36, 6, 6)) diff --git a/stdlib/LinearAlgebra/test/lapack.jl b/stdlib/LinearAlgebra/test/lapack.jl index 652c6c2e27e6c..000438a004b23 100644 --- a/stdlib/LinearAlgebra/test/lapack.jl +++ b/stdlib/LinearAlgebra/test/lapack.jl @@ -805,8 +805,26 @@ end B = zeros(elty, n, n) LinearAlgebra.LAPACK.lacpy!(B, A, uplo) C = uplo == 'L' ? tril(A) : (uplo == 'U' ? triu(A) : A) - @test B ≈ C + @test B == C + B = zeros(elty, n+1, n+1) + LinearAlgebra.LAPACK.lacpy!(B, A, uplo) + C = uplo == 'L' ? tril(A) : (uplo == 'U' ? triu(A) : A) + @test view(B, 1:n, 1:n) == C end + A = rand(elty, n, n+1) + B = zeros(elty, n, n) + LinearAlgebra.LAPACK.lacpy!(B, A, 'L') + @test B == view(tril(A), 1:n, 1:n) + B = zeros(elty, n, n+1) + LinearAlgebra.LAPACK.lacpy!(B, A, 'U') + @test B == triu(A) + A = rand(elty, n+1, n) + B = zeros(elty, n, n) + LinearAlgebra.LAPACK.lacpy!(B, A, 'U') + @test B == view(triu(A), 1:n, 1:n) + B = zeros(elty, n+1, n) + LinearAlgebra.LAPACK.lacpy!(B, A, 'L') + @test B == tril(A) end end From b69fc5786331a60c5357bafb901be077e5e3a90d Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Mon, 10 Jun 2024 13:20:51 +0530 Subject: [PATCH 29/71] Backport "Avoid concatenating LazyString in setindex! for triangular matrices #54631" to v1.11 (#54742) --- stdlib/LinearAlgebra/src/triangular.jl | 46 +++++++++++++++++--------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/stdlib/LinearAlgebra/src/triangular.jl b/stdlib/LinearAlgebra/src/triangular.jl index e782c0929f09f..c3def1318db86 100644 --- a/stdlib/LinearAlgebra/src/triangular.jl +++ b/stdlib/LinearAlgebra/src/triangular.jl @@ -269,10 +269,24 @@ Base.isstored(A::UpperTriangular, i::Int, j::Int) = @propagate_inbounds getindex(A::UpperTriangular, i::Integer, j::Integer) = i <= j ? A.data[i,j] : _zero(A.data,j,i) +_zero_triangular_half_str(::Type{<:UpperOrUnitUpperTriangular}) = "lower" +_zero_triangular_half_str(::Type{<:LowerOrUnitLowerTriangular}) = "upper" + +@noinline function throw_nonzeroerror(T, @nospecialize(x), i, j) + Ts = _zero_triangular_half_str(T) + Tn = nameof(T) + throw(ArgumentError( + lazy"cannot set index in the $Ts triangular part ($i, $j) of an $Tn matrix to a nonzero value ($x)")) +end +@noinline function throw_nononeerror(T, @nospecialize(x), i, j) + Tn = nameof(T) + throw(ArgumentError( + lazy"cannot set index on the diagonal ($i, $j) of an $Tn matrix to a non-unit value ($x)")) +end + @propagate_inbounds function setindex!(A::UpperTriangular, x, i::Integer, j::Integer) if i > j - iszero(x) || throw(ArgumentError("cannot set index in the lower triangular part " * - lazy"($i, $j) of an UpperTriangular matrix to a nonzero value ($x)")) + iszero(x) || throw_nonzeroerror(typeof(A), x, i, j) else A.data[i,j] = x end @@ -281,11 +295,9 @@ end @propagate_inbounds function setindex!(A::UnitUpperTriangular, x, i::Integer, j::Integer) if i > j - iszero(x) || throw(ArgumentError("cannot set index in the lower triangular part " * - lazy"($i, $j) of a UnitUpperTriangular matrix to a nonzero value ($x)")) + iszero(x) || throw_nonzeroerror(typeof(A), x, i, j) elseif i == j - x == oneunit(x) || throw(ArgumentError(lazy"cannot set index on the diagonal ($i, $j) " * - lazy"of a UnitUpperTriangular matrix to a non-unit value ($x)")) + x == oneunit(x) || throw_nononeerror(typeof(A), x, i, j) else A.data[i,j] = x end @@ -294,8 +306,7 @@ end @propagate_inbounds function setindex!(A::LowerTriangular, x, i::Integer, j::Integer) if i < j - iszero(x) || throw(ArgumentError("cannot set index in the upper triangular part " * - lazy"($i, $j) of a LowerTriangular matrix to a nonzero value ($x)")) + iszero(x) || throw_nonzeroerror(typeof(A), x, i, j) else A.data[i,j] = x end @@ -304,28 +315,31 @@ end @propagate_inbounds function setindex!(A::UnitLowerTriangular, x, i::Integer, j::Integer) if i < j - iszero(x) || throw(ArgumentError("cannot set index in the upper triangular part " * - lazy"($i, $j) of a UnitLowerTriangular matrix to a nonzero value ($x)")) + iszero(x) || throw_nonzeroerror(typeof(A), x, i, j) elseif i == j - x == oneunit(x) || throw(ArgumentError(lazy"cannot set index on the diagonal ($i, $j) " * - lazy"of a UnitLowerTriangular matrix to a non-unit value ($x)")) + x == oneunit(x) || throw_nononeerror(typeof(A), x, i, j) else A.data[i,j] = x end return A end +@noinline function throw_setindex_structuralzero_error(T, @nospecialize(x)) + Ts = _zero_triangular_half_str(T) + Tn = nameof(T) + throw(ArgumentError( + lazy"cannot set indices in the $Ts triangular part of an $Tn matrix to a nonzero value ($x)")) +end + @inline function fill!(A::UpperTriangular, x) - iszero(x) || throw(ArgumentError("cannot set indices in the lower triangular part " * - lazy"of an UpperTriangular matrix to a nonzero value ($x)")) + iszero(x) || throw_setindex_structuralzero_error(typeof(A), x) for col in axes(A,2), row in firstindex(A,1):col @inbounds A.data[row, col] = x end A end @inline function fill!(A::LowerTriangular, x) - iszero(x) || throw(ArgumentError("cannot set indices in the upper triangular part " * - lazy"of a LowerTriangular matrix to a nonzero value ($x)")) + iszero(x) || throw_setindex_structuralzero_error(typeof(A), x) for col in axes(A,2), row in col:lastindex(A,1) @inbounds A.data[row, col] = x end From c057940f574c06ccbd10ec95cef963845122ea8b Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Mon, 10 Jun 2024 10:35:45 -0300 Subject: [PATCH 30/71] Don't test with opaque pointers --- test/llvmpasses/alloc-opt-pass.ll | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/llvmpasses/alloc-opt-pass.ll b/test/llvmpasses/alloc-opt-pass.ll index d5adbb424f848..5d9030a3866b2 100644 --- a/test/llvmpasses/alloc-opt-pass.ll +++ b/test/llvmpasses/alloc-opt-pass.ll @@ -1,7 +1,5 @@ ; This file is a part of Julia. License is MIT: https://julialang.org/license -; RUN: opt -enable-new-pm=1 --opaque-pointers=0 --load-pass-plugin=libjulia-codegen%shlibext -passes='function(AllocOpt)' -S %s | FileCheck %s --check-prefixes=CHECK,TYPED - ; RUN: opt -enable-new-pm=1 --opaque-pointers=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='function(AllocOpt)' -S %s | FileCheck %s --check-prefixes=CHECK,OPAQUE @tag = external addrspace(10) global {} From 4d53a3839201830b8fc132a0ea5a367fb126eda5 Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Thu, 30 May 2024 16:25:00 -0300 Subject: [PATCH 31/71] Fix trampoline assembly for build on clang 18 on apple silicon (#54634) This avoids a: `error: non-private labels cannot appear between .cfi_startproc / .cfi_endproc pairs` error. That error was introduced in https://reviews.llvm.org/D155245#4657075 see also https://github.com/llvm/llvm-project/issues/72802 (cherry picked from commit a4e793ee31612625d9c48ebd89a53c3a5c0f6eb6) --- cli/trampolines/trampolines_aarch64.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/trampolines/trampolines_aarch64.S b/cli/trampolines/trampolines_aarch64.S index 2d87ae6dcdb1c..ccb9a647ac6c3 100644 --- a/cli/trampolines/trampolines_aarch64.S +++ b/cli/trampolines/trampolines_aarch64.S @@ -5,9 +5,9 @@ #define XX(name) \ .global CNAME(name) SEP \ +CNAME(name)##: SEP \ .cfi_startproc SEP \ .p2align 2 SEP \ -CNAME(name)##: SEP \ adrp x16, PAGE(CNAME(name##_addr)) SEP \ ldr x16, [x16, PAGEOFF(CNAME(name##_addr))] SEP \ br x16 SEP \ From fe512619e6e79a32ca83a2451679f7356fff5fe7 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 7 Jun 2024 13:11:18 -0400 Subject: [PATCH 32/71] fix prepend StackOverflow issue (#54718) Attempt to fix #54711 Test introduced by #36227 --------- Co-authored-by: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> (cherry picked from commit 9477472629b6833b20679adf49acb18e97ca8f99) --- base/array.jl | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/base/array.jl b/base/array.jl index 19d79479bef39..8824e77799690 100644 --- a/base/array.jl +++ b/base/array.jl @@ -1312,8 +1312,7 @@ end append!(a::AbstractVector, iter) = _append!(a, IteratorSize(iter), iter) push!(a::AbstractVector, iter...) = append!(a, iter) - -append!(a::AbstractVector, iter...) = foldl(append!, iter, init=a) +append!(a::AbstractVector, iter...) = (for v in iter; append!(a, v); end; return a) function _append!(a::AbstractVector, ::Union{HasLength,HasShape}, iter) n = Int(length(iter))::Int @@ -1372,10 +1371,9 @@ function prepend!(a::Vector{T}, items::Union{AbstractVector{<:T},Tuple}) where T return a end -prepend!(a::Vector, iter) = _prepend!(a, IteratorSize(iter), iter) -pushfirst!(a::Vector, iter...) = prepend!(a, iter) - -prepend!(a::AbstractVector, iter...) = foldr((v, a) -> prepend!(a, v), iter, init=a) +prepend!(a::AbstractVector, iter) = _prepend!(a, IteratorSize(iter), iter) +pushfirst!(a::AbstractVector, iter...) = prepend!(a, iter) +prepend!(a::AbstractVector, iter...) = (for v = reverse(iter); prepend!(a, v); end; return a) function _prepend!(a::Vector, ::Union{HasLength,HasShape}, iter) @_terminates_locally_meta From 115d0903a132240d2440fbb02fdd3146c6391655 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Sun, 9 Jun 2024 03:01:10 -0400 Subject: [PATCH 33/71] Reimplement dummy pkg prompt as standard prompt (#54674) (cherry picked from commit 77c28ab286f48afe2512e2ae7f7310b87ca3345e) --- stdlib/REPL/src/LineEdit.jl | 11 +++- stdlib/REPL/src/REPL.jl | 117 +++++++++++++++++++++++------------- stdlib/REPL/test/repl.jl | 10 +-- 3 files changed, 90 insertions(+), 48 deletions(-) diff --git a/stdlib/REPL/src/LineEdit.jl b/stdlib/REPL/src/LineEdit.jl index 9888654dc21c5..0a5329816101a 100644 --- a/stdlib/REPL/src/LineEdit.jl +++ b/stdlib/REPL/src/LineEdit.jl @@ -76,9 +76,10 @@ mutable struct MIState key_repeats::Int last_action::Symbol current_action::Symbol + async_channel::Channel end -MIState(i, mod, c, a, m) = MIState(i, mod, c, a, m, String[], 0, Char[], 0, :none, :none) +MIState(i, mod, c, a, m) = MIState(i, mod, c, a, m, String[], 0, Char[], 0, :none, :none, Channel()) const BufferLike = Union{MIState,ModeState,IOBuffer} const State = Union{MIState,ModeState} @@ -2310,7 +2311,7 @@ keymap_data(state, ::Union{HistoryPrompt, PrefixHistoryPrompt}) = state Base.isempty(s::PromptState) = s.input_buffer.size == 0 -on_enter(s::PromptState) = s.p.on_enter(s) +on_enter(s::MIState) = state(s).p.on_enter(s) move_input_start(s::BufferLike) = (seek(buffer(s), 0); nothing) move_input_end(buf::IOBuffer) = (seekend(buf); nothing) @@ -2830,7 +2831,11 @@ function prompt!(term::TextTerminal, prompt::ModalInterface, s::MIState = init_s old_state = mode(s) while true kmap = keymap(s, prompt) - fcn = match_input(kmap, s) + waitany(( + @async(eof(term) || peek(term, Char)), + @async(wait(s.async_channel)), + ), throw=true) + fcn = isempty(s.async_channel) ? match_input(kmap, s) : take!(s.async_channel) kdata = keymap_data(s, prompt) s.current_action = :unknown # if the to-be-run action doesn't update this field, # :unknown will be recorded in the last_action field diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index bf807f2a8a6c9..cf6738793de92 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -242,7 +242,7 @@ function check_for_missing_packages_and_run_hooks(ast) mods = modules_to_be_loaded(ast) filter!(mod -> isnothing(Base.identify_package(String(mod))), mods) # keep missing modules if !isempty(mods) - isempty(install_packages_hooks) && Base.require_stdlib(Base.PkgId(Base.UUID("44cfe95a-1eb2-52ea-b672-e2afdf69b78f"), "Pkg")) + isempty(install_packages_hooks) && load_pkg() for f in install_packages_hooks Base.invokelatest(f, mods) && return end @@ -536,6 +536,7 @@ mutable struct LineEditREPL <: AbstractREPL answer_color::String shell_color::String help_color::String + pkg_color::String history_file::Bool in_shell::Bool in_help::Bool @@ -548,13 +549,13 @@ mutable struct LineEditREPL <: AbstractREPL interface::ModalInterface backendref::REPLBackendRef frontend_task::Task - function LineEditREPL(t,hascolor,prompt_color,input_color,answer_color,shell_color,help_color,history_file,in_shell,in_help,envcolors) + function LineEditREPL(t,hascolor,prompt_color,input_color,answer_color,shell_color,help_color,pkg_color,history_file,in_shell,in_help,envcolors) opts = Options() opts.hascolor = hascolor if !hascolor opts.beep_colors = [""] end - new(t,hascolor,prompt_color,input_color,answer_color,shell_color,help_color,history_file,in_shell, + new(t,hascolor,prompt_color,input_color,answer_color,shell_color,help_color,pkg_color,history_file,in_shell, in_help,envcolors,false,nothing, opts, nothing, Tuple{String,Int}[]) end end @@ -571,6 +572,7 @@ LineEditREPL(t::TextTerminal, hascolor::Bool, envcolors::Bool=false) = hascolor ? Base.answer_color() : "", hascolor ? Base.text_colors[:red] : "", hascolor ? Base.text_colors[:yellow] : "", + hascolor ? Base.text_colors[:blue] : "", false, false, false, envcolors ) @@ -1041,6 +1043,20 @@ setup_interface( extra_repl_keymap::Any = repl.options.extra_keymap ) = setup_interface(repl, hascolor, extra_repl_keymap) +const Pkg_pkgid = Base.PkgId(Base.UUID("44cfe95a-1eb2-52ea-b672-e2afdf69b78f"), "Pkg") +const Pkg_REPLExt_pkgid = Base.PkgId(Base.UUID("ceef7b17-42e7-5b1c-81d4-4cc4a2494ccf"), "REPLExt") + +function load_pkg() + @lock Base.require_lock begin + REPLExt = Base.require_stdlib(Pkg_pkgid, "REPLExt") + # require_stdlib does not guarantee that the `__init__` of the package is done when loading is done async + # but we need to wait for the repl mode to be set up + lock = get(Base.package_locks, Pkg_REPLExt_pkgid.uuid, nothing) + lock !== nothing && wait(lock[2]) + return REPLExt + end +end + # This non keyword method can be precompiled which is important function setup_interface( repl::LineEditREPL, @@ -1116,6 +1132,34 @@ function setup_interface( end, sticky = true) + # Set up dummy Pkg mode that will be replaced once Pkg is loaded + # use 6 dots to occupy the same space as the most likely "@v1.xx" env name + dummy_pkg_mode = Prompt("(......) $PKG_PROMPT", + prompt_prefix = hascolor ? repl.pkg_color : "", + prompt_suffix = hascolor ? + (repl.envcolors ? Base.input_color : repl.input_color) : "", + repl = repl, + complete = LineEdit.EmptyCompletionProvider(), + on_done = respond(line->nothing, repl, julia_prompt), + on_enter = function (s::MIState) + # This is hit when the user tries to execute a command before the real Pkg mode has been + # switched to. Ok to do this even if Pkg is loading on the other task because of the loading lock. + REPLExt = load_pkg() + if REPLExt isa Module && isdefined(REPLExt, :PkgCompletionProvider) + for mode in repl.interface.modes + if mode isa LineEdit.Prompt && mode.complete isa REPLExt.PkgCompletionProvider + # pkg mode + buf = copy(LineEdit.buffer(s)) + transition(s, mode) do + LineEdit.state(s, mode).input_buffer = buf + end + end + end + end + return true + end, + sticky = true) + ################################# Stage II ############################# @@ -1123,7 +1167,8 @@ function setup_interface( # We will have a unified history for all REPL modes hp = REPLHistoryProvider(Dict{Symbol,Prompt}(:julia => julia_prompt, :shell => shell_mode, - :help => help_mode)) + :help => help_mode, + :pkg => dummy_pkg_mode)) if repl.history_file try hist_path = find_hist_file() @@ -1146,6 +1191,7 @@ function setup_interface( julia_prompt.hist = hp shell_mode.hist = hp help_mode.hist = hp + dummy_pkg_mode.hist = hp julia_prompt.on_done = respond(x->Base.parse_input_line(x,filename=repl_filename(repl,hp)), repl, julia_prompt) @@ -1186,47 +1232,36 @@ function setup_interface( end, ']' => function (s::MIState,o...) if isempty(s) || position(LineEdit.buffer(s)) == 0 - # print a dummy pkg prompt while Pkg loads - LineEdit.clear_line(LineEdit.terminal(s)) - # use 6 .'s here because its the same width as the most likely `@v1.xx` env name - print(LineEdit.terminal(s), styled"{blue,bold:({gray:......}) pkg> }") - pkg_mode = nothing - transition_finished = false - iolock = Base.ReentrantLock() # to avoid race between tasks reading stdin & input buffer - # spawn Pkg load to avoid blocking typing during loading. Typing will block if only 1 thread + buf = copy(LineEdit.buffer(s)) + transition(s, dummy_pkg_mode) do + LineEdit.state(s, dummy_pkg_mode).input_buffer = buf + end + # load Pkg on another thread if available so that typing in the dummy Pkg prompt + # isn't blocked, but instruct the main REPL task to do the transition via s.async_channel t_replswitch = Threads.@spawn begin - pkgid = Base.PkgId(Base.UUID("44cfe95a-1eb2-52ea-b672-e2afdf69b78f"), "Pkg") - REPLExt = Base.require_stdlib(pkgid, "REPLExt") + REPLExt = load_pkg() if REPLExt isa Module && isdefined(REPLExt, :PkgCompletionProvider) - for mode in repl.interface.modes - if mode isa LineEdit.Prompt && mode.complete isa REPLExt.PkgCompletionProvider - pkg_mode = mode - break + put!(s.async_channel, + function (s::MIState, _) + LineEdit.mode(s) === dummy_pkg_mode || return :ok + for mode in repl.interface.modes + if mode isa LineEdit.Prompt && mode.complete isa REPLExt.PkgCompletionProvider + buf = copy(LineEdit.buffer(s)) + transition(s, mode) do + LineEdit.state(s, mode).input_buffer = buf + end + if !isempty(s) && @invokelatest(LineEdit.check_for_hint(s)) + @invokelatest(LineEdit.refresh_line(s)) + end + break + end + end + return :ok end - end - end - if pkg_mode !== nothing - @lock iolock begin - buf = copy(LineEdit.buffer(s)) - transition(s, pkg_mode) do - LineEdit.state(s, pkg_mode).input_buffer = buf - end - if !isempty(s) - @invokelatest(LineEdit.check_for_hint(s)) && @invokelatest(LineEdit.refresh_line(s)) - end - transition_finished = true - end + ) end end Base.errormonitor(t_replswitch) - # while loading just accept all keys, no keymap functionality - while !istaskdone(t_replswitch) - # wait but only take if task is still running - peek(stdin, Char) - @lock iolock begin - transition_finished || edit_insert(s, read(stdin, Char)) - end - end else edit_insert(s, ']') end @@ -1409,9 +1444,9 @@ function setup_interface( b = Dict{Any,Any}[skeymap, mk, prefix_keymap, LineEdit.history_keymap, LineEdit.default_keymap, LineEdit.escape_defaults] prepend!(b, extra_repl_keymap) - shell_mode.keymap_dict = help_mode.keymap_dict = LineEdit.keymap(b) + shell_mode.keymap_dict = help_mode.keymap_dict = dummy_pkg_mode.keymap_dict = LineEdit.keymap(b) - allprompts = LineEdit.TextInterface[julia_prompt, shell_mode, help_mode, search_prompt, prefix_prompt] + allprompts = LineEdit.TextInterface[julia_prompt, shell_mode, help_mode, dummy_pkg_mode, search_prompt, prefix_prompt] return ModalInterface(allprompts) end diff --git a/stdlib/REPL/test/repl.jl b/stdlib/REPL/test/repl.jl index 83df34a056578..f50bfb4528ea2 100644 --- a/stdlib/REPL/test/repl.jl +++ b/stdlib/REPL/test/repl.jl @@ -500,8 +500,9 @@ for prompt = ["TestΠ", () -> randstring(rand(1:10))] repl_mode = repl.interface.modes[1] shell_mode = repl.interface.modes[2] help_mode = repl.interface.modes[3] - histp = repl.interface.modes[4] - prefix_mode = repl.interface.modes[5] + pkg_mode = repl.interface.modes[4] + histp = repl.interface.modes[5] + prefix_mode = repl.interface.modes[6] hp = REPL.REPLHistoryProvider(Dict{Symbol,Any}(:julia => repl_mode, :shell => shell_mode, @@ -1559,8 +1560,9 @@ for prompt = ["TestΠ", () -> randstring(rand(1:10))] repl_mode = repl.interface.modes[1] shell_mode = repl.interface.modes[2] help_mode = repl.interface.modes[3] - histp = repl.interface.modes[4] - prefix_mode = repl.interface.modes[5] + pkg_mode = repl.interface.modes[4] + histp = repl.interface.modes[5] + prefix_mode = repl.interface.modes[6] hp = REPL.REPLHistoryProvider(Dict{Symbol,Any}(:julia => repl_mode, :shell => shell_mode, From b7aed983bcf0df9132d6766367430e501d0d3f75 Mon Sep 17 00:00:00 2001 From: Jishnu Bhattacharya Date: Mon, 10 Jun 2024 13:12:20 +0530 Subject: [PATCH 34/71] LazyString in interpolated error messages involving types (#54737) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These are often badly inferred, e.g. in: ```julia julia> @report_opt 2//2 ┌ Warning: skipping (::Base.var"#show_bound#661")(io::IO, b) @ Base show.jl:2777 to avoid parsing too much code └ @ Revise ~/.julia/packages/Revise/bAgL0/src/packagedef.jl:1092 ┌ Warning: skipping (::Base.var"#show_bound#661")(io::IO, b) @ Base show.jl:2777 to avoid parsing too much code └ @ Revise ~/.julia/packages/Revise/bAgL0/src/packagedef.jl:1092 ═════ 32 possible errors found ═════ ┌ //(n::Int64, d::Int64) @ Base ./rational.jl:84 │┌ Rational(n::Int64, d::Int64) @ Base ./rational.jl:48 ││┌ Rational{Int64}(num::Int64, den::Int64) @ Base ./rational.jl:32 │││┌ __throw_rational_argerror_zero(T::Type{Int64}) @ Base ./rational.jl:30 ││││┌ string(::String, ::Type{Int64}, ::String, ::Type{Int64}, ::String) @ Base ./strings/io.jl:189 │││││┌ print_to_string(::String, ::Type{Int64}, ::String, ::Type{Int64}, ::String) @ Base ./strings/io.jl:148 ││││││┌ print(io::IOBuffer, x::DataType) @ Base ./strings/io.jl:35 │││││││┌ show(io::IOBuffer, x::DataType) @ Base ./show.jl:970 ││││││││┌ _show_type(io::IOBuffer, x::Type) @ Base ./show.jl:975 │││││││││┌ show_typealias(io::IOBuffer, x::Type) @ Base ./show.jl:810 ││││││││││┌ make_typealias(x::Type) @ Base ./show.jl:620 │││││││││││┌ modulesof!(s::Set{Module}, x::Type) @ Base ./show.jl:595 ││││││││││││ runtime dispatch detected: Base.modulesof!(s::Set{Module}, %20::Any)::Any │││││││││││└──────────────────── │││││││││││┌ modulesof!(s::Set{Module}, x::Type) @ Base ./show.jl:596 ││││││││││││ runtime dispatch detected: Base.modulesof!(s::Set{Module}, %34::Any)::Any │││││││││││└──────────────────── │││││││││││┌ modulesof!(s::Set{Module}, x::TypeVar) @ Base ./show.jl:589 ││││││││││││ runtime dispatch detected: Base.modulesof!(s::Set{Module}, %1::Any)::Set{Module} │││││││││││└──────────────────── │││││││││┌ show_typealias(io::IOBuffer, x::Type) @ Base ./show.jl:813 ││││││││││┌ show_typealias(io::IOBuffer, name::GlobalRef, x::Type, env::Core.SimpleVector, wheres::Vector{TypeVar}) @ Base ./show.jl:760 │││││││││││┌ show_typeparams(io::IOContext{IOBuffer}, env::Core.SimpleVector, orig::Core.SimpleVector, wheres::Vector{TypeVar}) @ Base ./show.jl:724 ││││││││││││┌ show(io::IOContext{IOBuffer}, tv::TypeVar) @ Base ./show.jl:2788 │││││││││││││┌ (::Base.var"#show_bound#661")(io::IOContext{IOBuffer}, b::Any) @ Base ./show.jl:2780 ││││││││││││││ runtime dispatch detected: show(io::IOContext{IOBuffer}, b::Any)::Any │││││││││││││└──────────────────── │││││││││││┌ show_typeparams(io::IOContext{IOBuffer}, env::Core.SimpleVector, orig::Core.SimpleVector, wheres::Vector{TypeVar}) @ Base ./show.jl:719 ││││││││││││ runtime dispatch detected: show(io::IOContext{IOBuffer}, %252::Any)::Any │││││││││││└──────────────────── │││││││││││┌ show_typeparams(io::IOContext{IOBuffer}, env::Core.SimpleVector, orig::Core.SimpleVector, wheres::Vector{TypeVar}) @ Base ./show.jl:722 ││││││││││││ runtime dispatch detected: show(io::IOContext{IOBuffer}, %313::Any)::Any │││││││││││└──────────────────── │││││││││││┌ show_typeparams(io::IOContext{IOBuffer}, env::Core.SimpleVector, orig::Core.SimpleVector, wheres::Vector{TypeVar}) @ Base ./show.jl:727 ││││││││││││ runtime dispatch detected: show(io::IOContext{IOBuffer}, %191::Any)::Any │││││││││││└──────────────────── ││││││││┌ _show_type(io::IOBuffer, x::Type) @ Base ./show.jl:978 │││││││││┌ show_datatype(io::IOBuffer, x::DataType) @ Base ./show.jl:1094 ││││││││││┌ show_datatype(io::IOBuffer, x::DataType, wheres::Vector{TypeVar}) @ Base ./show.jl:1097 │││││││││││┌ maybe_kws_nt(x::DataType) @ Base ./show.jl:1085 ││││││││││││ runtime dispatch detected: eltype(%76::DataType)::Any │││││││││││└──────────────────── ││││││││││┌ show_datatype(io::IOBuffer, x::DataType, wheres::Vector{TypeVar}) @ Base ./show.jl:1186 │││││││││││┌ show_typeparams(io::IOBuffer, env::Core.SimpleVector, orig::Core.SimpleVector, wheres::Vector{TypeVar}) @ Base ./show.jl:724 ││││││││││││┌ show(io::IOBuffer, tv::TypeVar) @ Base ./show.jl:2788 │││││││││││││┌ (::Base.var"#show_bound#661")(io::IOBuffer, b::Any) @ Base ./show.jl:2780 ││││││││││││││ runtime dispatch detected: show(io::IOBuffer, b::Any)::Any │││││││││││││└──────────────────── │││││││││││┌ show_typeparams(io::IOBuffer, env::Core.SimpleVector, orig::Core.SimpleVector, wheres::Vector{TypeVar}) @ Base ./show.jl:719 ││││││││││││ runtime dispatch detected: show(io::IOBuffer, %250::Any)::Any │││││││││││└──────────────────── │││││││││││┌ show_typeparams(io::IOBuffer, env::Core.SimpleVector, orig::Core.SimpleVector, wheres::Vector{TypeVar}) @ Base ./show.jl:722 ││││││││││││ runtime dispatch detected: show(io::IOBuffer, %310::Any)::Any │││││││││││└──────────────────── │││││││││││┌ show_typeparams(io::IOBuffer, env::Core.SimpleVector, orig::Core.SimpleVector, wheres::Vector{TypeVar}) @ Base ./show.jl:727 ││││││││││││ runtime dispatch detected: show(io::IOBuffer, %190::Any)::Any │││││││││││└──────────────────── ││││││││││┌ show_datatype(io::IOBuffer, x::DataType, wheres::Vector{TypeVar}) @ Base ./show.jl:1157 │││││││││││ runtime dispatch detected: show(io::IOBuffer, %224::Any)::Any ││││││││││└──────────────────── ││││││││││┌ show_datatype(io::IOBuffer, x::DataType, wheres::Vector{TypeVar}) @ Base ./show.jl:1162 │││││││││││ runtime dispatch detected: show(io::IOBuffer, %54::Any)::Any ││││││││││└──────────────────── ││││││││││┌ show_datatype(io::IOBuffer, x::DataType, wheres::Vector{TypeVar}) @ Base ./show.jl:1148 │││││││││││ runtime dispatch detected: show(io::IOBuffer, %57::Any)::Any ││││││││││└──────────────────── ││││││││││┌ show_datatype(io::IOBuffer, x::DataType, wheres::Vector{TypeVar}) @ Base ./show.jl:1150 │││││││││││ runtime dispatch detected: show(io::IOBuffer, %54::Any)::Any ││││││││││└──────────────────── ││││││││││┌ show_datatype(io::IOBuffer, x::DataType, wheres::Vector{TypeVar}) @ Base ./show.jl:1172 │││││││││││ runtime dispatch detected: Base.show_at_namedtuple(io::IOBuffer, %329::Tuple, %328::DataType)::Any ││││││││││└──────────────────── ││││││││┌ _show_type(io::IOBuffer, x::Type) @ Base ./show.jl:981 │││││││││┌ show_unionaliases(io::IOBuffer, x::Union) @ Base ./show.jl:901 ││││││││││┌ make_typealiases(x::Union) @ Base ./show.jl:822 │││││││││││┌ modulesof!(s::Set{Module}, x::Union) @ Base ./show.jl:595 ││││││││││││ runtime dispatch detected: Base.modulesof!(s::Set{Module}, %3::Any)::Any │││││││││││└──────────────────── │││││││││││┌ modulesof!(s::Set{Module}, x::Union) @ Base ./show.jl:596 ││││││││││││ runtime dispatch detected: Base.modulesof!(s::Set{Module}, %17::Any)::Any │││││││││││└──────────────────── │││││││││┌ show_unionaliases(io::IOBuffer, x::Union) @ Base ./show.jl:914 ││││││││││ runtime dispatch detected: show(io::IOBuffer, %89::Any)::Any │││││││││└──────────────────── │││││││││┌ show_unionaliases(io::IOBuffer, x::Union) @ Base ./show.jl:920 ││││││││││ runtime dispatch detected: Base.show_typealias(io::IOBuffer, %206::Any, x::Union, %204::Core.SimpleVector, %205::Vector{TypeVar})::Any │││││││││└──────────────────── │││││││││┌ show_unionaliases(io::IOBuffer, x::Union) @ Base ./show.jl:928 ││││││││││ runtime dispatch detected: Base.show_typealias(io::IOBuffer, %269::Any, x::Union, %267::Core.SimpleVector, %268::Vector{TypeVar})::Any │││││││││└──────────────────── ││││││││┌ _show_type(io::IOBuffer, x::Type) @ Base ./show.jl:985 │││││││││┌ show_delim_array(io::IOBuffer, itr::Vector{Any}, op::Char, delim::Char, cl::Char, delim_one::Bool) @ Base ./show.jl:1392 ││││││││││┌ show_delim_array(io::IOBuffer, itr::Vector{Any}, op::Char, delim::Char, cl::Char, delim_one::Bool, i1::Int64, l::Int64) @ Base ./show.jl:1403 │││││││││││ runtime dispatch detected: show(%3::IOContext{IOBuffer}, %52::Any)::Any ││││││││││└──────────────────── ││││││││┌ _show_type(io::IOBuffer, x::Type) @ Base ./show.jl:1012 │││││││││┌ show_datatype(io::IOContext{IOBuffer}, x::DataType, wheres::Vector{TypeVar}) @ Base ./show.jl:1185 ││││││││││┌ show_type_name(io::IOContext{IOBuffer}, tn::Core.TypeName) @ Base ./show.jl:1059 │││││││││││ runtime dispatch detected: Base.isvisible(%29::Symbol, %86::Module, %80::Any)::Bool ││││││││││└──────────────────── │││││││││┌ show_datatype(io::IOContext{IOBuffer}, x::DataType, wheres::Vector{TypeVar}) @ Base ./show.jl:1157 ││││││││││ runtime dispatch detected: show(io::IOContext{IOBuffer}, %227::Any)::Any │││││││││└──────────────────── │││││││││┌ show_datatype(io::IOContext{IOBuffer}, x::DataType, wheres::Vector{TypeVar}) @ Base ./show.jl:1162 ││││││││││ runtime dispatch detected: show(io::IOContext{IOBuffer}, %55::Any)::Any │││││││││└──────────────────── │││││││││┌ show_datatype(io::IOContext{IOBuffer}, x::DataType, wheres::Vector{TypeVar}) @ Base ./show.jl:1148 ││││││││││ runtime dispatch detected: show(io::IOContext{IOBuffer}, %58::Any)::Any │││││││││└──────────────────── │││││││││┌ show_datatype(io::IOContext{IOBuffer}, x::DataType, wheres::Vector{TypeVar}) @ Base ./show.jl:1150 ││││││││││ runtime dispatch detected: show(io::IOContext{IOBuffer}, %55::Any)::Any │││││││││└──────────────────── │││││││││┌ show_datatype(io::IOContext{IOBuffer}, x::DataType, wheres::Vector{TypeVar}) @ Base ./show.jl:1172 ││││││││││ runtime dispatch detected: Base.show_at_namedtuple(io::IOContext{IOBuffer}, %338::Tuple, %337::DataType)::Any │││││││││└──────────────────── │││││││││┌ show_datatype(io::IOContext{IOBuffer}, x::DataType, wheres::Vector{TypeVar}) @ Base ./show.jl:1180 ││││││││││ runtime dispatch detected: Base.show_at_namedtuple(io::IOContext{IOBuffer}, %387::Tuple, %391::DataType)::Any │││││││││└──────────────────── ││││││││┌ _show_type(io::IOBuffer, x::Type) @ Base ./show.jl:1014 │││││││││ runtime dispatch detected: show(%98::IOContext{IOBuffer}, %99::Any)::Any ││││││││└──────────────────── │││││││┌ show(io::IOBuffer, x::DataType) @ Base ./show.jl:970 ││││││││ runtime dispatch detected: Base._show_type(io::IOBuffer, %1::Any)::Nothing │││││││└──────────────────── ``` I haven't looked through all instances thoroughly, just a quick `grep`. However, this PR should address several common cases. (cherry picked from commit 2230f7989700d0c1983a6d0b566c9f0899615f9c) --- base/Enums.jl | 21 +++++++++++---------- base/abstractarray.jl | 16 +++++++++------- base/c.jl | 2 +- base/indices.jl | 4 ++-- base/intfuncs.jl | 4 ++-- base/io.jl | 2 +- base/iterators.jl | 6 +++--- base/multidimensional.jl | 2 +- base/parse.jl | 2 +- base/rational.jl | 2 +- base/sort.jl | 2 +- base/subarray.jl | 5 +++-- base/tuple.jl | 2 +- 13 files changed, 37 insertions(+), 33 deletions(-) diff --git a/base/Enums.jl b/base/Enums.jl index 45a1b66753484..6e9efd8ccde38 100644 --- a/base/Enums.jl +++ b/base/Enums.jl @@ -90,7 +90,7 @@ 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"))) +@noinline enum_argument_error(typename, x) = throw(ArgumentError(LazyString("invalid value for Enum ", typename, ": ", x))) """ @enum EnumName[::BaseType] value1[=x] value2[=y] @@ -143,7 +143,7 @@ julia> Symbol(apple) """ macro enum(T::Union{Symbol,Expr}, syms...) if isempty(syms) - throw(ArgumentError("no arguments given for Enum $T")) + throw(ArgumentError(LazyString("no arguments given for Enum ", T))) end basetype = Int32 typename = T @@ -151,10 +151,11 @@ macro enum(T::Union{Symbol,Expr}, syms...) 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")) + throw(ArgumentError( + LazyString("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")) + throw(ArgumentError(LazyString("invalid type expression for enum ", T))) end values = Vector{basetype}() seen = Set{Symbol}() @@ -169,32 +170,32 @@ macro enum(T::Union{Symbol,Expr}, syms...) s isa LineNumberNode && continue if isa(s, Symbol) if i == typemin(basetype) && !isempty(values) - throw(ArgumentError("overflow in value \"$s\" of Enum $typename")) + throw(ArgumentError(LazyString("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")) + throw(ArgumentError(LazyString("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))) + throw(ArgumentError(LazyString("invalid argument for Enum ", typename, ": ", s))) end s = s::Symbol if !Base.isidentifier(s) - throw(ArgumentError("invalid name for Enum $typename; \"$s\" is not a valid identifier")) + throw(ArgumentError(LazyString("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")) + throw(ArgumentError(LazyString("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")) + throw(ArgumentError(LazyString("name \"", s, "\" in Enum ", typename, " is not unique"))) end push!(seen, s) if length(values) == 1 diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 56390de1eb5d3..058297b3c3152 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -748,7 +748,7 @@ julia> checkindex(Bool, 1:20, 21) false ``` """ -checkindex(::Type{Bool}, inds, i) = throw(ArgumentError("unable to check bounds for indices of type $(typeof(i))")) +checkindex(::Type{Bool}, inds, i) = throw(ArgumentError(LazyString("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::IdentityUnitRange, i::Real) = checkindex(Bool, inds.indices, i) checkindex(::Type{Bool}, inds::OneTo{T}, i::T) where {T<:BitInteger} = unsigned(i - one(i)) < unsigned(last(inds)) @@ -1519,12 +1519,14 @@ much more common case where aliasing does not occur. By default, unaliascopy(A::Array) = copy(A) unaliascopy(A::AbstractArray)::typeof(A) = (@noinline; _unaliascopy(A, copy(A))) _unaliascopy(A::T, C::T) where {T} = C -_unaliascopy(A, C) = throw(ArgumentError(""" - an array of type `$(typename(typeof(A)).wrapper)` shares memory with another argument - and must make a preventative copy of itself in order to maintain consistent semantics, - but `copy(::$(typeof(A)))` returns a new array of type `$(typeof(C))`. - To fix, implement: - `Base.unaliascopy(A::$(typename(typeof(A)).wrapper))::typeof(A)`""")) +function _unaliascopy(A, C) + Aw = typename(typeof(A)).wrapper + throw(ArgumentError(LazyString("an array of type `", Aw, "` shares memory with another argument ", + "and must make a preventative copy of itself in order to maintain consistent semantics, ", + "but `copy(::", typeof(A), ")` returns a new array of type `", typeof(C), "`.\n", + """To fix, implement: + `Base.unaliascopy(A::""", Aw, ")::typeof(A)`"))) +end unaliascopy(A) = A """ diff --git a/base/c.jl b/base/c.jl index eb552d3507662..c1b34579e0a0b 100644 --- a/base/c.jl +++ b/base/c.jl @@ -344,7 +344,7 @@ function ccall_macro_lower(convention, func, rettype, types, args, nreq) check = quote if !isa(func, Ptr{Cvoid}) name = $name - throw(ArgumentError("interpolated function `$name` was not a Ptr{Cvoid}, but $(typeof(func))")) + throw(ArgumentError(LazyString("interpolated function `", name, "` was not a Ptr{Cvoid}, but ", typeof(func)))) end end push!(statements, check) diff --git a/base/indices.jl b/base/indices.jl index 0bd46f9787dab..bf4fd8b5a7cb6 100644 --- a/base/indices.jl +++ b/base/indices.jl @@ -310,9 +310,9 @@ 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))")) + throw(ArgumentError(LazyString("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))")) +to_index(i) = throw(ArgumentError(LazyString("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: diff --git a/base/intfuncs.jl b/base/intfuncs.jl index 3f1c7f4367b4f..e27c5f665b3a8 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -954,7 +954,7 @@ julia> bitstring(2.2) ``` """ function bitstring(x::T) where {T} - isprimitivetype(T) || throw(ArgumentError("$T not a primitive type")) + isprimitivetype(T) || throw(ArgumentError(LazyString(T, " not a primitive type"))) sz = sizeof(T) * 8 str = StringMemory(sz) i = sz @@ -1054,7 +1054,7 @@ julia> digits!([2, 2, 2, 2, 2, 2], 10, base = 2) 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")) + throw(ArgumentError(LazyString("type ", T, " too small for base ", base))) isempty(a) && return a if base > 0 diff --git a/base/io.jl b/base/io.jl index fb883234be4df..a0b80f6f3e056 100644 --- a/base/io.jl +++ b/base/io.jl @@ -1426,7 +1426,7 @@ 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")) + ismarked(io) || throw(ArgumentError(LazyString(T, " not marked"))) m = io.mark seek(io, m) io.mark = -1 # must be after seek, or seek may fail diff --git a/base/iterators.jl b/base/iterators.jl index a0b3a6cd4672d..8fa09e6d7e406 100644 --- a/base/iterators.jl +++ b/base/iterators.jl @@ -15,7 +15,7 @@ using .Base: AbstractRange, AbstractUnitRange, UnitRange, LinearIndices, TupleOrBottom, (:), |, +, -, *, !==, !, ==, !=, <=, <, >, >=, missing, any, _counttuple, eachindex, ntuple, zero, prod, reduce, in, firstindex, lastindex, - tail, fieldtypes, min, max, minimum, zero, oneunit, promote, promote_shape + tail, fieldtypes, min, max, minimum, zero, oneunit, promote, promote_shape, LazyString using Core: @doc if Base !== Core.Compiler @@ -1099,7 +1099,7 @@ _prod_size(t::Tuple) = (_prod_size1(t[1], IteratorSize(t[1]))..., _prod_size(tai _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))")) + throw(ArgumentError(LazyString("Cannot compute size for object of type ", typeof(a)))) axes(P::ProductIterator) = _prod_indices(P.iterators) _prod_indices(::Tuple{}) = () @@ -1107,7 +1107,7 @@ _prod_indices(t::Tuple) = (_prod_axes1(t[1], IteratorSize(t[1]))..., _prod_indic _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))")) + throw(ArgumentError(LazyString("Cannot compute indices for object of type ", typeof(a)))) ndims(p::ProductIterator) = length(axes(p)) length(P::ProductIterator) = reduce(checked_mul, size(P); init=1) diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 80a76a5c34ef5..519ac06e6e7ec 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -589,7 +589,7 @@ module IteratorsMD else # Given the fact that StepRange 1:2:4 === 1:2:3, we lost the original size information # and thus cannot calculate the correct linear indices when the steps are not 1. - throw(ArgumentError("LinearIndices for $(typeof(inds)) with non-1 step size is not yet supported.")) + throw(ArgumentError(LazyString("LinearIndices for ", typeof(inds), " with non-1 step size is not yet supported."))) end end diff --git a/base/parse.jl b/base/parse.jl index 3f714934206fc..2530e5a46146a 100644 --- a/base/parse.jl +++ b/base/parse.jl @@ -386,7 +386,7 @@ function tryparse_internal(::Type{T}, s::AbstractString, raise::Bool; kwargs...) 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")) + throw(ArgumentError(LazyString("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) diff --git a/base/rational.jl b/base/rational.jl index b27baf5973d3b..36c26f3da8476 100644 --- a/base/rational.jl +++ b/base/rational.jl @@ -27,7 +27,7 @@ end checked_den(num::T, den::T) where T<:Integer = checked_den(T, num, den) 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)")) +@noinline __throw_rational_argerror_zero(T) = throw(ArgumentError(LazyString("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) if T <: Union{Unsigned, Bool} diff --git a/base/sort.jl b/base/sort.jl index fc27d30499cfb..eb28daca9699f 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -2461,7 +2461,7 @@ function _sort!(v::AbstractVector, a::Algorithm, o::Ordering, kw) @getkw lo hi scratch legacy_dispatch_entry if legacy_dispatch_entry === a # This error prevents infinite recursion for unknown algorithms - throw(ArgumentError("Base.Sort._sort!(::$(typeof(v)), ::$(typeof(a)), ::$(typeof(o)), ::Any) is not defined")) + throw(ArgumentError(LazyString("Base.Sort._sort!(::", typeof(v), ", ::", typeof(a), ", ::", typeof(o), ", ::Any) is not defined"))) else sort!(v, lo, hi, a, o) scratch diff --git a/base/subarray.jl b/base/subarray.jl index b67b917a478b3..d7011c50421c0 100644 --- a/base/subarray.jl +++ b/base/subarray.jl @@ -424,7 +424,8 @@ 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]))")) +substrides(strds, I::Tuple{Any, Vararg{Any}}) = throw(ArgumentError( + LazyString("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] @@ -436,7 +437,7 @@ compute_stride1(s, inds, I::Tuple{ScalarIndex, Vararg{Any}}) = (@inline; compute_stride1(s*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]))")) +compute_stride1(s, inds, I::Tuple{Any, Vararg{Any}}) = throw(ArgumentError(LazyString("invalid strided index type ", typeof(I[1])))) elsize(::Type{<:SubArray{<:Any,<:Any,P}}) where {P} = elsize(P) diff --git a/base/tuple.jl b/base/tuple.jl index 3e39e8d63cb86..23b2270e96cc4 100644 --- a/base/tuple.jl +++ b/base/tuple.jl @@ -458,7 +458,7 @@ _totuple(::Type{Tuple{}}, itr, s...) = () function _totuple_err(@nospecialize T) @noinline - throw(ArgumentError("too few elements for tuple type $T")) + throw(ArgumentError(LazyString("too few elements for tuple type ", T))) end function _totuple(::Type{T}, itr, s::Vararg{Any,N}) where {T,N} From 90c432f226da00d026a9d6c7886b43679c59ea35 Mon Sep 17 00:00:00 2001 From: Oscar Smith Date: Mon, 10 Jun 2024 12:45:39 -0400 Subject: [PATCH 35/71] Document GenericMemory and AtomicMemory (#54642) Closes https://github.com/JuliaLang/julia/issues/53854. After talking with @vtjnash, we are ready to commit to the `GenericMemory` interface. Sorry @nsajko that this took me so long to get around to. --------- Co-authored-by: Marek Kaluba Co-authored-by: Neven Sajko (cherry picked from commit 589fd1a0266eef6d8d234a59754a72a1802757cb) --- base/genericmemory.jl | 32 +++++++++++++++++++++++++++----- doc/src/base/arrays.md | 1 + 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/base/genericmemory.jl b/base/genericmemory.jl index c1dc215a68d33..82a956411aabd 100644 --- a/base/genericmemory.jl +++ b/base/genericmemory.jl @@ -3,9 +3,21 @@ ## genericmemory.jl: Managed Memory """ - GenericMemory{kind::Symbol, T, addrspace=Core.CPU} <: AbstractVector{T} + GenericMemory{kind::Symbol, T, addrspace=Core.CPU} <: DenseVector{T} -One-dimensional dense array with elements of type `T`. +Fixed-size [`DenseVector{T}`](@ref DenseVector). + +`kind` can currently be either `:not_atomic` or `:atomic`. For details on what `:atomic` implies, see [`AtomicMemory`](@ref) + +`addrspace` can currently only be set to Core.CPU. It is designed to to permit extension by other systems +such as GPUs, which might define values such as: +``` +module CUDA +const Generic = bitcast(Core.AddrSpace{CUDA}, 0) +const Global = bitcast(Core.AddrSpace{CUDA}, 1) +end +``` +The exact semantics of these other addrspaces is defined by the specific backend, but will error if the user is attempting to access these on the CPU. !!! compat "Julia 1.11" This type requires Julia 1.11 or later. @@ -15,7 +27,7 @@ GenericMemory """ Memory{T} == GenericMemory{:not_atomic, T, Core.CPU} -One-dimensional dense array with elements of type `T`. +Fixed-size [`DenseVector{T}`](@ref DenseVector). !!! compat "Julia 1.11" This type requires Julia 1.11 or later. @@ -25,8 +37,18 @@ Memory """ AtomicMemory{T} == GenericMemory{:atomic, T, Core.CPU} -One-dimensional dense array with elements of type `T`, where each element is -independently atomic when accessed, and cannot be set non-atomically. +Fixed-size [`DenseVector{T}`](@ref DenseVector). +Access to its any of its elements is performed atomically (with `:monotonic` ordering). +Setting any of the elements must be accomplished using the `@atomic` macro and explicitly specifying ordering. + +!!! warning + Each element is independently atomic when accessed, and cannot be set non-atomically. + Currently the `@atomic` macro and higher level interface have not been completed, + but the building blocks for a future implementation are the internal intrinsics + `Core.memoryrefget`, `Core.memoryrefset!`, `Core.memoryref_isassigned`, `Core.memoryrefswap!`, + `Core.memoryrefmodify!`, and `Core.memoryrefreplace!`. + +For details, see [Atomic Operations](@ref man-atomic-operations) !!! compat "Julia 1.11" This type requires Julia 1.11 or later. diff --git a/doc/src/base/arrays.md b/doc/src/base/arrays.md index 20e8e81614b9e..9acf11ea4fa68 100644 --- a/doc/src/base/arrays.md +++ b/doc/src/base/arrays.md @@ -30,6 +30,7 @@ Base.StridedArray Base.StridedVector Base.StridedMatrix Base.StridedVecOrMat +Base.GenericMemory Base.Memory Base.MemoryRef Base.Slices From cfe9c49e5523ea4eabec056c0447fef84f721bee Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Mon, 10 Jun 2024 16:01:35 -0400 Subject: [PATCH 36/71] make: use `readelf` for LLVM symbol version detection (#54713) Apparently on some distributions `nm --with-symbol-versions` does not report symbol versions, despite the flag. `readelf` should be a more reliable alternative which is also part of binutils. See https://github.com/spack/spack/issues/44534#issuecomment-2151796917 for more information (cherry picked from commit d0f165f056f6e6f6e69fa963539934bc665311be) --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 5e1d150e8b06b..f1c71c28a7265 100644 --- a/src/Makefile +++ b/src/Makefile @@ -126,7 +126,7 @@ ifeq ($(JULIACODEGEN),LLVM) ifneq ($(USE_SYSTEM_LLVM),0) # USE_SYSTEM_LLVM != 0 CG_LLVMLINK += $(LLVM_LDFLAGS) $(shell $(LLVM_CONFIG_HOST) --libs --system-libs) -LLVM_SHLIB_SYMBOL_VERSION := $(shell nm -D --with-symbol-versions $(shell $(LLVM_CONFIG_HOST) --libfiles --link-shared | awk '{print $1; exit}') | \ +LLVM_SHLIB_SYMBOL_VERSION := $(shell readelf -W --dyn-syms $(shell $(LLVM_CONFIG_HOST) --libfiles --link-shared | awk '{print $1; exit}') | \ grep _ZN4llvm3Any6TypeId | head -n 1 | sed -ne 's/.*@//p') # HACK: llvm-config doesn't correctly point to shared libs on all platforms From e6dd695de5abc56b851ad74e601d1fa806218c57 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Tue, 11 Jun 2024 05:53:26 -0400 Subject: [PATCH 37/71] REPL: improve prompt! async function handler (#54760) (cherry picked from commit e7893a1fa4c7310a3d8abd2353c54f924faa3d65) --- stdlib/REPL/src/LineEdit.jl | 71 +++++++++++++++++++---------------- stdlib/REPL/src/REPL.jl | 2 +- stdlib/REPL/src/precompile.jl | 12 ++---- 3 files changed, 42 insertions(+), 43 deletions(-) diff --git a/stdlib/REPL/src/LineEdit.jl b/stdlib/REPL/src/LineEdit.jl index 0a5329816101a..116bb10bae273 100644 --- a/stdlib/REPL/src/LineEdit.jl +++ b/stdlib/REPL/src/LineEdit.jl @@ -76,10 +76,10 @@ mutable struct MIState key_repeats::Int last_action::Symbol current_action::Symbol - async_channel::Channel + async_channel::Channel{Function} end -MIState(i, mod, c, a, m) = MIState(i, mod, c, a, m, String[], 0, Char[], 0, :none, :none, Channel()) +MIState(i, mod, c, a, m) = MIState(i, mod, c, a, m, String[], 0, Char[], 0, :none, :none, Channel{Function}()) const BufferLike = Union{MIState,ModeState,IOBuffer} const State = Union{MIState,ModeState} @@ -2829,40 +2829,45 @@ function prompt!(term::TextTerminal, prompt::ModalInterface, s::MIState = init_s try activate(prompt, s, term, term) old_state = mode(s) + l = Base.ReentrantLock() + t = @async while true + fcn = take!(s.async_channel) + status = @lock l fcn(s) + status ∈ (:ok, :ignore) || break + end + Base.errormonitor(t) while true kmap = keymap(s, prompt) - waitany(( - @async(eof(term) || peek(term, Char)), - @async(wait(s.async_channel)), - ), throw=true) - fcn = isempty(s.async_channel) ? match_input(kmap, s) : take!(s.async_channel) - kdata = keymap_data(s, prompt) - s.current_action = :unknown # if the to-be-run action doesn't update this field, - # :unknown will be recorded in the last_action field - local status - # errors in keymaps shouldn't cause the REPL to fail, so wrap in a - # try/catch block - try - status = fcn(s, kdata) - catch e - @error "Error in the keymap" exception=e,catch_backtrace() - # try to cleanup and get `s` back to its original state before returning - transition(s, :reset) - transition(s, old_state) - status = :done - end - status !== :ignore && (s.last_action = s.current_action) - if status === :abort - s.aborted = true - return buffer(s), false, false - elseif status === :done - return buffer(s), true, false - elseif status === :suspend - if Sys.isunix() - return buffer(s), true, true + fcn = match_input(kmap, s) + @lock l begin + kdata = keymap_data(s, prompt) + s.current_action = :unknown # if the to-be-run action doesn't update this field, + # :unknown will be recorded in the last_action field + local status + # errors in keymaps shouldn't cause the REPL to fail, so wrap in a + # try/catch block + try + status = fcn(s, kdata) + catch e + @error "Error in the keymap" exception=e,catch_backtrace() + # try to cleanup and get `s` back to its original state before returning + transition(s, :reset) + transition(s, old_state) + status = :done + end + status !== :ignore && (s.last_action = s.current_action) + if status === :abort + s.aborted = true + return buffer(s), false, false + elseif status === :done + return buffer(s), true, false + elseif status === :suspend + if Sys.isunix() + return buffer(s), true, true + end + else + @assert status ∈ (:ok, :ignore) end - else - @assert status ∈ (:ok, :ignore) end end finally diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index cf6738793de92..d17ad54a802ba 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -1242,7 +1242,7 @@ function setup_interface( REPLExt = load_pkg() if REPLExt isa Module && isdefined(REPLExt, :PkgCompletionProvider) put!(s.async_channel, - function (s::MIState, _) + function (s::MIState) LineEdit.mode(s) === dummy_pkg_mode || return :ok for mode in repl.interface.modes if mode isa LineEdit.Prompt && mode.complete isa REPLExt.PkgCompletionProvider diff --git a/stdlib/REPL/src/precompile.jl b/stdlib/REPL/src/precompile.jl index 3154642b74307..7c45bc8a0be5b 100644 --- a/stdlib/REPL/src/precompile.jl +++ b/stdlib/REPL/src/precompile.jl @@ -220,14 +220,8 @@ end generate_precompile_statements() precompile(Tuple{typeof(getproperty), REPL.REPLBackend, Symbol}) - -# Helps Pkg repl mode switch -precompile(Tuple{typeof(REPL.Terminals.clear_line), REPL.Terminals.TTYTerminal}) -precompile(Tuple{typeof(Base.print), REPL.Terminals.TTYTerminal, Base.AnnotatedString{String}}) -precompile(Tuple{typeof(Base.peek), Base.TTY, Type{Char}}) -precompile(Tuple{typeof(Base.similar), Array{String, 1}}) -precompile(Tuple{typeof(Base.Iterators.enumerate), Array{String, 1}}) -precompile(Tuple{typeof(Base.setindex!), Array{String, 1}, String, Int64}) -precompile(Tuple{typeof(Base.convert), Type{Base.Dict{String, Union{Array{String, 1}, String}}}, Base.Dict{String, Any}}) +precompile(Tuple{typeof(Base.take!), Base.Channel{Function}}) +precompile(Tuple{typeof(Base.put!), Base.Channel{Function}, Function}) +precompile(Tuple{typeof(Core.kwcall), NamedTuple{names, T} where T<:Tuple where names, typeof(REPL.LineEdit.complete_line), REPL.LineEdit.EmptyCompletionProvider, Any}) end # Precompile From 2dec97f9f8e7a5ccb0e59014e5f88c646006bdfc Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Tue, 11 Jun 2024 16:18:56 -0400 Subject: [PATCH 38/71] fix double-counting and non-deterministic results in `summarysize` (#54606) fixes #53061 Co-authored-by: Orestis Ousoultzoglou (cherry picked from commit 68fe51285f928ca5ca3629ad28ede14f0877b671) --- base/reflection.jl | 15 ++++++++++++--- base/summarysize.jl | 34 +++++++++++++++++++++++++--------- src/datatype.c | 33 +++++++++++++++++++++++++++++++++ test/misc.jl | 17 +++++++++++++++++ 4 files changed, 87 insertions(+), 12 deletions(-) diff --git a/base/reflection.jl b/base/reflection.jl index 550f9cf0bceaf..fd3e7931b2ce8 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -510,6 +510,17 @@ function datatype_nfields(dt::DataType) return unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).nfields end +""" + Base.datatype_npointers(dt::DataType) -> Int + +Return the number of pointers in the layout of a datatype. +""" +function datatype_npointers(dt::DataType) + @_foldable_meta + dt.layout == C_NULL && throw(UndefRefError()) + return unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).npointers +end + """ Base.datatype_pointerfree(dt::DataType) -> Bool @@ -518,9 +529,7 @@ Can be called on any `isconcretetype`. """ function datatype_pointerfree(dt::DataType) @_foldable_meta - dt.layout == C_NULL && throw(UndefRefError()) - npointers = unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).npointers - return npointers == 0 + return datatype_npointers(dt) == 0 end """ diff --git a/base/summarysize.jl b/base/summarysize.jl index 2505824768099..4f2646c7641b7 100644 --- a/base/summarysize.jl +++ b/base/summarysize.jl @@ -8,6 +8,9 @@ struct SummarySize chargeall::Any end +nth_pointer_isdefined(obj, i::Int) = ccall(:jl_nth_pointer_isdefined, Cint, (Any, Csize_t), obj, i-1) != 0 +get_nth_pointer(obj, i::Int) = ccall(:jl_get_nth_pointer, Any, (Any, Csize_t), obj, i-1) + """ Base.summarysize(obj; exclude=Union{...}, chargeall=Union{...}) -> Int @@ -26,7 +29,7 @@ julia> Base.summarysize(1.0) 8 julia> Base.summarysize(Ref(rand(100))) -864 +848 julia> sizeof(Ref(rand(100))) 8 @@ -50,15 +53,28 @@ function summarysize(obj; val = x[i] end elseif isa(x, GenericMemory) - nf = length(x) - if @inbounds @inline isassigned(x, i) - val = x[i] + T = eltype(x) + if Base.allocatedinline(T) + np = datatype_npointers(T) + nf = length(x) * np + idx = (i-1) ÷ np + 1 + if @inbounds @inline isassigned(x, idx) + elt = x[idx] + p = (i-1) % np + 1 + if nth_pointer_isdefined(elt, p) + val = get_nth_pointer(elt, p) + end + end + else + nf = length(x) + if @inbounds @inline isassigned(x, i) + val = x[i] + end end else - nf = nfields(x) - ft = typeof(x).types - if !isbitstype(ft[i]) && isdefined(x, i) - val = getfield(x, i) + nf = datatype_npointers(typeof(x)) + if nth_pointer_isdefined(x, i) + val = get_nth_pointer(x, i) end end if nf > i @@ -82,7 +98,7 @@ end # 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 + if datatype_npointers(typeof(obj)) > 0 push!(ss.frontier_x, obj) push!(ss.frontier_i, 1) end diff --git a/src/datatype.c b/src/datatype.c index f9f7dec3d1b13..51115c032118c 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -2246,6 +2246,39 @@ JL_DLLEXPORT size_t jl_get_field_offset(jl_datatype_t *ty, int field) return jl_field_offset(ty, field - 1); } +jl_value_t *get_nth_pointer(jl_value_t *v, size_t i) +{ + jl_datatype_t *dt = (jl_datatype_t*)jl_typeof(v); + const jl_datatype_layout_t *ly = dt->layout; + uint32_t npointers = ly->npointers; + if (i >= npointers) + jl_bounds_error_int(v, i); + const uint8_t *ptrs8 = (const uint8_t *)jl_dt_layout_ptrs(ly); + const uint16_t *ptrs16 = (const uint16_t *)jl_dt_layout_ptrs(ly); + const uint32_t *ptrs32 = (const uint32_t*)jl_dt_layout_ptrs(ly); + uint32_t fld; + if (ly->flags.fielddesc_type == 0) + fld = ptrs8[i]; + else if (ly->flags.fielddesc_type == 1) + fld = ptrs16[i]; + else + fld = ptrs32[i]; + return jl_atomic_load_relaxed((_Atomic(jl_value_t*)*)(&((jl_value_t**)v)[fld])); +} + +JL_DLLEXPORT jl_value_t *jl_get_nth_pointer(jl_value_t *v, size_t i) +{ + jl_value_t *ptrf = get_nth_pointer(v, i); + if (__unlikely(ptrf == NULL)) + jl_throw(jl_undefref_exception); + return ptrf; +} + +JL_DLLEXPORT int jl_nth_pointer_isdefined(jl_value_t *v, size_t i) +{ + return get_nth_pointer(v, i) != NULL; +} + #ifdef __cplusplus } #endif diff --git a/test/misc.jl b/test/misc.jl index e870c7f491c13..f5034e012765d 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -571,6 +571,23 @@ end # issue #44780 @test summarysize(BigInt(2)^1000) > summarysize(BigInt(2)) +# issue #53061 +mutable struct S53061 + x::Union{Float64, Tuple{Float64, Float64}} + y::Union{Float64, Tuple{Float64, Float64}} +end +let s = S53061[S53061(rand(), (rand(),rand())) for _ in 1:10^4] + @test allequal(summarysize(s) for i in 1:10) +end +struct Z53061 + x::S53061 + y::Int64 +end +let z = Z53061[Z53061(S53061(rand(), (rand(),rand())), 0) for _ in 1:10^4] + @test allequal(summarysize(z) for i in 1:10) + @test abs(summarysize(z) - 640000)/640000 <= 0.01 +end + ## test conversion from UTF-8 to UTF-16 (for Windows APIs) # empty arrays From a6d098e70c5e389619d82a701e27f6fd441a5bee Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Tue, 11 Jun 2024 17:46:07 -0400 Subject: [PATCH 39/71] REPL: Fully populate the dummy Pkg prompt (#54759) (cherry picked from commit 3fc35778cc26bd9d3bb29070ce023be5d3c5f53d) --- stdlib/REPL/src/Pkg_beforeload.jl | 121 ++++++++++++++++++++++++++++++ stdlib/REPL/src/REPL.jl | 17 +---- 2 files changed, 124 insertions(+), 14 deletions(-) create mode 100644 stdlib/REPL/src/Pkg_beforeload.jl diff --git a/stdlib/REPL/src/Pkg_beforeload.jl b/stdlib/REPL/src/Pkg_beforeload.jl new file mode 100644 index 0000000000000..78b76374cf580 --- /dev/null +++ b/stdlib/REPL/src/Pkg_beforeload.jl @@ -0,0 +1,121 @@ +## Pkg stuff needed before Pkg has loaded + +const Pkg_pkgid = Base.PkgId(Base.UUID("44cfe95a-1eb2-52ea-b672-e2afdf69b78f"), "Pkg") +const Pkg_REPLExt_pkgid = Base.PkgId(Base.UUID("ceef7b17-42e7-5b1c-81d4-4cc4a2494ccf"), "REPLExt") + +function load_pkg() + @lock Base.require_lock begin + REPLExt = Base.require_stdlib(Pkg_pkgid, "REPLExt") + # require_stdlib does not guarantee that the `__init__` of the package is done when loading is done async + # but we need to wait for the repl mode to be set up + lock = get(Base.package_locks, Pkg_REPLExt_pkgid.uuid, nothing) + lock !== nothing && wait(lock[2]) + return REPLExt + end +end + +## Below here copied/tweaked from Pkg Types.jl so that the dummy Pkg prompt +# can populate the env correctly before Pkg loads + +function safe_realpath(path) + isempty(path) && return path + if ispath(path) + try + return realpath(path) + catch + return path + end + end + a, b = splitdir(path) + return joinpath(safe_realpath(a), b) +end + +function find_project_file(env::Union{Nothing,String}=nothing) + project_file = nothing + if env isa Nothing + project_file = Base.active_project() + project_file === nothing && return nothing # in the Pkg version these are pkgerrors + elseif startswith(env, '@') + project_file = Base.load_path_expand(env) + project_file === nothing && return nothing + elseif env isa String + if isdir(env) + isempty(readdir(env)) || return nothing + project_file = joinpath(env, Base.project_names[end]) + else + project_file = endswith(env, ".toml") ? abspath(env) : + abspath(env, Base.project_names[end]) + end + end + @assert project_file isa String && + (isfile(project_file) || !ispath(project_file) || + isdir(project_file) && isempty(readdir(project_file))) + return safe_realpath(project_file) +end + +function find_root_base_project(start_project::String) + project_file = start_project + while true + base_project_file = Base.base_project(project_file) + base_project_file === nothing && return project_file + project_file = base_project_file + end +end + +function relative_project_path(project_file::String, path::String) + # compute path relative the project + # realpath needed to expand symlinks before taking the relative path + return relpath(safe_realpath(abspath(path)), safe_realpath(dirname(project_file))) +end + +function projname(project_file::String) + p = Base.TOML.Parser() + Base.TOML.reinit!(p, read(project_file, String); filepath=project_file) + proj = Base.TOML.parse(p) + name = get(proj, "name", nothing) + if name === nothing + name = basename(dirname(project_file)) + end + for depot in Base.DEPOT_PATH + envdir = joinpath(depot, "environments") + if startswith(abspath(project_file), abspath(envdir)) + return "@" * name + end + end + return name +end + +prev_project_file = nothing +prev_project_timestamp = nothing +prev_prefix = "" + +function Pkg_promptf() + global prev_project_timestamp, prev_prefix, prev_project_file + project_file = find_project_file() + prefix = "" + if project_file !== nothing + if prev_project_file == project_file && prev_project_timestamp == mtime(project_file) + prefix = prev_prefix + else + project_name = projname(project_file) + if project_name !== nothing + root = find_root_base_project(project_file) + rootname = projname(root) + if root !== project_file + path_prefix = "/" * dirname(relative_project_path(root, project_file)) + else + path_prefix = "" + end + if textwidth(rootname) > 30 + rootname = first(rootname, 27) * "..." + end + prefix = "($(rootname)$(path_prefix)) " + prev_prefix = prefix + prev_project_timestamp = mtime(project_file) + prev_project_file = project_file + end + end + end + # Note no handling of Pkg.offline, as the Pkg version does here + return "$(prefix)pkg> " +end diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index d17ad54a802ba..a210c2fe2cf95 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -144,6 +144,8 @@ using .REPLCompletions include("TerminalMenus/TerminalMenus.jl") include("docview.jl") +include("Pkg_beforeload.jl") + @nospecialize # use only declared type signatures answer_color(::AbstractREPL) = "" @@ -1043,19 +1045,6 @@ setup_interface( extra_repl_keymap::Any = repl.options.extra_keymap ) = setup_interface(repl, hascolor, extra_repl_keymap) -const Pkg_pkgid = Base.PkgId(Base.UUID("44cfe95a-1eb2-52ea-b672-e2afdf69b78f"), "Pkg") -const Pkg_REPLExt_pkgid = Base.PkgId(Base.UUID("ceef7b17-42e7-5b1c-81d4-4cc4a2494ccf"), "REPLExt") - -function load_pkg() - @lock Base.require_lock begin - REPLExt = Base.require_stdlib(Pkg_pkgid, "REPLExt") - # require_stdlib does not guarantee that the `__init__` of the package is done when loading is done async - # but we need to wait for the repl mode to be set up - lock = get(Base.package_locks, Pkg_REPLExt_pkgid.uuid, nothing) - lock !== nothing && wait(lock[2]) - return REPLExt - end -end # This non keyword method can be precompiled which is important function setup_interface( @@ -1134,7 +1123,7 @@ function setup_interface( # Set up dummy Pkg mode that will be replaced once Pkg is loaded # use 6 dots to occupy the same space as the most likely "@v1.xx" env name - dummy_pkg_mode = Prompt("(......) $PKG_PROMPT", + dummy_pkg_mode = Prompt(Pkg_promptf, prompt_prefix = hascolor ? repl.pkg_color : "", prompt_suffix = hascolor ? (repl.envcolors ? Base.input_color : repl.input_color) : "", From 193fee8119ba2fd8299c8a0fea9714bfbcd92059 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Fri, 7 Jun 2024 22:58:32 -0400 Subject: [PATCH 40/71] lowering: Recognize argument destructuring inside macro hygiene (#54702) Fixes #54701 (cherry picked from commit 75951ecbfd89a95c13fc39bd5531214f29b98074) --- src/macroexpand.scm | 22 +++++++++------------- test/syntax.jl | 16 ++++++++++++++++ 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/macroexpand.scm b/src/macroexpand.scm index 424e921a35713..bbc21a081d56d 100644 --- a/src/macroexpand.scm +++ b/src/macroexpand.scm @@ -219,30 +219,26 @@ lst))) ;; get the name from a function formal argument expression, allowing `(escape x)` -(define (try-arg-name v) - (cond ((symbol? v) (list v)) +(define (try-arg-name v (escaped #f)) + (cond ((symbol? v) (if escaped '() (list v))) ((atom? v) '()) (else (case (car v) - ((|::|) (if (length= v 2) '() (try-arg-name (cadr v)))) - ((... kw =) (try-arg-name (cadr v))) - ((escape) (list v)) - ((hygienic-scope) (try-arg-name (cadr v))) + ((|::|) (if (length= v 2) '() (try-arg-name (cadr v) escaped))) + ((... kw =) (try-arg-name (cadr v) escaped)) + ((escape) (if escaped (list (cadr v)) '())) + ((hygienic-scope) (try-arg-name (cadr v) escaped)) + ((tuple) (apply nconc (map (lambda (e) (try-arg-name e escaped)) (cdr v)))) ((meta) ;; allow certain per-argument annotations (if (nospecialize-meta? v #t) - (try-arg-name (caddr v)) + (try-arg-name (caddr v) escaped) '())) (else '()))))) ;; get names from a formal argument list, specifying whether to include escaped ones (define (safe-arg-names lst (escaped #f)) (apply nconc - (map (lambda (v) - (let ((vv (try-arg-name v))) - (if (eq? escaped (and (pair? vv) (pair? (car vv)) (eq? (caar vv) 'escape))) - (if escaped (list (cadar vv)) vv) - '()))) - lst))) + (map (lambda (v) (try-arg-name v escaped)) lst))) ;; arg names, looking only at positional args (define (safe-llist-positional-args lst (escaped #f)) diff --git a/test/syntax.jl b/test/syntax.jl index 25d3e7282818a..a51ed9c964f5c 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -3637,3 +3637,19 @@ end @test array == [7] @test execs == 4 end + +# Issue #54701 - Macro hygiene of argument destructuring +macro makef54701() + quote + call(f) = f((1, 2)) + function $(esc(:f54701))() + call() do (a54701, b54701) + return a54701+b54701 + end + end + end +end +@makef54701 +@test f54701() == 3 +@test !@isdefined(a54701) +@test !@isdefined(b54701) From 9874d82624a8e7a50a2220448ae453de21ff562e Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Sat, 8 Jun 2024 01:10:54 -0400 Subject: [PATCH 41/71] Don't let setglobal! implicitly create bindings (#54678) PR #44231 (part of Julia 1.9) introduced the ability to modify globals with `Mod.sym = val` syntax. However, the intention of this syntax was always to modify *existing* globals in other modules. Unfortunately, as implemented, it also implicitly creates new bindings in the other module, even if the binding was not previously declared. This was not intended, but it's a bit of a syntax corner case, so nobody caught it at the time. After some extensive discussions and taking into account the near future direction we want to go with bindings (#54654 for both), the consensus was reached that we should try to undo the implicit creation of bindings (but not the ability to assign the *value* of globals in other modules). Note that this was always an error until Julia 1.9, so hopefully it hasn't crept into too many packages yet. We'll see what pkgeval says. If use is extensive, we may want to consider a softer removal strategy. Across base and stdlib, there's two cases affected by this change: 1. A left over debug statement in `precompile` that wanted to assign a new variable in Base for debugging. Removed in this PR. 2. Distributed wanting to create new bindings. This is a legimitate use case for wanting to create bindings in other modules. This is fixed in https://github.com/JuliaLang/Distributed.jl/pull/102. As noted in that PR, the recommended replacement where implicit binding creation is desired is: ``` Core.eval(mod, Expr(:global, sym)) invokelatest(setglobal!, mod, sym, val) ``` The `invokelatest` is not presently required, but may be needed by #54654, so it's included in the recommendation now. Fixes #54607 (cherry picked from commit b7e72322b3fd06bff0812aa09f28994c67ec5b67) --- base/docs/basedocs.jl | 11 ++++++++--- doc/src/manual/embedding.md | 2 +- src/builtins.c | 12 ++++++------ src/codegen.cpp | 39 ++++++++++++++++++++++++------------- src/interpreter.c | 6 +++++- src/julia-syntax.scm | 1 + src/julia.h | 2 +- src/module.c | 9 ++++++--- src/toplevel.c | 8 ++++---- test/core.jl | 1 + test/precompile.jl | 1 - test/syntax.jl | 37 +++++++++++++++++++++++++++++++++++ 12 files changed, 96 insertions(+), 33 deletions(-) diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index d4785df98ec21..d1a51ae9fdc4c 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -2490,12 +2490,17 @@ cases. See also [`setproperty!`](@ref Base.setproperty!) and [`getglobal`](@ref) # Examples -```jldoctest -julia> module M end; +```jldoctest; filter = r"Stacktrace:(\\n \\[[0-9]+\\].*)*" +julia> module M; global a; end; julia> M.a # same as `getglobal(M, :a)` ERROR: UndefVarError: `a` not defined in `M` -Suggestion: check for spelling errors or missing imports. +Suggestion: add an appropriate import or assignment. This global was declared but not assigned. +Stacktrace: + [1] getproperty(x::Module, f::Symbol) + @ Base ./Base.jl:42 + [2] top-level scope + @ none:1 julia> setglobal!(M, :a, 1) 1 diff --git a/doc/src/manual/embedding.md b/doc/src/manual/embedding.md index 9df9a6c198003..f578e10764101 100644 --- a/doc/src/manual/embedding.md +++ b/doc/src/manual/embedding.md @@ -412,7 +412,7 @@ per pointer using ```c jl_module_t *mod = jl_main_module; jl_sym_t *var = jl_symbol("var"); -jl_binding_t *bp = jl_get_binding_wr(mod, var); +jl_binding_t *bp = jl_get_binding_wr(mod, var, 1); jl_checked_assignment(bp, mod, var, val); ``` diff --git a/src/builtins.c b/src/builtins.c index 85f5ec206d795..37ea8d18b2340 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -1355,7 +1355,7 @@ JL_CALLABLE(jl_f_setglobal) jl_atomic_error("setglobal!: module binding cannot be written non-atomically"); else if (order >= jl_memory_order_seq_cst) jl_fence(); - jl_binding_t *b = jl_get_binding_wr(mod, var); + jl_binding_t *b = jl_get_binding_wr(mod, var, 0); jl_checked_assignment(b, mod, var, args[2]); // release store if (order >= jl_memory_order_seq_cst) jl_fence(); @@ -1393,7 +1393,7 @@ JL_CALLABLE(jl_f_set_binding_type) JL_TYPECHK(set_binding_type!, symbol, (jl_value_t*)s); jl_value_t *ty = nargs == 2 ? (jl_value_t*)jl_any_type : args[2]; JL_TYPECHK(set_binding_type!, type, ty); - jl_binding_t *b = jl_get_binding_wr(m, s); + jl_binding_t *b = jl_get_binding_wr(m, s, 0); jl_value_t *old_ty = NULL; if (jl_atomic_cmpswap_relaxed(&b->ty, &old_ty, ty)) { jl_gc_wb(b, ty); @@ -1420,7 +1420,7 @@ JL_CALLABLE(jl_f_swapglobal) if (order == jl_memory_order_notatomic) jl_atomic_error("swapglobal!: module binding cannot be written non-atomically"); // is seq_cst already, no fence needed - jl_binding_t *b = jl_get_binding_wr(mod, var); + jl_binding_t *b = jl_get_binding_wr(mod, var, 0); return jl_checked_swap(b, mod, var, args[2]); } @@ -1438,7 +1438,7 @@ JL_CALLABLE(jl_f_modifyglobal) JL_TYPECHK(modifyglobal!, symbol, (jl_value_t*)var); if (order == jl_memory_order_notatomic) jl_atomic_error("modifyglobal!: module binding cannot be written non-atomically"); - jl_binding_t *b = jl_get_binding_wr(mod, var); + jl_binding_t *b = jl_get_binding_wr(mod, var, 0); // is seq_cst already, no fence needed return jl_checked_modify(b, mod, var, args[2], args[3]); } @@ -1467,7 +1467,7 @@ JL_CALLABLE(jl_f_replaceglobal) jl_atomic_error("replaceglobal!: module binding cannot be written non-atomically"); if (failure_order == jl_memory_order_notatomic) jl_atomic_error("replaceglobal!: module binding cannot be accessed non-atomically"); - jl_binding_t *b = jl_get_binding_wr(mod, var); + jl_binding_t *b = jl_get_binding_wr(mod, var, 0); // is seq_cst already, no fence needed return jl_checked_replace(b, mod, var, args[2], args[3]); } @@ -1496,7 +1496,7 @@ JL_CALLABLE(jl_f_setglobalonce) jl_atomic_error("setglobalonce!: module binding cannot be written non-atomically"); if (failure_order == jl_memory_order_notatomic) jl_atomic_error("setglobalonce!: module binding cannot be accessed non-atomically"); - jl_binding_t *b = jl_get_binding_wr(mod, var); + jl_binding_t *b = jl_get_binding_wr(mod, var, 0); // is seq_cst already, no fence needed jl_value_t *old = jl_checked_assignonce(b, mod, var, args[2]); return old == NULL ? jl_true : jl_false; diff --git a/src/codegen.cpp b/src/codegen.cpp index 0e874e8d44be8..ad13cf8d1124e 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -940,7 +940,7 @@ static const auto jlgetbindingwrorerror_func = new JuliaFunction<>{ [](LLVMContext &C) { auto T_pjlvalue = JuliaType::get_pjlvalue_ty(C); return FunctionType::get(T_pjlvalue, - {T_pjlvalue, T_pjlvalue}, false); + {T_pjlvalue, T_pjlvalue, getInt32Ty(C)}, false); }, nullptr, }; @@ -2087,7 +2087,7 @@ static Type *julia_type_to_llvm(jl_codectx_t &ctx, jl_value_t *jt, bool *isboxed static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value *fval, StringRef name, jl_value_t *sig, jl_value_t *jlrettype, bool is_opaque_closure, bool gcstack_arg, BitVector *used_arguments=nullptr, size_t *args_begin=nullptr); static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaval = -1); static Value *global_binding_pointer(jl_codectx_t &ctx, jl_module_t *m, jl_sym_t *s, - jl_binding_t **pbnd, bool assign); + jl_binding_t **pbnd, bool assign, bool alloc); static jl_cgval_t emit_checked_var(jl_codectx_t &ctx, Value *bp, jl_sym_t *name, jl_value_t *scope, bool isvol, MDNode *tbaa); static jl_cgval_t emit_sparam(jl_codectx_t &ctx, size_t i); static Value *emit_condition(jl_codectx_t &ctx, const jl_cgval_t &condV, const Twine &msg); @@ -3176,7 +3176,7 @@ static jl_value_t *jl_ensure_rooted(jl_codectx_t &ctx, jl_value_t *val) static jl_cgval_t emit_globalref(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t *name, AtomicOrdering order) { jl_binding_t *bnd = NULL; - Value *bp = global_binding_pointer(ctx, mod, name, &bnd, false); + Value *bp = global_binding_pointer(ctx, mod, name, &bnd, false, false); if (bp == NULL) return jl_cgval_t(); bp = julia_binding_pvalue(ctx, bp); @@ -3195,10 +3195,10 @@ static jl_cgval_t emit_globalref(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t * static jl_cgval_t emit_globalop(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t *sym, jl_cgval_t rval, const jl_cgval_t &cmp, AtomicOrdering Order, AtomicOrdering FailOrder, bool issetglobal, bool isreplaceglobal, bool isswapglobal, bool ismodifyglobal, bool issetglobalonce, - const jl_cgval_t *modifyop) + const jl_cgval_t *modifyop, bool alloc) { jl_binding_t *bnd = NULL; - Value *bp = global_binding_pointer(ctx, mod, sym, &bnd, true); + Value *bp = global_binding_pointer(ctx, mod, sym, &bnd, true, alloc); if (bp == NULL) return jl_cgval_t(); if (bnd && !bnd->constp) { @@ -3608,7 +3608,8 @@ static bool emit_f_opglobal(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, isswapglobal, ismodifyglobal, issetglobalonce, - modifyop); + modifyop, + false); return true; } } @@ -5289,7 +5290,7 @@ static void emit_hasnofield_error_ifnot(jl_codectx_t &ctx, Value *ok, jl_sym_t * // if the reference currently bound or assign == true, // pbnd will also be assigned with the binding address static Value *global_binding_pointer(jl_codectx_t &ctx, jl_module_t *m, jl_sym_t *s, - jl_binding_t **pbnd, bool assign) + jl_binding_t **pbnd, bool assign, bool alloc) { jl_binding_t *b = jl_get_module_binding(m, s, 1); if (assign) { @@ -5319,9 +5320,17 @@ static Value *global_binding_pointer(jl_codectx_t &ctx, jl_module_t *m, jl_sym_t ctx.builder.CreateCondBr(iscached, have_val, not_found); not_found->insertInto(ctx.f); ctx.builder.SetInsertPoint(not_found); - Value *bval = ctx.builder.CreateCall(prepare_call(assign ? jlgetbindingwrorerror_func : jlgetbindingorerror_func), - { literal_pointer_val(ctx, (jl_value_t*)m), - literal_pointer_val(ctx, (jl_value_t*)s) }); + Value *bval = nullptr; + if (assign) { + bval = ctx.builder.CreateCall(prepare_call(jlgetbindingwrorerror_func), + { literal_pointer_val(ctx, (jl_value_t*)m), + literal_pointer_val(ctx, (jl_value_t*)s), + ConstantInt::get(getInt32Ty(ctx.builder.getContext()), alloc)}); + } else { + bval = ctx.builder.CreateCall(prepare_call(jlgetbindingorerror_func), + { literal_pointer_val(ctx, (jl_value_t*)m), + literal_pointer_val(ctx, (jl_value_t*)s)}); + } setName(ctx.emission_context, bval, jl_symbol_name(m->name) + StringRef(".") + jl_symbol_name(s) + ".found"); ctx.builder.CreateAlignedStore(bval, bindinggv, Align(sizeof(void*)))->setOrdering(AtomicOrdering::Release); ctx.builder.CreateBr(have_val); @@ -5338,7 +5347,8 @@ static Value *global_binding_pointer(jl_codectx_t &ctx, jl_module_t *m, jl_sym_t // this will fail at runtime, so defer to the runtime to create the error ctx.builder.CreateCall(prepare_call(jlgetbindingwrorerror_func), { literal_pointer_val(ctx, (jl_value_t*)m), - literal_pointer_val(ctx, (jl_value_t*)s) }); + literal_pointer_val(ctx, (jl_value_t*)s), + ConstantInt::get(getInt32Ty(ctx.builder.getContext()), alloc) }); CreateTrap(ctx.builder); return NULL; } @@ -5833,17 +5843,20 @@ static void emit_assignment(jl_codectx_t &ctx, jl_value_t *l, jl_value_t *r, ssi jl_module_t *mod; jl_sym_t *sym; + bool toplevel = jl_is_module(ctx.linfo->def.value); + bool alloc = toplevel; if (jl_is_symbol(l)) { mod = ctx.module; sym = (jl_sym_t*)l; } else { assert(jl_is_globalref(l)); + alloc &= jl_globalref_mod(l) == ctx.module; mod = jl_globalref_mod(l); sym = jl_globalref_name(l); } emit_globalop(ctx, mod, sym, rval_info, jl_cgval_t(), AtomicOrdering::Release, AtomicOrdering::NotAtomic, - true, false, false, false, false, nullptr); + true, false, false, false, false, nullptr, alloc); // Global variable. Does not need debug info because the debugger knows about // its memory location. } @@ -6288,7 +6301,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_ } if (jl_is_symbol(sym)) { jl_binding_t *bnd = NULL; - Value *bp = global_binding_pointer(ctx, mod, sym, &bnd, true); + Value *bp = global_binding_pointer(ctx, mod, sym, &bnd, true, true); if (bp) ctx.builder.CreateCall(prepare_call(jldeclareconst_func), { bp, literal_pointer_val(ctx, (jl_value_t*)mod), literal_pointer_val(ctx, (jl_value_t*)sym) }); diff --git a/src/interpreter.c b/src/interpreter.c index c80986cd7ded0..b65950519ebc0 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -568,9 +568,13 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip, else { jl_module_t *modu; jl_sym_t *sym; + // Plain assignment is allowed to create bindings at + // toplevel and only for the current module + int alloc = toplevel; if (jl_is_globalref(lhs)) { modu = jl_globalref_mod(lhs); sym = jl_globalref_name(lhs); + alloc &= modu == s->module; } else { assert(jl_is_symbol(lhs)); @@ -578,7 +582,7 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip, sym = (jl_sym_t*)lhs; } JL_GC_PUSH1(&rhs); - jl_binding_t *b = jl_get_binding_wr(modu, sym); + jl_binding_t *b = jl_get_binding_wr(modu, sym, alloc); jl_checked_assignment(b, modu, sym, rhs); JL_GC_POP(); } diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 7d9f8711ec714..17092ca4cf751 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -4232,6 +4232,7 @@ f(x) = yt(x) (put! globals ref #t) `(block (toplevel-only set_binding_type! ,(cadr e)) + (global ,ref) (call (core set_binding_type!) ,(cadr ref) (inert ,(caddr ref)) ,(caddr e)))) `(call (core typeassert) ,@(cdr e)))) fname lam namemap defined toplevel interp opaq globals locals)))) diff --git a/src/julia.h b/src/julia.h index c6b685733ea30..0ddbdf79e2fad 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1913,7 +1913,7 @@ JL_DLLEXPORT jl_binding_t *jl_get_binding_or_error(jl_module_t *m, jl_sym_t *var JL_DLLEXPORT jl_value_t *jl_module_globalref(jl_module_t *m, jl_sym_t *var); JL_DLLEXPORT jl_value_t *jl_get_binding_type(jl_module_t *m, jl_sym_t *var); // get binding for assignment -JL_DLLEXPORT jl_binding_t *jl_get_binding_wr(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var); +JL_DLLEXPORT jl_binding_t *jl_get_binding_wr(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var, int alloc); JL_DLLEXPORT jl_binding_t *jl_get_binding_for_method_def(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var); JL_DLLEXPORT int jl_boundp(jl_module_t *m, jl_sym_t *var); JL_DLLEXPORT int jl_defines_or_exports_p(jl_module_t *m, jl_sym_t *var); diff --git a/src/module.c b/src/module.c index 3aadb181b5e97..f33c9bc4f31d3 100644 --- a/src/module.c +++ b/src/module.c @@ -219,13 +219,16 @@ static void check_safe_newbinding(jl_module_t *m, jl_sym_t *var) static jl_module_t *jl_binding_dbgmodule(jl_binding_t *b, jl_module_t *m, jl_sym_t *var) JL_GLOBALLY_ROOTED; // get binding for assignment -JL_DLLEXPORT jl_binding_t *jl_get_binding_wr(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var) +JL_DLLEXPORT jl_binding_t *jl_get_binding_wr(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var, int alloc) { jl_binding_t *b = jl_get_module_binding(m, var, 1); jl_binding_t *b2 = jl_atomic_load_relaxed(&b->owner); if (b2 != b) { - if (b2 == NULL) + if (b2 == NULL) { check_safe_newbinding(m, var); + if (!alloc) + jl_errorf("Global %s.%s does not exist and cannot be assigned. Declare it using `global` before attempting assignment.", jl_symbol_name(m->name), jl_symbol_name(var)); + } if (b2 != NULL || (!jl_atomic_cmpswap(&b->owner, &b2, b) && b2 != b)) { jl_module_t *from = jl_binding_dbgmodule(b, m, var); if (from == m) @@ -784,7 +787,7 @@ JL_DLLEXPORT jl_value_t *jl_get_global(jl_module_t *m, jl_sym_t *var) JL_DLLEXPORT void jl_set_global(jl_module_t *m JL_ROOTING_ARGUMENT, jl_sym_t *var, jl_value_t *val JL_ROOTED_ARGUMENT) { - jl_binding_t *bp = jl_get_binding_wr(m, var); + jl_binding_t *bp = jl_get_binding_wr(m, var, 0); jl_checked_assignment(bp, m, var, val); } diff --git a/src/toplevel.c b/src/toplevel.c index caa01015c362a..a641a635562bb 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -155,7 +155,7 @@ static jl_value_t *jl_eval_module_expr(jl_module_t *parent_module, jl_expr_t *ex } } else { - jl_binding_t *b = jl_get_binding_wr(parent_module, name); + jl_binding_t *b = jl_get_binding_wr(parent_module, name, 1); jl_declare_constant(b, parent_module, name); jl_value_t *old = NULL; if (!jl_atomic_cmpswap(&b->value, &old, (jl_value_t*)newm)) { @@ -325,7 +325,7 @@ void jl_eval_global_expr(jl_module_t *m, jl_expr_t *ex, int set_type) { gs = (jl_sym_t*)arg; } if (!jl_binding_resolved_p(gm, gs)) { - jl_binding_t *b = jl_get_binding_wr(gm, gs); + jl_binding_t *b = jl_get_binding_wr(gm, gs, 1); if (set_type) { jl_value_t *old_ty = NULL; // maybe set the type too, perhaps @@ -622,7 +622,7 @@ static void import_module(jl_module_t *JL_NONNULL m, jl_module_t *import, jl_sym jl_symbol_name(name), jl_symbol_name(m->name)); } else { - b = jl_get_binding_wr(m, name); + b = jl_get_binding_wr(m, name, 1); } jl_declare_constant(b, m, name); jl_checked_assignment(b, m, name, (jl_value_t*)import); @@ -874,7 +874,7 @@ jl_value_t *jl_toplevel_eval_flex(jl_module_t *JL_NONNULL m, jl_value_t *e, int gm = m; gs = (jl_sym_t*)arg; } - jl_binding_t *b = jl_get_binding_wr(gm, gs); + jl_binding_t *b = jl_get_binding_wr(gm, gs, 1); jl_declare_constant(b, gm, gs); JL_GC_POP(); return jl_nothing; diff --git a/test/core.jl b/test/core.jl index 9950056aa8b32..55e48fdd70910 100644 --- a/test/core.jl +++ b/test/core.jl @@ -8135,6 +8135,7 @@ end @test_broken Int isa Union{Union, Type{Union{Int,T1}} where {T1}} let M = @__MODULE__ + Core.eval(M, :(global a_typed_global)) @test Core.set_binding_type!(M, :a_typed_global, Tuple{Union{Integer,Nothing}}) === nothing @test Core.get_binding_type(M, :a_typed_global) === Tuple{Union{Integer,Nothing}} @test Core.set_binding_type!(M, :a_typed_global, Tuple{Union{Integer,Nothing}}) === nothing diff --git a/test/precompile.jl b/test/precompile.jl index d8471120051db..2570a425a03b7 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -1045,7 +1045,6 @@ precompile_test_harness("code caching") do dir @test mi.specTypes.parameters[end] === Integer ? !hv : hv end - setglobal!(Main, :inval, invalidations) idxs = findall(==("verify_methods"), invalidations) idxsbits = filter(idxs) do i mi = invalidations[i-1] diff --git a/test/syntax.jl b/test/syntax.jl index a51ed9c964f5c..2933ae1db1dfa 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -3653,3 +3653,40 @@ end @test f54701() == 3 @test !@isdefined(a54701) @test !@isdefined(b54701) + +# Issue #54607 - binding creation in foreign modules should not be permitted +module Foreign54607 + # Syntactic, not dynamic + try_to_create_binding1() = (Foreign54607.foo = 2) + @eval try_to_create_binding2() = ($(GlobalRef(Foreign54607, :foo)) = 2) + function global_create_binding() + global bar + bar = 3 + end + baz = 4 + begin; + @Base.Experimental.force_compile + compiled_assign = 5 + end + @eval $(GlobalRef(Foreign54607, :gr_assign)) = 6 +end +@test_throws ErrorException (Foreign54607.foo = 1) +@test_throws ErrorException Foreign54607.try_to_create_binding1() +@test_throws ErrorException Foreign54607.try_to_create_binding2() +@test_throws ErrorException begin + @Base.Experimental.force_compile + (Foreign54607.foo = 1) +end +@test_throws ErrorException @eval (GlobalRef(Foreign54607, :gr_assign2)) = 7 +Foreign54607.global_create_binding() +@test isdefined(Foreign54607, :bar) +@test isdefined(Foreign54607, :baz) +@test isdefined(Foreign54607, :compiled_assign) +@test isdefined(Foreign54607, :gr_assign) +Foreign54607.bar = 8 +@test Foreign54607.bar == 8 +begin + @Base.Experimental.force_compile + Foreign54607.bar = 9 +end +@test Foreign54607.bar == 9 From 63b66b87dde96beb853f9041c296b8614416089a Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Sat, 8 Jun 2024 23:22:38 -0400 Subject: [PATCH 42/71] Fix uuidkey of exts in fast path of `require_stdlib` (#54730) The wrong uuid was being used for the fast path for exts (cherry picked from commit 1b6ec0d8e1ecb268dcce13d79dbdccc09d4f8938) --- base/loading.jl | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index 90302ef24c8fe..13bb282a58c52 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -2453,51 +2453,52 @@ function _require_from_serialized(uuidkey::PkgId, path::String, ocachepath::Unio end # load a serialized file directly from append_bundled_depot_path for uuidkey without stalechecks -function require_stdlib(uuidkey::PkgId, ext::Union{Nothing, String}=nothing) +function require_stdlib(package_uuidkey::PkgId, ext::Union{Nothing, String}=nothing) @lock require_lock begin - if root_module_exists(uuidkey) - return loaded_modules[uuidkey] + # the PkgId of the ext, or package if not an ext + this_uuidkey = ext isa String ? PkgId(uuid5(package_uuidkey.uuid, ext), ext) : package_uuidkey + if root_module_exists(this_uuidkey) + return loaded_modules[this_uuidkey] end # first since this is a stdlib, try to look there directly first env = Sys.STDLIB #sourcepath = "" if ext === nothing - sourcepath = normpath(env, uuidkey.name, "src", uuidkey.name * ".jl") + sourcepath = normpath(env, this_uuidkey.name, "src", this_uuidkey.name * ".jl") else - sourcepath = find_ext_path(normpath(joinpath(env, uuidkey.name)), ext) - uuidkey = PkgId(uuid5(uuidkey.uuid, ext), ext) + sourcepath = find_ext_path(normpath(joinpath(env, package_uuidkey.name)), ext) end - #mbypath = manifest_uuid_path(env, uuidkey) - #if mbypath isa String - # sourcepath = entry_path(mbypath, uuidkey.name) + #mbypath = manifest_uuid_path(env, this_uuidkey) + #if mbypath isa String && isfile_casesensitive(mbypath) + # sourcepath = mbypath #else # # if the user deleted the stdlib folder, we next try using their environment - # sourcepath = locate_package_env(uuidkey) + # sourcepath = locate_package_env(this_uuidkey) # if sourcepath !== nothing # sourcepath, env = sourcepath # end #end #if sourcepath === nothing # throw(ArgumentError(""" - # Package $(repr("text/plain", uuidkey)) is required but does not seem to be installed. + # Package $(repr("text/plain", this_uuidkey)) is required but does not seem to be installed. # """)) #end - set_pkgorigin_version_path(uuidkey, sourcepath) + set_pkgorigin_version_path(this_uuidkey, sourcepath) depot_path = append_bundled_depot_path!(empty(DEPOT_PATH)) - newm = start_loading(uuidkey) + newm = start_loading(this_uuidkey) newm === nothing || return newm try - newm = _require_search_from_serialized(uuidkey, sourcepath, UInt128(0), false; DEPOT_PATH=depot_path) + newm = _require_search_from_serialized(this_uuidkey, sourcepath, UInt128(0), false; DEPOT_PATH=depot_path) finally - end_loading(uuidkey, newm) + end_loading(this_uuidkey, newm) end if newm isa Module # After successfully loading, notify downstream consumers - insert_extension_triggers(env, uuidkey) - run_package_callbacks(uuidkey) + insert_extension_triggers(env, this_uuidkey) + run_package_callbacks(this_uuidkey) else # if the user deleted their bundled depot, next try to load it completely normally - newm = _require_prelocked(uuidkey) + newm = _require_prelocked(this_uuidkey) end return newm end From 95a379219baa4f4b747cff0c9a739e611d11b0e5 Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Wed, 12 Jun 2024 20:34:43 -0400 Subject: [PATCH 43/71] Handle no-postdominator case in finalizer pass (#54765) This pass was assuming that the post-dominator of all finalizer uses exists as a real BB in the CFG. Resolves https://github.com/JuliaLang/julia/issues/54596 (cherry picked from commit 6ec2b1fb46dc6c41cbb5eb6973f6a2cae4682538) --- base/compiler/ssair/domtree.jl | 2 ++ base/compiler/ssair/passes.jl | 1 + test/compiler/irpasses.jl | 21 +++++++++++++++++++++ 3 files changed, 24 insertions(+) diff --git a/base/compiler/ssair/domtree.jl b/base/compiler/ssair/domtree.jl index bbae3a4d8c0fc..5c78809675a56 100644 --- a/base/compiler/ssair/domtree.jl +++ b/base/compiler/ssair/domtree.jl @@ -657,6 +657,8 @@ end Compute the nearest common (post-)dominator of `a` and `b`. """ function nearest_common_dominator(domtree::GenericDomTree, a::BBNumber, b::BBNumber) + a == 0 && return a + b == 0 && return b alevel = domtree.nodes[a].level blevel = domtree.nodes[b].level # W.l.g. assume blevel <= alevel diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index 6f20ca6b20ec8..152fc3be3d1c3 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -1619,6 +1619,7 @@ function try_resolve_finalizer!(ir::IRCode, idx::Int, finalizer_idx::Int, defuse end all(check_defuse, defuse.uses) || return nothing all(check_defuse, defuse.defs) || return nothing + bb_insert_block != 0 || return nothing # verify post-dominator of all uses exists # Check #3 dominates(domtree, finalizer_bb, bb_insert_block) || return nothing diff --git a/test/compiler/irpasses.jl b/test/compiler/irpasses.jl index c124832030cbd..6d6cd74028594 100644 --- a/test/compiler/irpasses.jl +++ b/test/compiler/irpasses.jl @@ -1820,3 +1820,24 @@ function f53521() end end @test code_typed(f53521)[1][2] === Nothing + +# https://github.com/JuliaLang/julia/issues/54596 +# finalized object's uses have no postdominator +let f = (x)->nothing, mi = Base.method_instance(f, (Base.RefValue{Nothing},)), code = Any[ + # Basic Block 1 + Expr(:new, Base.RefValue{Nothing}, nothing) + Expr(:call, Core.finalizer, f, SSAValue(1), true, mi) + GotoIfNot(false, 6) + # Basic Block 2 + Expr(:call, Base.getfield, SSAValue(1), :x) + ReturnNode(SSAValue(4)) + # Basic Block 3 + Expr(:call, Base.getfield, SSAValue(1), :x) + ReturnNode(SSAValue(6)) +] + ir = make_ircode(code; ssavaluetypes=Any[Base.RefValue{Nothing}, Nothing, Any, Nothing, Any, Nothing, Any]) + inlining = Core.Compiler.InliningState(Core.Compiler.NativeInterpreter()) + Core.Compiler.verify_ir(ir) + ir = Core.Compiler.sroa_pass!(ir, inlining) + Core.Compiler.verify_ir(ir) +end From 592afcbda52c73694f5f6670d5ea6761480340cc Mon Sep 17 00:00:00 2001 From: Max Horn Date: Mon, 10 Jun 2024 00:45:07 +0200 Subject: [PATCH 44/71] Don't expose guard pages to malloc_stack API consumers (#54591) Whether or not a guard page is in effect is an implementation detail and consumers of the `malloc_stack` API should not have to worry about that. In particular, if a stack of a certain size is requested, a stack of that size should be delivered, and not be reduced on some systems because we park a guard page in that range. This also helps consumers of the gcext API implementing stack scanning (i.e., GAP.jl), as it does not have to worry about running into those guard pages anymore. (cherry picked from commit 5dfd57d25c189f6d5558dac9499b45f8bb9b271d) --- src/gc-stacks.c | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/gc-stacks.c b/src/gc-stacks.c index 0318162289f11..3f40e2fc15d81 100644 --- a/src/gc-stacks.c +++ b/src/gc-stacks.c @@ -22,13 +22,22 @@ // number of stacks to always keep available per pool #define MIN_STACK_MAPPINGS_PER_POOL 5 +#if defined(_OS_WINDOWS_) || (!defined(_OS_OPENBSD_) && !defined(JL_HAVE_UCONTEXT) && !defined(JL_HAVE_SIGALTSTACK)) +#define JL_USE_GUARD_PAGE 1 const size_t jl_guard_size = (4096 * 8); +#else +const size_t jl_guard_size = 0; +#endif + static _Atomic(uint32_t) num_stack_mappings = 0; #ifdef _OS_WINDOWS_ #define MAP_FAILED NULL static void *malloc_stack(size_t bufsz) JL_NOTSAFEPOINT { + size_t guard_size = LLT_ALIGN(jl_guard_size, jl_page_size); + bufsz += guard_size; + void *stk = VirtualAlloc(NULL, bufsz, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (stk == NULL) return MAP_FAILED; @@ -37,6 +46,8 @@ static void *malloc_stack(size_t bufsz) JL_NOTSAFEPOINT VirtualFree(stk, 0, MEM_RELEASE); return MAP_FAILED; } + stk = (char *)stk + guard_size; + jl_atomic_fetch_add(&num_stack_mappings, 1); return stk; } @@ -44,6 +55,12 @@ static void *malloc_stack(size_t bufsz) JL_NOTSAFEPOINT static void free_stack(void *stkbuf, size_t bufsz) { +#ifdef JL_USE_GUARD_PAGE + size_t guard_size = LLT_ALIGN(jl_guard_size, jl_page_size); + bufsz += guard_size; + stkbuf = (char *)stkbuf - guard_size; +#endif + VirtualFree(stkbuf, 0, MEM_RELEASE); jl_atomic_fetch_add(&num_stack_mappings, -1); } @@ -52,15 +69,22 @@ static void free_stack(void *stkbuf, size_t bufsz) static void *malloc_stack(size_t bufsz) JL_NOTSAFEPOINT { +#ifdef JL_USE_GUARD_PAGE + size_t guard_size = LLT_ALIGN(jl_guard_size, jl_page_size); + bufsz += guard_size; +#endif + void* stk = mmap(0, bufsz, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (stk == MAP_FAILED) return MAP_FAILED; -#if !defined(JL_HAVE_UCONTEXT) && !defined(JL_HAVE_SIGALTSTACK) - // setup a guard page to detect stack overflow + +#ifdef JL_USE_GUARD_PAGE + // set up a guard page to detect stack overflow if (mprotect(stk, jl_guard_size, PROT_NONE) == -1) { munmap(stk, bufsz); return MAP_FAILED; } + stk = (char *)stk + guard_size; #endif jl_atomic_fetch_add(&num_stack_mappings, 1); return stk; @@ -68,6 +92,12 @@ static void *malloc_stack(size_t bufsz) JL_NOTSAFEPOINT static void free_stack(void *stkbuf, size_t bufsz) { +#ifdef JL_USE_GUARD_PAGE + size_t guard_size = LLT_ALIGN(jl_guard_size, jl_page_size); + bufsz += guard_size; + stkbuf = (char *)stkbuf - guard_size; +#endif + munmap(stkbuf, bufsz); jl_atomic_fetch_add(&num_stack_mappings, -1); } From 5bccb82e0c4ed0168d35786c7ceeea26a5d7cbe4 Mon Sep 17 00:00:00 2001 From: Cody Tapscott <84105208+topolarity@users.noreply.github.com> Date: Fri, 5 Apr 2024 14:50:20 -0400 Subject: [PATCH 45/71] TOML: Improve type-stability of BigInt/UInt support (#53955) From a type-stability perspective, this restores a lot of our behavior before #47903. As it turns out, 10 of the 11 uses of `parse_int` (now called `parse_integer`) introduced in that PR are unnecessary since the TOML format already requires the parsed value to be within a very limited range. Note that this change does not actually revert any functionality (in contrast to #49576) (cherry picked from commit 59c3c71c6e75e2ea6872d6feb75f830c5e5c0b4d) --- base/toml_parser.jl | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/base/toml_parser.jl b/base/toml_parser.jl index eb7ffda2f5940..ed49bd8dbc80b 100644 --- a/base/toml_parser.jl +++ b/base/toml_parser.jl @@ -854,7 +854,7 @@ function parse_number_or_date_start(l::Parser) ate, contains_underscore = @try accept_batch_underscore(l, isdigit, readed_zero) read_underscore |= contains_underscore if (read_digit || ate) && ok_end_value(peek(l)) - return parse_int(l, contains_underscore) + return parse_integer(l, contains_underscore) end # Done with integers here @@ -904,7 +904,18 @@ function parse_float(l::Parser, contains_underscore)::Err{Float64} return v end -for (name, T1, T2, n1, n2) in (("int", Int64, Int128, 17, 33), +function parse_int(l::Parser, contains_underscore, base=nothing)::Err{Int64} + s = take_string_or_substring(l, contains_underscore) + v = try + Base.parse(Int64, s; base=base) + catch e + e isa Base.OverflowError && return(ParserError(ErrOverflowError)) + error("internal parser error: did not correctly discredit $(repr(s)) as an int") + end + return v +end + +for (name, T1, T2, n1, n2) in (("integer", Int64, Int128, 17, 33), ("hex", UInt64, UInt128, 18, 34), ("oct", UInt64, UInt128, 24, 45), ("bin", UInt64, UInt128, 66, 130), @@ -1103,7 +1114,7 @@ function _parse_local_time(l::Parser, skip_hour=false)::Err{NTuple{4, Int64}} end # DateTime in base only manages 3 significant digits in fractional # second - fractional_second = parse_int(l, false) + fractional_second = parse_int(l, false)::Int64 # Truncate off the rest eventual digits accept_batch(l, isdigit) end From 0211c8334572a330f4402859897c23a14bc5ea19 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 12 Jun 2024 22:10:20 -0400 Subject: [PATCH 46/71] [TOML] remove Dates hack, replace with explicit usage (#54755) This hack will cease functioning soon (#54739), so it must be removed so that packages can stop relying on it. As an aid, now provide some basic conversion and printing support for the Base.TOML variants of this as well such that users can round-trip the contents (if they are well-formed as a date/time). (cherry picked from commit 2ce12e965c4da2c042343cf6258aab9d7c7f4065) --- base/toml_parser.jl | 35 +++++++++++++++++------------------ stdlib/Dates/src/types.jl | 5 +++++ stdlib/TOML/Project.toml | 3 ++- stdlib/TOML/src/TOML.jl | 21 +++++++++++++++++---- stdlib/TOML/src/print.jl | 6 +++++- 5 files changed, 46 insertions(+), 24 deletions(-) diff --git a/base/toml_parser.jl b/base/toml_parser.jl index ed49bd8dbc80b..1a72e4e7d4bd2 100644 --- a/base/toml_parser.jl +++ b/base/toml_parser.jl @@ -9,8 +9,8 @@ module TOML using Base: IdSet -# In case we do not have the Dates stdlib available # we parse DateTime into these internal structs, +# unless a different DateTime library is passed to the Parser constructor # note that these do not do any argument checking struct Date year::Int @@ -85,12 +85,10 @@ mutable struct Parser # Filled in in case we are parsing a file to improve error messages filepath::Union{String, Nothing} - # Gets populated with the Dates stdlib if it exists + # Optionally populate with the Dates stdlib to change the type of Date types returned Dates::Union{Module, Nothing} end -const DATES_PKGID = Base.PkgId(Base.UUID("ade2ca70-3891-5945-98fb-dc099432e06a"), "Dates") - function Parser(str::String; filepath=nothing) root = TOMLDict() l = Parser( @@ -109,7 +107,7 @@ function Parser(str::String; filepath=nothing) IdSet{TOMLDict}(), # defined_tables root, filepath, - isdefined(Base, :maybe_root_module) ? Base.maybe_root_module(DATES_PKGID) : nothing, + nothing ) startup(l) return l @@ -151,8 +149,6 @@ end # Errors # ########## -throw_internal_error(msg) = error("internal TOML parser error: $msg") - # Many functions return a ParserError. We want this to bubble up # all the way and have this error be returned to the user # if the parse is called with `raise=false`. This macro @@ -900,7 +896,7 @@ end function parse_float(l::Parser, contains_underscore)::Err{Float64} s = take_string_or_substring(l, contains_underscore) v = Base.tryparse(Float64, s) - v === nothing && return(ParserError(ErrGenericValueError)) + v === nothing && return ParserError(ErrGenericValueError) return v end @@ -909,8 +905,8 @@ function parse_int(l::Parser, contains_underscore, base=nothing)::Err{Int64} v = try Base.parse(Int64, s; base=base) catch e - e isa Base.OverflowError && return(ParserError(ErrOverflowError)) - error("internal parser error: did not correctly discredit $(repr(s)) as an int") + e isa Base.OverflowError && return ParserError(ErrOverflowError) + rethrow() end return v end @@ -932,8 +928,8 @@ for (name, T1, T2, n1, n2) in (("integer", Int64, Int128, 17, 33), Base.parse(BigInt, s; base) end catch e - e isa Base.OverflowError && return(ParserError(ErrOverflowError)) - error("internal parser error: did not correctly discredit $(repr(s)) as an int") + e isa Base.OverflowError && return ParserError(ErrOverflowError) + rethrow() end return v end @@ -1030,8 +1026,9 @@ function try_return_datetime(p, year, month, day, h, m, s, ms) if Dates !== nothing try return Dates.DateTime(year, month, day, h, m, s, ms) - catch - return ParserError(ErrParsingDateTime) + catch ex + ex isa ArgumentError && return ParserError(ErrParsingDateTime) + rethrow() end else return DateTime(year, month, day, h, m, s, ms) @@ -1043,8 +1040,9 @@ function try_return_date(p, year, month, day) if Dates !== nothing try return Dates.Date(year, month, day) - catch - return ParserError(ErrParsingDateTime) + catch ex + ex isa ArgumentError && return ParserError(ErrParsingDateTime) + rethrow() end else return Date(year, month, day) @@ -1065,8 +1063,9 @@ function try_return_time(p, h, m, s, ms) if Dates !== nothing try return Dates.Time(h, m, s, ms) - catch - return ParserError(ErrParsingDateTime) + catch ex + ex isa ArgumentError && return ParserError(ErrParsingDateTime) + rethrow() end else return Time(h, m, s, ms) diff --git a/stdlib/Dates/src/types.jl b/stdlib/Dates/src/types.jl index 7391c277b0718..e1f7f900bff51 100644 --- a/stdlib/Dates/src/types.jl +++ b/stdlib/Dates/src/types.jl @@ -492,3 +492,8 @@ end Base.OrderStyle(::Type{<:AbstractTime}) = Base.Ordered() Base.ArithmeticStyle(::Type{<:AbstractTime}) = Base.ArithmeticWraps() + +# minimal Base.TOML support +Date(d::Base.TOML.Date) = Date(d.year, d.month, d.day) +Time(t::Base.TOML.Time) = Time(t.hour, t.minute, t.second, t.ms) +DateTime(dt::Base.TOML.DateTime) = DateTime(Date(dt.date), Time(dt.time)) diff --git a/stdlib/TOML/Project.toml b/stdlib/TOML/Project.toml index 17fc8be19ec8e..ceb4acf8bbc65 100644 --- a/stdlib/TOML/Project.toml +++ b/stdlib/TOML/Project.toml @@ -6,12 +6,13 @@ version = "1.0.3" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" [compat] +Dates = "1.11.0" julia = "1.6" [extras] Downloads = "f43a241f-c20a-4ad4-852c-f6b1247861c6" -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" Tar = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" p7zip_jll = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" [targets] diff --git a/stdlib/TOML/src/TOML.jl b/stdlib/TOML/src/TOML.jl index 858b75a2e0eff..7414b5dc686f4 100644 --- a/stdlib/TOML/src/TOML.jl +++ b/stdlib/TOML/src/TOML.jl @@ -7,6 +7,8 @@ and to serialize Julia data structures to TOML format. """ module TOML +using Dates + module Internals # The parser is defined in Base using Base.TOML: Parser, parse, tryparse, ParserError, isvalid_barekey_char, reinit! @@ -36,6 +38,17 @@ performance if a larger number of small files are parsed. """ const Parser = Internals.Parser +""" + DTParser() + +Constructor for a TOML `Parser` which returns date and time objects from Dates. +""" +function DTParser(args...; kwargs...) + parser = Parser(args...; kwargs...) + parser.Dates = Dates + return parser +end + """ parsefile(f::AbstractString) parsefile(p::Parser, f::AbstractString) @@ -46,7 +59,7 @@ Parse file `f` and return the resulting table (dictionary). Throw a See also [`TOML.tryparsefile`](@ref). """ parsefile(f::AbstractString) = - Internals.parse(Parser(readstring(f); filepath=abspath(f))) + Internals.parse(DTParser(readstring(f); filepath=abspath(f))) parsefile(p::Parser, f::AbstractString) = Internals.parse(Internals.reinit!(p, readstring(f); filepath=abspath(f))) @@ -60,7 +73,7 @@ Parse file `f` and return the resulting table (dictionary). Return a See also [`TOML.parsefile`](@ref). """ tryparsefile(f::AbstractString) = - Internals.tryparse(Parser(readstring(f); filepath=abspath(f))) + Internals.tryparse(DTParser(readstring(f); filepath=abspath(f))) tryparsefile(p::Parser, f::AbstractString) = Internals.tryparse(Internals.reinit!(p, readstring(f); filepath=abspath(f))) @@ -74,7 +87,7 @@ Throw a [`ParserError`](@ref) upon failure. See also [`TOML.tryparse`](@ref). """ parse(str::AbstractString) = - Internals.parse(Parser(String(str))) + Internals.parse(DTParser(String(str))) parse(p::Parser, str::AbstractString) = Internals.parse(Internals.reinit!(p, String(str))) parse(io::IO) = parse(read(io, String)) @@ -90,7 +103,7 @@ Return a [`ParserError`](@ref) upon failure. See also [`TOML.parse`](@ref). """ tryparse(str::AbstractString) = - Internals.tryparse(Parser(String(str))) + Internals.tryparse(DTParser(String(str))) tryparse(p::Parser, str::AbstractString) = Internals.tryparse(Internals.reinit!(p, String(str))) tryparse(io::IO) = tryparse(read(io, String)) diff --git a/stdlib/TOML/src/print.jl b/stdlib/TOML/src/print.jl index 91ea4fd392e4b..168a7f63c6b5b 100644 --- a/stdlib/TOML/src/print.jl +++ b/stdlib/TOML/src/print.jl @@ -34,7 +34,8 @@ function print_toml_escaped(io::IO, s::AbstractString) end const MbyFunc = Union{Function, Nothing} -const TOMLValue = Union{AbstractVector, AbstractDict, Dates.DateTime, Dates.Time, Dates.Date, Bool, Integer, AbstractFloat, AbstractString} +const TOMLValue = Union{AbstractVector, AbstractDict, Bool, Integer, AbstractFloat, AbstractString, + Dates.DateTime, Dates.Time, Dates.Date, Base.TOML.DateTime, Base.TOML.Time, Base.TOML.Date} ######## @@ -89,6 +90,9 @@ function printvalue(f::MbyFunc, io::IO, value::AbstractVector, sorted::Bool) end function printvalue(f::MbyFunc, io::IO, value::TOMLValue, sorted::Bool) + value isa Base.TOML.DateTime && (value = Dates.DateTime(value)) + value isa Base.TOML.Time && (value = Dates.Time(value)) + value isa Base.TOML.Date && (value = Dates.Date(value)) value isa Dates.DateTime ? Base.print(io, Dates.format(value, Dates.dateformat"YYYY-mm-dd\THH:MM:SS.sss\Z")) : value isa Dates.Time ? Base.print(io, Dates.format(value, Dates.dateformat"HH:MM:SS.sss")) : value isa Dates.Date ? Base.print(io, Dates.format(value, Dates.dateformat"YYYY-mm-dd")) : From d3e7b4597adc9aed1d6bfa49e426028036ed9123 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 7 Jun 2024 20:54:34 -0400 Subject: [PATCH 47/71] add try/catch around scheduler to reset sleep state (#54721) Fixes #54700 Mostly just an indentation change, so recommend viewing with whitespace hidden (or if backporting). (cherry picked from commit b1e5a864263e359ca3f30e83a7ad188e73e01255) --- src/julia.h | 4 +- src/scheduler.c | 251 +++++++++++++++++++++++++----------------------- 2 files changed, 133 insertions(+), 122 deletions(-) diff --git a/src/julia.h b/src/julia.h index 0ddbdf79e2fad..59e85e878e162 100644 --- a/src/julia.h +++ b/src/julia.h @@ -2339,8 +2339,8 @@ extern int had_exception; __eh_ct = jl_current_task; \ size_t __excstack_state = jl_excstack_state(__eh_ct); \ jl_enter_handler(__eh_ct, &__eh); \ - if (1) - /* TRY BLOCK; */ + for (i__try=1; i__try; i__try=0) + #define JL_CATCH \ if (!had_exception) \ jl_eh_restore_state_noexcept(__eh_ct, &__eh); \ diff --git a/src/scheduler.c b/src/scheduler.c index e95c221548f9e..0c0c3be9fb7fd 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -459,140 +459,151 @@ JL_DLLEXPORT jl_task_t *jl_task_get_next(jl_value_t *trypoptask, jl_value_t *q, } continue; } - task = get_next_task(trypoptask, q); // note: this should not yield - if (ptls != ct->ptls) { - // sigh, a yield was detected, so let's go ahead and handle it anyway by starting over - ptls = ct->ptls; - if (set_not_sleeping(ptls)) { - JL_PROBE_RT_SLEEP_CHECK_TASK_WAKE(ptls); + volatile int isrunning = 1; + JL_TRY { + task = get_next_task(trypoptask, q); // note: this should not yield + if (ptls != ct->ptls) { + // sigh, a yield was detected, so let's go ahead and handle it anyway by starting over + ptls = ct->ptls; + if (set_not_sleeping(ptls)) { + JL_PROBE_RT_SLEEP_CHECK_TASK_WAKE(ptls); + } + continue; // jump to JL_CATCH } - if (task) - return task; - continue; - } - if (task) { - if (set_not_sleeping(ptls)) { - JL_PROBE_RT_SLEEP_CHECK_TASK_WAKE(ptls); + if (task) { + if (set_not_sleeping(ptls)) { + JL_PROBE_RT_SLEEP_CHECK_TASK_WAKE(ptls); + } + continue; // jump to JL_CATCH } - return task; - } - // IO is always permitted, but outside a threaded region, only - // thread 0 will process messages. - // Inside a threaded region, any thread can listen for IO messages, - // and one thread should win this race and watch the event loop, - // but we bias away from idle threads getting parked here. - // - // The reason this works is somewhat convoluted, and closely tied to [^store_buffering_1]: - // - After decrementing _threadedregion, the thread is required to - // call jl_wakeup_thread(0), that will kick out any thread who is - // already there, and then eventually thread 0 will get here. - // - Inside a _threadedregion, there must exist at least one - // thread that has a happens-before relationship on the libuv lock - // before reaching this decision point in the code who will see - // the lock as unlocked and thus must win this race here. - int uvlock = 0; - if (jl_atomic_load_relaxed(&_threadedregion)) { - uvlock = jl_mutex_trylock(&jl_uv_mutex); - } - else if (ptls->tid == 0) { - uvlock = 1; - JL_UV_LOCK(); - } - else { - // Since we might have started some IO work, we might need - // to ensure tid = 0 will go watch that new event source. - // If trylock would have succeeded, that may have been our - // responsibility, so need to make sure thread 0 will take care - // of us. - if (jl_atomic_load_relaxed(&jl_uv_mutex.owner) == NULL) // aka trylock - jl_wakeup_thread(0); - } - if (uvlock) { - int enter_eventloop = may_sleep(ptls); - int active = 0; - if (jl_atomic_load_relaxed(&jl_uv_n_waiters) != 0) - // if we won the race against someone who actually needs - // the lock to do real work, we need to let them have it instead - enter_eventloop = 0; - if (enter_eventloop) { - uv_loop_t *loop = jl_global_event_loop(); - loop->stop_flag = 0; - JULIA_DEBUG_SLEEPWAKE( ptls->uv_run_enter = cycleclock() ); - active = uv_run(loop, UV_RUN_ONCE); - JULIA_DEBUG_SLEEPWAKE( ptls->uv_run_leave = cycleclock() ); - jl_gc_safepoint(); + // IO is always permitted, but outside a threaded region, only + // thread 0 will process messages. + // Inside a threaded region, any thread can listen for IO messages, + // and one thread should win this race and watch the event loop, + // but we bias away from idle threads getting parked here. + // + // The reason this works is somewhat convoluted, and closely tied to [^store_buffering_1]: + // - After decrementing _threadedregion, the thread is required to + // call jl_wakeup_thread(0), that will kick out any thread who is + // already there, and then eventually thread 0 will get here. + // - Inside a _threadedregion, there must exist at least one + // thread that has a happens-before relationship on the libuv lock + // before reaching this decision point in the code who will see + // the lock as unlocked and thus must win this race here. + int uvlock = 0; + if (jl_atomic_load_relaxed(&_threadedregion)) { + uvlock = jl_mutex_trylock(&jl_uv_mutex); } - JL_UV_UNLOCK(); - // optimization: check again first if we may have work to do. - // Otherwise we got a spurious wakeup since some other thread - // that just wanted to steal libuv from us. We will just go - // right back to sleep on the individual wake signal to let - // them take it from us without conflict. - if (active || !may_sleep(ptls)) { - if (set_not_sleeping(ptls)) { - JL_PROBE_RT_SLEEP_CHECK_UV_WAKE(ptls); + else if (ptls->tid == 0) { + uvlock = 1; + JL_UV_LOCK(); + } + else { + // Since we might have started some IO work, we might need + // to ensure tid = 0 will go watch that new event source. + // If trylock would have succeeded, that may have been our + // responsibility, so need to make sure thread 0 will take care + // of us. + if (jl_atomic_load_relaxed(&jl_uv_mutex.owner) == NULL) // aka trylock + jl_wakeup_thread(0); + } + if (uvlock) { + int enter_eventloop = may_sleep(ptls); + int active = 0; + if (jl_atomic_load_relaxed(&jl_uv_n_waiters) != 0) + // if we won the race against someone who actually needs + // the lock to do real work, we need to let them have it instead + enter_eventloop = 0; + if (enter_eventloop) { + uv_loop_t *loop = jl_global_event_loop(); + loop->stop_flag = 0; + JULIA_DEBUG_SLEEPWAKE( ptls->uv_run_enter = cycleclock() ); + active = uv_run(loop, UV_RUN_ONCE); + JULIA_DEBUG_SLEEPWAKE( ptls->uv_run_leave = cycleclock() ); + jl_gc_safepoint(); + } + JL_UV_UNLOCK(); + // optimization: check again first if we may have work to do. + // Otherwise we got a spurious wakeup since some other thread + // that just wanted to steal libuv from us. We will just go + // right back to sleep on the individual wake signal to let + // them take it from us without conflict. + if (active || !may_sleep(ptls)) { + if (set_not_sleeping(ptls)) { + JL_PROBE_RT_SLEEP_CHECK_UV_WAKE(ptls); + } + start_cycles = 0; + continue; // jump to JL_CATCH + } + if (!enter_eventloop && !jl_atomic_load_relaxed(&_threadedregion) && ptls->tid == 0) { + // thread 0 is the only thread permitted to run the event loop + // so it needs to stay alive, just spin-looping if necessary + if (set_not_sleeping(ptls)) { + JL_PROBE_RT_SLEEP_CHECK_UV_WAKE(ptls); + } + start_cycles = 0; + continue; // jump to JL_CATCH } - start_cycles = 0; - continue; } - if (!enter_eventloop && !jl_atomic_load_relaxed(&_threadedregion) && ptls->tid == 0) { - // thread 0 is the only thread permitted to run the event loop - // so it needs to stay alive, just spin-looping if necessary - if (set_not_sleeping(ptls)) { - JL_PROBE_RT_SLEEP_CHECK_UV_WAKE(ptls); + + // any thread which wants us running again will have to observe + // sleep_check_state==sleeping and increment nrunning for us + int wasrunning = jl_atomic_fetch_add_relaxed(&nrunning, -1); + assert(wasrunning); + isrunning = 0; + if (wasrunning == 1) { + // This was the last running thread, and there is no thread with !may_sleep + // so make sure tid 0 is notified to check wait_empty + // TODO: this also might be a good time to check again that + // libuv's queue is truly empty, instead of during delete_thread + if (ptls->tid != 0) { + uv_mutex_lock(&ptls->sleep_lock); + uv_cond_wait(&ptls->wake_signal, &ptls->sleep_lock); + uv_mutex_unlock(&ptls->sleep_lock); } - start_cycles = 0; - continue; } - } - // any thread which wants us running again will have to observe - // sleep_check_state==sleeping and increment nrunning for us - int wasrunning = jl_atomic_fetch_add_relaxed(&nrunning, -1); - assert(wasrunning); - if (wasrunning == 1) { - // This was the last running thread, and there is no thread with !may_sleep - // so make sure tid 0 is notified to check wait_empty - // TODO: this also might be a good time to check again that - // libuv's queue is truly empty, instead of during delete_thread - if (ptls->tid != 0) { - uv_mutex_lock(&ptls->sleep_lock); + // the other threads will just wait for an individual wake signal to resume + JULIA_DEBUG_SLEEPWAKE( ptls->sleep_enter = cycleclock() ); + int8_t gc_state = jl_gc_safe_enter(ptls); + uv_mutex_lock(&ptls->sleep_lock); + while (may_sleep(ptls)) { + task = wait_empty; + if (ptls->tid == 0 && task && jl_atomic_load_relaxed(&nrunning) == 0) { + wasrunning = jl_atomic_fetch_add_relaxed(&nrunning, 1); + assert(!wasrunning); + wasrunning = !set_not_sleeping(ptls); + assert(!wasrunning); + JL_PROBE_RT_SLEEP_CHECK_TASK_WAKE(ptls); + if (!ptls->finalizers_inhibited) + ptls->finalizers_inhibited++; // this annoyingly is rather sticky (we should like to reset it at the end of jl_task_wait_empty) + break; + } + // else should we warn the user of certain deadlock here if tid == 0 && nrunning == 0? uv_cond_wait(&ptls->wake_signal, &ptls->sleep_lock); - uv_mutex_unlock(&ptls->sleep_lock); } - } - - // the other threads will just wait for an individual wake signal to resume - JULIA_DEBUG_SLEEPWAKE( ptls->sleep_enter = cycleclock() ); - int8_t gc_state = jl_gc_safe_enter(ptls); - uv_mutex_lock(&ptls->sleep_lock); - while (may_sleep(ptls)) { - task = wait_empty; - if (ptls->tid == 0 && task && jl_atomic_load_relaxed(&nrunning) == 0) { - wasrunning = jl_atomic_fetch_add_relaxed(&nrunning, 1); - assert(!wasrunning); - wasrunning = !set_not_sleeping(ptls); - assert(!wasrunning); - JL_PROBE_RT_SLEEP_CHECK_TASK_WAKE(ptls); - if (!ptls->finalizers_inhibited) - ptls->finalizers_inhibited++; // this annoyingly is rather sticky (we should like to reset it at the end of jl_task_wait_empty) - break; + assert(jl_atomic_load_relaxed(&ptls->sleep_check_state) == not_sleeping); + assert(jl_atomic_load_relaxed(&nrunning)); + start_cycles = 0; + uv_mutex_unlock(&ptls->sleep_lock); + JULIA_DEBUG_SLEEPWAKE( ptls->sleep_leave = cycleclock() ); + jl_gc_safe_leave(ptls, gc_state); // contains jl_gc_safepoint + if (task) { + assert(task == wait_empty); + wait_empty = NULL; + continue; } - // else should we warn the user of certain deadlock here if tid == 0 && nrunning == 0? - uv_cond_wait(&ptls->wake_signal, &ptls->sleep_lock); } - assert(jl_atomic_load_relaxed(&ptls->sleep_check_state) == not_sleeping); - assert(jl_atomic_load_relaxed(&nrunning)); - start_cycles = 0; - uv_mutex_unlock(&ptls->sleep_lock); - JULIA_DEBUG_SLEEPWAKE( ptls->sleep_leave = cycleclock() ); - jl_gc_safe_leave(ptls, gc_state); // contains jl_gc_safepoint - if (task) { - assert(task == wait_empty); - wait_empty = NULL; - return task; + JL_CATCH { + // probably SIGINT, but possibly a user mistake in trypoptask + if (!isrunning) + jl_atomic_fetch_add_relaxed(&nrunning, 1); + set_not_sleeping(ptls); + jl_rethrow(); } + if (task) + return task; } else { // maybe check the kernel for new messages too From f41f896e1894014ddbf62c332d6af114a68a8e1a Mon Sep 17 00:00:00 2001 From: KristofferC Date: Thu, 13 Jun 2024 14:41:30 +0200 Subject: [PATCH 48/71] bump Pkg to latest 1.11 --- .../Pkg-d10463b49ef54580747ba6378bbe56cc9783077a.tar.gz/md5 | 1 + .../Pkg-d10463b49ef54580747ba6378bbe56cc9783077a.tar.gz/sha512 | 1 + .../Pkg-f112626414a4f666306adb84936ea85aec77c89d.tar.gz/md5 | 1 - .../Pkg-f112626414a4f666306adb84936ea85aec77c89d.tar.gz/sha512 | 1 - stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/Pkg-d10463b49ef54580747ba6378bbe56cc9783077a.tar.gz/md5 create mode 100644 deps/checksums/Pkg-d10463b49ef54580747ba6378bbe56cc9783077a.tar.gz/sha512 delete mode 100644 deps/checksums/Pkg-f112626414a4f666306adb84936ea85aec77c89d.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-f112626414a4f666306adb84936ea85aec77c89d.tar.gz/sha512 diff --git a/deps/checksums/Pkg-d10463b49ef54580747ba6378bbe56cc9783077a.tar.gz/md5 b/deps/checksums/Pkg-d10463b49ef54580747ba6378bbe56cc9783077a.tar.gz/md5 new file mode 100644 index 0000000000000..fe756fe94c54e --- /dev/null +++ b/deps/checksums/Pkg-d10463b49ef54580747ba6378bbe56cc9783077a.tar.gz/md5 @@ -0,0 +1 @@ +37a6fc0747a6fef4ea012a7225a7d4bf diff --git a/deps/checksums/Pkg-d10463b49ef54580747ba6378bbe56cc9783077a.tar.gz/sha512 b/deps/checksums/Pkg-d10463b49ef54580747ba6378bbe56cc9783077a.tar.gz/sha512 new file mode 100644 index 0000000000000..c54d5953ab2a0 --- /dev/null +++ b/deps/checksums/Pkg-d10463b49ef54580747ba6378bbe56cc9783077a.tar.gz/sha512 @@ -0,0 +1 @@ +89be88b2318c7cdd173ba505696e89ee9426f962cb13308cea946ff485e93ac6f5cf3c15b4fd2de225eabeb580a100ed9c592512b975b753c76b40690844fb4e diff --git a/deps/checksums/Pkg-f112626414a4f666306adb84936ea85aec77c89d.tar.gz/md5 b/deps/checksums/Pkg-f112626414a4f666306adb84936ea85aec77c89d.tar.gz/md5 deleted file mode 100644 index 423607f56c6dc..0000000000000 --- a/deps/checksums/Pkg-f112626414a4f666306adb84936ea85aec77c89d.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -1f7791c24085bfbe03e2ec6df9e3e095 diff --git a/deps/checksums/Pkg-f112626414a4f666306adb84936ea85aec77c89d.tar.gz/sha512 b/deps/checksums/Pkg-f112626414a4f666306adb84936ea85aec77c89d.tar.gz/sha512 deleted file mode 100644 index 8b0db150dfff8..0000000000000 --- a/deps/checksums/Pkg-f112626414a4f666306adb84936ea85aec77c89d.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -0541b8a46366022dddc54fba199619178cd297243ae56b3334b434c22aa8390fd0f50013b0549fb102a9895e091942736bd63b9967aff63d6f02b29d60ec83b0 diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 0d9ae50e19a8f..5dc8ff86c5dc6 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = release-1.11 -PKG_SHA1 = f112626414a4f666306adb84936ea85aec77c89d +PKG_SHA1 = d10463b49ef54580747ba6378bbe56cc9783077a PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From 8bdf1fa27c19790856960090e3543f2f9d7e641b Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Tue, 18 Jun 2024 21:00:07 +0900 Subject: [PATCH 49/71] effects: add new `@consistent_overlay` macro (#54322) This PR serves to replace #51080 and close #52940. It extends the `:nonoverlayed` to `UInt8` and introduces the `CONSISTENT_OVERLAY` effect bit, allowing for concrete evaluation of overlay methods using the original non-overlayed counterparts when applied. This newly added `:nonoverlayed`-bit is enabled through the newly added `Base.Experimental.@consistent_overlay mt def` macro. `@consistent_overlay` is similar to `@overlay`, but it sets the `:nonoverlayed`-bit to `CONSISTENT_OVERLAY` for the target method definition, allowing the method to be concrete-evaluated. To use this feature safely, I have also added quite precise documentation to `@consistent_overlay`. --- base/boot.jl | 3 +- base/compiler/abstractinterpretation.jl | 14 +++- base/compiler/compiler.jl | 11 ++- base/compiler/effects.jl | 59 +++++++++------ base/compiler/inferencestate.jl | 5 +- base/compiler/ssair/show.jl | 3 +- base/compiler/typeinfer.jl | 3 + base/essentials.jl | 36 ++++++--- base/experimental.jl | 98 +++++++++++++++++++++++-- base/expr.jl | 17 +++-- src/julia.h | 14 ++-- src/method.c | 30 +++++--- test/compiler/AbstractInterpreter.jl | 85 +++++++++++++++++---- test/namedtuple.jl | 4 +- 14 files changed, 291 insertions(+), 91 deletions(-) diff --git a/base/boot.jl b/base/boot.jl index bfee3c17336bc..e8156b325a843 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -282,7 +282,8 @@ macro _foldable_meta() #=:notaskstate=#true, #=:inaccessiblememonly=#true, #=:noub=#true, - #=:noub_if_noinbounds=#false)) + #=:noub_if_noinbounds=#false, + #=:consistent_overlay=#false)) end macro inline() Expr(:meta, :inline) end diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index b13df848ce605..ba8a352c1b528 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -491,7 +491,7 @@ function add_call_backedges!(interp::AbstractInterpreter, @nospecialize(rettype) # ignore the `:nonoverlayed` property if `interp` doesn't use overlayed method table # since it will never be tainted anyway if !isoverlayed(method_table(interp)) - all_effects = Effects(all_effects; nonoverlayed=false) + all_effects = Effects(all_effects; nonoverlayed=ALWAYS_FALSE) end all_effects === Effects() && return nothing end @@ -889,7 +889,15 @@ function concrete_eval_eligible(interp::AbstractInterpreter, mi = result.edge if mi !== nothing && is_foldable(effects) if f !== nothing && is_all_const_arg(arginfo, #=start=#2) - if is_nonoverlayed(interp) || is_nonoverlayed(effects) + if (is_nonoverlayed(interp) || is_nonoverlayed(effects) || + # Even if overlay methods are involved, when `:consistent_overlay` is + # explicitly applied, we can still perform concrete evaluation using the + # original methods for executing them. + # While there's a chance that the non-overlayed counterparts may raise + # non-egal exceptions, it will not impact the compilation validity, since: + # - the results of the concrete evaluation will not be inlined + # - the exception types from the concrete evaluation will not be propagated + is_consistent_overlay(effects)) return :concrete_eval end # disable concrete-evaluation if this function call is tainted by some overlayed @@ -2770,7 +2778,7 @@ function override_effects(effects::Effects, override::EffectsOverride) notaskstate = override.notaskstate ? true : effects.notaskstate, inaccessiblememonly = override.inaccessiblememonly ? ALWAYS_TRUE : effects.inaccessiblememonly, noub = override.noub ? ALWAYS_TRUE : - override.noub_if_noinbounds && effects.noub !== ALWAYS_TRUE ? NOUB_IF_NOINBOUNDS : + (override.noub_if_noinbounds && effects.noub !== ALWAYS_TRUE) ? NOUB_IF_NOINBOUNDS : effects.noub) end diff --git a/base/compiler/compiler.jl b/base/compiler/compiler.jl index 8b9c26be2ec81..4e2fe3da3496f 100644 --- a/base/compiler/compiler.jl +++ b/base/compiler/compiler.jl @@ -47,10 +47,11 @@ struct EffectsOverride inaccessiblememonly::Bool noub::Bool noub_if_noinbounds::Bool + consistent_overlay::Bool end function EffectsOverride( override::EffectsOverride = - EffectsOverride(false, false, false, false, false, false, false, false, false); + EffectsOverride(false, false, false, false, false, false, false, false, false, false); consistent::Bool = override.consistent, effect_free::Bool = override.effect_free, nothrow::Bool = override.nothrow, @@ -59,7 +60,8 @@ function EffectsOverride( notaskstate::Bool = override.notaskstate, inaccessiblememonly::Bool = override.inaccessiblememonly, noub::Bool = override.noub, - noub_if_noinbounds::Bool = override.noub_if_noinbounds) + noub_if_noinbounds::Bool = override.noub_if_noinbounds, + consistent_overlay::Bool = override.consistent_overlay) return EffectsOverride( consistent, effect_free, @@ -69,9 +71,10 @@ function EffectsOverride( notaskstate, inaccessiblememonly, noub, - noub_if_noinbounds) + noub_if_noinbounds, + consistent_overlay) end -const NUM_EFFECTS_OVERRIDES = 9 # sync with julia.h +const NUM_EFFECTS_OVERRIDES = 10 # sync with julia.h # essential files and libraries include("essentials.jl") diff --git a/base/compiler/effects.jl b/base/compiler/effects.jl index a3d30baef9efa..0375b8dba922c 100644 --- a/base/compiler/effects.jl +++ b/base/compiler/effects.jl @@ -43,16 +43,21 @@ following meanings: except that it may access or modify mutable memory pointed to by its call arguments. This may later be refined to `ALWAYS_TRUE` in a case when call arguments are known to be immutable. This state corresponds to LLVM's `inaccessiblemem_or_argmemonly` function attribute. -- `noub::UInt8`: indicates that the method will not execute any undefined behavior (for any input). - Note that undefined behavior may technically cause the method to violate any other effect - assertions (such as `:consistent` or `:effect_free`) as well, but we do not model this, - and they assume the absence of undefined behavior. - * `ALWAYS_TRUE`: this method is guaranteed to not execute any undefined behavior. +- `noub::UInt8`: + * `ALWAYS_TRUE`: this method is guaranteed to not execute any undefined behavior (for any input). * `ALWAYS_FALSE`: this method may execute undefined behavior. * `NOUB_IF_NOINBOUNDS`: this method is guaranteed to not execute any undefined behavior if the caller does not set nor propagate the `@inbounds` context. -- `nonoverlayed::Bool`: indicates that any methods that may be called within this method - are not defined in an [overlayed method table](@ref OverlayMethodTable). + Note that undefined behavior may technically cause the method to violate any other effect + assertions (such as `:consistent` or `:effect_free`) as well, but we do not model this, + and they assume the absence of undefined behavior. +- `nonoverlayed::UInt8`: + * `ALWAYS_TRUE`: this method is guaranteed to not invoke any methods that defined in an + [overlayed method table](@ref OverlayMethodTable). + * `CONSISTENT_OVERLAY`: this method may invoke overlayed methods, but all such overlayed + methods are `:consistent` with their non-overlayed original counterparts + (see [`Base.@assume_effects`](@ref) for the exact definition of `:consistenct`-cy). + * `ALWAYS_FALSE`: this method may invoke overlayed methods. Note that the representations above are just internal implementation details and thus likely to change in the future. See [`Base.@assume_effects`](@ref) for more detailed explanation @@ -94,8 +99,10 @@ The output represents the state of different effect properties in the following - `+u` (green): `true` - `-u` (red): `false` - `?u` (yellow): `NOUB_IF_NOINBOUNDS` - -Additionally, if the `nonoverlayed` property is false, a red prime symbol (′) is displayed after the tuple. +8. `:nonoverlayed` (`o`): + - `+o` (green): `ALWAYS_TRUE` + - `-o` (red): `ALWAYS_FALSE` + - `?o` (yellow): `CONSISTENT_OVERLAY` """ struct Effects consistent::UInt8 @@ -105,7 +112,7 @@ struct Effects notaskstate::Bool inaccessiblememonly::UInt8 noub::UInt8 - nonoverlayed::Bool + nonoverlayed::UInt8 function Effects( consistent::UInt8, effect_free::UInt8, @@ -114,7 +121,7 @@ struct Effects notaskstate::Bool, inaccessiblememonly::UInt8, noub::UInt8, - nonoverlayed::Bool) + nonoverlayed::UInt8) return new( consistent, effect_free, @@ -150,10 +157,13 @@ const INACCESSIBLEMEM_OR_ARGMEMONLY = 0x01 << 1 # :noub bits const NOUB_IF_NOINBOUNDS = 0x01 << 1 -const EFFECTS_TOTAL = Effects(ALWAYS_TRUE, ALWAYS_TRUE, true, true, true, ALWAYS_TRUE, ALWAYS_TRUE, true) -const EFFECTS_THROWS = Effects(ALWAYS_TRUE, ALWAYS_TRUE, false, true, true, ALWAYS_TRUE, ALWAYS_TRUE, true) -const EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, ALWAYS_FALSE, true) # unknown mostly, but it's not overlayed at least (e.g. it's not a call) -const _EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, ALWAYS_FALSE, false) # unknown really +# :nonoverlayed bits +const CONSISTENT_OVERLAY = 0x01 << 1 + +const EFFECTS_TOTAL = Effects(ALWAYS_TRUE, ALWAYS_TRUE, true, true, true, ALWAYS_TRUE, ALWAYS_TRUE, ALWAYS_TRUE) +const EFFECTS_THROWS = Effects(ALWAYS_TRUE, ALWAYS_TRUE, false, true, true, ALWAYS_TRUE, ALWAYS_TRUE, ALWAYS_TRUE) +const EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, ALWAYS_FALSE, ALWAYS_TRUE) # unknown mostly, but it's not overlayed at least (e.g. it's not a call) +const _EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, ALWAYS_FALSE, ALWAYS_FALSE) # unknown really function Effects(effects::Effects = _EFFECTS_UNKNOWN; consistent::UInt8 = effects.consistent, @@ -163,7 +173,7 @@ function Effects(effects::Effects = _EFFECTS_UNKNOWN; notaskstate::Bool = effects.notaskstate, inaccessiblememonly::UInt8 = effects.inaccessiblememonly, noub::UInt8 = effects.noub, - nonoverlayed::Bool = effects.nonoverlayed) + nonoverlayed::UInt8 = effects.nonoverlayed) return Effects( consistent, effect_free, @@ -229,8 +239,11 @@ function is_better_effects(new::Effects, old::Effects) elseif new.noub != old.noub return false end - if new.nonoverlayed - any_improved |= !old.nonoverlayed + if new.nonoverlayed == ALWAYS_TRUE + any_improved |= old.nonoverlayed != ALWAYS_TRUE + elseif new.nonoverlayed == CONSISTENT_OVERLAY + old.nonoverlayed == ALWAYS_TRUE && return false + any_improved |= old.nonoverlayed != CONSISTENT_OVERLAY elseif new.nonoverlayed != old.nonoverlayed return false end @@ -265,7 +278,7 @@ is_notaskstate(effects::Effects) = effects.notaskstate is_inaccessiblememonly(effects::Effects) = effects.inaccessiblememonly === ALWAYS_TRUE is_noub(effects::Effects) = effects.noub === ALWAYS_TRUE is_noub_if_noinbounds(effects::Effects) = effects.noub === NOUB_IF_NOINBOUNDS -is_nonoverlayed(effects::Effects) = effects.nonoverlayed +is_nonoverlayed(effects::Effects) = effects.nonoverlayed === ALWAYS_TRUE # implies `is_notaskstate` & `is_inaccessiblememonly`, but not explicitly checked here is_foldable(effects::Effects) = @@ -295,6 +308,8 @@ is_effect_free_if_inaccessiblememonly(effects::Effects) = !iszero(effects.effect is_inaccessiblemem_or_argmemonly(effects::Effects) = effects.inaccessiblememonly === INACCESSIBLEMEM_OR_ARGMEMONLY +is_consistent_overlay(effects::Effects) = effects.nonoverlayed === CONSISTENT_OVERLAY + function encode_effects(e::Effects) return ((e.consistent % UInt32) << 0) | ((e.effect_free % UInt32) << 3) | @@ -315,7 +330,7 @@ function decode_effects(e::UInt32) _Bool((e >> 7) & 0x01), UInt8((e >> 8) & 0x03), UInt8((e >> 10) & 0x03), - _Bool((e >> 12) & 0x01)) + UInt8((e >> 12) & 0x03)) end function encode_effects_override(eo::EffectsOverride) @@ -329,6 +344,7 @@ function encode_effects_override(eo::EffectsOverride) eo.inaccessiblememonly && (e |= (0x0001 << 6)) eo.noub && (e |= (0x0001 << 7)) eo.noub_if_noinbounds && (e |= (0x0001 << 8)) + eo.consistent_overlay && (e |= (0x0001 << 9)) return e end @@ -342,7 +358,8 @@ function decode_effects_override(e::UInt16) !iszero(e & (0x0001 << 5)), !iszero(e & (0x0001 << 6)), !iszero(e & (0x0001 << 7)), - !iszero(e & (0x0001 << 8))) + !iszero(e & (0x0001 << 8)), + !iszero(e & (0x0001 << 9))) end decode_statement_effects_override(ssaflag::UInt32) = diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index f08fbac8af360..9dd676685cfda 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -329,7 +329,10 @@ mutable struct InferenceState end if def isa Method - ipo_effects = Effects(ipo_effects; nonoverlayed=is_nonoverlayed(def)) + nonoverlayed = is_nonoverlayed(def) ? ALWAYS_TRUE : + is_effect_overridden(def, :consistent_overlay) ? CONSISTENT_OVERLAY : + ALWAYS_FALSE + ipo_effects = Effects(ipo_effects; nonoverlayed) end restrict_abstract_call_sites = isa(def, Module) diff --git a/base/compiler/ssair/show.jl b/base/compiler/ssair/show.jl index 3936a82a6560e..873ab2c68f7a8 100644 --- a/base/compiler/ssair/show.jl +++ b/base/compiler/ssair/show.jl @@ -1019,8 +1019,9 @@ function Base.show(io::IO, e::Effects) printstyled(io, effectbits_letter(e, :inaccessiblememonly, 'm'); color=effectbits_color(e, :inaccessiblememonly)) print(io, ',') printstyled(io, effectbits_letter(e, :noub, 'u'); color=effectbits_color(e, :noub)) + print(io, ',') + printstyled(io, effectbits_letter(e, :nonoverlayed, 'o'); color=effectbits_color(e, :nonoverlayed)) print(io, ')') - e.nonoverlayed || printstyled(io, '′'; color=:red) end @specialize diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 8b049efa241e7..83575cd6f9278 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -492,6 +492,9 @@ function adjust_effects(ipo_effects::Effects, def::Method) elseif is_effect_overridden(override, :noub_if_noinbounds) && ipo_effects.noub !== ALWAYS_TRUE ipo_effects = Effects(ipo_effects; noub=NOUB_IF_NOINBOUNDS) end + if is_effect_overridden(override, :consistent_overlay) + ipo_effects = Effects(ipo_effects; nonoverlayed=CONSISTENT_OVERLAY) + end return ipo_effects end diff --git a/base/essentials.jl b/base/essentials.jl index 5fdc718085df0..45f0829da66df 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -212,7 +212,8 @@ macro _total_meta() #=:notaskstate=#true, #=:inaccessiblememonly=#true, #=:noub=#true, - #=:noub_if_noinbounds=#false)) + #=:noub_if_noinbounds=#false, + #=:consistent_overlay=#false)) end # can be used in place of `@assume_effects :foldable` (supposed to be used for bootstrapping) macro _foldable_meta() @@ -225,7 +226,8 @@ macro _foldable_meta() #=:notaskstate=#true, #=:inaccessiblememonly=#true, #=:noub=#true, - #=:noub_if_noinbounds=#false)) + #=:noub_if_noinbounds=#false, + #=:consistent_overlay=#false)) end # can be used in place of `@assume_effects :terminates_locally` (supposed to be used for bootstrapping) macro _terminates_locally_meta() @@ -238,7 +240,8 @@ macro _terminates_locally_meta() #=:notaskstate=#false, #=:inaccessiblememonly=#false, #=:noub=#false, - #=:noub_if_noinbounds=#false)) + #=:noub_if_noinbounds=#false, + #=:consistent_overlay=#false)) end # can be used in place of `@assume_effects :terminates_globally` (supposed to be used for bootstrapping) macro _terminates_globally_meta() @@ -251,7 +254,8 @@ macro _terminates_globally_meta() #=:notaskstate=#false, #=:inaccessiblememonly=#false, #=:noub=#false, - #=:noub_if_noinbounds=#false)) + #=:noub_if_noinbounds=#false, + #=:consistent_overlay=#false)) end # can be used in place of `@assume_effects :terminates_globally :notaskstate` (supposed to be used for bootstrapping) macro _terminates_globally_notaskstate_meta() @@ -264,7 +268,8 @@ macro _terminates_globally_notaskstate_meta() #=:notaskstate=#true, #=:inaccessiblememonly=#false, #=:noub=#false, - #=:noub_if_noinbounds=#false)) + #=:noub_if_noinbounds=#false, + #=:consistent_overlay=#false)) end # can be used in place of `@assume_effects :terminates_globally :noub` (supposed to be used for bootstrapping) macro _terminates_globally_noub_meta() @@ -277,7 +282,8 @@ macro _terminates_globally_noub_meta() #=:notaskstate=#false, #=:inaccessiblememonly=#false, #=:noub=#true, - #=:noub_if_noinbounds=#false)) + #=:noub_if_noinbounds=#false, + #=:consistent_overlay=#false)) end # can be used in place of `@assume_effects :effect_free :terminates_locally` (supposed to be used for bootstrapping) macro _effect_free_terminates_locally_meta() @@ -290,7 +296,8 @@ macro _effect_free_terminates_locally_meta() #=:notaskstate=#false, #=:inaccessiblememonly=#false, #=:noub=#false, - #=:noub_if_noinbounds=#false)) + #=:noub_if_noinbounds=#false, + #=:consistent_overlay=#false)) end # can be used in place of `@assume_effects :nothrow :noub` (supposed to be used for bootstrapping) macro _nothrow_noub_meta() @@ -303,7 +310,8 @@ macro _nothrow_noub_meta() #=:notaskstate=#false, #=:inaccessiblememonly=#false, #=:noub=#true, - #=:noub_if_noinbounds=#false)) + #=:noub_if_noinbounds=#false, + #=:consistent_overlay=#false)) end # can be used in place of `@assume_effects :nothrow` (supposed to be used for bootstrapping) macro _nothrow_meta() @@ -316,7 +324,8 @@ macro _nothrow_meta() #=:notaskstate=#false, #=:inaccessiblememonly=#false, #=:noub=#false, - #=:noub_if_noinbounds=#false)) + #=:noub_if_noinbounds=#false, + #=:consistent_overlay=#false)) end # can be used in place of `@assume_effects :nothrow` (supposed to be used for bootstrapping) macro _noub_meta() @@ -329,7 +338,8 @@ macro _noub_meta() #=:notaskstate=#false, #=:inaccessiblememonly=#false, #=:noub=#true, - #=:noub_if_noinbounds=#false)) + #=:noub_if_noinbounds=#false, + #=:consistent_overlay=#false)) end # can be used in place of `@assume_effects :notaskstate` (supposed to be used for bootstrapping) macro _notaskstate_meta() @@ -342,7 +352,8 @@ macro _notaskstate_meta() #=:notaskstate=#true, #=:inaccessiblememonly=#false, #=:noub=#false, - #=:noub_if_noinbounds=#false)) + #=:noub_if_noinbounds=#false, + #=:consistent_overlay=#false)) end # can be used in place of `@assume_effects :noub_if_noinbounds` (supposed to be used for bootstrapping) macro _noub_if_noinbounds_meta() @@ -355,7 +366,8 @@ macro _noub_if_noinbounds_meta() #=:notaskstate=#false, #=:inaccessiblememonly=#false, #=:noub=#false, - #=:noub_if_noinbounds=#true)) + #=:noub_if_noinbounds=#true, + #=:consistent_overlay=#false)) end # another version of inlining that propagates an inbounds context diff --git a/base/experimental.jl b/base/experimental.jl index 8dbdcacd65376..58c7258120f3f 100644 --- a/base/experimental.jl +++ b/base/experimental.jl @@ -318,7 +318,7 @@ function show_error_hints(io, ex, args...) isnothing(hinters) && return for handler in hinters try - Base.invokelatest(handler, io, ex, args...) + @invokelatest handler(io, ex, args...) catch err tn = typeof(handler).name @error "Hint-handler $handler for $(typeof(ex)) in $(tn.module) caused an error" @@ -330,17 +330,99 @@ end include("opaque_closure.jl") """ - Experimental.@overlay mt [function def] + Base.Experimental.@overlay mt def Define a method and add it to the method table `mt` instead of to the global method table. This can be used to implement a method override mechanism. Regular compilation will not consider these methods, and you should customize the compilation flow to look in these method tables (e.g., using [`Core.Compiler.OverlayMethodTable`](@ref)). + +!!! note + Please be aware that when defining overlay methods using `@overlay`, it is not necessary + to have an original method that corresponds exactly in terms of how the method dispatches. + This means that the method overlay mechanism enabled by `@overlay` is not implemented by + replacing the methods themselves, but through an additional and prioritized method + lookup during the method dispatch. + + Considering this, it is important to understand that in compilations using an overlay + method table like the following, the method dispatched by `callx(x)` is not the regular + method `callx(::Float64)`, but the overlay method `callx(x::Real)`: + ```julia + callx(::Real) = :real + @overlay SOME_OVERLAY_MT callx(::Real) = :overlay_real + callx(::Float64) = :float64 + + # some overlay callsite + let x::Float64 + callx(x) #> :overlay_real + end + ``` """ macro overlay(mt, def) - def = macroexpand(__module__, def) # to expand @inline, @generated, etc - is_function_def(def) || error("@overlay requires a function definition") - return esc(overlay_def!(mt, def)) + inner = Base.unwrap_macrocalls(def) + is_function_def(inner) || error("@overlay requires a function definition") + overlay_def!(mt, inner) + return esc(def) +end + +""" + Base.Experimental.@consistent_overlay mt def + +This macro operates almost identically to [`Base.Experimental.@overlay`](@ref), defining a +new overlay method. The key difference with this macro is that it informs the compiler that +the invocation of the overlay method it defines is `:consistent` with a regular, +non-overlayed method call. + +More formally, when evaluating a generic function call ``f(x)`` at a specific world age +``i``, if a regular method call ``fᵢ(x)`` is redirected to an overlay method call ``fᵢ′(x)`` +defined by this macro, ``fᵢ(x)`` and ``fᵢ′(x)`` are considered `:consistent` if the following +conditions are met: +- If ``fᵢ(x)`` returns a value ``y``, then ``fᵢ′(x)`` also returns some value ``yᵢ``, and ``y ≡ yᵢ`` holds. +- If ``fᵢ(x)`` throws an exception, then ``fᵢ′(x)`` also throws some exception. + +For a detailed definition of `:consistent`-cy, consult the corresponding section in +[`Base.@assume_effects`](@ref). + +!!! note + Note that the requirements for `:consistent`-cy include not only that the return values + are egal, but also that the manner of termination is the same. However, it's important + to aware that when they throw exceptions, the exceptions themselves don't necessarily + have to be egal. In other words, if ``fᵢ(x)`` throws an exception, ``fᵢ′(x)`` is + required to also throw one, but the exact exceptions may differ. + +!!! note + Please note that the `:consistent`-cy requirement applies not to method itself but to + _method invocation_. This means that for the use of `@consistent_overlay`, it is + necessary for method invocations with the native regular compilation and those with + a compilation with overlay method table to be `:consistent`. + + For example, it is important to understand that, `@consistent_overlay` can be used like + the following: + ```julia + callsin(x::Real) = x < 0 ? error(x) : sin(x) + @consistent_overlay SOME_OVERLAY_MT callsin(x::Float64) = + x < 0 ? error_somehow(x) : sin(x) + ``` + However, be aware that this `@consistent_overlay` will immediately become invalid if a + new method for `callsin` is defined subsequently, such as: + ```julia + callsin(x::Float64) = cos(x) + ``` + + This specifically implies that the use of `@consistent_overlay` should be restricted as + much as possible to cases where a regular method with a concrete signature is replaced + by an overlay method with the same concrete signature. + + This constraint is closely related to the note in [`Base.Experimental.@overlay`](@ref); + you are advised to consult that as well. +""" +macro consistent_overlay(mt, def) + inner = Base.unwrap_macrocalls(def) + is_function_def(inner) || error("@consistent_overlay requires a function definition") + overlay_def!(mt, inner) + override = Core.Compiler.EffectsOverride(; consistent_overlay=true) + Base.pushmeta!(def::Expr, Base.form_purity_expr(override)) + return esc(def) end function overlay_def!(mt, @nospecialize ex) @@ -367,11 +449,11 @@ let new_mt(name::Symbol, mod::Module) = begin end """ - Experimental.@MethodTable(name) + Base.Experimental.@MethodTable name Create a new MethodTable in the current module, bound to `name`. This method table can be -used with the [`Experimental.@overlay`](@ref) macro to define methods for a function without -adding them to the global method table. +used with the [`Base.Experimental.@overlay`](@ref) macro to define methods for a function +without adding them to the global method table. """ :@MethodTable diff --git a/base/expr.jl b/base/expr.jl index b90f75e75a76f..dc85f3591efa1 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -484,7 +484,7 @@ CodeInfo( !!! compat "Julia 1.10" The usage within a function body requires at least Julia 1.10. -!!! compact "Julia 1.11" +!!! compat "Julia 1.11" The code block annotation requires at least Julia 1.11. !!! warning @@ -530,7 +530,7 @@ The `:consistent` setting asserts that for egal (`===`) inputs: !!! note The `:consistent`-cy assertion is made world-age wise. More formally, write - ``fᵢ`` for the evaluation of ``f`` in world-age ``i``, then we require: + ``fᵢ`` for the evaluation of ``f`` in world-age ``i``, then this setting requires: ```math ∀ i, x, y: x ≡ y → fᵢ(x) ≡ fᵢ(y) ``` @@ -742,7 +742,7 @@ macro assume_effects(args...) lastex = args[end] override = compute_assumed_settings(args[begin:end-1]) if is_function_def(unwrap_macrocalls(lastex)) - return esc(pushmeta!(lastex, form_purity_expr(override))) + return esc(pushmeta!(lastex::Expr, form_purity_expr(override))) elseif isexpr(lastex, :macrocall) && lastex.args[1] === Symbol("@ccall") lastex.args[1] = GlobalRef(Base, Symbol("@ccall_effects")) insert!(lastex.args, 3, Core.Compiler.encode_effects_override(override)) @@ -767,7 +767,7 @@ function compute_assumed_settings(settings) for setting in settings override = compute_assumed_setting(override, setting) override === nothing && - throw(ArgumentError("@assume_effects $setting not supported")) + throw(ArgumentError("`@assume_effects $setting` not supported")) end return override end @@ -815,10 +815,11 @@ function compute_assumed_setting(override::EffectsOverride, @nospecialize(settin end function form_purity_expr(override::EffectsOverride) - return Expr(:purity, - override.consistent, override.effect_free, override.nothrow, - override.terminates_globally, override.terminates_locally, override.notaskstate, - override.inaccessiblememonly, override.noub, override.noub_if_noinbounds) + ex = Expr(:purity) + for i = 1:Core.Compiler.NUM_EFFECTS_OVERRIDES + push!(ex.args, getfield(override, i)) + end + return ex end """ diff --git a/src/julia.h b/src/julia.h index 59e85e878e162..3a7d594562243 100644 --- a/src/julia.h +++ b/src/julia.h @@ -261,11 +261,12 @@ typedef union __jl_purity_overrides_t { uint16_t ipo_inaccessiblememonly : 1; uint16_t ipo_noub : 1; uint16_t ipo_noub_if_noinbounds : 1; + uint16_t ipo_consistent_overlay : 1; } overrides; uint16_t bits; } _jl_purity_overrides_t; -#define NUM_EFFECTS_OVERRIDES 9 +#define NUM_EFFECTS_OVERRIDES 10 #define NUM_IR_FLAGS 12 // This type describes a single function body @@ -439,13 +440,14 @@ typedef struct _jl_code_instance_t { // uint8_t ipo_inaccessiblememonly : 2; _Atomic(uint32_t) purity_bits; // purity_flags: - // uint8_t consistent : 2; + // uint8_t consistent : 3; // uint8_t effect_free : 2; - // uint8_t nothrow : 2; - // uint8_t terminates : 2; - // uint8_t nonoverlayed : 1; - // uint8_t notaskstate : 2; + // uint8_t nothrow : 1; + // uint8_t terminates : 1; + // uint8_t notaskstate : 1; // uint8_t inaccessiblememonly : 2; + // uint8_t noub : 2; + // uint8_t nonoverlayed : 2; jl_value_t *analysis_results; // Analysis results about this code (IPO-safe) // compilation state cache diff --git a/src/method.c b/src/method.c index ac5475d54a4d5..7e6c82b5221b2 100644 --- a/src/method.c +++ b/src/method.c @@ -347,15 +347,27 @@ static void jl_code_info_set_ir(jl_code_info_t *li, jl_expr_t *ir) li->constprop = 2; else if (jl_is_expr(ma) && ((jl_expr_t*)ma)->head == jl_purity_sym) { if (jl_expr_nargs(ma) == NUM_EFFECTS_OVERRIDES) { - li->purity.overrides.ipo_consistent = jl_unbox_bool(jl_exprarg(ma, 0)); - li->purity.overrides.ipo_effect_free = jl_unbox_bool(jl_exprarg(ma, 1)); - li->purity.overrides.ipo_nothrow = jl_unbox_bool(jl_exprarg(ma, 2)); - li->purity.overrides.ipo_terminates_globally = jl_unbox_bool(jl_exprarg(ma, 3)); - li->purity.overrides.ipo_terminates_locally = jl_unbox_bool(jl_exprarg(ma, 4)); - li->purity.overrides.ipo_notaskstate = jl_unbox_bool(jl_exprarg(ma, 5)); - li->purity.overrides.ipo_inaccessiblememonly = jl_unbox_bool(jl_exprarg(ma, 6)); - li->purity.overrides.ipo_noub = jl_unbox_bool(jl_exprarg(ma, 7)); - li->purity.overrides.ipo_noub_if_noinbounds = jl_unbox_bool(jl_exprarg(ma, 8)); + // N.B. this code allows multiple :purity expressions to be present in a single `:meta` node + int8_t consistent = jl_unbox_bool(jl_exprarg(ma, 0)); + if (consistent) li->purity.overrides.ipo_consistent = consistent; + int8_t effect_free = jl_unbox_bool(jl_exprarg(ma, 1)); + if (effect_free) li->purity.overrides.ipo_effect_free = effect_free; + int8_t nothrow = jl_unbox_bool(jl_exprarg(ma, 2)); + if (nothrow) li->purity.overrides.ipo_nothrow = nothrow; + int8_t terminates_globally = jl_unbox_bool(jl_exprarg(ma, 3)); + if (terminates_globally) li->purity.overrides.ipo_terminates_globally = terminates_globally; + int8_t terminates_locally = jl_unbox_bool(jl_exprarg(ma, 4)); + if (terminates_locally) li->purity.overrides.ipo_terminates_locally = terminates_locally; + int8_t notaskstate = jl_unbox_bool(jl_exprarg(ma, 5)); + if (notaskstate) li->purity.overrides.ipo_notaskstate = notaskstate; + int8_t inaccessiblememonly = jl_unbox_bool(jl_exprarg(ma, 6)); + if (inaccessiblememonly) li->purity.overrides.ipo_inaccessiblememonly = inaccessiblememonly; + int8_t noub = jl_unbox_bool(jl_exprarg(ma, 7)); + if (noub) li->purity.overrides.ipo_noub = noub; + int8_t noub_if_noinbounds = jl_unbox_bool(jl_exprarg(ma, 8)); + if (noub_if_noinbounds) li->purity.overrides.ipo_noub_if_noinbounds = noub_if_noinbounds; + int8_t consistent_overlay = jl_unbox_bool(jl_exprarg(ma, 9)); + if (consistent_overlay) li->purity.overrides.ipo_consistent_overlay = consistent_overlay; } } else diff --git a/test/compiler/AbstractInterpreter.jl b/test/compiler/AbstractInterpreter.jl index 776b9fd69ce9b..507773dabb35f 100644 --- a/test/compiler/AbstractInterpreter.jl +++ b/test/compiler/AbstractInterpreter.jl @@ -10,7 +10,7 @@ include("newinterp.jl") # OverlayMethodTable # ================== -using Base.Experimental: @MethodTable, @overlay +using Base.Experimental: @MethodTable, @overlay, @consistent_overlay # @overlay method with return type annotation @MethodTable RT_METHOD_DEF @@ -20,8 +20,8 @@ using Base.Experimental: @MethodTable, @overlay end @newinterp MTOverlayInterp -@MethodTable OverlayedMT -CC.method_table(interp::MTOverlayInterp) = CC.OverlayMethodTable(CC.get_inference_world(interp), OverlayedMT) +@MethodTable OVERLAY_MT +CC.method_table(interp::MTOverlayInterp) = CC.OverlayMethodTable(CC.get_inference_world(interp), OVERLAY_MT) function CC.add_remark!(interp::MTOverlayInterp, ::CC.InferenceState, remark) if interp.meta !== nothing @@ -32,7 +32,8 @@ function CC.add_remark!(interp::MTOverlayInterp, ::CC.InferenceState, remark) end strangesin(x) = sin(x) -@overlay OverlayedMT strangesin(x::Float64) = iszero(x) ? nothing : cos(x) +@overlay OVERLAY_MT strangesin(x::Float64) = + iszero(x) ? throw(StrangeSinError()) : x < 0 ? nothing : cos(x) # inference should use the overlayed method table @test Base.return_types((Float64,); interp=MTOverlayInterp()) do x @@ -84,7 +85,7 @@ end |> only === Float64 # not fully covered overlay method match overlay_match(::Any) = nothing -@overlay OverlayedMT overlay_match(::Int) = missing +@overlay OVERLAY_MT overlay_match(::Int) = missing @test Base.return_types((Any,); interp=MTOverlayInterp()) do x overlay_match(x) end |> only === Union{Nothing,Missing} @@ -116,11 +117,48 @@ Base.@assume_effects :total totalcall(f, args...) = f(args...) end end |> only === Nothing +# override `:native_executable` to allow concrete-eval for overlay-ed methods +function myfactorial(x::Int, raise) + res = 1 + 0 ≤ x < 20 || raise("x is too big") + Base.@assume_effects :terminates_locally while x > 1 + res *= x + x -= 1 + end + return res +end +raise_on_gpu1(x) = error(x) +@overlay OVERLAY_MT @noinline raise_on_gpu1(x) = #=do something with GPU=# error(x) +raise_on_gpu2(x) = error(x) +@consistent_overlay OVERLAY_MT @noinline raise_on_gpu2(x) = #=do something with GPU=# error(x) +raise_on_gpu3(x) = error(x) +@consistent_overlay OVERLAY_MT @noinline Base.@assume_effects :foldable raise_on_gpu3(x) = #=do something with GPU=# error_on_gpu(x) +cpu_factorial(x::Int) = myfactorial(x, error) +gpu_factorial1(x::Int) = myfactorial(x, raise_on_gpu1) +gpu_factorial2(x::Int) = myfactorial(x, raise_on_gpu2) +gpu_factorial3(x::Int) = myfactorial(x, raise_on_gpu3) + +@test Base.infer_effects(cpu_factorial, (Int,); interp=MTOverlayInterp()) |> Core.Compiler.is_nonoverlayed +@test Base.infer_effects(gpu_factorial1, (Int,); interp=MTOverlayInterp()) |> !Core.Compiler.is_nonoverlayed +@test Base.infer_effects(gpu_factorial2, (Int,); interp=MTOverlayInterp()) |> Core.Compiler.is_consistent_overlay +let effects = Base.infer_effects(gpu_factorial3, (Int,); interp=MTOverlayInterp()) + # check if `@consistent_overlay` together works with `@assume_effects` + # N.B. the overlaid `raise_on_gpu3` is not :foldable otherwise since `error_on_gpu` is (intetionally) undefined. + @test Core.Compiler.is_consistent_overlay(effects) + @test Core.Compiler.is_foldable(effects) +end +@test Base.infer_return_type(; interp=MTOverlayInterp()) do + Val(gpu_factorial2(3)) +end == Val{6} +@test Base.infer_return_type(; interp=MTOverlayInterp()) do + Val(gpu_factorial3(3)) +end == Val{6} + # GPUCompiler needs accurate inference through kwfunc with the overlay of `Core.throw_inexacterror` # https://github.com/JuliaLang/julia/issues/48097 @newinterp Issue48097Interp -@MethodTable Issue48097MT -CC.method_table(interp::Issue48097Interp) = CC.OverlayMethodTable(CC.get_inference_world(interp), Issue48097MT) +@MethodTable ISSUE_48097_MT +CC.method_table(interp::Issue48097Interp) = CC.OverlayMethodTable(CC.get_inference_world(interp), ISSUE_48097_MT) CC.InferenceParams(::Issue48097Interp) = CC.InferenceParams(; unoptimize_throw_blocks=false) function CC.concrete_eval_eligible(interp::Issue48097Interp, @nospecialize(f), result::CC.MethodCallResult, arginfo::CC.ArgInfo, sv::CC.AbsIntState) @@ -132,7 +170,7 @@ function CC.concrete_eval_eligible(interp::Issue48097Interp, end return ret end -@overlay Issue48097MT @noinline Core.throw_inexacterror(f::Symbol, ::Type{T}, val) where {T} = return +@overlay ISSUE_48097_MT @noinline Core.throw_inexacterror(f::Symbol, ::Type{T}, val) where {T} = return issue48097(; kwargs...) = return 42 @test fully_eliminated(; interp=Issue48097Interp(), retval=42) do issue48097(; a=1f0, b=1.0) @@ -146,28 +184,45 @@ inner52938(x, types::Type, args...; kwargs...) = x outer52938(x) = @inline inner52938(x, Tuple{}; foo=Ref(42), bar=1) @test fully_eliminated(outer52938, (Any,); interp=Issue52938Interp(), retval=Argument(2)) +# https://github.com/JuliaGPU/CUDA.jl/issues/2241 +@newinterp Cuda2241Interp +@MethodTable CUDA_2241_MT +CC.method_table(interp::Cuda2241Interp) = CC.OverlayMethodTable(CC.get_inference_world(interp), CUDA_2241_MT) +inner2241(f, types::Type, args...; kwargs...) = nothing +function outer2241(f) + @inline inner2241(f, Tuple{}; foo=Ref(42), bar=1) + return nothing +end +# NOTE CUDA.jl overlays `throw_boundserror` in a way that causes effects, but these effects +# are ignored for this call graph at the `@assume_effects` annotation on `typejoin`. +# Here it's important to use `@consistent_overlay` to avoid tainting the `:nonoverlayed` bit. +const cuda_kernel_state = Ref{Any}() +@consistent_overlay CUDA_2241_MT @inline Base.throw_boundserror(A, I) = + (cuda_kernel_state[] = (A, I); error()) +@test fully_eliminated(outer2241, (Nothing,); interp=Cuda2241Interp(), retval=nothing) + # Should not concrete-eval overlayed methods in semi-concrete interpretation @newinterp OverlaySinInterp -@MethodTable OverlaySinMT -CC.method_table(interp::OverlaySinInterp) = CC.OverlayMethodTable(CC.get_inference_world(interp), OverlaySinMT) +@MethodTable OVERLAY_SIN_MT +CC.method_table(interp::OverlaySinInterp) = CC.OverlayMethodTable(CC.get_inference_world(interp), OVERLAY_SIN_MT) overlay_sin1(x) = error("Not supposed to be called.") -@overlay OverlaySinMT overlay_sin1(x) = cos(x) -@overlay OverlaySinMT Base.sin(x::Union{Float32,Float64}) = overlay_sin1(x) +@overlay OVERLAY_SIN_MT overlay_sin1(x) = cos(x) +@overlay OVERLAY_SIN_MT Base.sin(x::Union{Float32,Float64}) = overlay_sin1(x) let oc = Base.code_ircode(; interp=OverlaySinInterp()) do sin(0.) end |> only |> first |> Core.OpaqueClosure @test oc() == cos(0.) end -@overlay OverlaySinMT Base.sin(x::Union{Float32,Float64}) = @noinline overlay_sin1(x) +@overlay OVERLAY_SIN_MT Base.sin(x::Union{Float32,Float64}) = @noinline overlay_sin1(x) let oc = Base.code_ircode(; interp=OverlaySinInterp()) do sin(0.) end |> only |> first |> Core.OpaqueClosure @test oc() == cos(0.) end _overlay_sin2(x) = error("Not supposed to be called.") -@overlay OverlaySinMT _overlay_sin2(x) = cos(x) +@overlay OVERLAY_SIN_MT _overlay_sin2(x) = cos(x) overlay_sin2(x) = _overlay_sin2(x) -@overlay OverlaySinMT Base.sin(x::Union{Float32,Float64}) = @noinline overlay_sin2(x) +@overlay OVERLAY_SIN_MT Base.sin(x::Union{Float32,Float64}) = @noinline overlay_sin2(x) let oc = Base.code_ircode(; interp=OverlaySinInterp()) do sin(0.) end |> only |> first |> Core.OpaqueClosure diff --git a/test/namedtuple.jl b/test/namedtuple.jl index 48aa8ea4a2591..558ba94bda8d0 100644 --- a/test/namedtuple.jl +++ b/test/namedtuple.jl @@ -398,14 +398,14 @@ for f in (Base.merge, Base.structdiff) fallback_func(a::NamedTuple, b::NamedTuple) = @invoke f(a::NamedTuple, b::NamedTuple) @testset let eff = Base.infer_effects(fallback_func) @test Core.Compiler.is_foldable(eff) - @test eff.nonoverlayed + @test Core.Compiler.is_nonoverlayed(eff) end @test only(Base.return_types(fallback_func)) == NamedTuple # test if `max_methods = 4` setting works as expected general_func(a::NamedTuple, b::NamedTuple) = f(a, b) @testset let eff = Base.infer_effects(general_func) @test Core.Compiler.is_foldable(eff) - @test eff.nonoverlayed + @test Core.Compiler.is_nonoverlayed(eff) end @test only(Base.return_types(general_func)) == NamedTuple end From 6aba3b247cc35911044939ccb725ddab5e81b87c Mon Sep 17 00:00:00 2001 From: KristofferC Date: Tue, 18 Jun 2024 17:14:38 +0200 Subject: [PATCH 50/71] Revert "Test and fix non-int-length bug in `view(::Memory, ::Union{UnitRange, Base.OneTo})` (#53991)" This reverts commit eb96c0763a7a224a9598b27054910356cf6dc60e. --- base/genericmemory.jl | 2 +- test/arrayops.jl | 3 --- test/core.jl | 3 --- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/base/genericmemory.jl b/base/genericmemory.jl index 82a956411aabd..06469a3362285 100644 --- a/base/genericmemory.jl +++ b/base/genericmemory.jl @@ -330,7 +330,7 @@ end isempty(inds) && return T[] # needed to allow view(Memory{T}(undef, 0), 2:1) @boundscheck checkbounds(m, inds) ref = MemoryRef(m, first(inds)) # @inbounds would be safe here but does not help performance. - dims = (Int(length(inds)),) + dims = (length(inds),) $(Expr(:new, :(Array{T, 1}), :ref, :dims)) end end diff --git a/test/arrayops.jl b/test/arrayops.jl index 1c36453a6adae..0fcf8dfa73a24 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -3206,9 +3206,6 @@ end @test @inferred(view(mem, :))::Vector{Int} == mem @test @inferred(reshape(mem, 5, 2))::Matrix{Int} == reshape(11:20, 5, 2) - # 53990 - @test @inferred(view(mem, unsigned(1):10))::Vector{Int} == 11:20 - empty_mem = Memory{Module}(undef, 0) @test_throws BoundsError view(empty_mem, 0:1) @test_throws BoundsError view(empty_mem, 1:2) diff --git a/test/core.jl b/test/core.jl index 55e48fdd70910..1d8e260fe4a0a 100644 --- a/test/core.jl +++ b/test/core.jl @@ -5524,9 +5524,6 @@ let a = Base.StringVector(2^17) @test sizeof(c) == 0 end -# issue #53990 / https://github.com/JuliaLang/julia/pull/53896#discussion_r1555087951 -@test Base.StringVector(UInt64(2)) isa Vector{UInt8} - @test_throws ArgumentError eltype(Bottom) # issue #16424, re-evaluating type definitions From fc09c2702205f48131d6573bd4ffc777b4244d17 Mon Sep 17 00:00:00 2001 From: KristofferC Date: Tue, 18 Jun 2024 17:15:38 +0200 Subject: [PATCH 51/71] Revert "Make reshape and view on Memory produce Arrays and delete wrap (#53896)" This reverts commit 146a7db5da95ea4678432ceb4d0cf9a37deb96f5. --- NEWS.md | 1 + base/array.jl | 51 ++++++++++++++++++++++++++++++++++++++++++ base/exports.jl | 1 + base/genericmemory.jl | 25 --------------------- base/iobuffer.jl | 12 ++++++---- base/strings/string.jl | 5 +---- doc/src/base/arrays.md | 1 + test/arrayops.jl | 50 ++++++++++++++--------------------------- 8 files changed, 80 insertions(+), 66 deletions(-) diff --git a/NEWS.md b/NEWS.md index 927fa4ea5fb81..11424d5df1381 100644 --- a/NEWS.md +++ b/NEWS.md @@ -88,6 +88,7 @@ New library functions * `Sys.username()` can be used to return the current user's username ([#51897]). * `Sys.isreadable(), Sys.iswritable()` can be used to check if the current user has access permissions that permit reading and writing, respectively. ([#53320]). +* `wrap(Array, m::Union{MemoryRef{T}, Memory{T}}, dims)` is the safe counterpart to `unsafe_wrap` ([#52049]). * `GC.logging_enabled()` can be used to test whether GC logging has been enabled via `GC.enable_logging` ([#51647]). * `IdSet` is now exported from Base and considered public ([#53262]). * `@time` now reports a count of any lock conflicts where a `ReentrantLock` had to wait, plus a new macro diff --git a/base/array.jl b/base/array.jl index 8824e77799690..190b37cc12cf6 100644 --- a/base/array.jl +++ b/base/array.jl @@ -3060,3 +3060,54 @@ intersect(r::AbstractRange, v::AbstractVector) = intersect(v, r) _getindex(v, i) end end + +""" + wrap(Array, m::Union{Memory{T}, MemoryRef{T}}, dims) + +Create an array of size `dims` using `m` as the underlying memory. This can be thought of as a safe version +of [`unsafe_wrap`](@ref) utilizing `Memory` or `MemoryRef` instead of raw pointers. +""" +function wrap end + +# validity checking for _wrap calls, separate from allocation of Array so that it can be more likely to inline into the caller +function _wrap(ref::MemoryRef{T}, dims::NTuple{N, Int}) where {T, N} + mem = ref.mem + mem_len = length(mem) + 1 - memoryrefoffset(ref) + len = Core.checked_dims(dims...) + @boundscheck mem_len >= len || invalid_wrap_err(mem_len, dims, len) + if N != 1 && !(ref === GenericMemoryRef(mem) && len === mem_len) + mem = ccall(:jl_genericmemory_slice, Memory{T}, (Any, Ptr{Cvoid}, Int), mem, ref.ptr_or_offset, len) + ref = MemoryRef(mem) + end + return ref +end + +@noinline invalid_wrap_err(len, dims, proddims) = throw(DimensionMismatch( + "Attempted to wrap a MemoryRef of length $len with an Array of size dims=$dims, which is invalid because prod(dims) = $proddims > $len, so that the array would have more elements than the underlying memory can store.")) + +@eval @propagate_inbounds function wrap(::Type{Array}, m::MemoryRef{T}, dims::NTuple{N, Integer}) where {T, N} + dims = convert(Dims, dims) + ref = _wrap(m, dims) + $(Expr(:new, :(Array{T, N}), :ref, :dims)) +end + +@eval @propagate_inbounds function wrap(::Type{Array}, m::Memory{T}, dims::NTuple{N, Integer}) where {T, N} + dims = convert(Dims, dims) + ref = _wrap(MemoryRef(m), dims) + $(Expr(:new, :(Array{T, N}), :ref, :dims)) +end +@eval @propagate_inbounds function wrap(::Type{Array}, m::MemoryRef{T}, l::Integer) where {T} + dims = (Int(l),) + ref = _wrap(m, dims) + $(Expr(:new, :(Array{T, 1}), :ref, :dims)) +end +@eval @propagate_inbounds function wrap(::Type{Array}, m::Memory{T}, l::Integer) where {T} + dims = (Int(l),) + ref = _wrap(MemoryRef(m), (l,)) + $(Expr(:new, :(Array{T, 1}), :ref, :dims)) +end +@eval @propagate_inbounds function wrap(::Type{Array}, m::Memory{T}) where {T} + ref = MemoryRef(m) + dims = (length(m),) + $(Expr(:new, :(Array{T, 1}), :ref, :dims)) +end diff --git a/base/exports.jl b/base/exports.jl index ffbe0dd6830ba..03aa795c38a77 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -460,6 +460,7 @@ export vcat, vec, view, + wrap, zeros, # search, find, match and related functions diff --git a/base/genericmemory.jl b/base/genericmemory.jl index 06469a3362285..64a647cdc545a 100644 --- a/base/genericmemory.jl +++ b/base/genericmemory.jl @@ -310,28 +310,3 @@ function indcopy(sz::Dims, I::GenericMemory) src = eltype(I)[I[i][_findin(I[i], i < n ? (1:sz[i]) : (1:s))] for i = 1:n] dst, src end - -# Wrapping a memory region in an Array -@eval begin # @eval for the Array construction. Block for the docstring. - function reshape(m::GenericMemory{M, T}, dims::Vararg{Int, N}) where {M, T, N} - len = Core.checked_dims(dims...) - length(m) == len || throw(DimensionMismatch("parent has $(length(m)) elements, which is incompatible with size $(dims)")) - ref = MemoryRef(m) - $(Expr(:new, :(Array{T, N}), :ref, :dims)) - end - - """ - view(m::GenericMemory{M, T}, inds::Union{UnitRange, OneTo}) - - Create a vector `v::Vector{T}` backed by the specified indices of `m`. It is only safe to - resize `v` if `m` is subseqently not used. - """ - function view(m::GenericMemory{M, T}, inds::Union{UnitRange, OneTo}) where {M, T} - isempty(inds) && return T[] # needed to allow view(Memory{T}(undef, 0), 2:1) - @boundscheck checkbounds(m, inds) - ref = MemoryRef(m, first(inds)) # @inbounds would be safe here but does not help performance. - dims = (length(inds),) - $(Expr(:new, :(Array{T, 1}), :ref, :dims)) - end -end -view(m::GenericMemory, inds::Colon) = view(m, eachindex(m)) diff --git a/base/iobuffer.jl b/base/iobuffer.jl index f9585b0599919..38713908bef9c 100644 --- a/base/iobuffer.jl +++ b/base/iobuffer.jl @@ -42,7 +42,7 @@ end # allocate Vector{UInt8}s for IOBuffer storage that can efficiently become Strings StringMemory(n::Integer) = unsafe_wrap(Memory{UInt8}, _string_n(n)) -StringVector(n::Integer) = view(StringMemory(n), 1:n)::Vector{UInt8} +StringVector(n::Integer) = wrap(Array, StringMemory(n)) # IOBuffers behave like Files. They are typically readable and writable. They are seekable. (They can be appendable). @@ -466,7 +466,7 @@ function take!(io::IOBuffer) if nbytes == 0 || io.reinit data = StringVector(0) elseif io.writable - data = view(io.data, io.offset+1:nbytes+io.offset) + data = wrap(Array, MemoryRef(io.data, io.offset + 1), nbytes) else data = copyto!(StringVector(nbytes), 1, io.data, io.offset + 1, nbytes) end @@ -475,7 +475,7 @@ function take!(io::IOBuffer) if nbytes == 0 data = StringVector(0) elseif io.writable - data = view(io.data, io.ptr:io.ptr+nbytes-1) + data = wrap(Array, MemoryRef(io.data, io.ptr), nbytes) else data = read!(io, data) end @@ -501,7 +501,11 @@ state. This should only be used internally for performance-critical It might save an allocation compared to `take!` (if the compiler elides the Array allocation), as well as omits some checks. """ -_unsafe_take!(io::IOBuffer) = view(io.data, io.offset+1:io.size) +_unsafe_take!(io::IOBuffer) = + wrap(Array, io.size == io.offset ? + MemoryRef(Memory{UInt8}()) : + MemoryRef(io.data, io.offset + 1), + io.size - io.offset) function write(to::IO, from::GenericIOBuffer) written::Int = bytesavailable(from) diff --git a/base/strings/string.jl b/base/strings/string.jl index d091baeb6c663..b2afce897a937 100644 --- a/base/strings/string.jl +++ b/base/strings/string.jl @@ -117,10 +117,7 @@ String(s::AbstractString) = print_to_string(s) @assume_effects :total String(s::Symbol) = unsafe_string(unsafe_convert(Ptr{UInt8}, s)) unsafe_wrap(::Type{Memory{UInt8}}, s::String) = ccall(:jl_string_to_genericmemory, Ref{Memory{UInt8}}, (Any,), s) -function unsafe_wrap(::Type{Vector{UInt8}}, s::String) - mem = unsafe_wrap(Memory{UInt8}, s) - view(mem, eachindex(mem)) -end +unsafe_wrap(::Type{Vector{UInt8}}, s::String) = wrap(Array, unsafe_wrap(Memory{UInt8}, s)) Vector{UInt8}(s::CodeUnits{UInt8,String}) = copyto!(Vector{UInt8}(undef, length(s)), s) Vector{UInt8}(s::String) = Vector{UInt8}(codeunits(s)) diff --git a/doc/src/base/arrays.md b/doc/src/base/arrays.md index 9acf11ea4fa68..f7dfdd502c845 100644 --- a/doc/src/base/arrays.md +++ b/doc/src/base/arrays.md @@ -140,6 +140,7 @@ Base.reshape Base.dropdims Base.vec Base.SubArray +Base.wrap ``` ## Concatenation and permutation diff --git a/test/arrayops.jl b/test/arrayops.jl index 0fcf8dfa73a24..566dd44b8dcd9 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -3187,39 +3187,23 @@ end end end -@testset "Wrapping Memory into Arrays with view and reshape" begin - mem::Memory{Int} = Memory{Int}(undef, 10) .= 11:20 - - @test_throws DimensionMismatch reshape(mem, 10, 10) - @test_throws DimensionMismatch reshape(mem, 5) - @test_throws BoundsError view(mem, 1:10, 1:10) - @test_throws BoundsError view(mem, 1:11) - @test_throws BoundsError view(mem, 3:11) - @test_throws BoundsError view(mem, 0:4) - - @test @inferred(view(mem, 1:5))::Vector{Int} == 11:15 - @test @inferred(view(mem, 1:2))::Vector{Int} == 11:12 - @test @inferred(view(mem, 1:10))::Vector{Int} == 11:20 - @test @inferred(view(mem, 3:8))::Vector{Int} == 13:18 - @test @inferred(view(mem, 20:19))::Vector{Int} == [] - @test @inferred(view(mem, -5:-7))::Vector{Int} == [] - @test @inferred(view(mem, :))::Vector{Int} == mem - @test @inferred(reshape(mem, 5, 2))::Matrix{Int} == reshape(11:20, 5, 2) - - empty_mem = Memory{Module}(undef, 0) - @test_throws BoundsError view(empty_mem, 0:1) - @test_throws BoundsError view(empty_mem, 1:2) - @test_throws DimensionMismatch reshape(empty_mem, 1) - @test_throws DimensionMismatch reshape(empty_mem, 1, 2, 3) - @test_throws ArgumentError reshape(empty_mem, 2^16, 2^16, 2^16, 2^16) - - @test @inferred(view(empty_mem, 1:0))::Vector{Module} == [] - @test @inferred(view(empty_mem, 10:3))::Vector{Module} == [] - @test @inferred(view(empty_mem, :))::Vector{Module} == empty_mem - @test isempty(@inferred(reshape(empty_mem, 0, 7, 1))::Array{Module, 3}) - - offset_inds = OffsetArrays.IdOffsetRange(values=3:6, indices=53:56) - @test @inferred(view(collect(mem), offset_inds)) == view(mem, offset_inds) +@testset "Wrapping Memory into Arrays" begin + mem = Memory{Int}(undef, 10) .= 1 + memref = MemoryRef(mem) + @test_throws DimensionMismatch wrap(Array, mem, (10, 10)) + @test wrap(Array, mem, (5,)) == ones(Int, 5) + @test wrap(Array, mem, 2) == ones(Int, 2) + @test wrap(Array, memref, 10) == ones(Int, 10) + @test wrap(Array, memref, (2,2,2)) == ones(Int,2,2,2) + @test wrap(Array, mem, (5, 2)) == ones(Int, 5, 2) + + memref2 = MemoryRef(mem, 3) + @test wrap(Array, memref2, (5,)) == ones(Int, 5) + @test wrap(Array, memref2, 2) == ones(Int, 2) + @test wrap(Array, memref2, (2,2,2)) == ones(Int,2,2,2) + @test wrap(Array, memref2, (3, 2)) == ones(Int, 3, 2) + @test_throws DimensionMismatch wrap(Array, memref2, 9) + @test_throws DimensionMismatch wrap(Array, memref2, 10) end @testset "Memory size" begin From 410cac70309b56cc8120a02e995bd59511cb8fc7 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Tue, 18 Jun 2024 15:22:21 -0400 Subject: [PATCH 52/71] REPL: spawn to avoid blocking typing, fix pkg repl transition sequencing (#54785) Based on https://github.com/JuliaLang/julia/pull/54760#discussion_r1635765035 This changes the `prompt!` design to not consume on the two tasks until inside the lock and spawns the two tasks to avoid blocking typing --- stdlib/REPL/src/LineEdit.jl | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/stdlib/REPL/src/LineEdit.jl b/stdlib/REPL/src/LineEdit.jl index 116bb10bae273..d9a1d4b0c72f1 100644 --- a/stdlib/REPL/src/LineEdit.jl +++ b/stdlib/REPL/src/LineEdit.jl @@ -2824,22 +2824,27 @@ keymap_data(ms::MIState, m::ModalInterface) = keymap_data(state(ms), mode(ms)) function prompt!(term::TextTerminal, prompt::ModalInterface, s::MIState = init_state(term, prompt)) Base.reseteof(term) + l = Base.ReentrantLock() + t1 = Threads.@spawn :interactive while true + wait(s.async_channel) + status = @lock l begin + fcn = take!(s.async_channel) + fcn(s) + end + status ∈ (:ok, :ignore) || break + end raw!(term, true) enable_bracketed_paste(term) try activate(prompt, s, term, term) old_state = mode(s) - l = Base.ReentrantLock() - t = @async while true - fcn = take!(s.async_channel) - status = @lock l fcn(s) - status ∈ (:ok, :ignore) || break - end - Base.errormonitor(t) - while true - kmap = keymap(s, prompt) - fcn = match_input(kmap, s) + # spawn this because the main repl task is sticky (due to use of @async and _wait2) + # and we want to not block typing when the repl task thread is busy + t2 = Threads.@spawn :interactive while true + eof(term) || peek(term, Char) # wait before locking but don't consume @lock l begin + kmap = keymap(s, prompt) + fcn = match_input(kmap, s) kdata = keymap_data(s, prompt) s.current_action = :unknown # if the to-be-run action doesn't update this field, # :unknown will be recorded in the last_action field @@ -2870,7 +2875,10 @@ function prompt!(term::TextTerminal, prompt::ModalInterface, s::MIState = init_s end end end + return fetch(t2) finally + put!(s.async_channel, Returns(:done)) + wait(t1) raw!(term, false) && disable_bracketed_paste(term) end # unreachable From 0c06ae180074b3085c94ff6fb597143deb62f03c Mon Sep 17 00:00:00 2001 From: KristofferC Date: Wed, 19 Jun 2024 10:17:02 +0200 Subject: [PATCH 53/71] unexport wrap --- base/exports.jl | 1 - test/arrayops.jl | 24 ++++++++++++------------ 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/base/exports.jl b/base/exports.jl index 03aa795c38a77..ffbe0dd6830ba 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -460,7 +460,6 @@ export vcat, vec, view, - wrap, zeros, # search, find, match and related functions diff --git a/test/arrayops.jl b/test/arrayops.jl index 566dd44b8dcd9..0659cebca1e86 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -3190,20 +3190,20 @@ end @testset "Wrapping Memory into Arrays" begin mem = Memory{Int}(undef, 10) .= 1 memref = MemoryRef(mem) - @test_throws DimensionMismatch wrap(Array, mem, (10, 10)) - @test wrap(Array, mem, (5,)) == ones(Int, 5) - @test wrap(Array, mem, 2) == ones(Int, 2) - @test wrap(Array, memref, 10) == ones(Int, 10) - @test wrap(Array, memref, (2,2,2)) == ones(Int,2,2,2) - @test wrap(Array, mem, (5, 2)) == ones(Int, 5, 2) + @test_throws DimensionMismatch Base.wrap(Array, mem, (10, 10)) + @test Base.wrap(Array, mem, (5,)) == ones(Int, 5) + @test Base.wrap(Array, mem, 2) == ones(Int, 2) + @test Base.wrap(Array, memref, 10) == ones(Int, 10) + @test Base.wrap(Array, memref, (2,2,2)) == ones(Int,2,2,2) + @test Base.wrap(Array, mem, (5, 2)) == ones(Int, 5, 2) memref2 = MemoryRef(mem, 3) - @test wrap(Array, memref2, (5,)) == ones(Int, 5) - @test wrap(Array, memref2, 2) == ones(Int, 2) - @test wrap(Array, memref2, (2,2,2)) == ones(Int,2,2,2) - @test wrap(Array, memref2, (3, 2)) == ones(Int, 3, 2) - @test_throws DimensionMismatch wrap(Array, memref2, 9) - @test_throws DimensionMismatch wrap(Array, memref2, 10) + @test Base.wrap(Array, memref2, (5,)) == ones(Int, 5) + @test Base.wrap(Array, memref2, 2) == ones(Int, 2) + @test Base.wrap(Array, memref2, (2,2,2)) == ones(Int,2,2,2) + @test Base.wrap(Array, memref2, (3, 2)) == ones(Int, 3, 2) + @test_throws DimensionMismatch Base.wrap(Array, memref2, 9) + @test_throws DimensionMismatch Base.wrap(Array, memref2, 10) end @testset "Memory size" begin From 1d4cf2cf4e1f011830ad541bc5f8f6bcc1fea092 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Thu, 20 Jun 2024 22:07:59 -0400 Subject: [PATCH 54/71] REPL.prompt!: don't use Char peek (#54865) --- stdlib/REPL/src/LineEdit.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/REPL/src/LineEdit.jl b/stdlib/REPL/src/LineEdit.jl index d9a1d4b0c72f1..431e78551ed2f 100644 --- a/stdlib/REPL/src/LineEdit.jl +++ b/stdlib/REPL/src/LineEdit.jl @@ -2841,7 +2841,7 @@ function prompt!(term::TextTerminal, prompt::ModalInterface, s::MIState = init_s # spawn this because the main repl task is sticky (due to use of @async and _wait2) # and we want to not block typing when the repl task thread is busy t2 = Threads.@spawn :interactive while true - eof(term) || peek(term, Char) # wait before locking but don't consume + eof(term) || peek(term) # wait before locking but don't consume @lock l begin kmap = keymap(s, prompt) fcn = match_input(kmap, s) From 31dac53ff09d84be6a02d61980a25b036ee4bc46 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Tue, 18 Jun 2024 10:42:51 -0400 Subject: [PATCH 55/71] add sticky task warning to `@task` and `schedule` (#54815) The fact that `@async` causes the task that it was scheduled from to also become sticky is well documented in the warning in [`@async` docs](https://docs.julialang.org/en/v1/base/parallel/#Base.@async), but it's not clear that creating a task and scheduling it also has the same effect, by default. --- base/docs/basedocs.jl | 3 ++- base/task.jl | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index d1a51ae9fdc4c..91c9e44f1913b 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -1766,7 +1766,8 @@ The task will run in the "world age" from the parent at construction when [`sche !!! warning By default tasks will have the sticky bit set to true `t.sticky`. This models the historic default for [`@async`](@ref). Sticky tasks can only be run on the worker thread - they are first scheduled on. To obtain the behavior of [`Threads.@spawn`](@ref) set the sticky + they are first scheduled on, and when scheduled will make the task that they were scheduled + from sticky. To obtain the behavior of [`Threads.@spawn`](@ref) set the sticky bit manually to `false`. # Examples diff --git a/base/task.jl b/base/task.jl index ba96d7bca5095..553b0a94f098b 100644 --- a/base/task.jl +++ b/base/task.jl @@ -115,6 +115,13 @@ end 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. +!!! warning + By default tasks will have the sticky bit set to true `t.sticky`. This models the + historic default for [`@async`](@ref). Sticky tasks can only be run on the worker thread + they are first scheduled on, and when scheduled will make the task that they were scheduled + from sticky. To obtain the behavior of [`Threads.@spawn`](@ref) set the sticky + bit manually to `false`. + # Examples ```jldoctest julia> a1() = sum(i for i in 1:1000); @@ -839,6 +846,13 @@ the woken task. It is incorrect to use `schedule` on an arbitrary `Task` that has already been started. See [the API reference](@ref low-level-schedule-wait) for more information. +!!! warning + By default tasks will have the sticky bit set to true `t.sticky`. This models the + historic default for [`@async`](@ref). Sticky tasks can only be run on the worker thread + they are first scheduled on, and when scheduled will make the task that they were scheduled + from sticky. To obtain the behavior of [`Threads.@spawn`](@ref) set the sticky + bit manually to `false`. + # Examples ```jldoctest julia> a5() = sum(i for i in 1:1000); From b04de5422602178d2392ce8b3a1f85a1c96d4ef4 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Fri, 14 Jun 2024 17:58:40 -0400 Subject: [PATCH 56/71] REPL: fix projname when project_file is missing (#54795) --- stdlib/REPL/src/Pkg_beforeload.jl | 16 ++++++++++++---- stdlib/REPL/test/repl.jl | 22 ++++++++++++++++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/stdlib/REPL/src/Pkg_beforeload.jl b/stdlib/REPL/src/Pkg_beforeload.jl index 78b76374cf580..56c4e2562f7e6 100644 --- a/stdlib/REPL/src/Pkg_beforeload.jl +++ b/stdlib/REPL/src/Pkg_beforeload.jl @@ -69,10 +69,18 @@ function relative_project_path(project_file::String, path::String) end function projname(project_file::String) - p = Base.TOML.Parser() - Base.TOML.reinit!(p, read(project_file, String); filepath=project_file) - proj = Base.TOML.parse(p) - name = get(proj, "name", nothing) + if isfile(project_file) + name = try + p = Base.TOML.Parser() + Base.TOML.reinit!(p, read(project_file, String); filepath=project_file) + proj = Base.TOML.parse(p) + get(proj, "name", nothing) + catch + nothing + end + else + name = nothing + end if name === nothing name = basename(dirname(project_file)) end diff --git a/stdlib/REPL/test/repl.jl b/stdlib/REPL/test/repl.jl index f50bfb4528ea2..671c45a6bf2b1 100644 --- a/stdlib/REPL/test/repl.jl +++ b/stdlib/REPL/test/repl.jl @@ -1778,3 +1778,25 @@ end @test_broken isempty(undoc) @test undoc == [:AbstractREPL, :BasicREPL, :LineEditREPL, :StreamREPL] end + +@testset "Dummy Pkg prompt" begin + # do this in an empty depot to test default for new users + withenv("JULIA_DEPOT_PATH" => mktempdir(), "JULIA_LOAD_PATH" => nothing) do + prompt = readchomp(`$(Base.julia_cmd()[1]) --startup-file=no -e "using REPL; print(REPL.Pkg_promptf())"`) + @test prompt == "(@v$(VERSION.major).$(VERSION.minor)) pkg> " + end + + get_prompt(proj::String) = readchomp(`$(Base.julia_cmd()[1]) --startup-file=no $(proj) -e "using REPL; print(REPL.Pkg_promptf())"`) + + @test get_prompt("--project=$(pkgdir(REPL))") == "(REPL) pkg> " + + tdir = mkpath(joinpath(mktempdir(), "foo")) + @test get_prompt("--project=$tdir") == "(foo) pkg> " + + proj_file = joinpath(tdir, "Project.toml") + touch(proj_file) # make a bad Project.toml + @test get_prompt("--project=$proj_file") == "(foo) pkg> " + + write(proj_file, "name = \"Bar\"\n") + @test get_prompt("--project=$proj_file") == "(Bar) pkg> " +end From 202151d28077009ee3d69217b6d31278256f371a Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Thu, 13 Jun 2024 17:16:41 -0400 Subject: [PATCH 57/71] make recommendation clearer on manifest version mismatch (#54779) It seems common for people to not infer the recommended action here, so make it clearer. --- base/loading.jl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/base/loading.jl b/base/loading.jl index 13bb282a58c52..17e5db703da13 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -2184,15 +2184,16 @@ function collect_manifest_warnings() if !isempty(unsuitable_manifests) msg *= """ - Note that the following manifests in the load path were resolved with a different - julia version, which may be the cause of the error: + julia version, which may be the cause of the error. Try to re-resolve them in the + current version, or consider deleting them if that fails: $(join(unsuitable_manifests, "\n ")) """ end if !isempty(dev_manifests) msg *= """ - Note that the following manifests in the load path were resolved a potentially - different DEV version of the current version, which may be the cause - of the error: + different DEV version of the current version, which may be the cause of the error. + Try to re-resolve them in the current version, or consider deleting them if that fails: $(join(dev_manifests, "\n ")) """ end From ffb88e86d6c8eecb871b1ec0ab7d44fa9a694917 Mon Sep 17 00:00:00 2001 From: Kristoffer Date: Sat, 22 Jun 2024 08:39:41 +0200 Subject: [PATCH 58/71] fix REPL prompt on 1.10 --- stdlib/REPL/src/Pkg_beforeload.jl | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/stdlib/REPL/src/Pkg_beforeload.jl b/stdlib/REPL/src/Pkg_beforeload.jl index 56c4e2562f7e6..7d80cfee3d54f 100644 --- a/stdlib/REPL/src/Pkg_beforeload.jl +++ b/stdlib/REPL/src/Pkg_beforeload.jl @@ -53,15 +53,6 @@ function find_project_file(env::Union{Nothing,String}=nothing) return safe_realpath(project_file) end -function find_root_base_project(start_project::String) - project_file = start_project - while true - base_project_file = Base.base_project(project_file) - base_project_file === nothing && return project_file - project_file = base_project_file - end -end - function relative_project_path(project_file::String, path::String) # compute path relative the project # realpath needed to expand symlinks before taking the relative path @@ -107,17 +98,11 @@ function Pkg_promptf() else project_name = projname(project_file) if project_name !== nothing - root = find_root_base_project(project_file) - rootname = projname(root) - if root !== project_file - path_prefix = "/" * dirname(relative_project_path(root, project_file)) - else - path_prefix = "" - end - if textwidth(rootname) > 30 - rootname = first(rootname, 27) * "..." + pname = projname(project_file) + if textwidth(pname) > 30 + pname = first(pname, 27) * "..." end - prefix = "($(rootname)$(path_prefix)) " + prefix = "($(pname)) " prev_prefix = prefix prev_project_timestamp = mtime(project_file) prev_project_file = project_file From dff9905cfa2f0550d60b0e87e4833fadabe3b13f Mon Sep 17 00:00:00 2001 From: Kristoffer Date: Sat, 22 Jun 2024 20:20:02 +0200 Subject: [PATCH 59/71] bump Pkg to latest v1.11 --- .../Pkg-280f702c1ace34b51cd4551ed448b19516c36849.tar.gz/md5 | 1 + .../Pkg-280f702c1ace34b51cd4551ed448b19516c36849.tar.gz/sha512 | 1 + .../Pkg-d10463b49ef54580747ba6378bbe56cc9783077a.tar.gz/md5 | 1 - .../Pkg-d10463b49ef54580747ba6378bbe56cc9783077a.tar.gz/sha512 | 1 - stdlib/Pkg.version | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 deps/checksums/Pkg-280f702c1ace34b51cd4551ed448b19516c36849.tar.gz/md5 create mode 100644 deps/checksums/Pkg-280f702c1ace34b51cd4551ed448b19516c36849.tar.gz/sha512 delete mode 100644 deps/checksums/Pkg-d10463b49ef54580747ba6378bbe56cc9783077a.tar.gz/md5 delete mode 100644 deps/checksums/Pkg-d10463b49ef54580747ba6378bbe56cc9783077a.tar.gz/sha512 diff --git a/deps/checksums/Pkg-280f702c1ace34b51cd4551ed448b19516c36849.tar.gz/md5 b/deps/checksums/Pkg-280f702c1ace34b51cd4551ed448b19516c36849.tar.gz/md5 new file mode 100644 index 0000000000000..4abb92bd80663 --- /dev/null +++ b/deps/checksums/Pkg-280f702c1ace34b51cd4551ed448b19516c36849.tar.gz/md5 @@ -0,0 +1 @@ +abc7137d1f7e982957a605d3290f9e12 diff --git a/deps/checksums/Pkg-280f702c1ace34b51cd4551ed448b19516c36849.tar.gz/sha512 b/deps/checksums/Pkg-280f702c1ace34b51cd4551ed448b19516c36849.tar.gz/sha512 new file mode 100644 index 0000000000000..90c5e7cf45e19 --- /dev/null +++ b/deps/checksums/Pkg-280f702c1ace34b51cd4551ed448b19516c36849.tar.gz/sha512 @@ -0,0 +1 @@ +3dd9b0cf67a2846b23af0c666e4f4780a0dbc7348fa7d0689c2dccbe462a3b06240e3116b8e1c5c89747858d4c72370e0b3823ce635a09144901f338d74cc7fd diff --git a/deps/checksums/Pkg-d10463b49ef54580747ba6378bbe56cc9783077a.tar.gz/md5 b/deps/checksums/Pkg-d10463b49ef54580747ba6378bbe56cc9783077a.tar.gz/md5 deleted file mode 100644 index fe756fe94c54e..0000000000000 --- a/deps/checksums/Pkg-d10463b49ef54580747ba6378bbe56cc9783077a.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -37a6fc0747a6fef4ea012a7225a7d4bf diff --git a/deps/checksums/Pkg-d10463b49ef54580747ba6378bbe56cc9783077a.tar.gz/sha512 b/deps/checksums/Pkg-d10463b49ef54580747ba6378bbe56cc9783077a.tar.gz/sha512 deleted file mode 100644 index c54d5953ab2a0..0000000000000 --- a/deps/checksums/Pkg-d10463b49ef54580747ba6378bbe56cc9783077a.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -89be88b2318c7cdd173ba505696e89ee9426f962cb13308cea946ff485e93ac6f5cf3c15b4fd2de225eabeb580a100ed9c592512b975b753c76b40690844fb4e diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 5dc8ff86c5dc6..81fcdb434fc92 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = release-1.11 -PKG_SHA1 = d10463b49ef54580747ba6378bbe56cc9783077a +PKG_SHA1 = 280f702c1ace34b51cd4551ed448b19516c36849 PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 From 06fdfd7f848c2c2dc6fb28d9a05eee562a4733ad Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Wed, 12 Jun 2024 03:13:31 +0000 Subject: [PATCH 60/71] add missing compat entry to edit (#54769) (cherry picked from commit 3f8e1bd953ebff71cc430ec87977d06bd16e16f2) --- stdlib/InteractiveUtils/src/editless.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/stdlib/InteractiveUtils/src/editless.jl b/stdlib/InteractiveUtils/src/editless.jl index 5b87dc0c57d40..6d1d75f1072ea 100644 --- a/stdlib/InteractiveUtils/src/editless.jl +++ b/stdlib/InteractiveUtils/src/editless.jl @@ -223,6 +223,9 @@ Edit a file or directory optionally providing a line number to edit the file at. Return to the `julia` prompt when you quit the editor. The editor can be changed by setting `JULIA_EDITOR`, `VISUAL` or `EDITOR` as an environment variable. +!!! compat "Julia 1.9" + The `column` argument requires at least Julia 1.9. + See also [`InteractiveUtils.define_editor`](@ref). """ function edit(path::AbstractString, line::Integer=0, column::Integer=0) From b12f4e1ad77f3651ac6d04888a30a05846249252 Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Thu, 13 Jun 2024 10:33:35 -0700 Subject: [PATCH 61/71] [LinearAlgebra] Improve resilience to unknown libblastrampoline flags (#54781) When testing a new version of `libblastrampoline` that may have flags and values that we don't know about, raise a nice warning rather than a hard error. (cherry picked from commit 50445067690394c95ecc2d456542fb35c6c36c1d) --- stdlib/LinearAlgebra/src/lbt.jl | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/stdlib/LinearAlgebra/src/lbt.jl b/stdlib/LinearAlgebra/src/lbt.jl index aadcb45d606a3..02b4411566290 100644 --- a/stdlib/LinearAlgebra/src/lbt.jl +++ b/stdlib/LinearAlgebra/src/lbt.jl @@ -13,6 +13,19 @@ struct lbt_library_info_t f2c::Int32 cblas::Int32 end + +macro get_warn(map, key) + return quote + if !haskey($(esc(map)), $(esc(key))) + @warn(string("[LBT] Unknown key into ", $(string(map)), ": ", $(esc(key)), ", defaulting to :unknown")) + # All the unknown values share a common value: `-1` + $(esc(map))[$(esc(LBT_INTERFACE_UNKNOWN))] + else + $(esc(map))[$(esc(key))] + end + end +end + const LBT_INTERFACE_LP64 = 32 const LBT_INTERFACE_ILP64 = 64 const LBT_INTERFACE_UNKNOWN = -1 @@ -35,10 +48,12 @@ const LBT_INV_F2C_MAP = Dict(v => k for (k, v) in LBT_F2C_MAP) const LBT_COMPLEX_RETSTYLE_NORMAL = 0 const LBT_COMPLEX_RETSTYLE_ARGUMENT = 1 +const LBT_COMPLEX_RETSTYLE_FNDA = 2 const LBT_COMPLEX_RETSTYLE_UNKNOWN = -1 const LBT_COMPLEX_RETSTYLE_MAP = Dict( LBT_COMPLEX_RETSTYLE_NORMAL => :normal, LBT_COMPLEX_RETSTYLE_ARGUMENT => :argument, + LBT_COMPLEX_RETSTYLE_FNDA => :float_normal_double_argument, LBT_COMPLEX_RETSTYLE_UNKNOWN => :unknown, ) const LBT_INV_COMPLEX_RETSTYLE_MAP = Dict(v => k for (k, v) in LBT_COMPLEX_RETSTYLE_MAP) @@ -69,10 +84,10 @@ struct LBTLibraryInfo lib_info.handle, unsafe_string(lib_info.suffix), unsafe_wrap(Vector{UInt8}, lib_info.active_forwards, div(num_exported_symbols,8)+1), - LBT_INTERFACE_MAP[lib_info.interface], - LBT_COMPLEX_RETSTYLE_MAP[lib_info.complex_retstyle], - LBT_F2C_MAP[lib_info.f2c], - LBT_CBLAS_MAP[lib_info.cblas], + @get_warn(LBT_INTERFACE_MAP, lib_info.interface), + @get_warn(LBT_COMPLEX_RETSTYLE_MAP, lib_info.complex_retstyle), + @get_warn(LBT_F2C_MAP, lib_info.f2c), + @get_warn(LBT_CBLAS_MAP, lib_info.cblas), ) end end From 639f62ec364497fb23b9bb9826bd66be651e01b3 Mon Sep 17 00:00:00 2001 From: Christian Guinard <28689358+christiangnrd@users.noreply.github.com> Date: Fri, 14 Jun 2024 13:27:05 -0300 Subject: [PATCH 62/71] Add missing word in manifest warning (#54802) Noticed while looking at #54779. (cherry picked from commit d9a0c2553ba023adc54a297fce8cb3dc127be8e6) --- base/loading.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/loading.jl b/base/loading.jl index 17e5db703da13..f210d2f67c9ba 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -2191,7 +2191,7 @@ function collect_manifest_warnings() end if !isempty(dev_manifests) msg *= """ - - Note that the following manifests in the load path were resolved a potentially + - Note that the following manifests in the load path were resolved with a potentially different DEV version of the current version, which may be the cause of the error. Try to re-resolve them in the current version, or consider deleting them if that fails: $(join(dev_manifests, "\n ")) From 83ff23087049221cf2b20386e5162e81fbcfb207 Mon Sep 17 00:00:00 2001 From: Mark Kittisopikul Date: Wed, 19 Jun 2024 03:00:15 -0400 Subject: [PATCH 63/71] Artifacts: Change Dict{AbstractPlatform} to Dict{Platform} to reduce Pkg invalidations (#54073) This pull request now only changes `dl_dict = Dict{AbstractPlatform,Dict{String,Any}}()` to `dl_dict = Dict{Platform,Dict{String,Any}}()` in `artifact_meta`. This is possible since the other possible types for the key are either `Platform` or `nothing` unless someone overrides `Artifacts.unpack_platform`. (cherry picked from commit a14cc38512b6daab6b8417ebb8a64fc794ff89cc) --- stdlib/Artifacts/src/Artifacts.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/stdlib/Artifacts/src/Artifacts.jl b/stdlib/Artifacts/src/Artifacts.jl index e8a40c76b9e49..bd44369655ae4 100644 --- a/stdlib/Artifacts/src/Artifacts.jl +++ b/stdlib/Artifacts/src/Artifacts.jl @@ -9,7 +9,9 @@ that would be convenient to place within an immutable, life-cycled datastore. module Artifacts import Base: get, SHA1 -using Base.BinaryPlatforms, Base.TOML +using Base.BinaryPlatforms: AbstractPlatform, Platform, HostPlatform +using Base.BinaryPlatforms: tags, triplet, select_platform +using Base.TOML: TOML export artifact_exists, artifact_path, artifact_meta, artifact_hash, select_downloadable_artifacts, find_artifacts_toml, @artifact_str @@ -400,7 +402,7 @@ function artifact_meta(name::String, artifact_dict::Dict, artifacts_toml::String # If it's an array, find the entry that best matches our current platform if isa(meta, Vector) - dl_dict = Dict{AbstractPlatform,Dict{String,Any}}() + dl_dict = Dict{Platform,Dict{String,Any}}() for x in meta x = x::Dict{String, Any} dl_dict[unpack_platform(x, name, artifacts_toml)] = x From 46d070546734e29c0d7c0ad01bfa46150063043a Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Fri, 21 Jun 2024 12:59:16 -0400 Subject: [PATCH 64/71] fix var name in `reseteof(s::BufferStream)` (#54859) Evidently not covered by tests etc. (yet) (cherry picked from commit 3de5e5dfab9f90d3070dbb13b128f4bfd71979ce) --- base/stream.jl | 2 +- test/iobuffer.jl | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/base/stream.jl b/base/stream.jl index fdb19f7fec45b..d46c5fabb1940 100644 --- a/base/stream.jl +++ b/base/stream.jl @@ -1604,7 +1604,7 @@ end skip(s::BufferStream, n) = skip(s.buffer, n) -function reseteof(x::BufferStream) +function reseteof(s::BufferStream) lock(s.cond) do s.status = StatusOpen nothing diff --git a/test/iobuffer.jl b/test/iobuffer.jl index d82a68c61f780..0e74595d29d20 100644 --- a/test/iobuffer.jl +++ b/test/iobuffer.jl @@ -277,6 +277,7 @@ end c = zeros(UInt8,8) @test bytesavailable(bstream) == 8 @test !eof(bstream) + @test Base.reseteof(bstream) === nothing # TODO: Actually test intended effect read!(bstream,c) @test c == a[3:10] @test closewrite(bstream) === nothing From 82d961b65c9b2cd335863010c3c420a3789ab6b2 Mon Sep 17 00:00:00 2001 From: Neven Sajko Date: Sat, 22 Jun 2024 19:35:27 +0200 Subject: [PATCH 65/71] `nextind`, `prevind` doc strings: correct the Markdown (#54876) (cherry picked from commit 7cdbf744c4d916052615d6a30cf89b2e88c4aef6) --- base/tuple.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/base/tuple.jl b/base/tuple.jl index 23b2270e96cc4..5ab5b4b1c7a26 100644 --- a/base/tuple.jl +++ b/base/tuple.jl @@ -76,8 +76,8 @@ keys(@nospecialize t::Tuple) = OneTo(length(t)) """ prevind(A, i) -Return the index before `i` in `A`. The returned index is often equivalent to `i -- 1` for an integer `i`. This function can be useful for generic code. +Return the index before `i` in `A`. The returned index is often equivalent to +`i - 1` for an integer `i`. This function can be useful for generic code. !!! warning The returned index might be out of bounds. Consider using @@ -110,8 +110,8 @@ function prevind end """ nextind(A, i) -Return the index after `i` in `A`. The returned index is often equivalent to `i -+ 1` for an integer `i`. This function can be useful for generic code. +Return the index after `i` in `A`. The returned index is often equivalent to +`i + 1` for an integer `i`. This function can be useful for generic code. !!! warning The returned index might be out of bounds. Consider using From cf4f1bab961d0e99e8cfdd4c3ef0acb3bd3767fb Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Sat, 22 Jun 2024 14:05:02 -0400 Subject: [PATCH 66/71] serialization: fix relocatability bug (#54738) (cherry picked from commit 323e725c1e4848414b5642b8f54c24916b9ddd9e) --- src/staticdata.c | 12 ++++++++++-- src/staticdata_utils.c | 14 +++++++++++++- test/precompile.jl | 2 +- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/staticdata.c b/src/staticdata.c index ed11bcfc15171..76bb488731a92 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -461,6 +461,7 @@ void *native_functions; // opaque jl_native_code_desc_t blob used for fetching // table of struct field addresses to rewrite during saving static htable_t field_replace; +static htable_t relocatable_ext_cis; // array of definitions for the predefined function pointers // (reverse of fptr_to_id) @@ -693,7 +694,8 @@ static int needs_uniquing(jl_value_t *v) JL_NOTSAFEPOINT static void record_field_change(jl_value_t **addr, jl_value_t *newval) JL_NOTSAFEPOINT { - ptrhash_put(&field_replace, (void*)addr, newval); + if (*addr != newval) + ptrhash_put(&field_replace, (void*)addr, newval); } static jl_value_t *get_replaceable_field(jl_value_t **addr, int mutabl) JL_GC_DISABLED @@ -836,6 +838,8 @@ static void jl_insert_into_serialization_queue(jl_serializer_state *s, jl_value_ // TODO: if (ci in ci->defs->cache) record_field_change((jl_value_t**)&ci->next, NULL); } + if (jl_atomic_load_relaxed(&ci->inferred) && !is_relocatable_ci(&relocatable_ext_cis, ci)) + record_field_change((jl_value_t**)&ci->inferred, jl_nothing); } if (immediate) // must be things that can be recursively handled, and valid as type parameters @@ -1631,6 +1635,7 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED jl_atomic_store_release(&newci->min_world, 1); jl_atomic_store_release(&newci->max_world, 0); } + newci->relocatability = 0; } jl_atomic_store_relaxed(&newci->invoke, NULL); jl_atomic_store_relaxed(&newci->specsigflags, 0); @@ -2573,7 +2578,7 @@ static void jl_prepare_serialization_data(jl_array_t *mod_array, jl_array_t *new *edges = jl_alloc_vec_any(0); *method_roots_list = jl_alloc_vec_any(0); // Collect the new method roots for external specializations - jl_collect_new_roots(*method_roots_list, *new_ext_cis, worklist_key); + jl_collect_new_roots(&relocatable_ext_cis, *method_roots_list, *new_ext_cis, worklist_key); jl_collect_edges(*edges, *ext_targets, *new_ext_cis, world); } assert(edges_map == NULL); // jl_collect_edges clears this when done @@ -2974,6 +2979,7 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli assert((ct->reentrant_timing & 0b1110) == 0); ct->reentrant_timing |= 0b1000; if (worklist) { + htable_new(&relocatable_ext_cis, 0); jl_prepare_serialization_data(mod_array, newly_inferred, jl_worklist_key(worklist), &extext_methods, &new_ext_cis, &method_roots_list, &ext_targets, &edges); if (!emit_split) { @@ -2990,6 +2996,8 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli jl_save_system_image_to_stream(ff, mod_array, worklist, extext_methods, new_ext_cis, method_roots_list, ext_targets, edges); if (_native_data != NULL) native_functions = NULL; + if (worklist) + htable_free(&relocatable_ext_cis); // make sure we don't run any Julia code concurrently before this point // Re-enable running julia code for postoutput hooks, atexit, etc. jl_gc_enable_finalizers(ct, 1); diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index 05fce7cfc4630..8d3d071054083 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -209,6 +209,17 @@ static int has_backedge_to_worklist(jl_method_instance_t *mi, htable_t *visited, return found; } +static int is_relocatable_ci(htable_t *relocatable_ext_cis, jl_code_instance_t *ci) +{ + if (!ci->relocatability) + return 0; + jl_method_instance_t *mi = ci->def; + jl_method_t *m = mi->def.method; + if (!ptrhash_has(relocatable_ext_cis, ci) && jl_object_in_image((jl_value_t*)m) && (!jl_is_method(m) || jl_object_in_image((jl_value_t*)m->module))) + return 0; + return 1; +} + // Given the list of CodeInstances that were inferred during the build, select // those that are (1) external, (2) still valid, (3) are inferred to be called // from the worklist or explicitly added by a `precompile` statement, and @@ -258,7 +269,7 @@ static jl_array_t *queue_external_cis(jl_array_t *list) } // New roots for external methods -static void jl_collect_new_roots(jl_array_t *roots, jl_array_t *new_ext_cis, uint64_t key) +static void jl_collect_new_roots(htable_t *relocatable_ext_cis, jl_array_t *roots, jl_array_t *new_ext_cis, uint64_t key) { htable_t mset; htable_new(&mset, 0); @@ -269,6 +280,7 @@ static void jl_collect_new_roots(jl_array_t *roots, jl_array_t *new_ext_cis, uin jl_method_t *m = ci->def->def.method; assert(jl_is_method(m)); ptrhash_put(&mset, (void*)m, (void*)m); + ptrhash_put(relocatable_ext_cis, (void*)ci, (void*)ci); } int nwithkey; void *const *table = mset.table; diff --git a/test/precompile.jl b/test/precompile.jl index 2570a425a03b7..bc6a791627616 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -806,7 +806,7 @@ precompile_test_harness("code caching") do dir mi = minternal.specializations::Core.MethodInstance @test mi.specTypes == Tuple{typeof(M.getelsize),Vector{Int32}} ci = mi.cache - @test ci.relocatability == 1 + @test ci.relocatability == 0 @test ci.inferred !== nothing # ...and that we can add "untracked" roots & non-relocatable CodeInstances to them too Base.invokelatest() do From 782386fe2b2ce1b6550315e93a87db01ff375059 Mon Sep 17 00:00:00 2001 From: Kristoffer Date: Sun, 23 Jun 2024 10:11:49 +0200 Subject: [PATCH 67/71] Revert "[LBT] Upgrade to v5.9.0 (#54361)" This reverts commit 0df6f80003eeee247db2b61ba51ab8dcc00e1f91. --- deps/blastrampoline.version | 6 +- deps/checksums/blastrampoline | 68 +++++++++++------------ stdlib/libblastrampoline_jll/Project.toml | 4 +- 3 files changed, 39 insertions(+), 39 deletions(-) diff --git a/deps/blastrampoline.version b/deps/blastrampoline.version index cd23d50285549..616300377e3e6 100644 --- a/deps/blastrampoline.version +++ b/deps/blastrampoline.version @@ -2,6 +2,6 @@ BLASTRAMPOLINE_JLL_NAME := libblastrampoline ## source build -BLASTRAMPOLINE_VER := 5.9.0 -BLASTRAMPOLINE_BRANCH=v5.9.0 -BLASTRAMPOLINE_SHA1=3c451b99639a984126c6a618d4394ee582e872a2 +BLASTRAMPOLINE_VER := 5.8.0 +BLASTRAMPOLINE_BRANCH=v5.8.0 +BLASTRAMPOLINE_SHA1=81316155d4838392e8462a92bcac3eebe9acd0c7 diff --git a/deps/checksums/blastrampoline b/deps/checksums/blastrampoline index d62a7b5a69cc8..d72a584fd1b0c 100644 --- a/deps/checksums/blastrampoline +++ b/deps/checksums/blastrampoline @@ -1,34 +1,34 @@ -blastrampoline-3c451b99639a984126c6a618d4394ee582e872a2.tar.gz/md5/74f00893d201fa37910c52a1dfd96078 -blastrampoline-3c451b99639a984126c6a618d4394ee582e872a2.tar.gz/sha512/705a7169ce9743a08a88826a7f740ae80c660b396610a146acd6d3eceae09d0d5629e45ffd11ee8ed82b405e8648df705f2e0898b9a8c6f28cf1bb7618880549 -libblastrampoline.v5.9.0+0.aarch64-apple-darwin.tar.gz/md5/2d1fe8c420a126de2ee8328a77db23ab -libblastrampoline.v5.9.0+0.aarch64-apple-darwin.tar.gz/sha512/c91eead38796e3fa392287de9aef411617c2d13bb86d5f3849e24c0df71483595ad6495e983bdd17fb43a84765997b3f6ba6b08e4e43954faeb629de72c0cd8c -libblastrampoline.v5.9.0+0.aarch64-linux-gnu.tar.gz/md5/947a9b8ac0b4835a358d0b3837d227f2 -libblastrampoline.v5.9.0+0.aarch64-linux-gnu.tar.gz/sha512/b0d938dfb5c4314ec3625279a4e66283c983363ca7bd03e6626bef27d08d1566cc292b8043d30697f0f6e95299ea6be4b6b2f5acf55900767f622a9fe7f068d7 -libblastrampoline.v5.9.0+0.aarch64-linux-musl.tar.gz/md5/9c1482aaa2fa17c1bb30bcb1d26802e2 -libblastrampoline.v5.9.0+0.aarch64-linux-musl.tar.gz/sha512/c741d79a631550f7b8e5bf028f50551f3000515229306c88be6aa76de5aaf2b55bffd22c7f5f4015226a274c2f0cea8de6f7c3ea5010cad17ce242eb369c0336 -libblastrampoline.v5.9.0+0.armv6l-linux-gnueabihf.tar.gz/md5/f7b655a80c50848b15a7ab7dced9aede -libblastrampoline.v5.9.0+0.armv6l-linux-gnueabihf.tar.gz/sha512/29cc68195c3a10edd5965327d0c6a0ccb1ec818fdd8b57adeaa582cb0537eeabe0c019e55e048c2859292fa1532d6b6edeeb46f6adda97bae758305c9f99492e -libblastrampoline.v5.9.0+0.armv6l-linux-musleabihf.tar.gz/md5/0f3e0632cd4537ba5688912efd0f6a44 -libblastrampoline.v5.9.0+0.armv6l-linux-musleabihf.tar.gz/sha512/1446ee28607414169d24cfc965439380ef9de925b1327018c38d9aa981e6df6430b9e216bf489693c6e12b8b7baccfbfe5001884bbe07e76edd416157615c4a7 -libblastrampoline.v5.9.0+0.armv7l-linux-gnueabihf.tar.gz/md5/19a11466b7664e60028e24b39e79106b -libblastrampoline.v5.9.0+0.armv7l-linux-gnueabihf.tar.gz/sha512/01894533621c155e80b436251eab1185e6e38ec44d68154f24bd535fbeca2d9d5a9180f657f92da652db080526a4659a48282030e40e1d7b9e962a7b5643df5c -libblastrampoline.v5.9.0+0.armv7l-linux-musleabihf.tar.gz/md5/749654c9e8f333f2237649f6e5dc094d -libblastrampoline.v5.9.0+0.armv7l-linux-musleabihf.tar.gz/sha512/ee3f78d11c60c4c25297094ed8cce70baf830f78efd2a04be94398b5db457061cf8b443ca0f9508028c991bd927cb3aa076688e32b60a5079ad7b36a1699def6 -libblastrampoline.v5.9.0+0.i686-linux-gnu.tar.gz/md5/263a4f8bd1f269ef7f8f0d86a2ad88e9 -libblastrampoline.v5.9.0+0.i686-linux-gnu.tar.gz/sha512/74b49e2208bfdacfa9f51d8e7de29ec0de431009937e46f9bc6c4f24536eb39d54ed090ad120f5861ff9ccdeb97f3fce2df36bd203e454dd6b9f47083baadac4 -libblastrampoline.v5.9.0+0.i686-linux-musl.tar.gz/md5/38d066c1dd09eea0c922d6b695b39f8f -libblastrampoline.v5.9.0+0.i686-linux-musl.tar.gz/sha512/7c61739608e4bf5e6a08764efb1c2a8ea98ddcd54eb63bf5de627de208bcc3a69ce95bc7ae81722d2414fb33750eff47748fab93a903cc12f2eab1cff18d1df1 -libblastrampoline.v5.9.0+0.i686-w64-mingw32.tar.gz/md5/fd9678da295f74e49a519bb27cdf5ac3 -libblastrampoline.v5.9.0+0.i686-w64-mingw32.tar.gz/sha512/406f434a5595662cc0720818399cdc7b2377bea7053bc3791530fc789f34209d86988136820b304c9321d0d3cd9ac05f4f727683470be377ecbd3b33c812c451 -libblastrampoline.v5.9.0+0.powerpc64le-linux-gnu.tar.gz/md5/495cb39451d65b0d8cf8aa811d012b40 -libblastrampoline.v5.9.0+0.powerpc64le-linux-gnu.tar.gz/sha512/b465b4a41f19aeda0cc1298294e1f1b6bf21cb078d97d2c3df2618986fb65f3928608cdfcc762bb1d16c43755cd59e275912aaf528b643f137a240cd62ec825f -libblastrampoline.v5.9.0+0.x86_64-apple-darwin.tar.gz/md5/fa245d4f7aba107de9985d98813663d6 -libblastrampoline.v5.9.0+0.x86_64-apple-darwin.tar.gz/sha512/551a52a81460f494ff50c7b83a2aa73b788d0fc75fa25f99c2b694a9ec1eb69bdb9535bbd3710cdee8ddbe6a3369d6e7b3376e778f5bad5cde17439eb163d752 -libblastrampoline.v5.9.0+0.x86_64-linux-gnu.tar.gz/md5/e2a24428d271df5310e01b56aed41584 -libblastrampoline.v5.9.0+0.x86_64-linux-gnu.tar.gz/sha512/d38227a5d57f190be5c06d0d37d085256a48f86daf442496a33dec16fb1123c7626b90735932a597bd3cbaea9980d299a019b738618f7ac8e9dfc2e0274255f7 -libblastrampoline.v5.9.0+0.x86_64-linux-musl.tar.gz/md5/364c860a456b8700dd83056b76acdd63 -libblastrampoline.v5.9.0+0.x86_64-linux-musl.tar.gz/sha512/1716f805e24e660b4d831b59a0a15bde9e94f8660d797a729668f12520c777d9fbdbc8099d5851f89ffc0d6f69ddb097a00e8037dc061fb6bbb91d7463885aa4 -libblastrampoline.v5.9.0+0.x86_64-unknown-freebsd.tar.gz/md5/2c39cb711012e39f8b82de94beab5e2c -libblastrampoline.v5.9.0+0.x86_64-unknown-freebsd.tar.gz/sha512/748e119c12a440c50a49f0d471b61a32b84c9398192c17fbfa545d7d9d7e9cda68289a128f50b75499c752678c5f9dfb9bf8e5bf8394707d70eb26781f0e91f1 -libblastrampoline.v5.9.0+0.x86_64-w64-mingw32.tar.gz/md5/8c093a1273ce747d8073624a94e1d92b -libblastrampoline.v5.9.0+0.x86_64-w64-mingw32.tar.gz/sha512/98f14398581fcb14b5bc271033b070c07e03b771c5bdec80556d373a6197695a912a510e404bdb9e9be0d1e28df32217917dd2a3b50fe6dd68d422de142a0cc5 +blastrampoline-81316155d4838392e8462a92bcac3eebe9acd0c7.tar.gz/md5/0478361eac783b99002b1ad985182f05 +blastrampoline-81316155d4838392e8462a92bcac3eebe9acd0c7.tar.gz/sha512/2489ce5770a9861889a2d07e61440ba4f233a92efd4a3544747f83320e0e7a229a8fe01553d99f5f1d98713316f2506daf0adb7d024a46e32b3de1bb2966d637 +libblastrampoline.v5.8.0+1.aarch64-apple-darwin.tar.gz/md5/a28837b9838fef2b3831de3278ec7949 +libblastrampoline.v5.8.0+1.aarch64-apple-darwin.tar.gz/sha512/111ac2fe5f8f8102f2f7c9e9e6aa1d1a12d2db941238c949ff8e64b30335e8b2f6ecce0d5f577879c231eb839c06e259302b709f3d34e94a97047bfa984222f6 +libblastrampoline.v5.8.0+1.aarch64-linux-gnu.tar.gz/md5/9e781a026e03118df81347fb90f10d45 +libblastrampoline.v5.8.0+1.aarch64-linux-gnu.tar.gz/sha512/89469f32a666efd46437351a8fb16758c35e5aecc563d202b480c10ddf9fa5350a5a321076b79b0a1a07ec2cea0b73aa5c28979cc382a198fa96cca0b5899d25 +libblastrampoline.v5.8.0+1.aarch64-linux-musl.tar.gz/md5/b7acda2fdd157bbb183d0dd33643beef +libblastrampoline.v5.8.0+1.aarch64-linux-musl.tar.gz/sha512/cf4125a47334fe2ec0d5a4b11624b12e1366ec031500218f680ad5a53152b9d752c0c02a0b92d0e07f3eb21f2f8f58d0c587438a4869a72197bbd5e91531369d +libblastrampoline.v5.8.0+1.armv6l-linux-gnueabihf.tar.gz/md5/eafabd99fb1287d495acb8efb8091fde +libblastrampoline.v5.8.0+1.armv6l-linux-gnueabihf.tar.gz/sha512/63ff4e6bc400fa8ee713a1c5ae4af0a8e152d49860c6f5e94a17e426ad9f780d41cc0f84d33c75ea5347af1a53f07fc012798d603b6a94ea39f37cfd651a0719 +libblastrampoline.v5.8.0+1.armv6l-linux-musleabihf.tar.gz/md5/9788f74b375ef6b84c16c080f2be5bdd +libblastrampoline.v5.8.0+1.armv6l-linux-musleabihf.tar.gz/sha512/f00ebf794927404e2294a2fbb759b1e3e57836c7f683525fac0b2ac570da2c75904e43f154cf76fce310a624f9b35fbd40e6c7757882bb6f30db790f4221a543 +libblastrampoline.v5.8.0+1.armv7l-linux-gnueabihf.tar.gz/md5/4492bace63d8274d68ecdaa735e47e99 +libblastrampoline.v5.8.0+1.armv7l-linux-gnueabihf.tar.gz/sha512/8868283e6c5224b80145fdfd17f13f713053ba94e49c170f38f0cbf9f794185d7dec9c107ce65dc76121d3ac5b21d2f3857f619d8279bede86a906230ff59a71 +libblastrampoline.v5.8.0+1.armv7l-linux-musleabihf.tar.gz/md5/d66b6ed1d4e5f6a130f36791063e651d +libblastrampoline.v5.8.0+1.armv7l-linux-musleabihf.tar.gz/sha512/414ad07574a6e9aa670bbfea13eaea11da13129c9ccb4193cad708014c31493ff10ff427558b90cb16040fa64c8a325c2e375e3310c39fb37bb3e7fdb6a72a5f +libblastrampoline.v5.8.0+1.i686-linux-gnu.tar.gz/md5/595199a3a01174cfa4d9ce3407bf30dc +libblastrampoline.v5.8.0+1.i686-linux-gnu.tar.gz/sha512/02c3b0c3c0a411d5090a081f3bbbe38aaae40eaa5fe63d0690e0582e233cd9ce76483922557d4f65dc457e29a4e84d86ee5af20a60b082aec7bec4ca8607c1ca +libblastrampoline.v5.8.0+1.i686-linux-musl.tar.gz/md5/5832d0044842cb84f4e1e1b0a04b8205 +libblastrampoline.v5.8.0+1.i686-linux-musl.tar.gz/sha512/d28954d0feef6a33fa0bfeb59acb68821222d36a4e353eaf41936ee2c9aace719c2d0f0b0f080eafe2baecc67a29de4cacc0446aac776bbb615c4426d35c9c8f +libblastrampoline.v5.8.0+1.i686-w64-mingw32.tar.gz/md5/46391ac222980a0ad2c2d6d2b54db26d +libblastrampoline.v5.8.0+1.i686-w64-mingw32.tar.gz/sha512/6dd3434648a297639ef327efa5827d6aea70df551774e52ba395cdf187bfb603d365eed84780913fda5f3d12512ac54ccf784da3cf6317671ab346211f5984b3 +libblastrampoline.v5.8.0+1.powerpc64le-linux-gnu.tar.gz/md5/5f76f5c6a88c0caaa6419ba212f8cb94 +libblastrampoline.v5.8.0+1.powerpc64le-linux-gnu.tar.gz/sha512/785071e682075b2cebd992394e66169f4ee2db3a8e23affb88dc05d9abf55f49d597b2a7400a13c83ad106ad825b5ee666b01f8625e51aec267132573273991e +libblastrampoline.v5.8.0+1.x86_64-apple-darwin.tar.gz/md5/21beb51d448bd22e4608a16b3f4fde05 +libblastrampoline.v5.8.0+1.x86_64-apple-darwin.tar.gz/sha512/620ba64d93ef416e483f813617aa313957282d8361f920b5444702fa911ff0051d1f8a8814b5fa0b082fd4dc77d96cb8b763937c786959bbc97cbb6131617152 +libblastrampoline.v5.8.0+1.x86_64-linux-gnu.tar.gz/md5/14c1045ba4d400f490ddea5343a46f04 +libblastrampoline.v5.8.0+1.x86_64-linux-gnu.tar.gz/sha512/0fdae83f4df93b28951521cf426736367f568c1e76fb68eea42b045cc9a288b6836abb3206a6d61e4f88adcf198553e911c45231aecb0f552e06de28eb3bec54 +libblastrampoline.v5.8.0+1.x86_64-linux-musl.tar.gz/md5/59b110676fcb2fcfdcf670a5d435d555 +libblastrampoline.v5.8.0+1.x86_64-linux-musl.tar.gz/sha512/57a5022e9fabc0637a29f3c32f6180cb4f6a90282191232e299df6cea5265b535e4a0af4fde15c8fe80e5a59edea0fae96dd3a510f5720ecd78e85a2a9ffbfe0 +libblastrampoline.v5.8.0+1.x86_64-unknown-freebsd.tar.gz/md5/c22da112cfc7f9fa0f103d08f4b78965 +libblastrampoline.v5.8.0+1.x86_64-unknown-freebsd.tar.gz/sha512/ace02fac0dc6df472456007a081e0aaa85a6b17290321fb214349aac0f2d0f893df602dca28fc26ddfd4ed574fd9063bacff343249e5a1109f5d92dc9cb7a1d3 +libblastrampoline.v5.8.0+1.x86_64-w64-mingw32.tar.gz/md5/34fdc53745245887f968f420b2f02ed9 +libblastrampoline.v5.8.0+1.x86_64-w64-mingw32.tar.gz/sha512/bbf478736b7bd57b340ccd5b6744d526a7a95fc524d30fdf9af6e9d79285641be26fae5f9e5302d71a5be76b05c379e969a829e259d8100ba9c6ce202b632b3d diff --git a/stdlib/libblastrampoline_jll/Project.toml b/stdlib/libblastrampoline_jll/Project.toml index f33f89ff07802..6ee704bc7e1c4 100644 --- a/stdlib/libblastrampoline_jll/Project.toml +++ b/stdlib/libblastrampoline_jll/Project.toml @@ -1,13 +1,13 @@ name = "libblastrampoline_jll" uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" -version = "5.9.0+0" +version = "5.8.0+1" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" Artifacts = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" [compat] -julia = "1.12" +julia = "1.10" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" From a88c6389e2a739406c9ec2f49eee142a3fe0745a Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Tue, 18 Jun 2024 07:40:20 -0300 Subject: [PATCH 68/71] Do not add type tag size to the `alloc_typed` lowering for GC allocations (#54837) Enzyme.jl hit an issue where, in a dynamically typed allocation of size `GC_MAX_SZCLASS`, because we mistakenly added they type tag size to the allocation, the runtime disagreed if this was a pool allocation or a big allocation. Causing a crash in the GC (cherry picked from commit ded0b28f7f94c2785a6330786fe5efea2a440c86) --- src/julia_internal.h | 2 ++ src/llvm-final-gc-lowering.cpp | 2 +- test/llvmpasses/final-lower-gc.ll | 5 ++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/julia_internal.h b/src/julia_internal.h index 81888f01cc95e..4050410a1b1b2 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -508,6 +508,8 @@ STATIC_INLINE uint8_t JL_CONST_FUNC jl_gc_szclass_align8(unsigned sz) JL_NOTSAFE #define GC_MAX_SZCLASS (2032-sizeof(void*)) static_assert(ARRAY_CACHE_ALIGN_THRESHOLD > GC_MAX_SZCLASS, ""); + +// Size does NOT include the type tag!! STATIC_INLINE jl_value_t *jl_gc_alloc_(jl_ptls_t ptls, size_t sz, void *ty) { jl_value_t *v; diff --git a/src/llvm-final-gc-lowering.cpp b/src/llvm-final-gc-lowering.cpp index 5a53ce4d8e510..7349d5b6de927 100644 --- a/src/llvm-final-gc-lowering.cpp +++ b/src/llvm-final-gc-lowering.cpp @@ -211,7 +211,7 @@ void FinalLowerGC::lowerGCAllocBytes(CallInst *target, Function &F) } } else { auto size = builder.CreateZExtOrTrunc(target->getArgOperand(1), T_size); - size = builder.CreateAdd(size, ConstantInt::get(T_size, sizeof(void*))); + // allocTypedFunc does not include the type tag in the allocation size! newI = builder.CreateCall(allocTypedFunc, { ptls, size, type }); derefBytes = sizeof(void*); } diff --git a/test/llvmpasses/final-lower-gc.ll b/test/llvmpasses/final-lower-gc.ll index eb3b68662c2b4..3975edf0bc492 100644 --- a/test/llvmpasses/final-lower-gc.ll +++ b/test/llvmpasses/final-lower-gc.ll @@ -93,9 +93,8 @@ top: %pgcstack = call {}*** @julia.get_pgcstack() %ptls = call {}*** @julia.ptls_states() %ptls_i8 = bitcast {}*** %ptls to i8* -; CHECK: %0 = add i64 %size, 8 -; TYPED: %v = call noalias nonnull align {{[0-9]+}} dereferenceable(8) {} addrspace(10)* @ijl_gc_alloc_typed(i8* %ptls_i8, i64 %0, i64 12341234) -; OPAQUE: %v = call noalias nonnull align {{[0-9]+}} dereferenceable(8) ptr addrspace(10) @ijl_gc_alloc_typed(ptr %ptls_i8, i64 %0, i64 12341234) +; TYPED: %v = call noalias nonnull align {{[0-9]+}} dereferenceable(8) {} addrspace(10)* @ijl_gc_alloc_typed(i8* %ptls_i8, i64 %size, i64 12341234) +; OPAQUE: %v = call noalias nonnull align {{[0-9]+}} dereferenceable(8) ptr addrspace(10) @ijl_gc_alloc_typed(ptr %ptls_i8, i64 %size, i64 12341234) %v = call {} addrspace(10)* @julia.gc_alloc_bytes(i8* %ptls_i8, i64 %size, i64 12341234) %0 = bitcast {} addrspace(10)* %v to {} addrspace(10)* addrspace(10)* %1 = getelementptr {} addrspace(10)*, {} addrspace(10)* addrspace(10)* %0, i64 -1 From 9cf8b19c3670a196ca1a0dce77b198c0cf84364e Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Mon, 17 Jun 2024 22:55:18 -0400 Subject: [PATCH 69/71] Add openlibm to sysimg link line on windows (#53672) LLVM generates calls to math intrinsics like `trunc` and `rint` (at least in my local i686 mingw) build, so linking to openlibm is required. We already have this on the sysimg link line in `Base.link_image`, so this aligns those options. --------- Co-authored-by: Elliot Saba (cherry picked from commit 320366bbc00331671b38f7aba15812ffabcf1fcb) --- sysimage.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysimage.mk b/sysimage.mk index 6aa972ee1ea0a..96f26b071a997 100644 --- a/sysimage.mk +++ b/sysimage.mk @@ -17,7 +17,7 @@ $(build_private_libdir)/%.$(SHLIB_EXT): $(build_private_libdir)/%-o.a @$(call PRINT_LINK, $(CXX) $(LDFLAGS) -shared $(fPIC) -L$(build_private_libdir) -L$(build_libdir) -L$(build_shlibdir) -o $@ \ $(WHOLE_ARCHIVE) $< $(NO_WHOLE_ARCHIVE) \ $(if $(findstring -debug,$(notdir $@)),-ljulia-internal-debug -ljulia-debug,-ljulia-internal -ljulia) \ - $$([ $(OS) = WINNT ] && echo '' -lssp --disable-auto-import --disable-runtime-pseudo-reloc)) + $$([ $(OS) = WINNT ] && echo '' $(LIBM) -lssp --disable-auto-import --disable-runtime-pseudo-reloc)) @$(INSTALL_NAME_CMD)$(notdir $@) $@ @$(DSYMUTIL) $@ From 0db324cae174f17165c72a4ac548f8fc284e3025 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Sat, 22 Jun 2024 23:04:38 -0400 Subject: [PATCH 70/71] mark failing double counting test as broken on i686-linux (#54896) Introduced by https://github.com/JuliaLang/julia/pull/54606 See https://github.com/JuliaLang/julia/pull/54606#issuecomment-2183664446 Issue https://github.com/JuliaLang/julia/issues/54895 (cherry picked from commit 5da1f060c1a35992e45f3699d33d208593d84b86) --- test/misc.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/misc.jl b/test/misc.jl index f5034e012765d..d3c583faf8520 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -585,7 +585,8 @@ struct Z53061 end let z = Z53061[Z53061(S53061(rand(), (rand(),rand())), 0) for _ in 1:10^4] @test allequal(summarysize(z) for i in 1:10) - @test abs(summarysize(z) - 640000)/640000 <= 0.01 + # broken on i868 linux. issue #54895 + @test abs(summarysize(z) - 640000)/640000 <= 0.01 broken = Sys.WORD_SIZE == 32 && Sys.islinux() end ## test conversion from UTF-8 to UTF-16 (for Windows APIs) From c644312c0fad9f0bd33d5ad2662f60ca09ce0683 Mon Sep 17 00:00:00 2001 From: Zentrik Date: Mon, 1 Apr 2024 19:13:18 +0100 Subject: [PATCH 71/71] Fix calling LLVM_SIZE on windows (#53902) Per https://github.com/JuliaCI/julia-buildkite/pull/224#issuecomment-1474914609, the path needs to be updated so that `llvm-size` can find `libLLVM.dll`. (cherry picked from commit 657ce0482d54f9658c9cf4fd3560ca51aafdb03b) --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 2d4116147693d..6fbbe194aa615 100644 --- a/Makefile +++ b/Makefile @@ -657,7 +657,7 @@ win-extras: ifeq ($(USE_SYSTEM_LLVM), 1) LLVM_SIZE := llvm-size$(EXE) else -LLVM_SIZE := $(build_depsbindir)/llvm-size$(EXE) +LLVM_SIZE := PATH=$(build_bindir):$$PATH; $(build_depsbindir)/llvm-size$(EXE) endif build-stats: ifeq ($(USE_BINARYBUILDER_LLVM),1)